@abtnode/core 1.16.11-beta-0ae58a71 → 1.16.11-beta-f74663ac

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 (53) hide show
  1. package/lib/api/node.js +1 -1
  2. package/lib/api/team.js +5 -5
  3. package/lib/blocklet/downloader/blocklet-downloader.js +34 -19
  4. package/lib/blocklet/downloader/bundle-downloader.js +2 -2
  5. package/lib/blocklet/hooks.js +1 -1
  6. package/lib/blocklet/manager/base.js +9 -9
  7. package/lib/blocklet/manager/disk.js +286 -172
  8. package/lib/blocklet/manager/helper/install-application-from-backup.js +1 -1
  9. package/lib/blocklet/manager/helper/install-application-from-general.js +18 -3
  10. package/lib/blocklet/manager/helper/install-component-from-dev.js +0 -1
  11. package/lib/blocklet/manager/helper/install-component-from-upload.js +8 -4
  12. package/lib/blocklet/manager/helper/install-component-from-url.js +4 -2
  13. package/lib/blocklet/manager/helper/upgrade-components.js +7 -7
  14. package/lib/blocklet/manager/pm2-events.js +3 -3
  15. package/lib/blocklet/migration.js +2 -2
  16. package/lib/blocklet/storage/backup/base.js +1 -0
  17. package/lib/blocklet/storage/backup/blocklet-extras.js +1 -1
  18. package/lib/blocklet/storage/backup/blocklet.js +1 -1
  19. package/lib/blocklet/storage/backup/disk.js +1 -0
  20. package/lib/blocklet/storage/restore/blocklet-extras.js +1 -1
  21. package/lib/blocklet/storage/restore/blocklet.js +1 -1
  22. package/lib/blocklet/storage/restore/disk.js +1 -1
  23. package/lib/blocklet/storage/restore/spaces.js +1 -1
  24. package/lib/cert.js +5 -5
  25. package/lib/crons/rotate-pm2-logs/index.js +1 -1
  26. package/lib/event.js +11 -1
  27. package/lib/index.js +8 -2
  28. package/lib/migrations/1.16.11-component-status.js +22 -0
  29. package/lib/migrations/index.js +3 -3
  30. package/lib/monitor/blocklet-runtime-monitor.js +7 -2
  31. package/lib/processes/updater.js +1 -1
  32. package/lib/router/helper.js +5 -5
  33. package/lib/states/audit-log.js +1 -1
  34. package/lib/states/backup.js +5 -4
  35. package/lib/states/blocklet-extras.js +11 -1
  36. package/lib/states/blocklet.js +46 -39
  37. package/lib/states/cache.js +1 -1
  38. package/lib/states/node.js +6 -2
  39. package/lib/states/notification.js +1 -1
  40. package/lib/states/routing-snapshot.js +1 -1
  41. package/lib/states/site.js +1 -1
  42. package/lib/states/user.js +12 -6
  43. package/lib/team/manager.js +6 -6
  44. package/lib/util/blocklet.js +130 -118
  45. package/lib/util/launcher.js +238 -0
  46. package/lib/util/reset-node.js +19 -16
  47. package/lib/util/rpc.js +1 -1
  48. package/lib/util/store.js +1 -1
  49. package/lib/validators/space-gateway.js +3 -0
  50. package/lib/validators/util.js +3 -0
  51. package/lib/webhook/sender/api/index.js +1 -1
  52. package/lib/webhook/sender/slack/index.js +1 -1
  53. package/package.json +17 -17
@@ -38,6 +38,10 @@ const {
38
38
  getRolesFromAuthConfig,
39
39
  isInProgress,
40
40
  isBeforeInstalled,
41
+ getComponentsInternalInfo,
42
+ forEachComponentV2,
43
+ forEachComponentV2Sync,
44
+ findComponentByIdV2,
41
45
  } = require('@blocklet/meta/lib/util');
42
46
  const getComponentProcessId = require('@blocklet/meta/lib/get-component-process-id');
43
47
  const { update: updateMetaFile } = require('@blocklet/meta/lib/file');
@@ -49,6 +53,7 @@ const {
49
53
  BlockletStatus,
50
54
  BlockletSource,
51
55
  BlockletEvents,
56
+ BlockletInternalEvents,
52
57
  BLOCKLET_MODES,
53
58
  BlockletGroup,
54
59
  fromBlockletStatus,
@@ -68,6 +73,8 @@ const {
68
73
  BLOCKLET_CONTROLLER_STATUS,
69
74
  SUSPENDED_REASON,
70
75
  } = require('@blocklet/constant');
76
+ const isUndefined = require('lodash/isUndefined');
77
+ const { consumeServerlessNFT, consumeLauncherSession } = require('../../util/launcher');
71
78
  const util = require('../../util');
72
79
  const {
73
80
  refresh: refreshAccessibleExternalNodeIp,
@@ -82,7 +89,6 @@ const {
82
89
  stopBlockletProcess,
83
90
  deleteBlockletProcess,
84
91
  reloadBlockletProcess,
85
- getBlockletStatusFromProcess,
86
92
  checkBlockletProcessHealthy,
87
93
  validateBlocklet,
88
94
  validateBlockletChainInfo,
@@ -96,7 +102,6 @@ const {
96
102
  getBlocklet,
97
103
  ensureEnvDefault,
98
104
  getConfigFromPreferences,
99
- consumeServerlessNFT,
100
105
  validateAppConfig,
101
106
  checkDuplicateMountPoint,
102
107
  validateStore,
@@ -106,6 +111,9 @@ const {
106
111
  updateBlockletFallbackLogo,
107
112
  ensureAppLogo,
108
113
  getBlockletDidDomainList,
114
+ getProcessState,
115
+ getBlockletStatus,
116
+ shouldSkipComponent,
109
117
  exceedRedemptionPeriod,
110
118
  } = require('../../util/blocklet');
111
119
  const states = require('../../states');
@@ -135,7 +143,7 @@ const { migrateApplicationToStructV2 } = require('./helper/migrate-application-t
135
143
  const { getBackupFilesUrlFromEndpoint, getBackupEndpoint } = require('../../util/spaces');
136
144
  const { validateAddSpaceGateway, validateUpdateSpaceGateway } = require('../../validators/space-gateway');
137
145
 
138
- const { formatEnvironments, shouldUpdateBlockletStatus, getBlockletMeta, validateOwner } = util;
146
+ const { formatEnvironments, getBlockletMeta, validateOwner } = util;
139
147
 
140
148
  const statusLock = new Lock('blocklet-status-lock');
141
149
 
@@ -241,8 +249,8 @@ class BlockletManager extends BaseBlockletManager {
241
249
  this._rollbackCache = new RollbackCache({ dir: this.dataDirs.tmp });
242
250
 
243
251
  if (daemon) {
244
- blockletPm2Events.on('online', (data) => this._syncPm2Status('online', data.blockletDid));
245
- blockletPm2Events.on('stop', (data) => this._syncPm2Status('stop', data.blockletDid));
252
+ blockletPm2Events.on('online', (data) => this._syncPm2Status('online', data.blockletDid, data.componentDid));
253
+ blockletPm2Events.on('stop', (data) => this._syncPm2Status('stop', data.blockletDid, data.componentDid));
246
254
  }
247
255
  }
248
256
 
@@ -402,7 +410,7 @@ class BlockletManager extends BaseBlockletManager {
402
410
  throw new Error('Unknown source');
403
411
  }
404
412
 
405
- async diff({ did, hashFiles, rootDid }) {
413
+ diff({ did, hashFiles, rootDid }) {
406
414
  return diff({ did, hashFiles, rootDid, states, manager: this });
407
415
  }
408
416
 
@@ -410,7 +418,7 @@ class BlockletManager extends BaseBlockletManager {
410
418
  * After the dev function finished, the caller should send a BlockletEvents.installed event to the daemon
411
419
  * @returns {Object} blocklet
412
420
  */
413
- async dev(folder, { rootDid, mountPoint, defaultStoreUrl } = {}) {
421
+ dev(folder, { rootDid, mountPoint, defaultStoreUrl } = {}) {
414
422
  logger.info('dev component', { folder, rootDid, mountPoint });
415
423
 
416
424
  const meta = getBlockletMeta(folder, { defaultStoreUrl });
@@ -425,15 +433,15 @@ class BlockletManager extends BaseBlockletManager {
425
433
  return installComponentFromDev({ folder, meta, rootDid, mountPoint, manager: this, states });
426
434
  }
427
435
 
428
- async checkComponentsForUpdates({ did }) {
436
+ checkComponentsForUpdates({ did }) {
429
437
  return UpgradeComponents.check({ did, states });
430
438
  }
431
439
 
432
- async upgradeComponents({ updateId, selectedComponents: selectedComponentDids }, context = {}) {
433
- return UpgradeComponents.upgrade({ updateId, selectedComponentDids, context, states, manager: this });
440
+ upgradeComponents({ updateId, selectedComponents: componentDids }, context = {}) {
441
+ return UpgradeComponents.upgrade({ updateId, componentDids, context, states, manager: this });
434
442
  }
435
443
 
436
- async migrateApplicationToStructV2({ did, appSk, context = {} }) {
444
+ migrateApplicationToStructV2({ did, appSk, context = {} }) {
437
445
  return migrateApplicationToStructV2({ did, appSk, context, manager: this, states });
438
446
  }
439
447
 
@@ -447,8 +455,8 @@ class BlockletManager extends BaseBlockletManager {
447
455
  return { did, isInstalled: !!blocklet, isRunning };
448
456
  }
449
457
 
450
- async start({ did, throwOnError, checkHealthImmediately = false, e2eMode = false }, context) {
451
- logger.info('start blocklet', { did });
458
+ async start({ did, throwOnError, checkHealthImmediately = false, e2eMode = false, componentDids }, context) {
459
+ logger.info('start blocklet', { did, componentDids, throwOnError, checkHealthImmediately, e2eMode });
452
460
  // should check blocklet integrity
453
461
  const blocklet = await this.ensureBlocklet(did, { e2eMode });
454
462
 
@@ -458,7 +466,7 @@ class BlockletManager extends BaseBlockletManager {
458
466
  // blocklet may be manually stopped durning starting
459
467
  // so error message would not be sent if blocklet is stopped
460
468
  // so we need update status first
461
- await states.blocklet.setBlockletStatus(did, BlockletStatus.starting);
469
+ await states.blocklet.setBlockletStatus(did, BlockletStatus.starting, { componentDids });
462
470
  blocklet.status = BlockletStatus.starting;
463
471
 
464
472
  // validate requirement and engine
@@ -511,6 +519,7 @@ class BlockletManager extends BaseBlockletManager {
511
519
  nodeEnvironments,
512
520
  nodeInfo: await states.node.read(),
513
521
  e2eMode,
522
+ componentDids,
514
523
  });
515
524
 
516
525
  // check blocklet healthy
@@ -520,6 +529,7 @@ class BlockletManager extends BaseBlockletManager {
520
529
  context,
521
530
  minConsecutiveTime,
522
531
  timeout: startTimeout,
532
+ componentDids,
523
533
  };
524
534
 
525
535
  if (checkHealthImmediately) {
@@ -552,8 +562,8 @@ class BlockletManager extends BaseBlockletManager {
552
562
  severity: 'error',
553
563
  });
554
564
 
555
- await this.deleteProcess({ did });
556
- const res = await states.blocklet.setBlockletStatus(did, BlockletStatus.error);
565
+ await this.deleteProcess({ did, componentDids });
566
+ const res = await states.blocklet.setBlockletStatus(did, BlockletStatus.error, { componentDids });
557
567
  this.emit(BlockletEvents.startFailed, { ...res, error: { message: error.message } });
558
568
 
559
569
  if (throwOnError) {
@@ -564,14 +574,14 @@ class BlockletManager extends BaseBlockletManager {
564
574
  }
565
575
  }
566
576
 
567
- async stop({ did, updateStatus = true, silent = false }, context) {
568
- logger.info('stop blocklet', { did });
577
+ async stop({ did, updateStatus = true, silent = false, componentDids }, context) {
578
+ logger.info('stop blocklet', { did, componentDids, updateStatus, silent });
569
579
 
570
580
  const blocklet = await this.getBlocklet(did);
571
581
  const { processId } = blocklet.env;
572
582
 
573
583
  if (updateStatus) {
574
- await states.blocklet.setBlockletStatus(did, BlockletStatus.stopping);
584
+ await states.blocklet.setBlockletStatus(did, BlockletStatus.stopping, { componentDids });
575
585
  blocklet.status = BlockletStatus.stopping;
576
586
  this.emit(BlockletEvents.statusChange, blocklet);
577
587
  }
@@ -591,11 +601,12 @@ class BlockletManager extends BaseBlockletManager {
591
601
  silent,
592
602
  ...getHooksOutputFiles(b),
593
603
  }),
604
+ componentDids,
594
605
  });
595
606
  } catch (error) {
596
607
  logger.error('Failed to stop blocklet', { error, did });
597
608
  if (updateStatus) {
598
- const res = await states.blocklet.setBlockletStatus(did, BlockletStatus.error);
609
+ const res = await states.blocklet.setBlockletStatus(did, BlockletStatus.error, { componentDids });
599
610
  this.emit(BlockletEvents.statusChange, res);
600
611
  }
601
612
  throw error;
@@ -604,13 +615,18 @@ class BlockletManager extends BaseBlockletManager {
604
615
  logger.info('blocklet stopped successfully', { processId, did });
605
616
 
606
617
  if (updateStatus) {
607
- const res = await states.blocklet.setBlockletStatus(did, BlockletStatus.stopped);
618
+ const res = await states.blocklet.setBlockletStatus(did, BlockletStatus.stopped, { componentDids });
608
619
  // send notification to websocket channel
609
620
  this.emit(BlockletEvents.statusChange, res);
610
621
 
611
622
  // send notification to wallet
612
623
  this.emit(BlockletEvents.stopped, res);
613
624
 
625
+ this.emit(BlockletInternalEvents.componentsUpdated, {
626
+ appDid: blocklet.appDid,
627
+ components: getComponentsInternalInfo(res),
628
+ });
629
+
614
630
  return res;
615
631
  }
616
632
 
@@ -640,13 +656,23 @@ class BlockletManager extends BaseBlockletManager {
640
656
  }
641
657
 
642
658
  /**
643
- * FIXME: @linchen support cancel
644
659
  * @param {import('@abtnode/client').RequestRestoreBlockletInput} input
645
660
  * @memberof BlockletManager
646
661
  */
647
- // eslint-disable-next-line no-unused-vars
648
662
  async restoreBlocklet(input, context) {
649
663
  const { from, ...param } = input;
664
+
665
+ if (input.overwrite) {
666
+ try {
667
+ const blocklet = await this.getBlocklet(input.appPid);
668
+ if (blocklet) {
669
+ await this.delete({ did: input.appPid, keepData: false, keepLogsDir: false, keepConfigs: false }, context);
670
+ }
671
+ } catch (error) {
672
+ logger.error('Failed to delete blocklet', { error, did: input.appPid });
673
+ }
674
+ }
675
+
650
676
  if (from === 'spaces') {
651
677
  return this._restoreFromSpaces(param, context);
652
678
  }
@@ -664,22 +690,26 @@ class BlockletManager extends BaseBlockletManager {
664
690
  * @param {Record<string, any>} context
665
691
  * @returns {import('@abtnode/client').BlockletState}
666
692
  */
667
- async restart({ did }, context) {
693
+ async restart({ did, componentDids }, context) {
668
694
  logger.info('restart blocklet', { did });
669
-
670
695
  const blocklet = await this.getBlocklet(did);
671
-
672
696
  await this.checkControllerStatus(blocklet, 'restart');
673
-
674
- await states.blocklet.setBlockletStatus(did, BlockletStatus.stopping);
697
+ await states.blocklet.setBlockletStatus(did, BlockletStatus.stopping, { componentDids });
675
698
  const result = await states.blocklet.getBlocklet(did);
676
699
  this.emit(BlockletEvents.statusChange, result);
677
700
 
678
- const ticket = this.startQueue.push({ entity: 'blocklet', action: 'restart', id: did, did, context });
701
+ const ticket = this.startQueue.push({
702
+ entity: 'blocklet',
703
+ action: 'restart',
704
+ id: did,
705
+ did,
706
+ componentDids,
707
+ context,
708
+ });
679
709
  ticket.on('failed', async (err) => {
680
710
  logger.error('failed to restart blocklet', { did, error: err });
681
711
 
682
- const state = await states.blocklet.setBlockletStatus(did, BlockletStatus.stopped);
712
+ const state = await states.blocklet.setBlockletStatus(did, BlockletStatus.stopped, { componentDids });
683
713
  this.emit(BlockletEvents.statusChange, state);
684
714
 
685
715
  this._createNotification(did, {
@@ -695,18 +725,29 @@ class BlockletManager extends BaseBlockletManager {
695
725
  }
696
726
 
697
727
  // eslint-disable-next-line no-unused-vars
698
- async reload({ did }, context) {
728
+ async reload({ did, componentDids: list }, context) {
699
729
  const blocklet = await this.getBlocklet(did);
700
-
701
730
  await this.checkControllerStatus(blocklet, 'reload');
702
731
 
703
- await states.blocklet.setBlockletStatus(did, BlockletStatus.stopping);
704
- await reloadBlockletProcess(blocklet);
705
- const res = await states.blocklet.setBlockletStatus(did, BlockletStatus.running);
706
- logger.info('blocklet reload successfully', { did });
732
+ const componentDids = (blocklet.children || [])
733
+ .filter((x) => x.status === BlockletStatus.running)
734
+ .filter((x) => !shouldSkipComponent(x.meta.did, list))
735
+ .map((x) => x.meta.did);
736
+
737
+ if (!componentDids.length) {
738
+ throw new Error('No running component found');
739
+ }
707
740
 
708
- this.emit(BlockletEvents.statusChange, res);
709
- return res;
741
+ try {
742
+ await reloadBlockletProcess(blocklet, { componentDids });
743
+ const res = await states.blocklet.setBlockletStatus(did, BlockletStatus.running, { componentDids });
744
+ logger.info('blocklet reload successfully', { did, componentDids });
745
+ this.emit(BlockletEvents.statusChange, res);
746
+ return res;
747
+ } catch (error) {
748
+ logger.error('Failed to reload blocklet', { error, did, componentDids });
749
+ throw error;
750
+ }
710
751
  }
711
752
 
712
753
  async delete({ did, keepData, keepLogsDir, keepConfigs }, context) {
@@ -765,17 +806,17 @@ class BlockletManager extends BaseBlockletManager {
765
806
 
766
807
  const blocklet = await this.getBlocklet(did);
767
808
 
768
- if (isInProgress(blocklet.status || blocklet.status === BlockletStatus.running)) {
769
- throw new Error('Cannot reset when blocklet is in progress');
770
- }
809
+ if (!childDid) {
810
+ if (isInProgress(blocklet.status || blocklet.status === BlockletStatus.running)) {
811
+ throw new Error('Cannot reset when blocklet is in progress');
812
+ }
771
813
 
772
- try {
773
- await this.deleteProcess({ did }, context);
774
- } catch {
775
- // do nothing
776
- }
814
+ try {
815
+ await this.deleteProcess({ did }, context);
816
+ } catch {
817
+ // do nothing
818
+ }
777
819
 
778
- if (!childDid) {
779
820
  // Cleanup disk storage
780
821
  const { cacheDir, logsDir, dataDir } = blocklet.env;
781
822
  fs.removeSync(cacheDir);
@@ -793,6 +834,16 @@ class BlockletManager extends BaseBlockletManager {
793
834
  throw new Error('Child does not exist');
794
835
  }
795
836
 
837
+ if (isInProgress(child.status || child.status === BlockletStatus.running)) {
838
+ throw new Error('Cannot reset when component is in progress');
839
+ }
840
+
841
+ try {
842
+ await this.deleteProcess({ did, componentDids: [child.meta.did] }, context);
843
+ } catch {
844
+ // do nothing
845
+ }
846
+
796
847
  // Cleanup disk storage
797
848
  const { cacheDir, logsDir, dataDir } = child.env;
798
849
  fs.removeSync(cacheDir);
@@ -874,6 +925,11 @@ class BlockletManager extends BaseBlockletManager {
874
925
  action: `/blocklets/${newBlocklet.meta.did}/components`,
875
926
  });
876
927
 
928
+ this.emit(BlockletInternalEvents.componentsUpdated, {
929
+ appDid: blocklet.appDid,
930
+ components: getComponentsInternalInfo(newBlocklet),
931
+ });
932
+
877
933
  return newBlocklet;
878
934
  }
879
935
 
@@ -932,7 +988,14 @@ class BlockletManager extends BaseBlockletManager {
932
988
  // fallback blocklet status to error
933
989
  const blocklet = await states.blocklet.getBlocklet(inputDid);
934
990
  if (blocklet) {
935
- await states.blocklet.setBlockletStatus(blocklet.meta.did, BlockletStatus.error);
991
+ const componentDids = [];
992
+ forEachComponentV2Sync(blocklet, (x) => {
993
+ if ([BlockletStatus.waiting, BlockletStatus.downloading].includes(x.status)) {
994
+ componentDids.push(x.meta.did);
995
+ }
996
+ });
997
+
998
+ await states.blocklet.setBlockletStatus(blocklet.meta.did, BlockletStatus.error, { componentDids });
936
999
  }
937
1000
  statusLock.release();
938
1001
  } catch (err) {
@@ -945,15 +1008,15 @@ class BlockletManager extends BaseBlockletManager {
945
1008
  }
946
1009
 
947
1010
  // eslint-disable-next-line no-unused-vars
948
- async deleteProcess({ did }, context) {
1011
+ async deleteProcess({ did, componentDids }, context) {
949
1012
  const blocklet = await this.getBlocklet(did);
950
1013
 
951
- logger.info('delete blocklet process', { did });
1014
+ logger.info('delete blocklet process', { did, componentDids });
952
1015
 
953
- await deleteBlockletProcess(blocklet, context);
1016
+ await deleteBlockletProcess(blocklet, { ...context, componentDids });
954
1017
 
955
- const result = await states.blocklet.setBlockletStatus(did, BlockletStatus.stopped);
956
- logger.info('blocklet process deleted successfully', { did });
1018
+ const result = await states.blocklet.setBlockletStatus(did, BlockletStatus.stopped, { componentDids });
1019
+ logger.info('blocklet process deleted successfully', { did, componentDids });
957
1020
  return result;
958
1021
  }
959
1022
 
@@ -996,22 +1059,9 @@ class BlockletManager extends BaseBlockletManager {
996
1059
  }
997
1060
  }
998
1061
 
999
- async list({ includeRuntimeInfo = true, query, filter } = {}, context) {
1062
+ async list({ includeRuntimeInfo = true, query } = {}, context) {
1000
1063
  const condition = { ...flat(query || {}) };
1001
- if (filter === 'external-only') {
1002
- condition.controller = {
1003
- $exists: true,
1004
- };
1005
- }
1006
-
1007
- if (filter === 'external-excluded') {
1008
- condition.controller = {
1009
- $exists: false,
1010
- };
1011
- }
1012
-
1013
1064
  const blocklets = await states.blocklet.getBlocklets(condition);
1014
-
1015
1065
  if (includeRuntimeInfo) {
1016
1066
  return this._attachBlockletListRuntimeInfo({ blocklets }, context);
1017
1067
  }
@@ -1019,7 +1069,7 @@ class BlockletManager extends BaseBlockletManager {
1019
1069
  return blocklets;
1020
1070
  }
1021
1071
 
1022
- async listBackups() {
1072
+ listBackups() {
1023
1073
  return getBackupList(this.dataDirs.data);
1024
1074
  }
1025
1075
 
@@ -1031,19 +1081,18 @@ class BlockletManager extends BaseBlockletManager {
1031
1081
  }
1032
1082
 
1033
1083
  const tmpDids = Array.isArray(did) ? did : [did];
1034
- const [rootDid, ...childDids] = tmpDids;
1084
+ const [rootDid, childDid] = tmpDids;
1035
1085
  const rootMetaDid = await states.blocklet.getBlockletMetaDid(rootDid);
1036
- const dids = [rootMetaDid, ...childDids];
1037
1086
 
1038
- logger.info('config blocklet', { dids });
1087
+ logger.info('config blocklet', { rootDid, rootMetaDid, childDid });
1039
1088
 
1040
1089
  const ancestors = [];
1041
1090
  let blocklet = await this.getBlocklet(rootDid);
1042
- for (const childDid of childDids) {
1091
+ if (childDid) {
1043
1092
  ancestors.push(blocklet);
1044
1093
  blocklet = blocklet.children.find((x) => x.meta.did === childDid);
1045
1094
  if (!blocklet) {
1046
- throw new Error('Child blocklet does not exist', { dids });
1095
+ throw new Error('Child blocklet does not exist', { rootDid, childDid });
1047
1096
  }
1048
1097
  }
1049
1098
 
@@ -1056,8 +1105,8 @@ class BlockletManager extends BaseBlockletManager {
1056
1105
  await environmentNameSchema.validateAsync(x.key);
1057
1106
  } else if (BLOCKLET_CONFIGURABLE_KEY[x.key] && x.key.startsWith('BLOCKLET_')) {
1058
1107
  // app key
1059
- if (childDids.length) {
1060
- logger.error(`Cannot set ${x.key} to child blocklet`, [dids]);
1108
+ if (childDid) {
1109
+ logger.error(`Cannot set ${x.key} to child blocklet`, { rootDid, childDid });
1061
1110
  throw new Error(`Cannot set ${x.key} to child blocklet`);
1062
1111
  }
1063
1112
  await validateAppConfig(x, states);
@@ -1096,7 +1145,7 @@ class BlockletManager extends BaseBlockletManager {
1096
1145
  Object.assign(blocklet.configObj, configObj);
1097
1146
 
1098
1147
  // update db
1099
- await states.blockletExtras.setConfigs(dids, newConfigs);
1148
+ await states.blockletExtras.setConfigs([rootMetaDid, childDid].filter(Boolean), newConfigs);
1100
1149
 
1101
1150
  if (willAppSkChange) {
1102
1151
  const info = await states.node.read();
@@ -1116,7 +1165,7 @@ class BlockletManager extends BaseBlockletManager {
1116
1165
  }
1117
1166
 
1118
1167
  // update blocklet meta
1119
- if (blocklet.structVersion && !childDids.length) {
1168
+ if (blocklet.structVersion && !childDid) {
1120
1169
  const changedTitle = newConfigs.find((x) => x.key === BLOCKLET_CONFIGURABLE_KEY.BLOCKLET_APP_NAME)?.value;
1121
1170
  const changedDescription = newConfigs.find(
1122
1171
  (x) => x.key === BLOCKLET_CONFIGURABLE_KEY.BLOCKLET_APP_DESCRIPTION
@@ -1145,6 +1194,13 @@ class BlockletManager extends BaseBlockletManager {
1145
1194
  await this._updateDidDocument(newState);
1146
1195
  }
1147
1196
 
1197
+ if (!childDid && !newConfigs.some((x) => x.key === BLOCKLET_CONFIGURABLE_KEY.BLOCKLET_APP_SK)) {
1198
+ this.emit(BlockletInternalEvents.appConfigChanged, {
1199
+ appDid: newState.appDid,
1200
+ configs: newConfigs.map((x) => ({ key: x.key, value: x.value })),
1201
+ });
1202
+ }
1203
+
1148
1204
  this.emit(BlockletEvents.updated, newState);
1149
1205
 
1150
1206
  return newState;
@@ -1321,6 +1377,10 @@ class BlockletManager extends BaseBlockletManager {
1321
1377
  await states.blocklet.updateBlocklet(rootDid, { mountPoint: blocklet.mountPoint, children: blocklet.children });
1322
1378
 
1323
1379
  this.emit(BlockletEvents.upgraded, { blocklet, context: { ...context, createAuditLog: false } }); // trigger router refresh
1380
+ this.emit(BlockletInternalEvents.componentsUpdated, {
1381
+ appDid: blocklet.appDid,
1382
+ components: getComponentsInternalInfo(blocklet),
1383
+ });
1324
1384
 
1325
1385
  return this.getBlocklet(rootDid);
1326
1386
  }
@@ -1339,15 +1399,15 @@ class BlockletManager extends BaseBlockletManager {
1339
1399
  });
1340
1400
  }
1341
1401
 
1342
- async ensureBlocklet(did, opts = {}) {
1402
+ ensureBlocklet(did, opts = {}) {
1343
1403
  return getBlocklet({ ...opts, states, dataDirs: this.dataDirs, did, ensureIntegrity: true });
1344
1404
  }
1345
1405
 
1346
- async getBlocklet(did, opts = {}) {
1406
+ getBlocklet(did, opts = {}) {
1347
1407
  return getBlocklet({ ...opts, states, dataDirs: this.dataDirs, did, ensureIntegrity: false });
1348
1408
  }
1349
1409
 
1350
- async hasBlocklet({ did }) {
1410
+ hasBlocklet({ did }) {
1351
1411
  return states.blocklet.hasBlocklet(did);
1352
1412
  }
1353
1413
 
@@ -1379,60 +1439,77 @@ class BlockletManager extends BaseBlockletManager {
1379
1439
  return this.getBlocklet(did);
1380
1440
  }
1381
1441
 
1382
- async status(did, { forceSync = false } = {}) {
1383
- const fastReturnOnForceSync = async (blocklet = {}) => {
1384
- const { status } = blocklet;
1385
- const { added, waiting, downloading, installing, installed, upgrading } = BlockletStatus;
1442
+ async status(did, { forceSync = false, componentDids } = {}) {
1443
+ const blocklet = await this.getBlocklet(did);
1386
1444
 
1387
- if ([added, waiting, downloading, installing, installed, upgrading].includes(status)) {
1388
- return blocklet;
1389
- }
1445
+ // for backward compatibility
1446
+ if (!blocklet.structVersion) {
1447
+ return blocklet;
1448
+ }
1390
1449
 
1391
- const res = await states.blocklet.setBlockletStatus(did, BlockletStatus.stopped);
1392
- this.emit(BlockletEvents.statusChange, res);
1393
- return res;
1450
+ // {Record<BlockletStatus, Array<Did>>}
1451
+ const updates = new Map();
1452
+ const addToUpdates = (componentDid, status) => {
1453
+ if (updates.has(status)) {
1454
+ updates.get(status).push(componentDid);
1455
+ } else {
1456
+ updates.set(status, [componentDid]);
1457
+ }
1394
1458
  };
1395
1459
 
1396
- const blocklet = await this.getBlocklet(did);
1460
+ await forEachComponentV2(
1461
+ blocklet,
1462
+ async (component) => {
1463
+ if (!forceSync && !util.shouldUpdateBlockletStatus(component.status)) {
1464
+ return;
1465
+ }
1397
1466
 
1398
- let shouldUpdateStatus = forceSync || shouldUpdateBlockletStatus(blocklet.status);
1399
- if (isInProgress(blocklet.status)) {
1400
- const uptime = Date.now() - new Date(blocklet.updatedAt).getTime();
1401
- // FIXME @linchen 在彻底修复 blocklet status 稳定性之后, uptime check 应该去掉. 如果 hook 执行时间很长, uptime check 会影响 hook 执行过程中的 blocklet status
1402
- if (uptime > 120 * 1000) {
1403
- shouldUpdateStatus = true;
1404
- }
1405
- }
1467
+ if (shouldSkipComponent(component.meta.did, componentDids)) {
1468
+ return;
1469
+ }
1406
1470
 
1407
- if (!shouldUpdateStatus) {
1408
- return blocklet;
1409
- }
1471
+ try {
1472
+ const status = await getProcessState(component.env.processId);
1473
+ const oldStatus = component.status;
1474
+ if (component.status !== status) {
1475
+ component.status = status;
1476
+ addToUpdates(component.meta.did, status);
1477
+ logger.info('will sync status from pm2', { did, status, oldStatus, componentDid: component.meta.did });
1478
+ }
1479
+ } catch {
1480
+ if (
1481
+ ![
1482
+ BlockletStatus.added,
1483
+ BlockletStatus.waiting,
1484
+ BlockletStatus.downloading,
1485
+ BlockletStatus.installing,
1486
+ BlockletStatus.installed,
1487
+ BlockletStatus.upgrading,
1488
+ ].includes(component.status)
1489
+ ) {
1490
+ const status = BlockletStatus.stopped;
1491
+ component.status = status;
1492
+ addToUpdates(component.meta.did, status);
1493
+ }
1494
+ }
1495
+ },
1496
+ { parallel: true }
1497
+ );
1410
1498
 
1411
- try {
1412
- if (
1413
- blocklet.meta?.group === BlockletGroup.gateway &&
1414
- !blocklet.children?.length &&
1415
- isInProgress(blocklet.status)
1416
- ) {
1417
- const res = await states.blocklet.setBlockletStatus(did, BlockletStatus.stopped);
1418
- this.emit(BlockletEvents.statusChange, res);
1419
- return res;
1499
+ // iterate updates
1500
+ if (updates.size > 0) {
1501
+ for (const [status, dids] of updates) {
1502
+ logger.info('sync status from pm2', { did, status, componentDids: dids });
1503
+ await states.blocklet.setBlockletStatus(did, status, { componentDids: dids });
1420
1504
  }
1505
+ }
1421
1506
 
1422
- const status = await getBlockletStatusFromProcess(blocklet);
1423
- if (blocklet.status !== status) {
1424
- const res = await states.blocklet.setBlockletStatus(did, status);
1425
- this.emit(BlockletEvents.statusChange, res);
1426
- return res;
1427
- }
1507
+ blocklet.status = getBlockletStatus(blocklet);
1428
1508
 
1429
- return blocklet;
1430
- } catch (err) {
1431
- return fastReturnOnForceSync(blocklet);
1432
- }
1509
+ return blocklet;
1433
1510
  }
1434
1511
 
1435
- async refreshListCache() {
1512
+ refreshListCache() {
1436
1513
  this.list({ useCache: false }).catch((err) => {
1437
1514
  logger.error('refresh blocklet list failed', { error: err });
1438
1515
  });
@@ -1496,6 +1573,7 @@ class BlockletManager extends BaseBlockletManager {
1496
1573
  name: 'sync-blocklet-status',
1497
1574
  time: '*/60 * * * * *', // 60s
1498
1575
  fn: this._syncBlockletStatus.bind(this),
1576
+ options: { runOnInit: false },
1499
1577
  },
1500
1578
  {
1501
1579
  name: 'sync-blocklet-list',
@@ -1565,14 +1643,20 @@ class BlockletManager extends BaseBlockletManager {
1565
1643
 
1566
1644
  /**
1567
1645
  * @description
1568
- * @param {import('@abtnode/client').RequestAddBlockletSpaceGatewayInput} { did, spaceGateway }
1646
+ * @param {import('@abtnode/client').RequestAddBlockletSpaceGatewayInput} { did, spaceGatewayDid }
1569
1647
  * @return {Promise<void>}
1570
1648
  * @memberof BlockletManager
1571
1649
  */
1572
- async deleteBlockletSpaceGateway({ did, url }) {
1650
+ async deleteBlockletSpaceGateway({ did, spaceGatewayDid }) {
1573
1651
  const spaceGateways = await this.getBlockletSpaceGateways({ did });
1574
1652
 
1575
- const latestSpaceGateways = spaceGateways.filter((s) => s?.url !== url);
1653
+ const latestSpaceGateways = spaceGateways.filter((s) => {
1654
+ if (spaceGatewayDid) {
1655
+ return s?.did !== spaceGatewayDid;
1656
+ }
1657
+ // note: 兼容性处理,所有没有 did 的 spaceGateway 统一会被删除
1658
+ return !isUndefined(s?.did);
1659
+ });
1576
1660
 
1577
1661
  await states.blockletExtras.setSettings(did, { spaceGateways: latestSpaceGateways });
1578
1662
  }
@@ -1627,7 +1711,7 @@ class BlockletManager extends BaseBlockletManager {
1627
1711
  * @return {Promise<Array<import('@abtnode/client').Backup>>}
1628
1712
  * @memberof BlockletManager
1629
1713
  */
1630
- async getBlockletBackups({ did }) {
1714
+ getBlockletBackups({ did }) {
1631
1715
  return states.backup.getBlockletBackups({ did });
1632
1716
  }
1633
1717
 
@@ -1653,7 +1737,7 @@ class BlockletManager extends BaseBlockletManager {
1653
1737
  * oldBlocklet: {},
1654
1738
  * throwOnError: Error,
1655
1739
  * skipCheckStatusBeforeDownload: boolean,
1656
- * selectedComponentDids: Array<did>,
1740
+ * componentDids: Array<did>,
1657
1741
  * }} params
1658
1742
  * @return {*}
1659
1743
  * @memberof BlockletManager
@@ -1666,7 +1750,7 @@ class BlockletManager extends BaseBlockletManager {
1666
1750
  oldBlocklet,
1667
1751
  throwOnError,
1668
1752
  skipCheckStatusBeforeDownload,
1669
- selectedComponentDids,
1753
+ componentDids,
1670
1754
  skipCheckIntegrity,
1671
1755
  } = params;
1672
1756
  const { meta } = blocklet;
@@ -1703,8 +1787,8 @@ class BlockletManager extends BaseBlockletManager {
1703
1787
  const blockletForDownload = {
1704
1788
  ...blocklet,
1705
1789
  children: (blocklet.children || []).filter((x) => {
1706
- if (selectedComponentDids?.length) {
1707
- return selectedComponentDids.includes(x.meta.did);
1790
+ if (componentDids?.length) {
1791
+ return componentDids.includes(x.meta.did);
1708
1792
  }
1709
1793
  return x;
1710
1794
  }),
@@ -1775,12 +1859,12 @@ class BlockletManager extends BaseBlockletManager {
1775
1859
  }
1776
1860
 
1777
1861
  if (postAction === 'install') {
1778
- const state = await states.blocklet.setBlockletStatus(did, BlockletStatus.installing);
1862
+ const state = await states.blocklet.setBlockletStatus(did, BlockletStatus.installing, { componentDids });
1779
1863
  this.emit(BlockletEvents.statusChange, state);
1780
1864
  }
1781
1865
 
1782
1866
  if (postAction === 'upgrade') {
1783
- const state = await states.blocklet.setBlockletStatus(did, BlockletStatus.upgrading);
1867
+ const state = await states.blocklet.setBlockletStatus(did, BlockletStatus.upgrading, { componentDids });
1784
1868
  this.emit(BlockletEvents.statusChange, state);
1785
1869
  }
1786
1870
 
@@ -1794,13 +1878,13 @@ class BlockletManager extends BaseBlockletManager {
1794
1878
  try {
1795
1879
  // install blocklet
1796
1880
  if (postAction === 'install') {
1797
- await this._onInstall({ blocklet, context, oldBlocklet });
1881
+ await this._onInstall({ blocklet, componentDids, context, oldBlocklet });
1798
1882
  return;
1799
1883
  }
1800
1884
 
1801
1885
  // upgrade blocklet
1802
1886
  if (postAction === 'upgrade') {
1803
- await this._onUpgrade({ oldBlocklet, newBlocklet: blocklet, context });
1887
+ await this._onUpgrade({ oldBlocklet, componentDids, newBlocklet: blocklet, context });
1804
1888
  }
1805
1889
  } catch (error) {
1806
1890
  if (throwOnError) {
@@ -1809,7 +1893,7 @@ class BlockletManager extends BaseBlockletManager {
1809
1893
  }
1810
1894
  }
1811
1895
 
1812
- async _onInstall({ blocklet, context, oldBlocklet }) {
1896
+ async _onInstall({ blocklet, componentDids, context, oldBlocklet }) {
1813
1897
  const { meta } = blocklet;
1814
1898
  const { did, version } = meta;
1815
1899
  logger.info('do install blocklet', { did, version });
@@ -1817,6 +1901,7 @@ class BlockletManager extends BaseBlockletManager {
1817
1901
  try {
1818
1902
  const installedBlocklet = await this._installBlocklet({
1819
1903
  did,
1904
+ componentDids,
1820
1905
  context,
1821
1906
  oldBlocklet,
1822
1907
  });
@@ -1824,7 +1909,7 @@ class BlockletManager extends BaseBlockletManager {
1824
1909
  if (context.startImmediately && installedBlocklet?.settings.initialized) {
1825
1910
  try {
1826
1911
  logger.info('start blocklet after installed', { did });
1827
- await this.start({ did, checkHealthImmediately: true });
1912
+ await this.start({ did, checkHealthImmediately: true, componentDids });
1828
1913
  } catch (error) {
1829
1914
  logger.warn('attempt to start immediately failed', { did, error });
1830
1915
  }
@@ -1837,7 +1922,7 @@ class BlockletManager extends BaseBlockletManager {
1837
1922
  }
1838
1923
  }
1839
1924
 
1840
- async _onUpgrade({ oldBlocklet, newBlocklet, context }) {
1925
+ async _onUpgrade({ oldBlocklet, newBlocklet, componentDids, context }) {
1841
1926
  const { version, did } = newBlocklet.meta;
1842
1927
  logger.info('do upgrade blocklet', { did, version });
1843
1928
 
@@ -1845,6 +1930,7 @@ class BlockletManager extends BaseBlockletManager {
1845
1930
  await this._upgradeBlocklet({
1846
1931
  newBlocklet,
1847
1932
  oldBlocklet,
1933
+ componentDids,
1848
1934
  context,
1849
1935
  });
1850
1936
  } catch (err) {
@@ -1852,13 +1938,13 @@ class BlockletManager extends BaseBlockletManager {
1852
1938
  }
1853
1939
  }
1854
1940
 
1855
- async _onRestart({ did, context }) {
1856
- await this.stop({ did, updateStatus: false }, context);
1857
- await this.start({ did }, context);
1941
+ async _onRestart({ did, componentDids, context }) {
1942
+ await this.stop({ did, componentDids, updateStatus: false }, context);
1943
+ await this.start({ did, componentDids }, context);
1858
1944
  }
1859
1945
 
1860
1946
  async _onCheckIfStarted(jobInfo, { throwOnError } = {}) {
1861
- const { did, context, minConsecutiveTime = 5000, timeout } = jobInfo;
1947
+ const { did, context, minConsecutiveTime = 5000, timeout, componentDids } = jobInfo;
1862
1948
  const blocklet = await this.getBlocklet(did);
1863
1949
 
1864
1950
  const { meta } = blocklet;
@@ -1866,10 +1952,18 @@ class BlockletManager extends BaseBlockletManager {
1866
1952
 
1867
1953
  try {
1868
1954
  // healthy check
1869
- await checkBlockletProcessHealthy(blocklet, { minConsecutiveTime, timeout });
1955
+ await checkBlockletProcessHealthy(blocklet, { minConsecutiveTime, timeout, componentDids });
1870
1956
 
1871
1957
  // update blocklet status after healthy check
1872
- const res = await this.status(did, { forceSync: true });
1958
+ await states.blocklet.setBlockletStatus(did, BlockletStatus.running, { componentDids });
1959
+
1960
+ const res = await this.getBlocklet(did);
1961
+
1962
+ this.emit(BlockletInternalEvents.componentsUpdated, {
1963
+ appDid: blocklet.appDid,
1964
+ components: getComponentsInternalInfo(res),
1965
+ });
1966
+
1873
1967
  this.emit(BlockletEvents.started, res);
1874
1968
  } catch (error) {
1875
1969
  const status = await states.blocklet.getBlockletStatus(did);
@@ -1881,7 +1975,7 @@ class BlockletManager extends BaseBlockletManager {
1881
1975
  logger.error('check blocklet if started failed', { did, name, context, timeout, error });
1882
1976
 
1883
1977
  await this.deleteProcess({ did }, context);
1884
- await states.blocklet.setBlockletStatus(did, BlockletStatus.error);
1978
+ await states.blocklet.setBlockletStatus(did, BlockletStatus.error, { componentDids });
1885
1979
 
1886
1980
  this._createNotification(did, {
1887
1981
  title: 'Blocklet Start Failed',
@@ -2273,7 +2367,7 @@ class BlockletManager extends BaseBlockletManager {
2273
2367
  * @param {string} opt.did
2274
2368
  * @param {object} opt.context
2275
2369
  */
2276
- async _installBlocklet({ did, oldBlocklet, context, createNotification = true }) {
2370
+ async _installBlocklet({ did, oldBlocklet, componentDids, context, createNotification = true }) {
2277
2371
  try {
2278
2372
  // should ensure blocklet integrity
2279
2373
  let blocklet = await this.ensureBlocklet(did);
@@ -2297,7 +2391,9 @@ class BlockletManager extends BaseBlockletManager {
2297
2391
  // post install
2298
2392
  await this._runPostInstallHook(blocklet, context);
2299
2393
 
2300
- await states.blocklet.setBlockletStatus(did, BlockletStatus.installed);
2394
+ await states.blocklet.setInstalledAt(did);
2395
+
2396
+ await states.blocklet.setBlockletStatus(did, BlockletStatus.installed, { componentDids });
2301
2397
  blocklet = await this.getBlocklet(did);
2302
2398
  logger.info('blocklet installed', { source, did: meta.did });
2303
2399
 
@@ -2325,14 +2421,22 @@ class BlockletManager extends BaseBlockletManager {
2325
2421
  });
2326
2422
  }
2327
2423
 
2328
- if (blocklet?.controller?.nftId && process.env.NODE_ENV !== 'test') {
2329
- const nodeInfo = await states.node.read();
2330
- await consumeServerlessNFT({ nftId: blocklet.controller.nftId, nodeInfo, blocklet });
2331
- await states.blockletExtras.updateByDid(did, {
2332
- controller: { ...blocklet.controller, consumedAt: new Date().toISOString() },
2333
- });
2424
+ if (blocklet.controller && process.env.NODE_ENV !== 'test') {
2425
+ let isNFTConsumed = false;
2426
+ if (blocklet.controller.launcherSessionId && blocklet.controller.launcherUrl) {
2427
+ await consumeLauncherSession({ params: blocklet.controller, blocklet });
2428
+ isNFTConsumed = true;
2429
+ } else if (blocklet.controller.nftId) {
2430
+ await consumeServerlessNFT({ nftId: blocklet.controller.nftId, blocklet });
2431
+ isNFTConsumed = true;
2432
+ }
2334
2433
 
2335
- this.emit(BlockletEvents.nftConsumed, { blocklet, context });
2434
+ if (isNFTConsumed) {
2435
+ await states.blockletExtras.updateByDid(did, {
2436
+ controller: { ...blocklet.controller, consumedAt: new Date().toISOString() },
2437
+ });
2438
+ this.emit(BlockletEvents.nftConsumed, { blocklet, context });
2439
+ }
2336
2440
  }
2337
2441
 
2338
2442
  if (createNotification) {
@@ -2378,7 +2482,7 @@ class BlockletManager extends BaseBlockletManager {
2378
2482
  }
2379
2483
  }
2380
2484
 
2381
- async _upgradeBlocklet({ newBlocklet, oldBlocklet, context = {} }) {
2485
+ async _upgradeBlocklet({ newBlocklet, oldBlocklet, componentDids, context = {} }) {
2382
2486
  const { meta, source, deployedFrom, children } = newBlocklet;
2383
2487
  const { did, version, name, title } = meta;
2384
2488
 
@@ -2401,6 +2505,8 @@ class BlockletManager extends BaseBlockletManager {
2401
2505
 
2402
2506
  // update state
2403
2507
  await states.blocklet.upgradeBlocklet({ meta, source, deployedFrom, children });
2508
+ // ensure component status is upgrading
2509
+ await states.blocklet.setBlockletStatus(did, BlockletStatus.upgrading, { componentDids });
2404
2510
  await this._setConfigsFromMeta(did);
2405
2511
 
2406
2512
  // should ensure blocklet integrity
@@ -2441,14 +2547,14 @@ class BlockletManager extends BaseBlockletManager {
2441
2547
 
2442
2548
  logger.info('updated blocklet for upgrading', { did, version, source, name });
2443
2549
 
2444
- const status =
2445
- oldBlocklet.status === BlockletStatus.installed ? BlockletStatus.installed : BlockletStatus.stopped;
2446
- await states.blocklet.setBlockletStatus(did, status, { children: 'all' });
2447
-
2448
- // start new process
2449
2550
  if (oldBlocklet.status === BlockletStatus.running) {
2450
- await this.start({ did }, context);
2551
+ // start new process
2552
+ await this.start({ did, componentDids }, context);
2451
2553
  logger.info('started blocklet for upgrading', { did, version });
2554
+ } else {
2555
+ const status =
2556
+ oldBlocklet.status === BlockletStatus.installed ? BlockletStatus.installed : BlockletStatus.stopped;
2557
+ await states.blocklet.setBlockletStatus(did, status, { componentDids });
2452
2558
  }
2453
2559
 
2454
2560
  blocklet = await this.getBlocklet(did, context);
@@ -2478,6 +2584,11 @@ class BlockletManager extends BaseBlockletManager {
2478
2584
 
2479
2585
  await this._rollbackCache.remove({ did: blocklet.meta.did });
2480
2586
 
2587
+ this.emit(BlockletInternalEvents.componentsUpdated, {
2588
+ appDid: blocklet.appDid,
2589
+ components: getComponentsInternalInfo(blocklet),
2590
+ });
2591
+
2481
2592
  return blocklet;
2482
2593
  } catch (err) {
2483
2594
  const b = await this._rollback('upgrade', did, oldBlocklet);
@@ -2524,7 +2635,7 @@ class BlockletManager extends BaseBlockletManager {
2524
2635
  * @return {*}
2525
2636
  * @memberof BlockletManager
2526
2637
  */
2527
- async _downloadBlocklet(blocklet, context = {}) {
2638
+ _downloadBlocklet(blocklet, context = {}) {
2528
2639
  const {
2529
2640
  appDid,
2530
2641
  meta: { did },
@@ -2534,12 +2645,14 @@ class BlockletManager extends BaseBlockletManager {
2534
2645
  ...context,
2535
2646
  preDownload: async ({ downloadComponentIds }) => {
2536
2647
  // update children status
2537
- const blocklet1 = await states.blocklet.setBlockletStatus(did, BlockletStatus.downloading, {
2538
- children: downloadComponentIds,
2539
- });
2540
- this.emit(BlockletEvents.statusChange, blocklet1);
2648
+ if (downloadComponentIds?.length) {
2649
+ const blocklet1 = await states.blocklet.setBlockletStatus(did, BlockletStatus.downloading, {
2650
+ componentDids: downloadComponentIds,
2651
+ });
2652
+ this.emit(BlockletEvents.statusChange, blocklet1);
2653
+ }
2541
2654
  },
2542
- onProgress: async (data) => {
2655
+ onProgress: (data) => {
2543
2656
  this.emit(BlockletEvents.downloadBundleProgress, { appDid: appDid || did, meta: { did }, ...data });
2544
2657
  },
2545
2658
  postDownload: async ({ isCancelled }) => {
@@ -2551,16 +2664,17 @@ class BlockletManager extends BaseBlockletManager {
2551
2664
  });
2552
2665
  }
2553
2666
 
2554
- async _syncPm2Status(pm2Status, did) {
2667
+ async _syncPm2Status(pm2Status, did, componentDid) {
2555
2668
  try {
2556
- const state = await states.blocklet.getBlocklet(did);
2557
- if (state && util.shouldUpdateBlockletStatus(state.status)) {
2669
+ const blocklet = await states.blocklet.getBlocklet(did);
2670
+ const component = findComponentByIdV2(blocklet, componentDid);
2671
+ if (component && util.shouldUpdateBlockletStatus(component.status)) {
2558
2672
  const newStatus = pm2StatusMap[pm2Status];
2559
- await states.blocklet.setBlockletStatus(did, newStatus);
2560
- logger.info('sync pm2 status to blocklet', { did, pm2Status, newStatus });
2673
+ await states.blocklet.setBlockletStatus(did, newStatus, { componentDids: [componentDid] });
2674
+ logger.info('sync pm2 status to blocklet', { did, componentDid, pm2Status, newStatus });
2561
2675
  }
2562
2676
  } catch (error) {
2563
- logger.error('sync pm2 status to blocklet failed', { did, pm2Status, error });
2677
+ logger.error('sync pm2 status to blocklet failed', { did, componentDid, pm2Status, error });
2564
2678
  }
2565
2679
  }
2566
2680