@superblocksteam/sdk 2.0.131-next.1 → 2.0.132-next.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.
@@ -115,7 +115,6 @@ vi.mock("@superblocksteam/util", () => ({
115
115
  vi.mock("@superblocksteam/vite-plugin-file-sync/ai-service", () => ({
116
116
  AiService: class {
117
117
  initialize = vi.fn(async () => undefined);
118
- notifyMigrationAppArtifactsReady = vi.fn();
119
118
  removeIntegrationCache = vi.fn(async () => undefined);
120
119
  chatSessionStore = { invalidateCache: vi.fn() };
121
120
  // `dev.mts` reads this at startup for two paths: `syncHomeNpmrc`
@@ -655,7 +654,7 @@ describe("dev startup after S3 workspace restore", () => {
655
654
  }
656
655
  });
657
656
 
658
- it("keeps forced npm install when unchanged restore inputs still need private registry validation", async () => {
657
+ it("keeps forced npm install when unchanged restore inputs need private registry validation without a stamp", async () => {
659
658
  mockAiServiceState.npmRegistryClient = {
660
659
  getConfig: vi.fn(async () => ({
661
660
  source: "configured",
@@ -680,47 +679,450 @@ describe("dev startup after S3 workspace restore", () => {
680
679
  expect(mockLogger.info).toHaveBeenCalledWith(
681
680
  "Package install decision",
682
681
  expect.objectContaining({
683
- forcePackageInstall: true,
682
+ packageInstallRequired: true,
683
+ forcePackageInstall: false,
684
684
  forcePackageInstallRequested: true,
685
685
  packageJsonRequiresInstall: false,
686
+ privateRegistryRequiresInstallValidation: true,
686
687
  }),
687
688
  );
688
689
  });
689
690
 
690
- it("runs npm install at startup when private registries are configured even without package changes", async () => {
691
+ it("skips private registry validation install when lockfile already resolves through the configured registry", async () => {
692
+ const tmpDir = await fs.mkdtemp(
693
+ path.join(os.tmpdir(), "sdk-dev-private-registry-"),
694
+ );
691
695
  mockAiServiceState.npmRegistryClient = {
692
696
  getConfig: vi.fn(async () => ({
693
697
  source: "configured",
694
- config: { configured: true },
698
+ config: {
699
+ configured: true,
700
+ default: { url: "https://registry.example.com/npm/" },
701
+ },
702
+ })),
703
+ };
704
+ const unchangedPackageJson = {
705
+ name: "test-app",
706
+ dependencies: { leftpad: "1.0.0" },
707
+ };
708
+ const privateRegistryLockfile = {
709
+ name: "test-app",
710
+ lockfileVersion: 3,
711
+ packages: {
712
+ "node_modules/leftpad": {
713
+ version: "1.0.0",
714
+ resolved:
715
+ "https://registry.example.com/npm/leftpad/-/leftpad-1.0.0.tgz",
716
+ },
717
+ },
718
+ };
719
+
720
+ try {
721
+ await fs.writeFile(
722
+ path.join(tmpDir, "package.json"),
723
+ JSON.stringify(unchangedPackageJson, null, 2),
724
+ );
725
+ await fs.writeFile(
726
+ path.join(tmpDir, "package-lock.json"),
727
+ JSON.stringify(privateRegistryLockfile, null, 2),
728
+ );
729
+ await fs.mkdir(path.join(tmpDir, "node_modules"));
730
+ await fs.writeFile(
731
+ path.join(tmpDir, "node_modules", ".package-lock.json"),
732
+ JSON.stringify(privateRegistryLockfile, null, 2),
733
+ );
734
+ const { readPackage } = await import("read-pkg");
735
+ (readPackage as Mock).mockImplementation(async ({ cwd }) =>
736
+ JSON.parse(await fs.readFile(path.join(cwd, "package.json"), "utf-8")),
737
+ );
738
+ const { dev } = await import("./dev.mjs");
739
+
740
+ await dev(
741
+ buildDevOptions({
742
+ cwd: tmpDir,
743
+ forcePackageInstall: false,
744
+ packageJsonSnapshotBeforeRestore:
745
+ packageJsonSnapshot(unchangedPackageJson),
746
+ }) as any,
747
+ );
748
+
749
+ expect(execMock).not.toHaveBeenCalled();
750
+ expect(mockLogger.info).toHaveBeenCalledWith(
751
+ "Package install decision",
752
+ expect.objectContaining({
753
+ packageInstallRequired: false,
754
+ forcePackageInstall: false,
755
+ forcePackageInstallRequested: false,
756
+ packageJsonRequiresInstall: false,
757
+ privateRegistryRequiresInstallValidation: true,
758
+ lockfileAlreadyResolvesThroughRegistry: true,
759
+ }),
760
+ );
761
+ } finally {
762
+ await fs.rm(tmpDir, { recursive: true, force: true });
763
+ }
764
+ });
765
+
766
+ it("skips private registry validation install when scoped lockfile already resolves through the scoped registry", async () => {
767
+ const tmpDir = await fs.mkdtemp(
768
+ path.join(os.tmpdir(), "sdk-dev-private-registry-"),
769
+ );
770
+ mockAiServiceState.npmRegistryClient = {
771
+ getConfig: vi.fn(async () => ({
772
+ source: "configured",
773
+ config: {
774
+ configured: true,
775
+ scopes: {
776
+ "@superblocksteam": {
777
+ url: "https://scoped-registry.example.com/npm/",
778
+ },
779
+ },
780
+ },
695
781
  })),
696
782
  };
697
783
  const unchangedPackageJson = {
698
784
  name: "test-app",
699
785
  dependencies: { "@superblocksteam/library": "1.0.0" },
700
786
  };
701
- const { dev } = await import("./dev.mjs");
787
+ const privateRegistryLockfile = {
788
+ name: "test-app",
789
+ lockfileVersion: 3,
790
+ packages: {
791
+ "node_modules/@superblocksteam/library": {
792
+ version: "1.0.0",
793
+ resolved:
794
+ "https://scoped-registry.example.com/npm/@superblocksteam/library/-/library-1.0.0.tgz",
795
+ },
796
+ },
797
+ };
702
798
 
703
- await dev(
704
- buildDevOptions({
705
- forcePackageInstall: false,
706
- packageJsonSnapshotBeforeRestore:
707
- packageJsonSnapshot(unchangedPackageJson),
708
- }) as any,
799
+ try {
800
+ await fs.writeFile(
801
+ path.join(tmpDir, "package.json"),
802
+ JSON.stringify(unchangedPackageJson, null, 2),
803
+ );
804
+ await fs.writeFile(
805
+ path.join(tmpDir, "package-lock.json"),
806
+ JSON.stringify(privateRegistryLockfile, null, 2),
807
+ );
808
+ await fs.mkdir(path.join(tmpDir, "node_modules"));
809
+ await fs.writeFile(
810
+ path.join(tmpDir, "node_modules", ".package-lock.json"),
811
+ JSON.stringify(privateRegistryLockfile, null, 2),
812
+ );
813
+ const { readPackage } = await import("read-pkg");
814
+ (readPackage as Mock).mockImplementation(async ({ cwd }) =>
815
+ JSON.parse(await fs.readFile(path.join(cwd, "package.json"), "utf-8")),
816
+ );
817
+ const { dev } = await import("./dev.mjs");
818
+
819
+ await dev(
820
+ buildDevOptions({
821
+ cwd: tmpDir,
822
+ forcePackageInstall: false,
823
+ packageJsonSnapshotBeforeRestore:
824
+ packageJsonSnapshot(unchangedPackageJson),
825
+ }) as any,
826
+ );
827
+
828
+ expect(execMock).not.toHaveBeenCalled();
829
+ expect(mockLogger.info).toHaveBeenCalledWith(
830
+ "Package install decision",
831
+ expect.objectContaining({
832
+ packageInstallRequired: false,
833
+ privateRegistryRequiresInstallValidation: true,
834
+ lockfileAlreadyResolvesThroughRegistry: true,
835
+ }),
836
+ );
837
+ } finally {
838
+ await fs.rm(tmpDir, { recursive: true, force: true });
839
+ }
840
+ });
841
+
842
+ it("skips private registry validation install when scoped-only config has public unscoped dependencies", async () => {
843
+ const tmpDir = await fs.mkdtemp(
844
+ path.join(os.tmpdir(), "sdk-dev-private-registry-"),
709
845
  );
846
+ mockAiServiceState.npmRegistryClient = {
847
+ getConfig: vi.fn(async () => ({
848
+ source: "configured",
849
+ config: {
850
+ configured: true,
851
+ scopes: {
852
+ "@superblocksteam": {
853
+ url: "https://scoped-registry.example.com/npm/",
854
+ },
855
+ },
856
+ },
857
+ })),
858
+ };
859
+ const unchangedPackageJson = {
860
+ name: "test-app",
861
+ dependencies: {
862
+ "@superblocksteam/library": "1.0.0",
863
+ leftpad: "1.0.0",
864
+ },
865
+ };
866
+ const mixedLockfile = {
867
+ name: "test-app",
868
+ lockfileVersion: 3,
869
+ packages: {
870
+ "node_modules/@superblocksteam/library": {
871
+ version: "1.0.0",
872
+ resolved:
873
+ "https://scoped-registry.example.com/npm/@superblocksteam/library/-/library-1.0.0.tgz",
874
+ },
875
+ "node_modules/leftpad": {
876
+ version: "1.0.0",
877
+ resolved: "https://registry.npmjs.org/leftpad/-/leftpad-1.0.0.tgz",
878
+ },
879
+ },
880
+ };
710
881
 
711
- expectInstallToHaveRun();
712
- expect(mockLogger.info).toHaveBeenCalledWith(
713
- "Package install decision",
714
- expect.objectContaining({
715
- forcePackageInstall: false,
716
- forcePackageInstallRequested: false,
717
- packageJsonRequiresInstall: false,
718
- privateRegistryRequiresInstallValidation: true,
719
- }),
882
+ try {
883
+ await fs.writeFile(
884
+ path.join(tmpDir, "package.json"),
885
+ JSON.stringify(unchangedPackageJson, null, 2),
886
+ );
887
+ await fs.writeFile(
888
+ path.join(tmpDir, "package-lock.json"),
889
+ JSON.stringify(mixedLockfile, null, 2),
890
+ );
891
+ await fs.mkdir(path.join(tmpDir, "node_modules"));
892
+ await fs.writeFile(
893
+ path.join(tmpDir, "node_modules", ".package-lock.json"),
894
+ JSON.stringify(mixedLockfile, null, 2),
895
+ );
896
+ const { readPackage } = await import("read-pkg");
897
+ (readPackage as Mock).mockImplementation(async ({ cwd }) =>
898
+ JSON.parse(await fs.readFile(path.join(cwd, "package.json"), "utf-8")),
899
+ );
900
+ const { dev } = await import("./dev.mjs");
901
+
902
+ await dev(
903
+ buildDevOptions({
904
+ cwd: tmpDir,
905
+ forcePackageInstall: false,
906
+ packageJsonSnapshotBeforeRestore:
907
+ packageJsonSnapshot(unchangedPackageJson),
908
+ }) as any,
909
+ );
910
+
911
+ expect(execMock).not.toHaveBeenCalled();
912
+ expect(mockLogger.info).toHaveBeenCalledWith(
913
+ "Package install decision",
914
+ expect.objectContaining({
915
+ packageInstallRequired: false,
916
+ privateRegistryRequiresInstallValidation: true,
917
+ lockfileAlreadyResolvesThroughRegistry: true,
918
+ }),
919
+ );
920
+ } finally {
921
+ await fs.rm(tmpDir, { recursive: true, force: true });
922
+ }
923
+ });
924
+
925
+ it("runs private registry validation install when scoped lockfile resolves outside the scoped registry", async () => {
926
+ const tmpDir = await fs.mkdtemp(
927
+ path.join(os.tmpdir(), "sdk-dev-private-registry-"),
720
928
  );
929
+ mockAiServiceState.npmRegistryClient = {
930
+ getConfig: vi.fn(async () => ({
931
+ source: "configured",
932
+ config: {
933
+ configured: true,
934
+ scopes: {
935
+ "@superblocksteam": {
936
+ url: "https://scoped-registry.example.com/npm/",
937
+ },
938
+ },
939
+ },
940
+ })),
941
+ };
942
+ const unchangedPackageJson = {
943
+ name: "test-app",
944
+ dependencies: { "@superblocksteam/library": "1.0.0" },
945
+ };
946
+ const publicRegistryLockfile = {
947
+ name: "test-app",
948
+ lockfileVersion: 3,
949
+ packages: {
950
+ "node_modules/@superblocksteam/library": {
951
+ version: "1.0.0",
952
+ resolved:
953
+ "https://registry.npmjs.org/@superblocksteam/library/-/library-1.0.0.tgz",
954
+ },
955
+ },
956
+ };
957
+
958
+ try {
959
+ await fs.writeFile(
960
+ path.join(tmpDir, "package.json"),
961
+ JSON.stringify(unchangedPackageJson, null, 2),
962
+ );
963
+ await fs.writeFile(
964
+ path.join(tmpDir, "package-lock.json"),
965
+ JSON.stringify(publicRegistryLockfile, null, 2),
966
+ );
967
+ await fs.mkdir(path.join(tmpDir, "node_modules"));
968
+ await fs.writeFile(
969
+ path.join(tmpDir, "node_modules", ".package-lock.json"),
970
+ JSON.stringify(publicRegistryLockfile, null, 2),
971
+ );
972
+ const { readPackage } = await import("read-pkg");
973
+ (readPackage as Mock).mockImplementation(async ({ cwd }) =>
974
+ JSON.parse(await fs.readFile(path.join(cwd, "package.json"), "utf-8")),
975
+ );
976
+ const { dev } = await import("./dev.mjs");
977
+
978
+ await dev(
979
+ buildDevOptions({
980
+ cwd: tmpDir,
981
+ forcePackageInstall: false,
982
+ packageJsonSnapshotBeforeRestore:
983
+ packageJsonSnapshot(unchangedPackageJson),
984
+ }) as any,
985
+ );
986
+
987
+ expectInstallToHaveRun();
988
+ expect(mockLogger.info).toHaveBeenCalledWith(
989
+ "Package install decision",
990
+ expect.objectContaining({
991
+ packageInstallRequired: true,
992
+ privateRegistryRequiresInstallValidation: true,
993
+ lockfileAlreadyResolvesThroughRegistry: false,
994
+ }),
995
+ );
996
+ } finally {
997
+ await fs.rm(tmpDir, { recursive: true, force: true });
998
+ }
721
999
  });
722
1000
 
723
- it("does not upload package state when private registry validation install leaves the lockfile unchanged", async () => {
1001
+ it("runs private registry validation install when node_modules lockfile is missing", async () => {
1002
+ const tmpDir = await fs.mkdtemp(
1003
+ path.join(os.tmpdir(), "sdk-dev-private-registry-"),
1004
+ );
1005
+ mockAiServiceState.npmRegistryClient = {
1006
+ getConfig: vi.fn(async () => ({
1007
+ source: "configured",
1008
+ config: {
1009
+ configured: true,
1010
+ default: { url: "https://registry.example.com/npm/" },
1011
+ },
1012
+ })),
1013
+ };
1014
+ const unchangedPackageJson = {
1015
+ name: "test-app",
1016
+ dependencies: { leftpad: "1.0.0" },
1017
+ };
1018
+ const privateRegistryLockfile = {
1019
+ name: "test-app",
1020
+ lockfileVersion: 3,
1021
+ packages: {
1022
+ "node_modules/leftpad": {
1023
+ version: "1.0.0",
1024
+ resolved:
1025
+ "https://registry.example.com/npm/leftpad/-/leftpad-1.0.0.tgz",
1026
+ },
1027
+ },
1028
+ };
1029
+
1030
+ try {
1031
+ await fs.writeFile(
1032
+ path.join(tmpDir, "package.json"),
1033
+ JSON.stringify(unchangedPackageJson, null, 2),
1034
+ );
1035
+ await fs.writeFile(
1036
+ path.join(tmpDir, "package-lock.json"),
1037
+ JSON.stringify(privateRegistryLockfile, null, 2),
1038
+ );
1039
+ const { readPackage } = await import("read-pkg");
1040
+ (readPackage as Mock).mockImplementation(async ({ cwd }) =>
1041
+ JSON.parse(await fs.readFile(path.join(cwd, "package.json"), "utf-8")),
1042
+ );
1043
+ const { dev } = await import("./dev.mjs");
1044
+
1045
+ await dev(
1046
+ buildDevOptions({
1047
+ cwd: tmpDir,
1048
+ forcePackageInstall: false,
1049
+ packageJsonSnapshotBeforeRestore:
1050
+ packageJsonSnapshot(unchangedPackageJson),
1051
+ }) as any,
1052
+ );
1053
+
1054
+ expectInstallToHaveRun();
1055
+ expect(mockLogger.info).toHaveBeenCalledWith(
1056
+ "Package install decision",
1057
+ expect.objectContaining({
1058
+ packageInstallRequired: true,
1059
+ forcePackageInstall: false,
1060
+ forcePackageInstallRequested: false,
1061
+ packageJsonRequiresInstall: false,
1062
+ privateRegistryRequiresInstallValidation: true,
1063
+ lockfileAlreadyResolvesThroughRegistry: false,
1064
+ }),
1065
+ );
1066
+ } finally {
1067
+ await fs.rm(tmpDir, { recursive: true, force: true });
1068
+ }
1069
+ });
1070
+
1071
+ it("runs private registry validation install and warns when lockfile registry proof is malformed", async () => {
1072
+ const tmpDir = await fs.mkdtemp(
1073
+ path.join(os.tmpdir(), "sdk-dev-private-registry-"),
1074
+ );
1075
+ mockAiServiceState.npmRegistryClient = {
1076
+ getConfig: vi.fn(async () => ({
1077
+ source: "configured",
1078
+ config: {
1079
+ configured: true,
1080
+ default: { url: "https://registry.example.com/npm/" },
1081
+ },
1082
+ })),
1083
+ };
1084
+ const unchangedPackageJson = {
1085
+ name: "test-app",
1086
+ dependencies: { leftpad: "1.0.0" },
1087
+ };
1088
+
1089
+ try {
1090
+ await fs.writeFile(
1091
+ path.join(tmpDir, "package.json"),
1092
+ JSON.stringify(unchangedPackageJson, null, 2),
1093
+ );
1094
+ await fs.writeFile(path.join(tmpDir, "package-lock.json"), "{");
1095
+ await fs.mkdir(path.join(tmpDir, "node_modules"));
1096
+ await fs.writeFile(
1097
+ path.join(tmpDir, "node_modules", ".package-lock.json"),
1098
+ "{",
1099
+ );
1100
+ const { readPackage } = await import("read-pkg");
1101
+ (readPackage as Mock).mockImplementation(async ({ cwd }) =>
1102
+ JSON.parse(await fs.readFile(path.join(cwd, "package.json"), "utf-8")),
1103
+ );
1104
+ const { dev } = await import("./dev.mjs");
1105
+
1106
+ await dev(
1107
+ buildDevOptions({
1108
+ cwd: tmpDir,
1109
+ forcePackageInstall: false,
1110
+ packageJsonSnapshotBeforeRestore:
1111
+ packageJsonSnapshot(unchangedPackageJson),
1112
+ }) as any,
1113
+ );
1114
+
1115
+ expectInstallToHaveRun();
1116
+ expect(mockLogger.warn).toHaveBeenCalledWith(
1117
+ "Failed to parse package-lock.json for private registry skip check; falling back to install",
1118
+ expect.any(Object),
1119
+ );
1120
+ } finally {
1121
+ await fs.rm(tmpDir, { recursive: true, force: true });
1122
+ }
1123
+ });
1124
+
1125
+ it("runs npm install when private registry validation is the only trigger without registry-resolved lockfile entries", async () => {
724
1126
  const tmpDir = await fs.mkdtemp(
725
1127
  path.join(os.tmpdir(), "sdk-dev-private-registry-"),
726
1128
  );
@@ -747,7 +1149,10 @@ describe("dev startup after S3 workspace restore", () => {
747
1149
  mockAiServiceState.npmRegistryClient = {
748
1150
  getConfig: vi.fn(async () => ({
749
1151
  source: "configured",
750
- config: { configured: true },
1152
+ config: {
1153
+ configured: true,
1154
+ default: { url: "https://registry.example.com/" },
1155
+ },
751
1156
  })),
752
1157
  };
753
1158
  const { dev } = await import("./dev.mjs");
@@ -769,7 +1174,7 @@ describe("dev startup after S3 workspace restore", () => {
769
1174
  }
770
1175
  });
771
1176
 
772
- it("does NOT upload package state when the validation install only rewrites `resolved` URLs", async () => {
1177
+ it("does not upload package state when private registry validation only rewrites `resolved` URLs", async () => {
773
1178
  // The exact churn cycle flagged in review: `stripResolvedFromLockfile`
774
1179
  // removes every `resolved` URL before the install and npm writes them
775
1180
  // back, so a byte-level comparison would flag "changed" on EVERY
@@ -820,7 +1225,10 @@ describe("dev startup after S3 workspace restore", () => {
820
1225
  mockAiServiceState.npmRegistryClient = {
821
1226
  getConfig: vi.fn(async () => ({
822
1227
  source: "configured",
823
- config: { configured: true },
1228
+ config: {
1229
+ configured: true,
1230
+ default: { url: "https://registry.example.com/" },
1231
+ },
824
1232
  })),
825
1233
  };
826
1234
  execMock.mockImplementation(
@@ -837,6 +1245,12 @@ describe("dev startup after S3 workspace restore", () => {
837
1245
  path.join(cwd ?? tmpDir, "package-lock.json"),
838
1246
  JSON.stringify(rewrittenLockfile, null, 2),
839
1247
  )
1248
+ .then(() =>
1249
+ fs.writeFile(
1250
+ path.join(cwd ?? tmpDir, "node_modules", ".package-lock.json"),
1251
+ JSON.stringify(rewrittenLockfile, null, 2),
1252
+ ),
1253
+ )
840
1254
  .then(() => {
841
1255
  if (typeof opts === "function") {
842
1256
  opts(null, { stdout: "installed" });
@@ -860,15 +1274,34 @@ describe("dev startup after S3 workspace restore", () => {
860
1274
  expectInstallToHaveRun();
861
1275
  expect(mockSyncService.uploadDirectory).not.toHaveBeenCalled();
862
1276
  expect(mockSyncService.uploadDirectoryNowIfNeeded).not.toHaveBeenCalled();
1277
+
1278
+ execMock.mockClear();
1279
+ vi.resetModules();
1280
+ const { dev: devAfterRestart } = await import("./dev.mjs");
1281
+ await devAfterRestart(
1282
+ buildDevOptions({
1283
+ cwd: tmpDir,
1284
+ forcePackageInstall: false,
1285
+ packageJsonSnapshotBeforeRestore:
1286
+ packageJsonSnapshot(unchangedPackageJson),
1287
+ }) as any,
1288
+ );
1289
+
1290
+ expect(execMock).not.toHaveBeenCalled();
863
1291
  } finally {
864
1292
  await fs.rm(tmpDir, { recursive: true, force: true });
865
1293
  }
866
1294
  });
867
1295
 
868
- it("uploads package state when private registry validation install rewrites the lockfile", async () => {
1296
+ it("uploads package state when private registry validation rewrites the lockfile during a required install", async () => {
869
1297
  const tmpDir = await fs.mkdtemp(
870
1298
  path.join(os.tmpdir(), "sdk-dev-private-registry-"),
871
1299
  );
1300
+ const previousPackageJson = {
1301
+ name: "test-app",
1302
+ packageManager: "npm@10.0.0",
1303
+ dependencies: { "@superblocksteam/library": "0.9.0" },
1304
+ };
872
1305
  const unchangedPackageJson = {
873
1306
  name: "test-app",
874
1307
  packageManager: "npm@10.0.0",
@@ -929,7 +1362,7 @@ describe("dev startup after S3 workspace restore", () => {
929
1362
  cwd: tmpDir,
930
1363
  forcePackageInstall: false,
931
1364
  packageJsonSnapshotBeforeRestore:
932
- packageJsonSnapshot(unchangedPackageJson),
1365
+ packageJsonSnapshot(previousPackageJson),
933
1366
  }) as any,
934
1367
  );
935
1368
 
@@ -1242,9 +1675,13 @@ describe("dev background package install join semantics", () => {
1242
1675
  // catch wrongly swallowed the rejection. A rejecting upgrade promise
1243
1676
  // surfaces a generic Error (not InitialInstallFailed), so the local catch
1244
1677
  // must rethrow.
1678
+ const upgradeFailure = Promise.reject(
1679
+ new Error("simulated upgrade failure"),
1680
+ );
1681
+ upgradeFailure.catch(() => undefined);
1245
1682
  mockCheckVersions.mockResolvedValue({
1246
1683
  cliUpdated: true,
1247
- upgradePromises: [Promise.reject(new Error("simulated upgrade failure"))],
1684
+ upgradePromises: [upgradeFailure],
1248
1685
  });
1249
1686
 
1250
1687
  const originalExit = process.exit;
@@ -115,7 +115,6 @@ vi.mock("@superblocksteam/util", () => ({
115
115
  vi.mock("@superblocksteam/vite-plugin-file-sync/ai-service", () => ({
116
116
  AiService: class {
117
117
  initialize = vi.fn(async () => undefined);
118
- notifyMigrationAppArtifactsReady = vi.fn();
119
118
  removeIntegrationCache = vi.fn(async () => undefined);
120
119
  chatSessionStore = { invalidateCache: vi.fn() };
121
120
  getNpmRegistryClient = vi.fn(() => undefined);