@abtnode/core 1.7.18 → 1.7.21

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.
@@ -29,10 +29,17 @@ const {
29
29
  isFreeBlocklet,
30
30
  isComponentBlocklet,
31
31
  isDeletableBlocklet,
32
- getRequiredMissingConfigs,
32
+ getAppMissingConfigs,
33
33
  hasRunnableComponent,
34
+ forEachBlockletSync,
35
+ forEachChildSync,
34
36
  forEachBlocklet,
37
+ forEachChild,
38
+ getComponentId,
39
+ getComponentName,
40
+ getComponentBundleId,
35
41
  } = require('@blocklet/meta/lib/util');
42
+ const getComponentProcessId = require('@blocklet/meta/lib/get-component-process-id');
36
43
  const validateBlockletEntry = require('@blocklet/meta/lib/entry');
37
44
  const toBlockletDid = require('@blocklet/meta/lib/did');
38
45
  const { validateMeta } = require('@blocklet/meta/lib/validate');
@@ -57,6 +64,7 @@ const {
57
64
  BLOCKLET_DEFAULT_VERSION,
58
65
  BLOCKLET_LATEST_SPEC_VERSION,
59
66
  BLOCKLET_META_FILE,
67
+ BLOCKLET_CONFIGURABLE_KEY,
60
68
  } = require('@blocklet/meta/lib/constants');
61
69
  const util = require('../../util');
62
70
  const {
@@ -64,13 +72,13 @@ const {
64
72
  getFromCache: getAccessibleExternalNodeIp,
65
73
  } = require('../../util/get-accessible-external-node-ip');
66
74
  const {
67
- getBlockletDirs,
75
+ getComponentDirs,
68
76
  getBlockletMetaFromUrl,
69
77
  fillBlockletConfigs,
70
78
  ensureBlockletExpanded,
71
- getRootSystemEnvironments,
72
- getSystemEnvironments,
73
- getOverwrittenEnvironments,
79
+ getAppSystemEnvironments,
80
+ getComponentSystemEnvironments,
81
+ getAppOverwrittenEnvironments,
74
82
  getHealthyCheckTimeout,
75
83
  startBlockletProcess,
76
84
  stopBlockletProcess,
@@ -89,7 +97,6 @@ const {
89
97
  getRuntimeEnvironments,
90
98
  getSourceFromInstallParams,
91
99
  parseChildrenFromMeta,
92
- parseChildren,
93
100
  checkDuplicateComponents,
94
101
  getDiffFiles,
95
102
  getBundleDir,
@@ -136,6 +143,31 @@ const pm2StatusMap = {
136
143
  */
137
144
  const getBlockletEngineNameByPlatform = (blockletMeta) => getBlockletEngine(blockletMeta).interpreter;
138
145
 
146
+ const getSkippedProcessIds = ({ newBlocklet, oldBlocklet, context = {} }) => {
147
+ const { forceStartProcessIds = [] } = context;
148
+ const idMap = {};
149
+ const res = [];
150
+
151
+ forEachBlockletSync(oldBlocklet, (b, { ancestors }) => {
152
+ if (b.meta.dist?.integrity) {
153
+ idMap[getComponentProcessId(b, ancestors)] = b.meta.dist?.integrity;
154
+ }
155
+ });
156
+
157
+ forEachBlockletSync(newBlocklet, (b, { ancestors }) => {
158
+ const id = getComponentProcessId(b, ancestors);
159
+ if (forceStartProcessIds.includes(id)) {
160
+ return;
161
+ }
162
+
163
+ if (!b.meta.dist?.integrity || b.meta.dist.integrity === idMap[id]) {
164
+ res.push(id);
165
+ }
166
+ });
167
+
168
+ return res;
169
+ };
170
+
139
171
  class BlockletManager extends BaseBlockletManager {
140
172
  /**
141
173
  * @param {*} dataDirs generate by ../../util:getDataDirs
@@ -298,7 +330,7 @@ class BlockletManager extends BaseBlockletManager {
298
330
  }
299
331
 
300
332
  // check required config
301
- const missingProps = getRequiredMissingConfigs(blocklet);
333
+ const missingProps = getAppMissingConfigs(blocklet);
302
334
  if (missingProps.length) {
303
335
  throw new Error(
304
336
  `Missing required configuration to start the blocklet: ${missingProps.map((x) => x.key).join(',')}`
@@ -319,11 +351,12 @@ class BlockletManager extends BaseBlockletManager {
319
351
  // start process
320
352
  const nodeEnvironments = await states.node.getEnvironments();
321
353
  await startBlockletProcess(blocklet, {
322
- preStart: (b) =>
354
+ ...context,
355
+ preStart: (b, { env }) =>
323
356
  hooks.preStart(b, {
324
357
  appDir: b.env.appDir,
325
358
  hooks: Object.assign(b.meta.hooks || {}, b.meta.scripts || {}),
326
- env: getRuntimeEnvironments(b, nodeEnvironments, blocklet),
359
+ env,
327
360
  did, // root blocklet did,
328
361
  }),
329
362
  nodeEnvironments,
@@ -380,7 +413,7 @@ class BlockletManager extends BaseBlockletManager {
380
413
  logger.info('stop blocklet', { did });
381
414
 
382
415
  const blocklet = await this.ensureBlocklet(did);
383
- const { appId } = blocklet.env;
416
+ const { processId } = blocklet.env;
384
417
 
385
418
  if (updateStatus) {
386
419
  await states.blocklet.setBlockletStatus(did, BlockletStatus.stopping);
@@ -391,11 +424,11 @@ class BlockletManager extends BaseBlockletManager {
391
424
  const nodeEnvironments = await states.node.getEnvironments();
392
425
 
393
426
  await stopBlockletProcess(blocklet, {
394
- preStop: (b) =>
395
- hooks.preStop(b.env.appId, {
427
+ preStop: (b, { ancestors }) =>
428
+ hooks.preStop(b.env.processId, {
396
429
  appDir: b.env.appDir,
397
430
  hooks: Object.assign(b.meta.hooks || {}, b.meta.scripts || {}),
398
- env: getRuntimeEnvironments(b, nodeEnvironments, blocklet),
431
+ env: getRuntimeEnvironments(b, nodeEnvironments, ancestors),
399
432
  did, // root blocklet did
400
433
  notification: states.notification,
401
434
  context,
@@ -404,7 +437,7 @@ class BlockletManager extends BaseBlockletManager {
404
437
  }),
405
438
  });
406
439
 
407
- logger.info('blocklet stopped successfully', { appId, did });
440
+ logger.info('blocklet stopped successfully', { processId, did });
408
441
 
409
442
  if (updateStatus) {
410
443
  const res = await this.status(did, { forceSync: true });
@@ -467,11 +500,11 @@ class BlockletManager extends BaseBlockletManager {
467
500
  const nodeEnvironments = await states.node.getEnvironments();
468
501
 
469
502
  await deleteBlockletProcess(blocklet, {
470
- preDelete: (b) =>
471
- hooks.preUninstall(b.env.appId, {
503
+ preDelete: (b, { ancestors }) =>
504
+ hooks.preUninstall(b.env.processId, {
472
505
  appDir: b.env.appDir,
473
506
  hooks: Object.assign(b.meta.hooks || {}, b.meta.scripts || {}),
474
- env: getRuntimeEnvironments(b, nodeEnvironments, blocklet),
507
+ env: getRuntimeEnvironments(b, nodeEnvironments, ancestors),
475
508
  did, // root blocklet did
476
509
  notification: states.notification,
477
510
  context,
@@ -515,7 +548,7 @@ class BlockletManager extends BaseBlockletManager {
515
548
 
516
549
  // Reset config in db
517
550
  await states.blockletExtras.remove({ did: blocklet.meta.did });
518
- await this._setConfigs(did);
551
+ await this._setConfigsFromMeta(did);
519
552
  await this.updateBlockletEnvironment(did);
520
553
  await this.resetSiteByDid(did, context);
521
554
  } else {
@@ -532,8 +565,8 @@ class BlockletManager extends BaseBlockletManager {
532
565
  fs.removeSync(logsDir);
533
566
 
534
567
  // Reset config in db
535
- await states.blockletExtras.delChildConfigs(blocklet.meta.did, child.meta.did);
536
- await this._setConfigs(blocklet.meta.did, child.meta.did);
568
+ await states.blockletExtras.delConfigs([blocklet.meta.did, child.meta.did]);
569
+ await this._setConfigsFromMeta(blocklet.meta.did, child.meta.did);
537
570
  await this.updateBlockletEnvironment(did);
538
571
  }
539
572
 
@@ -545,17 +578,16 @@ class BlockletManager extends BaseBlockletManager {
545
578
  logger.info('delete blocklet component', { did, rootDid, keepData });
546
579
 
547
580
  const blocklet = await this.ensureBlocklet(rootDid);
548
- const doc = await states.blocklet.getBlocklet(rootDid);
549
-
550
- const child = doc.children.find((x) => x.meta.did === did);
551
-
581
+ const child = blocklet.children.find((x) => x.meta.did === did);
552
582
  if (!child) {
553
583
  throw new Error('Component does not exist');
554
584
  }
555
585
 
586
+ // delete state
587
+ const doc = await states.blocklet.getBlocklet(rootDid);
556
588
  doc.children = doc.children.filter((x) => x.meta.did !== did);
557
589
  const children = (await this._getDynamicChildrenFromSettings(blocklet.meta.did)).filter((x) => x.meta.did !== did);
558
- if (keepState !== false) {
590
+ if (keepData !== false && keepState !== false) {
559
591
  children.push({
560
592
  meta: pick(child.meta, ['did', 'name', 'bundleDid', 'bundleName', 'version', 'title', 'description']),
561
593
  mountPoint: child.mountPoint,
@@ -565,7 +597,21 @@ class BlockletManager extends BaseBlockletManager {
565
597
  }
566
598
 
567
599
  await states.blocklet.updateBlocklet(rootDid, doc);
568
- states.blockletExtras.setSettings(blocklet.meta.did, { children });
600
+ states.blockletExtras.setSettings(doc.meta.did, { children });
601
+
602
+ // delete process
603
+ try {
604
+ const skippedProcessIds = [];
605
+ forEachBlockletSync(blocklet, (b) => {
606
+ if (!b.env.id.startsWith(child.env.id)) {
607
+ skippedProcessIds.push(b.env.processId);
608
+ }
609
+ });
610
+ await deleteBlockletProcess(blocklet, { skippedProcessIds });
611
+ logger.info('delete blocklet process for deleting component', { did, rootDid });
612
+ } catch (err) {
613
+ logger.error('delete blocklet process for deleting component', { did, rootDid, error: err });
614
+ }
569
615
 
570
616
  // delete navigation
571
617
  const navigation = await states.blockletExtras.getSettings(blocklet.meta.did, 'navigation', []);
@@ -582,7 +628,7 @@ class BlockletManager extends BaseBlockletManager {
582
628
  fs.removeSync(logsDir);
583
629
  if (keepData === false) {
584
630
  fs.removeSync(dataDir);
585
- await states.blockletExtras.delChildConfigs(blocklet.meta.did, child.meta.did);
631
+ await states.blockletExtras.delConfigs([blocklet.meta.did, child.meta.did]);
586
632
  }
587
633
 
588
634
  const newBlocklet = await this.ensureBlocklet(rootDid);
@@ -620,7 +666,7 @@ class BlockletManager extends BaseBlockletManager {
620
666
 
621
667
  logger.info('delete blocklet process', { did });
622
668
 
623
- await deleteBlockletProcess(blocklet);
669
+ await deleteBlockletProcess(blocklet, context);
624
670
 
625
671
  const result = await states.blocklet.setBlockletStatus(did, BlockletStatus.stopped);
626
672
  logger.info('blocklet process deleted successfully', { did });
@@ -683,23 +729,31 @@ class BlockletManager extends BaseBlockletManager {
683
729
  }
684
730
 
685
731
  // eslint-disable-next-line no-unused-vars
686
- async config({ did, childDid, configs: newConfigs, skipHook }, context) {
687
- logger.info('config blocklet', { did });
732
+ async config({ did, configs: newConfigs, skipHook }, context) {
688
733
  if (!Array.isArray(newConfigs)) {
689
734
  throw new Error('configs list is not an array');
690
735
  }
691
736
 
692
- let blocklet = await this.ensureBlocklet(did);
693
- if (childDid) {
737
+ const dids = Array.isArray(did) ? did : [did];
738
+ const [rootDid, ...childDids] = dids;
739
+ logger.info('config blocklet', { dids });
740
+
741
+ let blocklet = await this.ensureBlocklet(rootDid);
742
+ for (const childDid of childDids) {
694
743
  blocklet = blocklet.children.find((x) => x.meta.did === childDid);
695
744
  if (!blocklet) {
696
- throw new Error('Child blocklet does not exist', { did, childDid });
745
+ throw new Error('Child blocklet does not exist', { dids });
697
746
  }
698
747
  }
699
748
 
700
749
  // run hook
701
750
  const nodeEnvironments = await states.node.getEnvironments();
702
751
  for (const x of newConfigs) {
752
+ if (BLOCKLET_CONFIGURABLE_KEY[x.key] && !!childDids.length) {
753
+ logger.error(`Cannot set ${x.key} to child blocklet`, [dids]);
754
+ throw new Error(`Cannot set ${x.key} to child blocklet`);
755
+ }
756
+
703
757
  if (x.key === 'BLOCKLET_APP_SK') {
704
758
  try {
705
759
  fromSecretKey(x.value);
@@ -752,7 +806,7 @@ class BlockletManager extends BaseBlockletManager {
752
806
 
753
807
  if (!skipHook) {
754
808
  // FIXME: we should also call preConfig for child blocklets
755
- await hooks.preConfig(blocklet.env.appId, {
809
+ await hooks.preConfig(blocklet.env.processId, {
756
810
  appDir: blocklet.env.appDir,
757
811
  hooks: Object.assign(blocklet.meta.hooks || {}, blocklet.meta.scripts || {}),
758
812
  exitOnError: true,
@@ -763,20 +817,11 @@ class BlockletManager extends BaseBlockletManager {
763
817
  }
764
818
 
765
819
  // update db
766
- const configs = childDid
767
- ? await states.blockletExtras.setChildConfigs(did, childDid, newConfigs)
768
- : await states.blockletExtras.setConfigs(did, newConfigs);
769
-
770
- const newState = await this.updateBlockletEnvironment(did);
820
+ await states.blockletExtras.setConfigs(dids, newConfigs);
821
+ await this.updateBlockletEnvironment(rootDid);
771
822
 
772
823
  // response
773
- if (!childDid) {
774
- newState.configs = configs;
775
- } else {
776
- const c = newState.children.find((x) => x.meta.did === childDid);
777
- c.configs = configs;
778
- }
779
-
824
+ const newState = await this.ensureBlocklet(rootDid);
780
825
  this.emit(BlockletEvents.updated, newState);
781
826
  return newState;
782
827
  }
@@ -915,24 +960,29 @@ class BlockletManager extends BaseBlockletManager {
915
960
 
916
961
  async checkChildrenForUpdates({ did }) {
917
962
  const blocklet = await states.blocklet.getBlocklet(did);
918
- const newStaticChildren = await parseChildrenFromMeta(blocklet.meta);
919
-
920
- const oldDynamicChildren = await this._getDynamicChildrenFromSettings(blocklet.meta.did, { skipDeleted: true });
921
- const noneBundleSourceChildren = oldDynamicChildren.filter((x) => !x.bundleSource);
922
- const dynamicConfig = oldDynamicChildren
923
- .filter((x) => x.bundleSource)
924
- .map((x) => ({ source: x.bundleSource, name: x.meta.name, title: x.meta.title, mountPoint: x.mountPoint }));
925
- const newDynamicChildren = [
926
- ...noneBundleSourceChildren,
927
- ...(await parseChildrenFromMeta(dynamicConfig, { dynamic: true })),
928
- ];
929
963
 
930
- checkDuplicateComponents([...newDynamicChildren, ...newStaticChildren]);
964
+ const newChildren = [];
931
965
 
932
- const updateList = getUpdateMetaList(
933
- [...blocklet.children.map((x) => x.meta), ...oldDynamicChildren.map((x) => x.meta)],
934
- [...newStaticChildren.map((x) => x.meta), ...newDynamicChildren.map((x) => x.meta)]
935
- );
966
+ for (const child of blocklet.children || []) {
967
+ if (child.bundleSource) {
968
+ const config = {
969
+ source: child.bundleSource,
970
+ name: child.meta.name,
971
+ title: child.meta.title,
972
+ mountPoint: child.mountPoint,
973
+ };
974
+ newChildren.push(...(await parseChildrenFromMeta([config], { dynamic: child.dynamic })));
975
+ } else {
976
+ newChildren.push({
977
+ ...child,
978
+ children: await parseChildrenFromMeta(child.meta),
979
+ });
980
+ }
981
+ }
982
+
983
+ checkDuplicateComponents(newChildren);
984
+
985
+ const updateList = getUpdateMetaList(blocklet, { ...blocklet, children: newChildren });
936
986
 
937
987
  if (!updateList.length) {
938
988
  return {};
@@ -941,8 +991,7 @@ class BlockletManager extends BaseBlockletManager {
941
991
  // start session
942
992
  const { id: updateId } = await states.session.start({
943
993
  did,
944
- staticChildren: newStaticChildren,
945
- dynamicChildren: newDynamicChildren,
994
+ children: newChildren,
946
995
  });
947
996
 
948
997
  return {
@@ -962,8 +1011,7 @@ class BlockletManager extends BaseBlockletManager {
962
1011
  } else {
963
1012
  const sessionData = await states.session.end(updateId);
964
1013
  did = sessionData.did;
965
- const { staticChildren = [], dynamicChildren = [] } = sessionData;
966
- children = [...staticChildren, ...dynamicChildren.map((x) => ({ ...x, dynamic: true }))];
1014
+ children = sessionData.children;
967
1015
  oldBlocklet = await this._getBlockletForInstallation(did);
968
1016
  }
969
1017
 
@@ -1070,7 +1118,7 @@ class BlockletManager extends BaseBlockletManager {
1070
1118
  }
1071
1119
  }
1072
1120
 
1073
- const children = await this._getChildren(meta);
1121
+ const children = await this._getChildrenForInstallation(meta);
1074
1122
  await validateBlocklet({ meta, children });
1075
1123
  const added = await states.blocklet.addBlocklet({
1076
1124
  meta,
@@ -1086,7 +1134,7 @@ class BlockletManager extends BaseBlockletManager {
1086
1134
  await states.blocklet.setBlockletStatus(did, BlockletStatus.installed);
1087
1135
 
1088
1136
  // Add environments
1089
- await this._setConfigs(did);
1137
+ await this._setConfigsFromMeta(did);
1090
1138
  await this.updateBlockletEnvironment(did);
1091
1139
 
1092
1140
  const blocklet = await this.ensureBlocklet(did);
@@ -1121,7 +1169,7 @@ class BlockletManager extends BaseBlockletManager {
1121
1169
  }
1122
1170
 
1123
1171
  const defaultPath = formatName(meta.name);
1124
- const children = await parseChildren([
1172
+ const children = [
1125
1173
  {
1126
1174
  meta: ensureMeta(meta),
1127
1175
  mountPoint: mountPoint || `/${defaultPath}`,
@@ -1130,18 +1178,24 @@ class BlockletManager extends BaseBlockletManager {
1130
1178
  status: BlockletStatus.installed,
1131
1179
  mode: BLOCKLET_MODES.DEVELOPMENT,
1132
1180
  dynamic: true,
1181
+ children: await parseChildrenFromMeta(meta, { ancestors: [{ mountPoint: mountPoint || `/${defaultPath}` }] }),
1133
1182
  },
1134
- ]);
1183
+ ];
1135
1184
  await validateBlocklet(children[0]);
1136
1185
  await states.blocklet.addChildren(rootDid, children);
1137
- await this._upsertDynamicNavigation(existRoot.meta.did, children[0]);
1138
1186
 
1139
- logger.info('add blocklet component for dev', { did, version, meta });
1187
+ const newBlocklet = await states.blocklet.getBlocklet(rootDid);
1188
+ await this._downloadBlocklet(newBlocklet, existRoot);
1189
+ await states.blocklet.setBlockletStatus(rootDid, BlockletStatus.stopped);
1190
+
1191
+ await this._upsertDynamicNavigation(existRoot.meta.did, children[0]);
1140
1192
 
1141
1193
  // Add environments
1142
- await this._setConfigs(rootDid);
1194
+ await this._setConfigsFromMeta(rootDid);
1143
1195
  await this.updateBlockletEnvironment(rootDid);
1144
1196
 
1197
+ logger.info('add blocklet component for dev', { did, version, meta });
1198
+
1145
1199
  const rootBlocklet = await this.ensureBlocklet(rootDid);
1146
1200
 
1147
1201
  return rootBlocklet;
@@ -1157,56 +1211,34 @@ class BlockletManager extends BaseBlockletManager {
1157
1211
  throw new Error(`Can not find blocklet in database by did ${did}`);
1158
1212
  }
1159
1213
 
1160
- // env
1161
- blocklet.env = {
1162
- appId: blocklet.meta.name,
1163
- // dataDir is /dataDir.data/blocklet.meta.name
1164
- // cacheDir is /dataDirs.cache/blocklet.meta.name
1165
- // logDir is /dataDirs.log/blocklet.meta.name
1166
- ...getBlockletDirs(blocklet, {
1167
- dataDirs: this.dataDirs,
1168
- ensure: true,
1169
- e2eMode,
1170
- validate: validateEnv,
1171
- }),
1172
- };
1173
-
1174
- // configs
1175
- const configs = await states.blockletExtras.getConfigs(blocklet.meta.did);
1176
- fillBlockletConfigs(blocklet, configs);
1177
-
1178
- // settings
1214
+ // app settings
1179
1215
  const settings = await states.blockletExtras.getSettings(blocklet.meta.did);
1180
1216
  blocklet.trustedPassports = get(settings, 'trustedPassports') || [];
1181
1217
  blocklet.enablePassportIssuance = get(settings, 'enablePassportIssuance', true);
1182
1218
  blocklet.settings = settings || {};
1183
1219
 
1184
- // site
1220
+ // app site
1185
1221
  blocklet.site = await this.getSiteByDid(blocklet.meta.did);
1186
1222
 
1187
- // handle child env
1188
- for (const child of blocklet.children) {
1189
- const {
1190
- meta: { did: childDid, name: childName },
1191
- } = child;
1192
- const {
1193
- meta: { name },
1194
- } = blocklet;
1195
-
1196
- // child env
1197
- child.env = {
1198
- appId: `${encodeURIComponent(name)}/${encodeURIComponent(childName)}`,
1199
- ...getBlockletDirs(child, {
1223
+ await forEachBlocklet(blocklet, async (component, { id, level, ancestors }) => {
1224
+ // component env
1225
+ component.env = {
1226
+ id,
1227
+ name: getComponentName(component, ancestors),
1228
+ processId: getComponentProcessId(component, ancestors),
1229
+ ...getComponentDirs(component, {
1200
1230
  dataDirs: this.dataDirs,
1201
1231
  ensure: true,
1202
- rootBlocklet: blocklet,
1232
+ validate: validateEnv,
1233
+ ancestors,
1234
+ e2eMode: level === 0 ? e2eMode : false,
1203
1235
  }),
1204
1236
  };
1205
1237
 
1206
- // child configs
1207
- const childConfigs = await states.blockletExtras.getChildConfigs(blocklet.meta.did, childDid);
1208
- fillBlockletConfigs(child, childConfigs, blocklet);
1209
- }
1238
+ // component config
1239
+ const configs = await states.blockletExtras.getConfigs([...ancestors.map((x) => x.meta.did), component.meta.did]);
1240
+ fillBlockletConfigs(component, configs);
1241
+ });
1210
1242
 
1211
1243
  return blocklet;
1212
1244
  }
@@ -1295,8 +1327,8 @@ class BlockletManager extends BaseBlockletManager {
1295
1327
  try {
1296
1328
  const app = blocklet.meta.group === BlockletGroup.gateway ? blocklet.children[0] : blocklet;
1297
1329
  if (app) {
1298
- const { appId } = app.env;
1299
- blocklet.runtimeInfo = await getRuntimeInfo(appId);
1330
+ const { processId } = app.env;
1331
+ blocklet.runtimeInfo = await getRuntimeInfo(processId);
1300
1332
  if (blocklet.runtimeInfo.status && shouldUpdateBlockletStatus(blocklet.status)) {
1301
1333
  blocklet.status = statusMap[blocklet.runtimeInfo.status];
1302
1334
  }
@@ -1312,24 +1344,24 @@ class BlockletManager extends BaseBlockletManager {
1312
1344
  }
1313
1345
  }
1314
1346
 
1315
- await Promise.all(
1316
- blocklet.children.map(async (child) => {
1317
- child.engine = getEngine(getBlockletEngineNameByPlatform(child.meta)).describe();
1318
- child.diskInfo = await getDiskInfo(child, {
1319
- useFakeDiskInfo: !diskInfo,
1320
- });
1321
- if (child.meta.group !== BlockletGroup.gateway) {
1322
- try {
1323
- child.runtimeInfo = await getRuntimeInfo(child.env.appId);
1324
- child.status = statusMap[blocklet.runtimeInfo.status];
1325
- } catch (err) {
1326
- if (err.code !== 'BLOCKLET_PROCESS_404') {
1327
- logger.error('failed to construct blocklet runtime info', { did, error: err });
1328
- }
1347
+ await forEachChild(blocklet, async (child) => {
1348
+ child.engine = getEngine(getBlockletEngineNameByPlatform(child.meta)).describe();
1349
+ child.diskInfo = await getDiskInfo(child, {
1350
+ useFakeDiskInfo: !diskInfo,
1351
+ });
1352
+ if (child.meta.group !== BlockletGroup.gateway) {
1353
+ try {
1354
+ child.runtimeInfo = await getRuntimeInfo(child.env.processId);
1355
+ if (child.runtimeInfo.status && shouldUpdateBlockletStatus(child.status)) {
1356
+ child.status = statusMap[child.runtimeInfo.status];
1357
+ }
1358
+ } catch (err) {
1359
+ if (err.code !== 'BLOCKLET_PROCESS_404') {
1360
+ logger.error('failed to construct blocklet runtime info', { did, error: err });
1329
1361
  }
1330
1362
  }
1331
- })
1332
- );
1363
+ }
1364
+ });
1333
1365
  }
1334
1366
 
1335
1367
  return blocklet;
@@ -1459,7 +1491,7 @@ class BlockletManager extends BaseBlockletManager {
1459
1491
  });
1460
1492
 
1461
1493
  if (context.startImmediately) {
1462
- const missingProps = getRequiredMissingConfigs(installedBlocklet);
1494
+ const missingProps = getAppMissingConfigs(installedBlocklet);
1463
1495
  if (!missingProps.length) {
1464
1496
  try {
1465
1497
  logger.info('start blocklet after installed', { did });
@@ -1538,29 +1570,37 @@ class BlockletManager extends BaseBlockletManager {
1538
1570
  const blocklet = await states.blocklet.getBlocklet(did);
1539
1571
  const nodeInfo = await states.node.read();
1540
1572
 
1541
- const rootSystemEnvironments = {
1542
- ...getRootSystemEnvironments(blockletWithEnv, nodeInfo),
1543
- ...getOverwrittenEnvironments(blockletWithEnv, nodeInfo),
1573
+ const appSystemEnvironments = {
1574
+ ...getAppSystemEnvironments(blockletWithEnv, nodeInfo),
1575
+ ...getAppOverwrittenEnvironments(blockletWithEnv, nodeInfo),
1544
1576
  };
1545
1577
 
1546
- // fill environments to blocklet and blocklet.children
1578
+ // fill environments to blocklet and components
1547
1579
  blocklet.environments = formatEnvironments({
1548
- ...getSystemEnvironments(blockletWithEnv),
1549
- ...rootSystemEnvironments,
1580
+ ...getComponentSystemEnvironments(blockletWithEnv),
1581
+ ...appSystemEnvironments,
1582
+ });
1583
+
1584
+ const envMap = {};
1585
+ forEachBlockletSync(blockletWithEnv, (child, { ancestors }) => {
1586
+ const id = getComponentId(child, ancestors);
1587
+ envMap[id] = child;
1550
1588
  });
1551
1589
 
1552
- for (const child of blocklet.children) {
1553
- const childWithEnv = blockletWithEnv.children.find((x) => x.meta.did === child.meta.did);
1590
+ forEachChildSync(blocklet, (child, { ancestors }) => {
1591
+ const id = getComponentId(child, ancestors);
1592
+
1593
+ const childWithEnv = envMap[id];
1554
1594
  if (childWithEnv) {
1555
1595
  child.environments = formatEnvironments({
1556
- ...getSystemEnvironments(childWithEnv), // system env of child blocklet
1557
- ...rootSystemEnvironments, // system env of root blocklet
1596
+ ...getComponentSystemEnvironments(childWithEnv), // system env of child blocklet
1597
+ ...appSystemEnvironments, // system env of root blocklet
1558
1598
  });
1559
1599
  }
1560
- }
1600
+ });
1561
1601
 
1562
1602
  // put BLOCKLET_APP_ID at root level for indexing
1563
- blocklet.appDid = rootSystemEnvironments.BLOCKLET_APP_ID;
1603
+ blocklet.appDid = appSystemEnvironments.BLOCKLET_APP_ID;
1564
1604
 
1565
1605
  // update state to db
1566
1606
  return states.blocklet.updateBlocklet(did, blocklet);
@@ -1653,10 +1693,6 @@ class BlockletManager extends BaseBlockletManager {
1653
1693
 
1654
1694
  const meta = await getBlockletMetaFromUrl(url);
1655
1695
 
1656
- if (meta.did === rootDid) {
1657
- throw new Error('Cannot add self as a component');
1658
- }
1659
-
1660
1696
  if (!isComponentBlocklet(meta)) {
1661
1697
  throw new Error('The blocklet cannot be a component');
1662
1698
  }
@@ -1678,14 +1714,15 @@ class BlockletManager extends BaseBlockletManager {
1678
1714
  did = found.did;
1679
1715
  }
1680
1716
 
1681
- const newChildren = await parseChildren([
1717
+ const newChildren = [
1682
1718
  {
1683
1719
  meta: ensureMeta(meta, { did, name }),
1684
1720
  mountPoint,
1685
1721
  bundleSource: { url },
1686
1722
  dynamic: true,
1723
+ children: await parseChildrenFromMeta(meta, { ...context, ancestors: [{ mountPoint }] }),
1687
1724
  },
1688
- ]);
1725
+ ];
1689
1726
 
1690
1727
  checkDuplicateComponents([...blocklet.children, ...newChildren]);
1691
1728
 
@@ -1735,7 +1772,7 @@ class BlockletManager extends BaseBlockletManager {
1735
1772
  meta,
1736
1773
  source: BlockletSource.custom,
1737
1774
  });
1738
- await this._setConfigs(did);
1775
+ await this._setConfigsFromMeta(did);
1739
1776
 
1740
1777
  // fake install bundle
1741
1778
  const bundleDir = getBundleDir(this.installDir, meta);
@@ -1801,14 +1838,14 @@ class BlockletManager extends BaseBlockletManager {
1801
1838
  newBlocklet.meta = ensureMeta(meta);
1802
1839
  newBlocklet.source = BlockletSource.upload;
1803
1840
  newBlocklet.deployedFrom = `Upload by ${context.user.fullName}`;
1804
- newBlocklet.children = await this._getChildren(meta);
1841
+ newBlocklet.children = await this._getChildrenForInstallation(meta);
1805
1842
  await validateBlocklet(newBlocklet);
1806
1843
  await this._downloadBlocklet(newBlocklet, oldBlocklet);
1807
1844
 
1808
1845
  return this._upgradeBlocklet({
1809
1846
  oldBlocklet,
1810
1847
  newBlocklet,
1811
- context,
1848
+ context: { ...context, forceStartProcessIds: [getComponentProcessId(newBlocklet)] },
1812
1849
  });
1813
1850
  }
1814
1851
 
@@ -1827,7 +1864,7 @@ class BlockletManager extends BaseBlockletManager {
1827
1864
  newBlocklet.meta = ensureMeta(meta);
1828
1865
  newBlocklet.source = BlockletSource.upload;
1829
1866
  newBlocklet.deployedFrom = `Upload by ${context.user.fullName}`;
1830
- newBlocklet.children = await this._getChildren(meta);
1867
+ newBlocklet.children = await this._getChildrenForInstallation(meta);
1831
1868
 
1832
1869
  await validateBlocklet(newBlocklet);
1833
1870
  await this._downloadBlocklet(newBlocklet, oldBlocklet);
@@ -1835,13 +1872,14 @@ class BlockletManager extends BaseBlockletManager {
1835
1872
  return this._upgradeBlocklet({
1836
1873
  oldBlocklet,
1837
1874
  newBlocklet,
1838
- context,
1875
+ context: { ...context, forceStartProcessIds: [getComponentProcessId(newBlocklet)] },
1839
1876
  });
1840
1877
  }
1841
1878
 
1842
1879
  // full deploy - install
1880
+
1843
1881
  const oldExtraState = await states.blockletExtras.findOne({ did: meta.did });
1844
- const children = await this._getChildren(meta);
1882
+ const children = await this._getChildrenForInstallation(meta);
1845
1883
  const blocklet = await states.blocklet.addBlocklet({
1846
1884
  meta,
1847
1885
  source: BlockletSource.upload,
@@ -1849,21 +1887,26 @@ class BlockletManager extends BaseBlockletManager {
1849
1887
  children,
1850
1888
  });
1851
1889
 
1852
- await validateBlocklet(blocklet);
1890
+ try {
1891
+ await this._setConfigsFromMeta(meta.did);
1892
+ await validateBlocklet(blocklet);
1853
1893
 
1854
- await this._setConfigs(meta.did);
1894
+ // download
1895
+ await this._downloadBlocklet(
1896
+ blocklet,
1897
+ // let downloader skip re-downloading dynamic blocklet
1898
+ { children: children.filter((x) => x.dynamic) }
1899
+ );
1855
1900
 
1856
- // download
1857
- await this._downloadBlocklet(
1858
- blocklet,
1859
- // let downloader skip re-downloading dynamic blocklet
1860
- { children: children.filter((x) => x.dynamic) }
1861
- );
1862
- return this._installBlocklet({
1863
- did: meta.did,
1864
- context,
1865
- oldBlocklet: { extraState: oldExtraState },
1866
- });
1901
+ return this._installBlocklet({
1902
+ did: meta.did,
1903
+ context,
1904
+ oldBlocklet: { extraState: oldExtraState },
1905
+ });
1906
+ } catch (error) {
1907
+ await this._rollback('install', meta.did, { extraState: oldExtraState });
1908
+ throw error;
1909
+ }
1867
1910
  }
1868
1911
 
1869
1912
  async _installComponentFromUpload({ rootDid, mountPoint, file, did, diffVersion, deleteSet, context }) {
@@ -1922,6 +1965,7 @@ class BlockletManager extends BaseBlockletManager {
1922
1965
  deployedFrom: `Upload by ${context.user.fullName}`,
1923
1966
  bundleSource: null,
1924
1967
  dynamic: true,
1968
+ children: await parseChildrenFromMeta(meta, { ancestors: [{ mountPoint }] }),
1925
1969
  };
1926
1970
  const index = newBlocklet.children.findIndex((child) => child.meta.did === meta.did);
1927
1971
  if (index >= 0) {
@@ -1931,12 +1975,14 @@ class BlockletManager extends BaseBlockletManager {
1931
1975
  }
1932
1976
  await this._upsertDynamicNavigation(newBlocklet.meta.did, newChild);
1933
1977
 
1978
+ await this._downloadBlocklet(newBlocklet, oldBlocklet);
1979
+
1934
1980
  await validateBlocklet(newBlocklet);
1935
1981
 
1936
1982
  return this._upgradeBlocklet({
1937
1983
  oldBlocklet,
1938
1984
  newBlocklet,
1939
- context,
1985
+ context: { ...context, forceStartProcessIds: [getComponentProcessId(newChild, [newBlocklet])] },
1940
1986
  });
1941
1987
  }
1942
1988
 
@@ -2058,15 +2104,12 @@ class BlockletManager extends BaseBlockletManager {
2058
2104
  blocklets.forEach(run);
2059
2105
  }
2060
2106
 
2061
- async _getChildren(meta) {
2107
+ async _getChildrenForInstallation(meta) {
2062
2108
  const staticChildren = await parseChildrenFromMeta(meta);
2063
- const dynamicChildren = await parseChildren(
2064
- await this._getDynamicChildrenFromSettings(meta.did, { skipDeleted: true }),
2065
- meta,
2066
- {
2067
- dynamic: true,
2068
- }
2069
- );
2109
+ const dynamicChildren = await this._getDynamicChildrenFromSettings(meta.did, { skipDeleted: true });
2110
+ for (const dynamicChild of dynamicChildren) {
2111
+ dynamicChild.children = await parseChildrenFromMeta(dynamicChild.meta, { ancestors: [dynamicChild] });
2112
+ }
2070
2113
  checkDuplicateComponents([...dynamicChildren, ...staticChildren]);
2071
2114
 
2072
2115
  return [...staticChildren, ...dynamicChildren];
@@ -2086,7 +2129,7 @@ class BlockletManager extends BaseBlockletManager {
2086
2129
 
2087
2130
  const oldExtraState = await states.blockletExtras.findOne({ did: meta.did });
2088
2131
 
2089
- const children = await this._getChildren(meta);
2132
+ const children = await this._getChildrenForInstallation(meta);
2090
2133
  try {
2091
2134
  const blocklet = await states.blocklet.addBlocklet({
2092
2135
  meta,
@@ -2097,7 +2140,7 @@ class BlockletManager extends BaseBlockletManager {
2097
2140
 
2098
2141
  await validateBlocklet(blocklet);
2099
2142
 
2100
- await this._setConfigs(did);
2143
+ await this._setConfigsFromMeta(did);
2101
2144
 
2102
2145
  logger.info('blocklet added to database', { did: meta.did });
2103
2146
 
@@ -2183,7 +2226,7 @@ class BlockletManager extends BaseBlockletManager {
2183
2226
  newBlocklet.meta = ensureMeta(meta);
2184
2227
  newBlocklet.source = source;
2185
2228
  newBlocklet.deployedFrom = deployedFrom;
2186
- newBlocklet.children = await this._getChildren(meta);
2229
+ newBlocklet.children = await this._getChildrenForInstallation(meta);
2187
2230
 
2188
2231
  await validateBlocklet(newBlocklet);
2189
2232
 
@@ -2379,7 +2422,7 @@ class BlockletManager extends BaseBlockletManager {
2379
2422
  // pre install
2380
2423
  const nodeEnvironments = await states.node.getEnvironments();
2381
2424
  const preInstall = (b) =>
2382
- hooks.preInstall(b.env.appId, {
2425
+ hooks.preInstall(b.env.processId, {
2383
2426
  hooks: Object.assign(b.meta.hooks || {}, b.meta.scripts || {}),
2384
2427
  env: { ...nodeEnvironments },
2385
2428
  appDir: b.env.appDir,
@@ -2394,10 +2437,10 @@ class BlockletManager extends BaseBlockletManager {
2394
2437
  blocklet = await this.ensureBlocklet(did);
2395
2438
 
2396
2439
  // post install
2397
- const postInstall = (b) =>
2398
- hooks.postInstall(b.env.appId, {
2440
+ const postInstall = (b, { ancestors }) =>
2441
+ hooks.postInstall(b.env.processId, {
2399
2442
  hooks: Object.assign(b.meta.hooks || {}, b.meta.scripts || {}),
2400
- env: getRuntimeEnvironments(b, nodeEnvironments, blocklet),
2443
+ env: getRuntimeEnvironments(b, nodeEnvironments, ancestors),
2401
2444
  appDir: b.env.appDir,
2402
2445
  did, // root blocklet did
2403
2446
  notification: states.notification,
@@ -2449,10 +2492,13 @@ class BlockletManager extends BaseBlockletManager {
2449
2492
  }
2450
2493
  }
2451
2494
 
2452
- async _upgradeBlocklet({ newBlocklet, oldBlocklet, context }) {
2495
+ async _upgradeBlocklet({ newBlocklet, oldBlocklet, context = {} }) {
2453
2496
  const { meta, source, deployedFrom, children } = newBlocklet;
2454
2497
  const { did, version, name } = meta;
2455
2498
 
2499
+ // ids
2500
+ context.skippedProcessIds = getSkippedProcessIds({ newBlocklet, oldBlocklet, context });
2501
+
2456
2502
  const oldVersion = oldBlocklet.meta.version;
2457
2503
  const action = semver.gt(oldBlocklet.meta.version, version) ? 'downgrade' : 'upgrade';
2458
2504
  try {
@@ -2466,14 +2512,14 @@ class BlockletManager extends BaseBlockletManager {
2466
2512
 
2467
2513
  // update state
2468
2514
  await states.blocklet.upgradeBlocklet({ meta, source, deployedFrom, children });
2469
- await this._setConfigs(did);
2515
+ await this._setConfigsFromMeta(did);
2470
2516
 
2471
2517
  let blocklet = await this.ensureBlocklet(did);
2472
2518
 
2473
2519
  // pre install
2474
2520
  const nodeEnvironments = await states.node.getEnvironments();
2475
2521
  const preInstall = (b) =>
2476
- hooks.preInstall(b.env.appId, {
2522
+ hooks.preInstall(b.env.processId, {
2477
2523
  hooks: Object.assign(b.meta.hooks || {}, b.meta.scripts || {}),
2478
2524
  env: { ...nodeEnvironments },
2479
2525
  appDir: b.env.appDir,
@@ -2488,10 +2534,10 @@ class BlockletManager extends BaseBlockletManager {
2488
2534
  blocklet = await this.ensureBlocklet(did);
2489
2535
 
2490
2536
  // post install
2491
- const postInstall = (b) =>
2492
- hooks.postInstall(b.env.appId, {
2537
+ const postInstall = (b, { ancestors }) =>
2538
+ hooks.postInstall(b.env.processId, {
2493
2539
  hooks: Object.assign(b.meta.hooks || {}, b.meta.scripts || {}),
2494
- env: getRuntimeEnvironments(b, nodeEnvironments, blocklet),
2540
+ env: getRuntimeEnvironments(b, nodeEnvironments, ancestors),
2495
2541
  appDir: b.env.appDir,
2496
2542
  did, // root blocklet did
2497
2543
  notification: states.notification,
@@ -2500,13 +2546,13 @@ class BlockletManager extends BaseBlockletManager {
2500
2546
  await forEachBlocklet(blocklet, postInstall, { parallel: true });
2501
2547
 
2502
2548
  // run migrations
2503
- const runMigration = (b) => {
2549
+ const runMigration = (b, { ancestors }) => {
2504
2550
  // BUG: 本身的定义是在执行 upgrade 操作时进行 migration,但是父 blocklet upgrade 时,子 blocklet 可能不需要 upgrade,就会导致子 blocklet 多走了一遍 migration 流程
2505
2551
  if (b.meta.did === did) {
2506
2552
  return runMigrationScripts({
2507
2553
  blocklet: b,
2508
2554
  appDir: b.env.appDir,
2509
- env: getRuntimeEnvironments(b, nodeEnvironments, blocklet),
2555
+ env: getRuntimeEnvironments(b, nodeEnvironments, ancestors),
2510
2556
  oldVersion,
2511
2557
  newVersion: version,
2512
2558
  did: b.meta.did,
@@ -2528,14 +2574,14 @@ class BlockletManager extends BaseBlockletManager {
2528
2574
 
2529
2575
  logger.info('updated blocklet for upgrading', { did, version, source, name });
2530
2576
 
2577
+ const status =
2578
+ oldBlocklet.status === BlockletStatus.installed ? BlockletStatus.installed : BlockletStatus.stopped;
2579
+ await states.blocklet.setBlockletStatus(did, status, { children: 'all' });
2580
+
2531
2581
  // start new process
2532
2582
  if (oldBlocklet.status === BlockletStatus.running) {
2533
2583
  await this.start({ did }, context);
2534
2584
  logger.info('started blocklet for upgrading', { did, version });
2535
- } else {
2536
- const status =
2537
- oldBlocklet.status === BlockletStatus.installed ? BlockletStatus.installed : BlockletStatus.stopped;
2538
- await states.blocklet.setBlockletStatus(did, status, { children: 'all' });
2539
2585
  }
2540
2586
 
2541
2587
  blocklet = await this.ensureBlocklet(did, context);
@@ -2775,26 +2821,43 @@ class BlockletManager extends BaseBlockletManager {
2775
2821
  meta: { name, did },
2776
2822
  } = blocklet;
2777
2823
 
2824
+ const oldComponentObj = {};
2825
+ const downloadComponentIds = [];
2826
+ const metaObj = {};
2778
2827
  const metas = [];
2779
- if (needBlockletDownload(blocklet)) {
2780
- metas.push(blocklet.meta);
2781
- }
2782
2828
 
2783
- const oldChildren = (oldBlocklet.children || []).reduce((o, x) => {
2784
- o[x.meta.did] = x;
2785
- return o;
2786
- }, {});
2829
+ forEachBlockletSync(oldBlocklet, (oldComponent, { id }) => {
2830
+ if (oldComponent.meta) {
2831
+ oldComponentObj[id] = oldComponent;
2832
+ metaObj[getComponentBundleId(oldComponent)] = oldComponent.meta;
2833
+ }
2834
+ });
2835
+
2836
+ forEachBlockletSync(blocklet, (component, { id: componentId }) => {
2837
+ if (needBlockletDownload(component, oldComponentObj[componentId])) {
2838
+ const bundleId = getComponentBundleId(component);
2839
+
2840
+ if (metaObj[bundleId]) {
2841
+ if (get(component, 'meta.dist.integrity') !== get(metaObj[bundleId], 'dist.integrity')) {
2842
+ // FIXME: what should we do if component bundle integrity not same in different apps
2843
+ logger.error(`find duplicate bundle with different integrity when downloading ${blocklet.meta.title}`, {
2844
+ bundleId,
2845
+ });
2846
+ }
2787
2847
 
2788
- for (const child of blocklet.children) {
2789
- const oldChild = oldChildren[child.meta.did];
2790
- if (needBlockletDownload(child, oldChild)) {
2791
- metas.push(child.meta);
2848
+ logger.info(`skip download duplicate bundle ${bundleId}`);
2849
+ return;
2850
+ }
2851
+
2852
+ metaObj[bundleId] = component.meta;
2853
+ metas.push(component.meta);
2854
+ downloadComponentIds.push(componentId);
2792
2855
  }
2793
- }
2856
+ });
2794
2857
 
2795
2858
  // update children status
2796
2859
  const blocklet1 = await states.blocklet.setBlockletStatus(did, BlockletStatus.downloading, {
2797
- children: metas.map((x) => ({ did: x.did })),
2860
+ children: downloadComponentIds,
2798
2861
  });
2799
2862
  this.emit(BlockletEvents.statusChange, blocklet1);
2800
2863
 
@@ -2937,18 +3000,24 @@ class BlockletManager extends BaseBlockletManager {
2937
3000
  return blocklet;
2938
3001
  }
2939
3002
 
2940
- async _setConfigs(did, childDid) {
3003
+ async _setConfigsFromMeta(did, childDid) {
2941
3004
  const blocklet = await states.blocklet.getBlocklet(did);
2942
- const { meta } = blocklet;
2943
3005
 
2944
3006
  if (!childDid) {
2945
- await states.blockletExtras.setConfigs(blocklet.meta.did, get(meta, 'environments', []));
2946
- for (const child of blocklet.children) {
2947
- await states.blockletExtras.setChildConfigs(blocklet.meta.did, child.meta.did, get(child.meta, 'environments'));
2948
- }
3007
+ await forEachBlocklet(blocklet, async (b, { ancestors }) => {
3008
+ await states.blockletExtras.setConfigs(
3009
+ [...ancestors.map((x) => x.meta.did), b.meta.did],
3010
+ get(b.meta, 'environments', [])
3011
+ );
3012
+ });
2949
3013
  } else {
2950
3014
  const child = blocklet.children.find((x) => x.meta.did === childDid);
2951
- await states.blockletExtras.setChildConfigs(blocklet.meta.did, child.meta.did, get(child.meta, 'environments'));
3015
+ await forEachBlocklet(child, async (b, { ancestors }) => {
3016
+ await states.blockletExtras.setConfigs(
3017
+ [blocklet.meta.did, ...ancestors.map((x) => x.meta.did), b.meta.did],
3018
+ get(b.meta, 'environments', [])
3019
+ );
3020
+ });
2952
3021
  }
2953
3022
  }
2954
3023