@abtnode/core 1.6.17 → 1.6.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.
package/lib/api/node.js CHANGED
@@ -93,7 +93,7 @@ class NodeAPI {
93
93
  async updateNodeInfo(entity = {}, context) {
94
94
  await validateNodeInfo(entity, context);
95
95
 
96
- if (entity.autoUpgrade) {
96
+ if (entity.autoUpgrade && process.env.NODE_ENV !== 'development') {
97
97
  try {
98
98
  canPackageReadWrite(process.env.ABT_NODE_BINARY_NAME, process.env.ABT_NODE_PACKAGE_NAME);
99
99
  } catch (err) {
@@ -1,68 +1,23 @@
1
- const childProcess = require('child_process');
2
1
  const get = require('lodash/get');
3
2
  const camelCase = require('lodash/camelCase');
3
+ const runScript = require('@abtnode/util/lib/run-script');
4
4
 
5
5
  // eslint-disable-next-line global-require
6
6
  const logger = require('@abtnode/logger')(`${require('../../package.json').name}:blocklet:hooks`);
7
7
 
8
8
  const { getSafeEnv } = require('../util');
9
9
 
10
- const runAsync = ({ appDir, env, hook, progress = false }) => {
11
- const safeEnv = getSafeEnv(env);
12
- const child = childProcess.exec(hook, {
13
- cwd: appDir,
14
- env: safeEnv,
15
- stdio: 'inherit',
16
- });
17
-
18
- if (progress) {
19
- child.stdout.pipe(process.stdout);
20
- child.stderr.pipe(process.stderr);
21
- }
22
-
23
- return new Promise((resolve, reject) => {
24
- const errorMessages = [];
25
- let hasUnhandledRejection = false;
26
-
27
- child.stderr.on('data', (err) => {
28
- // Check if has unhandledRejection in childProcess
29
- // https://stackoverflow.com/questions/32784649/gracefully-handle-errors-in-child-processes-in-nodejs
30
- if (err.includes('UnhandledPromiseRejectionWarning')) {
31
- hasUnhandledRejection = true;
32
- }
33
- errorMessages.push(err);
34
- });
35
-
36
- child.on('exit', (code) => {
37
- if (errorMessages.length > 0) {
38
- if (code !== 0 || hasUnhandledRejection) {
39
- return reject(new Error(errorMessages.join('\r\n')));
40
- }
41
-
42
- if (!progress) {
43
- errorMessages.forEach((message) => process.stderr.write(message));
44
- }
45
- }
46
-
47
- return resolve();
48
- });
49
- });
50
- };
51
-
52
- /**
53
- * @param {*} args.did root blocklet did
54
- */
55
- const runUserHook = async (name, args) => {
56
- const { appDir, hooks, env, exitOnError = true, progress = false, notification, did } = args;
57
- const hook = get(hooks, `[${name}]`) || get(hooks, `[${camelCase(name)}]`);
10
+ const runUserHook = async (appId, hookName, args) => {
11
+ const { appDir, hooks, env, exitOnError = true, silent = false, notification, did } = args;
12
+ const hook = get(hooks, `[${hookName}]`) || get(hooks, `[${camelCase(hookName)}]`);
58
13
 
59
14
  try {
60
15
  if (!hook) {
61
16
  return;
62
17
  }
63
18
 
64
- logger.info(`run hook:${name}:`, { hook });
65
- await runAsync({ appDir, env, hook, progress });
19
+ logger.info(`run hook:${hookName}:`, { hook });
20
+ await runScript(hook, [appId, hookName].join(':'), { cwd: appDir, env: getSafeEnv(env), silent });
66
21
  } catch (error) {
67
22
  logger.error(`run ${hook} error:`, { error });
68
23
 
@@ -77,33 +32,31 @@ const runUserHook = async (name, args) => {
77
32
  }
78
33
 
79
34
  if (exitOnError) {
80
- throw new Error(`Run [${name}] failed: ${error.message}`);
35
+ throw new Error(`Run [${hookName}] failed: ${error.message}`);
81
36
  }
82
37
  }
83
38
  };
84
39
 
85
- const preDeploy = (...args) => runUserHook('pre-deploy', ...args);
86
- const preInstall = (...args) => runUserHook('pre-install', ...args);
87
- const postInstall = (...args) => runUserHook('post-install', ...args);
88
-
89
- const preStart = async (blocklet, option) => {
40
+ const preDeploy = (appId, ...args) => runUserHook(appId, 'pre-deploy', ...args);
41
+ const preInstall = (appId, ...args) => runUserHook(appId, 'pre-install', ...args);
42
+ const postInstall = (appId, ...args) => runUserHook(appId, 'post-install', ...args);
43
+ const preConfig = (appId, ...args) => runUserHook(appId, 'pre-config', ...args);
44
+ const preStart = async (blocklet, options) => {
90
45
  // check required environments
91
46
  let environments = get(blocklet, 'meta.environments', []);
92
47
  if (!Array.isArray(environments)) {
93
48
  environments = [environments];
94
49
  }
95
50
 
96
- const tmp = environments.filter((e) => e.required && option.env[e.name] === undefined).map((e) => e.name);
51
+ const tmp = environments.filter((e) => e.required && options.env[e.name] === undefined).map((e) => e.name);
97
52
  if (tmp.length > 0) {
98
53
  throw new Error(`Required environments is not set: ${tmp.join(',')}`);
99
54
  }
100
55
 
101
- return runUserHook('pre-start', option);
56
+ return runUserHook(blocklet.env.appId, 'pre-start', options);
102
57
  };
103
58
 
104
- const preUninstall = (...args) => runUserHook('pre-uninstall', ...args);
105
- const preStop = (...args) => runUserHook('pre-stop', ...args);
106
-
107
- const preConfig = (...args) => runUserHook('pre-config', ...args);
59
+ const preUninstall = (appId, ...args) => runUserHook(appId, 'pre-uninstall', ...args);
60
+ const preStop = (appId, ...args) => runUserHook(appId, 'pre-stop', ...args);
108
61
 
109
62
  module.exports = { preDeploy, preInstall, postInstall, preStart, preUninstall, preStop, preConfig };
@@ -20,18 +20,17 @@ const logger = require('@abtnode/logger')('@abtnode/core:blocklet:manager');
20
20
  const downloadFile = require('@abtnode/util/lib/download-file');
21
21
  const Lock = require('@abtnode/util/lib/lock');
22
22
  const { getVcFromPresentation } = require('@abtnode/util/lib/vc');
23
- const { updateBlocklet: updateDidDocument } = require('@abtnode/util/lib/did-document');
24
- const { BLOCKLET_PURCHASE_NFT_TYPE } = require('@abtnode/constant');
23
+ const { VC_TYPE_BLOCKLET_PURCHASE } = require('@abtnode/constant');
25
24
 
26
25
  const getBlockletEngine = require('@blocklet/meta/lib/engine');
27
26
  const {
28
27
  isFreeBlocklet,
28
+ isComponentBlocklet,
29
29
  isDeletableBlocklet,
30
30
  getRequiredMissingConfigs,
31
31
  hasRunnableComponent,
32
32
  } = require('@blocklet/meta/lib/util');
33
33
  const validateBlockletEntry = require('@blocklet/meta/lib/entry');
34
- const { getBlockletInfo } = require('@blocklet/meta/lib');
35
34
  const toBlockletDid = require('@blocklet/meta/lib/did');
36
35
  const { validateMeta } = require('@blocklet/meta/lib/validate');
37
36
  const { update: updateMetaFile } = require('@blocklet/meta/lib/file');
@@ -90,6 +89,8 @@ const {
90
89
  checkDuplicateComponents,
91
90
  getDiffFiles,
92
91
  getBundleDir,
92
+ needBlockletDownload,
93
+ verifyPurchase,
93
94
  } = require('../../util/blocklet');
94
95
  const states = require('../../states');
95
96
  const BlockletRegistry = require('../registry');
@@ -197,6 +198,20 @@ class BlockletManager extends BaseBlockletManager {
197
198
  throw new Error('Unknown source');
198
199
  }
199
200
 
201
+ /**
202
+ * @param {String} rootDid
203
+ * @param {String} mountPoint
204
+ * @param {Boolean} context.blockletPurchaseVerified
205
+ *
206
+ * installFromUrl
207
+ * @param {String} url
208
+ *
209
+ * InstallFromUpload
210
+ * @param {Object} file
211
+ * @param {String} did for diff upload
212
+ * @param {String} diffVersion for diff upload
213
+ * @param {Array} deleteSet for diff upload
214
+ */
200
215
  async installComponent({ rootDid, mountPoint, url, file, did, diffVersion, deleteSet }, context = {}) {
201
216
  logger.debug('start install component', { rootDid, mountPoint, url });
202
217
 
@@ -244,8 +259,8 @@ class BlockletManager extends BaseBlockletManager {
244
259
  // FIXME: 这里的 trustedIssuers 相当于相信任何 VC,需要想更安全的方法
245
260
  verifyPresentation({ presentation: vcPresentation, trustedIssuers: [get(vc, 'issuer.id')], challenge });
246
261
 
247
- if (!vc.type.includes(BLOCKLET_PURCHASE_NFT_TYPE)) {
248
- throw new Error(`Expect ${BLOCKLET_PURCHASE_NFT_TYPE} VC type`);
262
+ if (!vc.type.includes(VC_TYPE_BLOCKLET_PURCHASE)) {
263
+ throw new Error(`Expect ${VC_TYPE_BLOCKLET_PURCHASE} VC type`);
249
264
  }
250
265
 
251
266
  const blockletUrl = get(vc, 'credentialSubject.purchased.blocklet.url');
@@ -293,7 +308,6 @@ class BlockletManager extends BaseBlockletManager {
293
308
  hooks: Object.assign(b.meta.hooks || {}, b.meta.scripts || {}),
294
309
  env: getRuntimeEnvironments(b, nodeEnvironments, blocklet),
295
310
  did, // root blocklet did,
296
- progress: blocklet.mode === BLOCKLET_MODES.DEVELOPMENT,
297
311
  }),
298
312
  nodeEnvironments,
299
313
  nodeInfo: await states.node.read(),
@@ -344,7 +358,7 @@ class BlockletManager extends BaseBlockletManager {
344
358
  }
345
359
  }
346
360
 
347
- async stop({ did, updateStatus = true }, context) {
361
+ async stop({ did, updateStatus = true, silent = false }, context) {
348
362
  logger.info('stop blocklet', { did });
349
363
 
350
364
  const blocklet = await this.ensureBlocklet(did);
@@ -360,7 +374,7 @@ class BlockletManager extends BaseBlockletManager {
360
374
 
361
375
  await stopBlockletProcess(blocklet, {
362
376
  preStop: (b) =>
363
- hooks.preStop({
377
+ hooks.preStop(b.env.appId, {
364
378
  appDir: b.env.appDir,
365
379
  hooks: Object.assign(b.meta.hooks || {}, b.meta.scripts || {}),
366
380
  env: getRuntimeEnvironments(b, nodeEnvironments, blocklet),
@@ -368,7 +382,7 @@ class BlockletManager extends BaseBlockletManager {
368
382
  notification: states.notification,
369
383
  context,
370
384
  exitOnError: false,
371
- progress: blocklet.mode === BLOCKLET_MODES.DEVELOPMENT,
385
+ silent,
372
386
  }),
373
387
  });
374
388
 
@@ -436,7 +450,7 @@ class BlockletManager extends BaseBlockletManager {
436
450
 
437
451
  await deleteBlockletProcess(blocklet, {
438
452
  preDelete: (b) =>
439
- hooks.preUninstall({
453
+ hooks.preUninstall(b.env.appId, {
440
454
  appDir: b.env.appDir,
441
455
  hooks: Object.assign(b.meta.hooks || {}, b.meta.scripts || {}),
442
456
  env: getRuntimeEnvironments(b, nodeEnvironments, blocklet),
@@ -512,12 +526,14 @@ class BlockletManager extends BaseBlockletManager {
512
526
  return result;
513
527
  }
514
528
 
515
- async detail({ did, attachRuntimeInfo = true }, context) {
529
+ async detail({ did, attachConfig = true, attachRuntimeInfo = true }, context) {
516
530
  if (!did) {
517
531
  throw new Error('did should not be empty');
518
532
  }
519
533
 
520
- const nodeInfo = await states.node.read();
534
+ if (!attachConfig) {
535
+ return states.blocklet.getBlocklet(did);
536
+ }
521
537
 
522
538
  if (!attachRuntimeInfo) {
523
539
  try {
@@ -528,6 +544,8 @@ class BlockletManager extends BaseBlockletManager {
528
544
  }
529
545
  }
530
546
 
547
+ const nodeInfo = await states.node.read();
548
+
531
549
  return this.attachRuntimeInfo({ did, nodeInfo, diskInfo: true, context });
532
550
  }
533
551
 
@@ -624,14 +642,13 @@ class BlockletManager extends BaseBlockletManager {
624
642
  }
625
643
 
626
644
  // FIXME: we should also call preConfig for child blocklets
627
- await hooks.preConfig({
645
+ await hooks.preConfig(blocklet.env.appId, {
628
646
  appDir: blocklet.env.appDir,
629
647
  hooks: Object.assign(blocklet.meta.hooks || {}, blocklet.meta.scripts || {}),
630
648
  exitOnError: true,
631
649
  env: { ...getRuntimeEnvironments(blocklet, nodeEnvironments), ...blocklet.configObj },
632
650
  did,
633
651
  context,
634
- progress: blocklet.mode === BLOCKLET_MODES.DEVELOPMENT,
635
652
  });
636
653
 
637
654
  // update db
@@ -819,21 +836,23 @@ class BlockletManager extends BaseBlockletManager {
819
836
  };
820
837
  }
821
838
 
822
- async updateChildren({ updateId, did: inputDid, children: inputChildren }, context) {
839
+ async updateChildren({ updateId, did: inputDid, children: inputChildren, oldBlocklet: inputOldBlocklet }, context) {
823
840
  let did;
824
841
  let children;
842
+ let oldBlocklet;
825
843
  if (!updateId && inputDid && inputChildren) {
826
844
  did = inputDid;
827
845
  children = inputChildren;
846
+ oldBlocklet = inputOldBlocklet;
828
847
  } else {
829
848
  const sessionData = await states.session.end(updateId);
830
849
  did = sessionData.did;
831
850
  const { staticChildren = [], dynamicChildren = [] } = sessionData;
832
851
  children = [...staticChildren, ...dynamicChildren.map((x) => ({ ...x, dynamic: true }))];
852
+ oldBlocklet = await states.blocklet.getBlocklet(did);
833
853
  }
834
854
 
835
855
  // get old blocklet
836
- const oldBlocklet = await states.blocklet.getBlocklet(did);
837
856
  const { meta } = oldBlocklet;
838
857
  const { name, version } = meta;
839
858
 
@@ -887,6 +906,11 @@ class BlockletManager extends BaseBlockletManager {
887
906
  // ============================================================================================
888
907
  // Internal API that are used by public APIs and called from CLI
889
908
  // ============================================================================================
909
+
910
+ /**
911
+ * After the dev function finished, the caller should send a BlockletEvents.deployed event to the daemon
912
+ * @returns {Object} blocklet
913
+ */
890
914
  async dev(folder) {
891
915
  logger.info('dev blocklet', { folder });
892
916
 
@@ -919,11 +943,13 @@ class BlockletManager extends BaseBlockletManager {
919
943
  await this.deleteProcess({ did });
920
944
  logger.info('delete blocklet precess for dev', { did, version });
921
945
  } catch (err) {
922
- logger.error('failed to delete blocklet process for dev', { error: err });
946
+ if (process.env.NODE_ENV !== 'development') {
947
+ logger.error('failed to delete blocklet process for dev', { error: err });
948
+ }
923
949
  }
924
950
 
925
951
  const children = await this._getChildren(meta);
926
- const blocklet = await states.blocklet.addBlocklet({
952
+ const added = await states.blocklet.addBlocklet({
927
953
  did,
928
954
  meta,
929
955
  source: BlockletSource.local,
@@ -934,15 +960,16 @@ class BlockletManager extends BaseBlockletManager {
934
960
  logger.info('add blocklet for dev', { did, version, meta });
935
961
 
936
962
  const oldBlocklet = { children: children.filter((x) => x.dynamic) }; // let downloader skip re-downloading dynamic blocklet
937
- await this._downloadBlocklet(blocklet, oldBlocklet);
963
+ await this._downloadBlocklet(added, oldBlocklet);
938
964
  await states.blocklet.setBlockletStatus(did, BlockletStatus.installed);
939
965
 
940
966
  // Add environments
941
967
  await this._setConfigs(did);
942
968
  await this.updateBlockletEnvironment(did);
943
969
 
944
- this.emit(BlockletEvents.deployed, { blocklet, context: {} });
945
- return this.ensureBlocklet(did);
970
+ const blocklet = await this.ensureBlocklet(did);
971
+
972
+ return blocklet;
946
973
  }
947
974
 
948
975
  async ensureBlocklet(did) {
@@ -1171,9 +1198,6 @@ class BlockletManager extends BaseBlockletManager {
1171
1198
  return;
1172
1199
  }
1173
1200
 
1174
- const blocklet1 = await states.blocklet.setBlockletStatus(did, BlockletStatus.downloading);
1175
- this.emit(BlockletEvents.statusChange, blocklet1);
1176
-
1177
1201
  preDownloadLock.release();
1178
1202
 
1179
1203
  const { isCancelled } = await this._downloadBlocklet(blocklet, oldBlocklet);
@@ -1319,14 +1343,15 @@ class BlockletManager extends BaseBlockletManager {
1319
1343
  const blocklet = await states.blocklet.getBlocklet(did);
1320
1344
  const nodeInfo = await states.node.read();
1321
1345
 
1322
- const rootSystemEnvironments = getRootSystemEnvironments(blockletWithEnv, nodeInfo);
1323
- const overwrittenEnvironments = getOverwrittenEnvironments(blockletWithEnv, nodeInfo);
1346
+ const rootSystemEnvironments = {
1347
+ ...getRootSystemEnvironments(blockletWithEnv, nodeInfo),
1348
+ ...getOverwrittenEnvironments(blockletWithEnv, nodeInfo),
1349
+ };
1324
1350
 
1325
1351
  // fill environments to blocklet and blocklet.children
1326
1352
  blocklet.environments = formatEnvironments({
1327
1353
  ...getSystemEnvironments(blockletWithEnv),
1328
1354
  ...rootSystemEnvironments,
1329
- ...overwrittenEnvironments,
1330
1355
  });
1331
1356
 
1332
1357
  for (const child of blocklet.children) {
@@ -1335,11 +1360,13 @@ class BlockletManager extends BaseBlockletManager {
1335
1360
  child.environments = formatEnvironments({
1336
1361
  ...getSystemEnvironments(childWithEnv), // system env of child blocklet
1337
1362
  ...rootSystemEnvironments, // system env of root blocklet
1338
- ...overwrittenEnvironments,
1339
1363
  });
1340
1364
  }
1341
1365
  }
1342
1366
 
1367
+ // put BLOCKLET_APP_ID at root level for indexing
1368
+ blocklet.appDid = rootSystemEnvironments.BLOCKLET_APP_ID;
1369
+
1343
1370
  // update state to db
1344
1371
  return states.blocklet.updateBlocklet(did, blocklet);
1345
1372
  }
@@ -1360,23 +1387,21 @@ class BlockletManager extends BaseBlockletManager {
1360
1387
 
1361
1388
  const registryUrl = registry || (await states.node.getBlockletRegistry());
1362
1389
  const info = await BlockletRegistry.getRegistryMeta(registryUrl);
1363
- const blocklet = await this.registry.getBlocklet(did, registryUrl);
1364
- if (!blocklet) {
1390
+ const meta = await this.registry.getBlocklet(did, registryUrl);
1391
+ if (!meta) {
1365
1392
  throw new Error('Can not install blocklet that not found in registry');
1366
1393
  }
1367
1394
 
1368
- const state = await states.blocklet.getBlocklet(blocklet.did);
1395
+ const state = await states.blocklet.getBlocklet(meta.did);
1369
1396
  if (state) {
1370
1397
  throw new Error('Can not install an already installed blocklet');
1371
1398
  }
1372
1399
 
1373
- if (isFreeBlocklet(blocklet) === false && !context.blockletPurchaseVerified) {
1374
- throw new Error('Can not install a non-free blocklet directly');
1375
- }
1400
+ verifyPurchase(meta, context);
1376
1401
 
1377
1402
  // install
1378
1403
  return this._install({
1379
- meta: blocklet,
1404
+ meta,
1380
1405
  source: BlockletSource.registry,
1381
1406
  deployedFrom: info.cdnUrl || registryUrl,
1382
1407
  context,
@@ -1426,6 +1451,12 @@ class BlockletManager extends BaseBlockletManager {
1426
1451
  throw new Error('Cannot add self as a component');
1427
1452
  }
1428
1453
 
1454
+ if (!isComponentBlocklet(meta)) {
1455
+ throw new Error('The blocklet cannot be a component');
1456
+ }
1457
+
1458
+ verifyPurchase(meta, context);
1459
+
1429
1460
  const newChildren = await parseChildren(blocklet.meta, {
1430
1461
  children: [
1431
1462
  {
@@ -1439,10 +1470,14 @@ class BlockletManager extends BaseBlockletManager {
1439
1470
 
1440
1471
  checkDuplicateComponents(blocklet.children, newChildren);
1441
1472
 
1473
+ // add component to db
1474
+ await states.blocklet.addChildren(rootDid, newChildren);
1475
+
1442
1476
  return this.updateChildren(
1443
1477
  {
1444
1478
  did: rootDid,
1445
1479
  children: [...blocklet.children, ...newChildren],
1480
+ oldBlocklet: blocklet,
1446
1481
  },
1447
1482
  context
1448
1483
  );
@@ -1705,19 +1740,6 @@ class BlockletManager extends BaseBlockletManager {
1705
1740
  );
1706
1741
  }
1707
1742
 
1708
- async getStatus(did) {
1709
- if (!did) {
1710
- throw new Error('did is required');
1711
- }
1712
-
1713
- const blocklet = await states.blocklet.getBlocklet(did);
1714
- if (!blocklet) {
1715
- return null;
1716
- }
1717
-
1718
- return { name: blocklet.meta.name, did: blocklet.meta.did, status: blocklet.status };
1719
- }
1720
-
1721
1743
  async prune() {
1722
1744
  const blocklets = await states.blocklet.getBlocklets();
1723
1745
  const settings = await states.blockletExtras.listSettings();
@@ -2126,7 +2148,7 @@ class BlockletManager extends BaseBlockletManager {
2126
2148
  // pre install
2127
2149
  const nodeEnvironments = await states.node.getEnvironments();
2128
2150
  const preInstall = (b) =>
2129
- hooks.preInstall({
2151
+ hooks.preInstall(b.env.appId, {
2130
2152
  hooks: Object.assign(b.meta.hooks || {}, b.meta.scripts || {}),
2131
2153
  env: { ...nodeEnvironments },
2132
2154
  appDir: b.env.appDir,
@@ -2142,7 +2164,7 @@ class BlockletManager extends BaseBlockletManager {
2142
2164
 
2143
2165
  // post install
2144
2166
  const postInstall = (b) =>
2145
- hooks.postInstall({
2167
+ hooks.postInstall(b.env.appId, {
2146
2168
  hooks: Object.assign(b.meta.hooks || {}, b.meta.scripts || {}),
2147
2169
  env: getRuntimeEnvironments(b, nodeEnvironments, blocklet),
2148
2170
  appDir: b.env.appDir,
@@ -2155,17 +2177,6 @@ class BlockletManager extends BaseBlockletManager {
2155
2177
  await states.blocklet.setBlockletStatus(did, BlockletStatus.installed);
2156
2178
  blocklet = await this.ensureBlocklet(did);
2157
2179
  logger.info('blocklet installed', { source, did: meta.did });
2158
- if (process.env.NODE_ENV !== 'test') {
2159
- const nodeInfo = await states.node.read();
2160
- const blockletInfo = getBlockletInfo(blocklet, nodeInfo.sk);
2161
- const updateDidDnsResult = await updateDidDocument({
2162
- wallet: blockletInfo.wallet,
2163
- domain: nodeInfo.didDomain,
2164
- nodeDid: nodeInfo.did,
2165
- didRegistryUrl: nodeInfo.didRegistry,
2166
- });
2167
- logger.info('updated did document', { updateDidDnsResult, blockletId: blocklet.meta.did });
2168
- }
2169
2180
 
2170
2181
  this.emit(BlockletEvents.installed, { blocklet, context });
2171
2182
 
@@ -2226,7 +2237,7 @@ class BlockletManager extends BaseBlockletManager {
2226
2237
  // pre install
2227
2238
  const nodeEnvironments = await states.node.getEnvironments();
2228
2239
  const preInstall = (b) =>
2229
- hooks.preInstall({
2240
+ hooks.preInstall(b.env.appId, {
2230
2241
  hooks: Object.assign(b.meta.hooks || {}, b.meta.scripts || {}),
2231
2242
  env: { ...nodeEnvironments },
2232
2243
  appDir: b.env.appDir,
@@ -2242,7 +2253,7 @@ class BlockletManager extends BaseBlockletManager {
2242
2253
 
2243
2254
  // post install
2244
2255
  const postInstall = (b) =>
2245
- hooks.postInstall({
2256
+ hooks.postInstall(b.env.appId, {
2246
2257
  hooks: Object.assign(b.meta.hooks || {}, b.meta.scripts || {}),
2247
2258
  env: getRuntimeEnvironments(b, nodeEnvironments, blocklet),
2248
2259
  appDir: b.env.appDir,
@@ -2258,10 +2269,10 @@ class BlockletManager extends BaseBlockletManager {
2258
2269
  if (b.meta.did === did) {
2259
2270
  return runMigrationScripts({
2260
2271
  blocklet: b,
2272
+ appDir: b.env.appDir,
2273
+ env: getRuntimeEnvironments(b, nodeEnvironments, blocklet),
2261
2274
  oldVersion,
2262
2275
  newVersion: version,
2263
- env: getRuntimeEnvironments(b, nodeEnvironments, blocklet),
2264
- appDir: b.env.appDir,
2265
2276
  did: b.meta.did,
2266
2277
  notification: states.notification,
2267
2278
  context,
@@ -2288,7 +2299,7 @@ class BlockletManager extends BaseBlockletManager {
2288
2299
  } else {
2289
2300
  const status =
2290
2301
  oldBlocklet.status === BlockletStatus.installed ? BlockletStatus.installed : BlockletStatus.stopped;
2291
- await states.blocklet.setBlockletStatus(did, status);
2302
+ await states.blocklet.setBlockletStatus(did, status, { children: 'all' });
2292
2303
  }
2293
2304
 
2294
2305
  blocklet = await this.ensureBlocklet(did, context);
@@ -2512,10 +2523,7 @@ class BlockletManager extends BaseBlockletManager {
2512
2523
  } = blocklet;
2513
2524
 
2514
2525
  const metas = [];
2515
- if (
2516
- ![BlockletSource.upload, BlockletSource.local].includes(blocklet.source) &&
2517
- get(oldBlocklet, 'meta.dist.integrity') !== get(blocklet, 'meta.dist.integrity')
2518
- ) {
2526
+ if (needBlockletDownload(blocklet)) {
2519
2527
  metas.push(blocklet.meta);
2520
2528
  }
2521
2529
 
@@ -2526,16 +2534,17 @@ class BlockletManager extends BaseBlockletManager {
2526
2534
 
2527
2535
  for (const child of blocklet.children) {
2528
2536
  const oldChild = oldChildren[child.meta.did];
2529
- if (
2530
- !oldChild ||
2531
- (![BlockletSource.upload, BlockletSource.local].includes(child.source) &&
2532
- child.sourceUrl &&
2533
- get(oldChild, 'meta.dist.integrity') !== get(child, 'meta.dist.integrity'))
2534
- ) {
2537
+ if (needBlockletDownload(child, oldChild)) {
2535
2538
  metas.push(child.meta);
2536
2539
  }
2537
2540
  }
2538
2541
 
2542
+ // update children status
2543
+ const blocklet1 = await states.blocklet.setBlockletStatus(did, BlockletStatus.downloading, {
2544
+ children: metas.map((x) => ({ did: x.did })),
2545
+ });
2546
+ this.emit(BlockletEvents.statusChange, blocklet1);
2547
+
2539
2548
  try {
2540
2549
  logger.info('Download Blocklet', { name, did, bundles: metas.map((x) => get(x, 'dist.tarball')) });
2541
2550
  const tasks = [];
@@ -1,67 +1,25 @@
1
1
  /* eslint-disable no-await-in-loop */
2
- const childProcess = require('child_process');
3
2
  const fs = require('fs-extra');
4
3
  const path = require('path');
5
4
  const semver = require('semver');
5
+ const runScript = require('@abtnode/util/lib/run-script');
6
6
 
7
7
  const { getMigrationScripts: getScripts } = require('../migrations');
8
8
  const { getSafeEnv } = require('../util');
9
9
  const { name } = require('../../package.json');
10
10
  const logger = require('@abtnode/logger')(`${name}:blocklet:migration`); // eslint-disable-line
11
11
 
12
- const _runScript = ({ appDir, env, migrationScript, progress = false }) => {
13
- const safeEnv = getSafeEnv(env);
14
-
15
- const child = childProcess.exec(`node ${migrationScript}`, {
16
- cwd: appDir,
17
- env: safeEnv,
18
- stdio: 'inherit',
19
- });
20
- let hasUnhandledRejection = false;
21
-
22
- if (progress) {
23
- child.stdout.pipe(process.stdout);
24
- child.stderr.pipe(process.stderr);
25
- }
26
-
27
- return new Promise((resolve, reject) => {
28
- const errorMessages = [];
29
-
30
- child.stderr.on('data', (err) => {
31
- // Check if has unhandledRejection in childProcess
32
- // https://stackoverflow.com/questions/32784649/gracefully-handle-errors-in-child-processes-in-nodejs
33
- if (err.includes('UnhandledPromiseRejectionWarning')) {
34
- hasUnhandledRejection = true;
35
- }
36
- errorMessages.push(err);
37
- });
38
-
39
- child.on('exit', (code) => {
40
- if (errorMessages.length > 0) {
41
- if (code !== 0 || hasUnhandledRejection) {
42
- return reject(new Error(errorMessages.join('\r\n')));
43
- }
44
-
45
- if (!progress) {
46
- errorMessages.forEach((message) => process.stderr.write(message));
47
- }
48
- }
49
-
50
- return resolve();
51
- });
52
- });
53
- };
54
-
55
12
  async function runScripts({
13
+ blocklet,
14
+ appDir,
15
+ env,
16
+ oldVersion,
56
17
  dbDir,
57
18
  backupDir,
58
19
  scriptsDir,
59
20
  printInfo,
60
21
  printSuccess,
61
22
  printError,
62
- appDir,
63
- env,
64
- oldVersion,
65
23
  }) {
66
24
  let scripts = [];
67
25
  try {
@@ -90,7 +48,11 @@ async function runScripts({
90
48
  const { script: scriptPath } = pendingScripts[i];
91
49
  try {
92
50
  printInfo(`Migration script started: ${scriptPath}`);
93
- await _runScript({ appDir, env, migrationScript: path.join(scriptsDir, scriptPath) });
51
+ await runScript(`node ${path.join(scriptsDir, scriptPath)}`, [blocklet.env.appId, 'migration'].join(':'), {
52
+ cwd: appDir,
53
+ env: getSafeEnv(env),
54
+ silent: false,
55
+ });
94
56
  printInfo(`Migration script executed: ${scriptPath}`);
95
57
  } catch (migrationErr) {
96
58
  printError(`Failed to execute migration script: ${scriptPath}, error: ${migrationErr.message}`);
@@ -123,6 +85,7 @@ async function doRestore({ dbDir, backupDir, printInfo, printSuccess }) {
123
85
  }
124
86
 
125
87
  module.exports = async ({
88
+ blocklet,
126
89
  appDir,
127
90
  env,
128
91
  oldVersion,
@@ -142,15 +105,16 @@ module.exports = async ({
142
105
  fs.ensureDirSync(backupDir);
143
106
 
144
107
  await runScripts({
108
+ blocklet,
109
+ appDir,
110
+ env,
111
+ oldVersion,
112
+ newVersion,
145
113
  dbDir,
146
114
  backupDir,
147
115
  scriptsDir,
148
116
  printError,
149
117
  printInfo,
150
118
  printSuccess,
151
- appDir,
152
- env,
153
- oldVersion,
154
- newVersion,
155
119
  });
156
120
  };