@abtnode/core 1.8.30 → 1.8.32

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.
@@ -23,7 +23,7 @@ const logger = require('@abtnode/logger')('@abtnode/core:blocklet:manager');
23
23
  const downloadFile = require('@abtnode/util/lib/download-file');
24
24
  const Lock = require('@abtnode/util/lib/lock');
25
25
  const { getVcFromPresentation } = require('@abtnode/util/lib/vc');
26
- const { VC_TYPE_BLOCKLET_PURCHASE, WHO_CAN_ACCESS } = require('@abtnode/constant');
26
+ const { VC_TYPE_BLOCKLET_PURCHASE, WHO_CAN_ACCESS, SERVER_ROLES } = require('@abtnode/constant');
27
27
 
28
28
  const getBlockletEngine = require('@blocklet/meta/lib/engine');
29
29
  const {
@@ -38,6 +38,7 @@ const {
38
38
  forEachChild,
39
39
  getComponentId,
40
40
  getComponentBundleId,
41
+ isExternalBlocklet,
41
42
  } = require('@blocklet/meta/lib/util');
42
43
  const getComponentProcessId = require('@blocklet/meta/lib/get-component-process-id');
43
44
  const validateBlockletEntry = require('@blocklet/meta/lib/entry');
@@ -47,6 +48,8 @@ const { update: updateMetaFile } = require('@blocklet/meta/lib/file');
47
48
  const { titleSchema, descriptionSchema, mountPointSchema, logoSchema } = require('@blocklet/meta/lib/schema');
48
49
  const hasReservedKey = require('@blocklet/meta/lib/has-reserved-key');
49
50
 
51
+ const { toExternalBlocklet } = toBlockletDid;
52
+
50
53
  const {
51
54
  BlockletStatus,
52
55
  BlockletSource,
@@ -105,9 +108,8 @@ const {
105
108
  getBlocklet,
106
109
  ensureEnvDefault,
107
110
  } = require('../../util/blocklet');
108
- const { parseSourceUrl } = require('../../util/registry');
111
+ const StoreUtil = require('../../util/store');
109
112
  const states = require('../../states');
110
- const BlockletRegistry = require('../registry');
111
113
  const BaseBlockletManager = require('./base');
112
114
  const { get: getEngine } = require('./engine');
113
115
  const blockletPm2Events = require('./pm2-events');
@@ -169,11 +171,14 @@ const getSkippedProcessIds = ({ newBlocklet, oldBlocklet, context = {} }) => {
169
171
  return res;
170
172
  };
171
173
 
174
+ const getBlockletIndex = (meta, controller) =>
175
+ controller ? toExternalBlocklet(meta.name, controller.id) : { did: meta.did, name: meta.name };
176
+
172
177
  class BlockletManager extends BaseBlockletManager {
173
178
  /**
174
179
  * @param {*} dataDirs generate by ../../util:getDataDirs
175
180
  */
176
- constructor({ dataDirs, registry, startQueue, installQueue, daemon = false, teamManager }) {
181
+ constructor({ dataDirs, startQueue, installQueue, daemon = false, teamManager }) {
177
182
  super();
178
183
 
179
184
  this.dataDirs = dataDirs;
@@ -190,7 +195,6 @@ class BlockletManager extends BaseBlockletManager {
190
195
  * { [download-did-version]: Lock }
191
196
  */
192
197
  this.downloadLocks = {};
193
- this.registry = registry;
194
198
 
195
199
  // cached installed blocklets for performance
196
200
  this.cachedBlocklets = null;
@@ -198,7 +202,7 @@ class BlockletManager extends BaseBlockletManager {
198
202
  // cached blocklet latest versions from each registries
199
203
  this.cachedBlockletVersions = new LRU({
200
204
  max: 40, // cache at most 40 blocklets
201
- maxAge: process.env.NODE_ENV === 'test' ? 500 : 5 * 60 * 1000, // cache for 5 minute
205
+ maxAge: process.env.NODE_ENV === 'test' ? 500 : 0.5 * 60 * 1000, // cache for 0.5 minute
202
206
  });
203
207
 
204
208
  if (daemon) {
@@ -216,8 +220,17 @@ class BlockletManager extends BaseBlockletManager {
216
220
  *
217
221
  * @param {{
218
222
  * url: string;
219
- * sync: boolean = false;
220
- * downloadTokenList: Array<{did: string, token: string};
223
+ * file: string;
224
+ * diffVersion: string;
225
+ * deleteSet: Array<string>;
226
+ * did: string;
227
+ * title: string;
228
+ * description: string;
229
+ * sync: boolean = false; // download synchronously, not use queue
230
+ * delay: number; // push download task to queue after a delay
231
+ * downloadTokenList: Array<{did: string, token: string}>;
232
+ * startImmediately: boolean;
233
+ * controller: Controller
221
234
  * }} params
222
235
  * @param {{
223
236
  * [key: string]: any
@@ -245,7 +258,8 @@ class BlockletManager extends BaseBlockletManager {
245
258
  }
246
259
 
247
260
  if (source === BlockletSource.url) {
248
- return this._installFromUrl({ url: params.url, sync: params.sync }, context);
261
+ const { url, controller, sync, delay } = params;
262
+ return this._installFromUrl({ url, controller, sync, delay }, context);
249
263
  }
250
264
 
251
265
  if (source === BlockletSource.upload) {
@@ -254,7 +268,8 @@ class BlockletManager extends BaseBlockletManager {
254
268
  }
255
269
 
256
270
  if (source === BlockletSource.registry) {
257
- return this._installFromStore({ did: params.did }, context);
271
+ const { did, controller, sync, delay } = params;
272
+ return this._installFromStore({ did, controller, sync, delay }, context);
258
273
  }
259
274
 
260
275
  if (source === BlockletSource.custom) {
@@ -283,15 +298,38 @@ class BlockletManager extends BaseBlockletManager {
283
298
  * @param {String} name custom component name
284
299
  *
285
300
  * @param {ConfigEntry} configs pre configs
301
+ * @param {Boolean} skipNavigation
286
302
  */
287
303
  async installComponent(
288
- { rootDid, mountPoint, url, file, did, diffVersion, deleteSet, title, name, configs, downloadTokenList },
304
+ {
305
+ rootDid,
306
+ mountPoint,
307
+ url,
308
+ file,
309
+ did,
310
+ diffVersion,
311
+ deleteSet,
312
+ title,
313
+ name,
314
+ configs,
315
+ downloadTokenList,
316
+ skipNavigation,
317
+ },
289
318
  context = {}
290
319
  ) {
291
320
  logger.debug('start install component', { rootDid, mountPoint, url });
292
321
 
293
322
  if (file) {
294
- return this._installComponentFromUpload({ rootDid, mountPoint, file, did, diffVersion, deleteSet, context });
323
+ return this._installComponentFromUpload({
324
+ rootDid,
325
+ mountPoint,
326
+ file,
327
+ did,
328
+ diffVersion,
329
+ deleteSet,
330
+ skipNavigation,
331
+ context,
332
+ });
295
333
  }
296
334
 
297
335
  if (url) {
@@ -305,6 +343,7 @@ class BlockletManager extends BaseBlockletManager {
305
343
  name,
306
344
  configs,
307
345
  downloadTokenList,
346
+ skipNavigation,
308
347
  });
309
348
  }
310
349
 
@@ -319,7 +358,7 @@ class BlockletManager extends BaseBlockletManager {
319
358
 
320
359
  if (checkPrice && !isFree && meta.nftFactory) {
321
360
  try {
322
- const registryMeta = await BlockletRegistry.getRegistryMeta(new URL(url).origin);
361
+ const registryMeta = await StoreUtil.getRegistryMeta(new URL(url).origin);
323
362
 
324
363
  if (registryMeta.chainHost) {
325
364
  const state = await getFactoryState(registryMeta.chainHost, meta.nftFactory);
@@ -332,12 +371,33 @@ class BlockletManager extends BaseBlockletManager {
332
371
  }
333
372
  }
334
373
 
335
- const { inStore, registryUrl } = await parseSourceUrl(url);
374
+ const { inStore, registryUrl } = await StoreUtil.parseSourceUrl(url);
375
+
376
+ return { meta, isFree, inStore, registryUrl };
377
+ }
378
+
379
+ async getBlockletByBundle({ did, name }, context) {
380
+ if (toBlockletDid(name) !== did) {
381
+ throw new Error('did and name does not match');
382
+ }
383
+
384
+ if (!context?.user) {
385
+ throw new Error('user does not exist');
386
+ }
387
+
388
+ let blockletDid = did;
389
+ let isExternal = false;
390
+
391
+ if (context.user.role === SERVER_ROLES.EXTERNAL_BLOCKLET_CONTROLLER) {
392
+ blockletDid = toExternalBlocklet(name, context.user.did, { didOnly: true });
393
+ isExternal = true;
394
+ }
395
+
396
+ const blocklet = await states.blocklet.getBlocklet(blockletDid);
336
397
 
337
- const blocklet = await states.blocklet.getBlocklet(meta.did);
338
398
  const isRunning = blocklet ? blocklet.status === BlockletStatus.running : false;
339
399
 
340
- return { meta, isFree, isInstalled: !!blocklet, isRunning, inStore, registryUrl };
400
+ return { isInstalled: !!blocklet, isRunning, blockletDid, isExternal };
341
401
  }
342
402
 
343
403
  async installBlockletFromVc({ vcPresentation, challenge }, context) {
@@ -364,6 +424,15 @@ class BlockletManager extends BaseBlockletManager {
364
424
  const blocklet = await this.ensureBlocklet(did, { e2eMode });
365
425
 
366
426
  try {
427
+ // blocklet may be manually stopped durning starting
428
+ // so error message would not be sent if blocklet is stopped
429
+ // so we need update status first
430
+ await states.blocklet.setBlockletStatus(did, BlockletStatus.starting);
431
+ blocklet.status = BlockletStatus.starting;
432
+
433
+ // validate requirement and engine
434
+ await validateBlocklet(blocklet);
435
+
367
436
  if (!hasRunnableComponent(blocklet)) {
368
437
  throw new Error('No runnable component found');
369
438
  }
@@ -376,9 +445,6 @@ class BlockletManager extends BaseBlockletManager {
376
445
  );
377
446
  }
378
447
 
379
- // update status
380
- await states.blocklet.setBlockletStatus(did, BlockletStatus.starting);
381
- blocklet.status = BlockletStatus.starting;
382
448
  this.emit(BlockletEvents.statusChange, blocklet);
383
449
 
384
450
  if (blocklet.mode === BLOCKLET_MODES.DEVELOPMENT) {
@@ -787,36 +853,46 @@ class BlockletManager extends BaseBlockletManager {
787
853
  return this.attachRuntimeInfo({ did, nodeInfo, diskInfo: true, context });
788
854
  }
789
855
 
790
- async list({ includeRuntimeInfo = true, useCache = true } = {}, context) {
856
+ async list({ includeRuntimeInfo = true, useCache = true, filter } = {}, context) {
791
857
  const blocklets = await states.blocklet.getBlocklets();
792
- if (!includeRuntimeInfo) {
793
- return blocklets;
794
- }
858
+ let list = blocklets;
859
+
860
+ if (includeRuntimeInfo) {
861
+ const nodeInfo = await states.node.read();
862
+ const updated = (
863
+ await Promise.all(
864
+ blocklets.map((x) => {
865
+ if (isBeforeInstalled(x.status)) {
866
+ return x;
867
+ }
795
868
 
796
- const nodeInfo = await states.node.read();
797
- const updated = (
798
- await Promise.all(
799
- blocklets.map((x) => {
800
- if (isBeforeInstalled(x.status)) {
801
- return x;
802
- }
869
+ const cachedBlocklet =
870
+ useCache && this.cachedBlocklets ? this.cachedBlocklets.find((y) => y.meta.did === x.meta.did) : null;
803
871
 
804
- const cachedBlocklet =
805
- useCache && this.cachedBlocklets ? this.cachedBlocklets.find((y) => y.meta.did === x.meta.did) : null;
872
+ return this.attachRuntimeInfo({
873
+ did: x.meta.did,
874
+ nodeInfo,
875
+ diskInfo: false,
876
+ context,
877
+ cachedBlocklet,
878
+ });
879
+ })
880
+ )
881
+ ).filter(Boolean);
806
882
 
807
- return this.attachRuntimeInfo({
808
- did: x.meta.did,
809
- nodeInfo,
810
- diskInfo: false,
811
- context,
812
- cachedBlocklet,
813
- });
814
- })
815
- )
816
- ).filter(Boolean);
883
+ this.cachedBlocklets = cloneDeep(updated);
884
+ list = updated;
885
+ }
817
886
 
818
- this.cachedBlocklets = cloneDeep(updated);
819
- return updated;
887
+ if (filter === 'external-excluded') {
888
+ return list.filter((x) => !isExternalBlocklet(x));
889
+ }
890
+
891
+ if (filter === 'external-only') {
892
+ return list.filter(isExternalBlocklet);
893
+ }
894
+
895
+ return list;
820
896
  }
821
897
 
822
898
  // eslint-disable-next-line no-unused-vars
@@ -1043,13 +1119,11 @@ class BlockletManager extends BaseBlockletManager {
1043
1119
 
1044
1120
  const upgradeFromRegistry = registryUrl || blocklet.deployedFrom;
1045
1121
 
1046
- const newVersionMeta = await this.registry.getBlockletMeta(
1047
- {
1048
- did: blocklet.meta.bundleDid,
1049
- registryUrl: upgradeFromRegistry,
1050
- },
1051
- context
1052
- );
1122
+ let newVersionMeta = await StoreUtil.getBlockletMeta({
1123
+ did: blocklet.meta.bundleDid,
1124
+ registryUrl: upgradeFromRegistry,
1125
+ });
1126
+ newVersionMeta = ensureMeta(newVersionMeta, { name: blocklet.meta.name, did: blocklet.meta.did });
1053
1127
 
1054
1128
  function getSignature(signatures = [], oldSignatures = []) {
1055
1129
  // if blocklet installed from local, upload, url, the signature is undefined, should return null
@@ -1335,20 +1409,31 @@ class BlockletManager extends BaseBlockletManager {
1335
1409
 
1336
1410
  const oldBlocklet = { children: children.filter((x) => x.dynamic) }; // let downloader skip re-downloading dynamic blocklet
1337
1411
  await this._downloadBlocklet(added, oldBlocklet);
1338
- await states.blocklet.setBlockletStatus(did, BlockletStatus.installed);
1339
1412
 
1340
- // Add environments
1413
+ // Add Config
1341
1414
  await this._setConfigsFromMeta(did);
1415
+ let blocklet = await this.ensureBlocklet(did);
1416
+
1417
+ // pre install
1418
+ await this._runPreInstallHook(blocklet);
1419
+
1420
+ // Add environments
1342
1421
  await this.updateBlockletEnvironment(did);
1422
+ blocklet = await this.ensureBlocklet(did);
1343
1423
 
1344
- const blocklet = await this.ensureBlocklet(did);
1424
+ // post install
1425
+ await this._runPostInstallHook(blocklet);
1426
+
1427
+ await states.blocklet.setBlockletStatus(did, BlockletStatus.installed);
1428
+
1429
+ blocklet = await this.ensureBlocklet(did);
1345
1430
 
1346
1431
  await fs.writeFile(path.join(blocklet.env.dataDir, 'logo.svg'), createDidLogo(blocklet.meta.did));
1347
1432
 
1348
1433
  return blocklet;
1349
1434
  }
1350
1435
 
1351
- async _devComponent({ folder, meta, rootDid, mountPoint }) {
1436
+ async _devComponent({ folder, meta, rootDid, mountPoint, skipNavigation = true }) {
1352
1437
  const { did, version } = meta;
1353
1438
 
1354
1439
  const existRoot = await states.blocklet.getBlocklet(rootDid);
@@ -1373,36 +1458,44 @@ class BlockletManager extends BaseBlockletManager {
1373
1458
  }
1374
1459
 
1375
1460
  const defaultPath = formatName(meta.name);
1376
- const children = [
1377
- {
1378
- meta: ensureMeta(meta),
1379
- mountPoint: mountPoint || `/${defaultPath}`,
1380
- source: BlockletSource.local,
1381
- deployedFrom: folder,
1382
- status: BlockletStatus.installed,
1383
- mode: BLOCKLET_MODES.DEVELOPMENT,
1384
- dynamic: true,
1385
- children: await parseChildrenFromMeta(meta, { ancestors: [{ mountPoint: mountPoint || `/${defaultPath}` }] }),
1386
- },
1387
- ];
1388
- await validateBlocklet(children[0]);
1389
- await states.blocklet.addChildren(rootDid, children);
1461
+ const component = {
1462
+ meta: ensureMeta(meta),
1463
+ mountPoint: mountPoint || `/${defaultPath}`,
1464
+ source: BlockletSource.local,
1465
+ deployedFrom: folder,
1466
+ status: BlockletStatus.installed,
1467
+ mode: BLOCKLET_MODES.DEVELOPMENT,
1468
+ dynamic: true,
1469
+ children: await parseChildrenFromMeta(meta, { ancestors: [{ mountPoint: mountPoint || `/${defaultPath}` }] }),
1470
+ };
1471
+ await validateBlocklet(component);
1472
+ await states.blocklet.addChildren(rootDid, [component]);
1390
1473
 
1391
1474
  const newBlocklet = await states.blocklet.getBlocklet(rootDid);
1392
1475
  await this._downloadBlocklet(newBlocklet, existRoot);
1393
1476
  await states.blocklet.setBlockletStatus(rootDid, BlockletStatus.stopped);
1394
1477
 
1395
- await this._upsertDynamicNavigation(existRoot.meta.did, children[0]);
1478
+ await this._upsertDynamicNavigation(existRoot.meta.did, component, { skipNavigation });
1396
1479
 
1397
- // Add environments
1480
+ // Add Config
1398
1481
  await this._setConfigsFromMeta(rootDid);
1482
+ let blocklet = await this.ensureBlocklet(rootDid);
1483
+
1484
+ // pre install
1485
+ await this._runPreInstallHook(blocklet);
1486
+
1487
+ // Add environments
1399
1488
  await this.updateBlockletEnvironment(rootDid);
1489
+ blocklet = await this.ensureBlocklet(rootDid);
1490
+
1491
+ // post install
1492
+ await this._runPostInstallHook(blocklet);
1400
1493
 
1401
1494
  logger.info('add blocklet component for dev', { did, version, meta });
1402
1495
 
1403
- const rootBlocklet = await this.ensureBlocklet(rootDid);
1496
+ blocklet = await this.ensureBlocklet(rootDid);
1404
1497
 
1405
- return rootBlocklet;
1498
+ return blocklet;
1406
1499
  }
1407
1500
 
1408
1501
  async ensureBlocklet(did, opts = {}) {
@@ -1857,13 +1950,15 @@ class BlockletManager extends BaseBlockletManager {
1857
1950
  * did: string;
1858
1951
  * registry: string;
1859
1952
  * sync: boolean;
1953
+ * delay: number;
1954
+ * controller: Controller;
1860
1955
  * }} params
1861
1956
  * @param {*} context
1862
1957
  * @return {*}
1863
1958
  * @memberof BlockletManager
1864
1959
  */
1865
1960
  async _installFromStore(params, context) {
1866
- const { did, registry, sync } = params;
1961
+ const { did, registry, sync, delay, controller } = params;
1867
1962
 
1868
1963
  logger.debug('start install blocklet', { did });
1869
1964
  if (!isValidDid(did)) {
@@ -1871,67 +1966,96 @@ class BlockletManager extends BaseBlockletManager {
1871
1966
  }
1872
1967
 
1873
1968
  const registryUrl = registry || (await states.node.getBlockletRegistry());
1874
- const info = await BlockletRegistry.getRegistryMeta(registryUrl);
1875
- const meta = await this.registry.getBlocklet(did, registryUrl);
1969
+ const info = await StoreUtil.getRegistryMeta(registryUrl);
1970
+ const meta = await StoreUtil.getBlockletMeta({ did, registryUrl });
1876
1971
  if (!meta) {
1877
1972
  throw new Error('Can not install blocklet that not found in registry');
1878
1973
  }
1879
1974
 
1880
- const state = await states.blocklet.getBlocklet(meta.did);
1975
+ const { did: blockletDid, name: blockletName } = getBlockletIndex(meta, controller);
1976
+
1977
+ const state = await states.blocklet.getBlocklet(blockletDid);
1978
+
1881
1979
  if (state) {
1882
1980
  throw new Error('Can not install an already installed blocklet');
1883
1981
  }
1884
1982
 
1983
+ if (controller?.vcId) {
1984
+ // sometimes nedb will throw error if use states.blocklet.count({ ['controller.vcId']: controller.vcId })
1985
+ const blocklets = await states.blocklet.find({}, { controller: 1 });
1986
+ const count = blocklets.filter((x) => x.controller?.vcId === controller.vcId).length;
1987
+
1988
+ if (count >= (controller.appMaxCount || 1)) {
1989
+ throw new Error(
1990
+ `You can only install ${controller.appMaxCount} blocklet with this credential: ${controller.vcId}`
1991
+ );
1992
+ }
1993
+ }
1994
+
1885
1995
  // install
1886
1996
  return this._install({
1887
- meta,
1997
+ meta: ensureMeta(meta, { did: blockletDid, name: blockletName }),
1888
1998
  source: BlockletSource.registry,
1889
1999
  deployedFrom: info.cdnUrl || registryUrl,
1890
2000
  sync,
2001
+ delay,
2002
+ controller,
1891
2003
  context,
1892
2004
  });
1893
2005
  }
1894
2006
 
1895
2007
  /**
1896
- *
2008
+ * @type {{
2009
+ * id: string;
2010
+ * vcId: string;
2011
+ * expireDate: Date;
2012
+ * }} Controller
1897
2013
  *
1898
2014
  * @param {{
1899
2015
  * url: string;
1900
2016
  * sync: boolean;
2017
+ * delay: number;
2018
+ * controller: Controller;
1901
2019
  * }} params
1902
2020
  * @param {{}} context
1903
2021
  * @return {*}
1904
2022
  * @memberof BlockletManager
1905
2023
  */
1906
2024
  async _installFromUrl(params, context) {
1907
- const { url, sync } = params;
2025
+ const { url, sync, delay, controller } = params;
1908
2026
 
1909
2027
  logger.debug('start install blocklet', { url });
1910
2028
 
1911
- const { inStore, registryUrl, blockletDid } = await parseSourceUrl(url);
2029
+ const bundleMeta = await getBlockletMetaFromUrl(url);
2030
+
2031
+ if (!bundleMeta) {
2032
+ throw new Error(`Can not install blocklet that not found by url: ${url}`);
2033
+ }
2034
+
2035
+ const { did: blockletDid, name: blockletName } = getBlockletIndex(bundleMeta, controller);
2036
+
2037
+ // install from store if url is a store url
2038
+ const { inStore, registryUrl, blockletDid: bundleDid } = await StoreUtil.parseSourceUrl(url, controller);
1912
2039
  if (inStore) {
1913
2040
  const exist = await states.blocklet.getBlocklet(blockletDid);
1914
2041
  if (exist) {
1915
- return this.upgrade({ did: blockletDid, registryUrl }, context);
2042
+ return this.upgrade({ did: blockletDid, registryUrl, sync, delay }, context);
1916
2043
  }
1917
2044
 
1918
- return this._installFromStore({ did: blockletDid, registry: registryUrl }, context);
2045
+ return this._installFromStore({ did: bundleDid, registry: registryUrl, controller, sync, delay }, context);
1919
2046
  }
1920
2047
 
1921
- const meta = await getBlockletMetaFromUrl(url);
1922
-
1923
- if (!meta) {
1924
- throw new Error(`Can not install blocklet that not found by url: ${url}`);
1925
- }
2048
+ const meta = ensureMeta(bundleMeta, { name: blockletName, did: blockletDid });
1926
2049
 
1927
2050
  // upgrade
1928
- const exist = await states.blocklet.getBlocklet(meta.did);
2051
+ const exist = await states.blocklet.getBlocklet(blockletDid);
1929
2052
  if (exist) {
1930
2053
  return this._upgrade({
1931
2054
  meta,
1932
2055
  source: BlockletSource.url,
1933
2056
  deployedFrom: url,
1934
2057
  sync,
2058
+ delay,
1935
2059
  context,
1936
2060
  });
1937
2061
  }
@@ -1942,6 +2066,8 @@ class BlockletManager extends BaseBlockletManager {
1942
2066
  source: BlockletSource.url,
1943
2067
  deployedFrom: url,
1944
2068
  sync,
2069
+ delay,
2070
+ controller,
1945
2071
  context,
1946
2072
  });
1947
2073
  }
@@ -1956,13 +2082,14 @@ class BlockletManager extends BaseBlockletManager {
1956
2082
  did: inputDid,
1957
2083
  configs,
1958
2084
  downloadTokenList,
2085
+ skipNavigation,
1959
2086
  }) {
1960
2087
  const blocklet = await this._getBlockletForInstallation(rootDid);
1961
2088
  if (!blocklet) {
1962
2089
  throw new Error('Root blocklet does not exist');
1963
2090
  }
1964
2091
 
1965
- const { inStore } = await parseSourceUrl(url);
2092
+ const { inStore } = await StoreUtil.parseSourceUrl(url);
1966
2093
 
1967
2094
  const meta = await getBlockletMetaFromUrl(url);
1968
2095
 
@@ -2018,7 +2145,7 @@ class BlockletManager extends BaseBlockletManager {
2018
2145
  await states.blocklet.addChildren(rootDid, [newChild]);
2019
2146
 
2020
2147
  // update navigation
2021
- await this._upsertDynamicNavigation(blocklet.meta.did, newChild);
2148
+ await this._upsertDynamicNavigation(blocklet.meta.did, newChild, { skipNavigation });
2022
2149
 
2023
2150
  // update configs
2024
2151
  if (Array.isArray(configs)) {
@@ -2100,7 +2227,7 @@ class BlockletManager extends BaseBlockletManager {
2100
2227
  .pipe(writeStream);
2101
2228
  readStream.on('error', (error) => {
2102
2229
  logger.error('File upload read stream failed', { error });
2103
- writeStream.destroy(new Error('File upload read stream failed'));
2230
+ writeStream.destroy(new Error(`File upload read stream failed, ${error.message}`));
2104
2231
  });
2105
2232
  writeStream.on('error', (error) => {
2106
2233
  logger.error('File upload write stream failed', { error });
@@ -2213,7 +2340,16 @@ class BlockletManager extends BaseBlockletManager {
2213
2340
  }
2214
2341
  }
2215
2342
 
2216
- async _installComponentFromUpload({ rootDid, mountPoint, file, did, diffVersion, deleteSet, context }) {
2343
+ async _installComponentFromUpload({
2344
+ rootDid,
2345
+ mountPoint,
2346
+ file,
2347
+ did,
2348
+ diffVersion,
2349
+ deleteSet,
2350
+ skipNavigation,
2351
+ context,
2352
+ }) {
2217
2353
  logger.info('install blocklet', { from: 'upload file' });
2218
2354
  // download
2219
2355
  const { cwd, tarFile } = await this._downloadFromUpload(file);
@@ -2280,7 +2416,7 @@ class BlockletManager extends BaseBlockletManager {
2280
2416
 
2281
2417
  checkDuplicateComponents(newBlocklet.children);
2282
2418
 
2283
- await this._upsertDynamicNavigation(newBlocklet.meta.did, newChild);
2419
+ await this._upsertDynamicNavigation(newBlocklet.meta.did, newChild, { skipNavigation });
2284
2420
 
2285
2421
  await this._downloadBlocklet(newBlocklet, oldBlocklet);
2286
2422
 
@@ -2403,12 +2539,14 @@ class BlockletManager extends BaseBlockletManager {
2403
2539
  * deployedFrom: string;
2404
2540
  * context: {}
2405
2541
  * sync: boolean = false;
2542
+ * delay: number;
2543
+ * controller: Controller;
2406
2544
  * }} params
2407
2545
  * @return {*}
2408
2546
  * @memberof BlockletManager
2409
2547
  */
2410
2548
  async _install(params) {
2411
- const { meta, source, deployedFrom, context, sync } = params;
2549
+ const { meta, source, deployedFrom, context, sync, delay, controller } = params;
2412
2550
 
2413
2551
  validateBlockletMeta(meta, { ensureDist: true });
2414
2552
 
@@ -2424,6 +2562,7 @@ class BlockletManager extends BaseBlockletManager {
2424
2562
  source,
2425
2563
  deployedFrom,
2426
2564
  children,
2565
+ controller,
2427
2566
  });
2428
2567
 
2429
2568
  await validateBlocklet(blocklet);
@@ -2459,31 +2598,33 @@ class BlockletManager extends BaseBlockletManager {
2459
2598
  return states.blocklet.getBlocklet(did);
2460
2599
  }
2461
2600
 
2462
- const ticket = this.installQueue.push(
2463
- {
2464
- entity: 'blocklet',
2465
- action: 'download',
2466
- id: did,
2467
- ...downloadParams,
2468
- },
2469
- did
2470
- );
2471
- ticket.on('failed', async (err) => {
2472
- logger.error('failed to install blocklet', { name, did, version, error: err });
2473
- try {
2474
- await this._rollback('install', did, { extraState: oldExtraState });
2475
- } catch (e) {
2476
- logger.error('failed to remove blocklet on install error', { did: meta.did, error: e });
2477
- }
2601
+ setTimeout(() => {
2602
+ const ticket = this.installQueue.push(
2603
+ {
2604
+ entity: 'blocklet',
2605
+ action: 'download',
2606
+ id: did,
2607
+ ...downloadParams,
2608
+ },
2609
+ did
2610
+ );
2611
+ ticket.on('failed', async (err) => {
2612
+ logger.error('failed to install blocklet', { name, did, version, error: err });
2613
+ try {
2614
+ await this._rollback('install', did, { extraState: oldExtraState });
2615
+ } catch (e) {
2616
+ logger.error('failed to remove blocklet on install error', { did: meta.did, error: e });
2617
+ }
2478
2618
 
2479
- states.notification.create({
2480
- title: 'Blocklet Install Failed',
2481
- description: `Blocklet ${name}@${version} install failed with error: ${err.message || 'queue exception'}`,
2482
- entityType: 'blocklet',
2483
- entityId: did,
2484
- severity: 'error',
2619
+ states.notification.create({
2620
+ title: 'Blocklet Install Failed',
2621
+ description: `Blocklet ${name}@${version} install failed with error: ${err.message || 'queue exception'}`,
2622
+ entityType: 'blocklet',
2623
+ entityId: did,
2624
+ severity: 'error',
2625
+ });
2485
2626
  });
2486
- });
2627
+ }, delay || 0);
2487
2628
 
2488
2629
  return blocklet1;
2489
2630
  } catch (err) {
@@ -2746,33 +2887,14 @@ class BlockletManager extends BaseBlockletManager {
2746
2887
  }
2747
2888
 
2748
2889
  // pre install
2749
- const nodeEnvironments = await states.node.getEnvironments();
2750
- const preInstall = (b) =>
2751
- hooks.preInstall(b.env.processId, {
2752
- hooks: Object.assign(b.meta.hooks || {}, b.meta.scripts || {}),
2753
- env: { ...nodeEnvironments },
2754
- appDir: b.env.appDir,
2755
- did, // root blocklet did
2756
- notification: states.notification,
2757
- context,
2758
- });
2759
- await forEachBlocklet(blocklet, preInstall, { parallel: true });
2890
+ await this._runPreInstallHook(blocklet, context);
2760
2891
 
2761
2892
  // Add environments
2762
2893
  await this.updateBlockletEnvironment(meta.did);
2763
2894
  blocklet = await this.ensureBlocklet(did);
2764
2895
 
2765
2896
  // post install
2766
- const postInstall = (b, { ancestors }) =>
2767
- hooks.postInstall(b.env.processId, {
2768
- hooks: Object.assign(b.meta.hooks || {}, b.meta.scripts || {}),
2769
- env: getRuntimeEnvironments(b, nodeEnvironments, ancestors),
2770
- appDir: b.env.appDir,
2771
- did, // root blocklet did
2772
- notification: states.notification,
2773
- context,
2774
- });
2775
- await forEachBlocklet(blocklet, postInstall, { parallel: true });
2897
+ await this._runPostInstallHook(blocklet, context);
2776
2898
 
2777
2899
  await states.blocklet.setBlockletStatus(did, BlockletStatus.installed);
2778
2900
  blocklet = await this.ensureBlocklet(did);
@@ -2858,33 +2980,14 @@ class BlockletManager extends BaseBlockletManager {
2858
2980
  let blocklet = await this.ensureBlocklet(did);
2859
2981
 
2860
2982
  // pre install
2861
- const nodeEnvironments = await states.node.getEnvironments();
2862
- const preInstall = (b) =>
2863
- hooks.preInstall(b.env.processId, {
2864
- hooks: Object.assign(b.meta.hooks || {}, b.meta.scripts || {}),
2865
- env: { ...nodeEnvironments },
2866
- appDir: b.env.appDir,
2867
- did, // root blocklet did
2868
- notification: states.notification,
2869
- context,
2870
- });
2871
- await forEachBlocklet(blocklet, preInstall, { parallel: true });
2983
+ await this._runPreInstallHook(blocklet, context);
2872
2984
 
2873
2985
  // Add environments
2874
2986
  await this.updateBlockletEnvironment(did);
2875
2987
  blocklet = await this.ensureBlocklet(did);
2876
2988
 
2877
2989
  // post install
2878
- const postInstall = (b, { ancestors }) =>
2879
- hooks.postInstall(b.env.processId, {
2880
- hooks: Object.assign(b.meta.hooks || {}, b.meta.scripts || {}),
2881
- env: getRuntimeEnvironments(b, nodeEnvironments, ancestors),
2882
- appDir: b.env.appDir,
2883
- did, // root blocklet did
2884
- notification: states.notification,
2885
- context,
2886
- });
2887
- await forEachBlocklet(blocklet, postInstall, { parallel: true });
2990
+ await this._runPostInstallHook(blocklet, context);
2888
2991
 
2889
2992
  logger.info('start migration');
2890
2993
  try {
@@ -2892,6 +2995,7 @@ class BlockletManager extends BaseBlockletManager {
2892
2995
  forEachBlockletSync(oldBlocklet, (b, { id }) => {
2893
2996
  oldVersions[id] = b.meta.version;
2894
2997
  });
2998
+ const nodeEnvironments = await states.node.getEnvironments();
2895
2999
  const runMigration = (b, { id, ancestors }) => {
2896
3000
  return runMigrationScripts({
2897
3001
  blocklet: b,
@@ -3241,11 +3345,7 @@ class BlockletManager extends BaseBlockletManager {
3241
3345
  logger.info('Download Blocklet', { name, did, bundles: metas.map((x) => get(x, 'dist.tarball')) });
3242
3346
  const tasks = [];
3243
3347
  for (const meta of metas) {
3244
- const url = await this.registry.resolveTarballURL({
3245
- did,
3246
- tarball: get(meta, 'dist.tarball'),
3247
- registryUrl: blocklet.source === BlockletSource.registry ? blocklet.deployedFrom : undefined,
3248
- });
3348
+ const url = meta.dist.tarball;
3249
3349
  tasks.push(this._downloadBundle(meta, did, url, context));
3250
3350
  }
3251
3351
  const results = await Promise.all(tasks);
@@ -3410,13 +3510,17 @@ class BlockletManager extends BaseBlockletManager {
3410
3510
  return { did, name };
3411
3511
  }
3412
3512
 
3413
- async _upsertDynamicNavigation(did, child) {
3513
+ async _upsertDynamicNavigation(did, child, { skipNavigation } = {}) {
3414
3514
  const navigation = await states.blockletExtras.getSettings(did, 'navigation', []);
3415
3515
  const item = { title: child.meta.title, child: child.meta.name };
3416
3516
  const index = navigation.findIndex((x) => x.child === item.child);
3417
3517
  if (index > -1) {
3418
- navigation.splice(index, 1, item);
3419
- } else {
3518
+ if (skipNavigation) {
3519
+ navigation.splice(index, 1);
3520
+ } else {
3521
+ navigation.splice(index, 1, item);
3522
+ }
3523
+ } else if (!skipNavigation) {
3420
3524
  navigation.push(item);
3421
3525
  }
3422
3526
  await states.blockletExtras.setSettings(did, { navigation });
@@ -3435,24 +3539,20 @@ class BlockletManager extends BaseBlockletManager {
3435
3539
  }
3436
3540
 
3437
3541
  async _getLatestBlockletVersionFromStore({ blocklet, version }) {
3542
+ const { deployedFrom: registryUrl } = blocklet;
3438
3543
  const { did, bundleDid } = blocklet.meta;
3439
3544
 
3440
3545
  let versions = this.cachedBlockletVersions.get(did);
3441
3546
 
3442
3547
  if (!versions) {
3443
- const { blockletRegistryList } = await states.node.read();
3444
- const tasks = blockletRegistryList.map((registry) =>
3445
- this.registry
3446
- .getBlockletMeta({ did: bundleDid, registryUrl: registry.url })
3447
- .then((item) => ({ did, version: item.version, registryUrl: registry.url }))
3448
- .catch((error) => {
3449
- if (error.response && error.response.status === 404) {
3450
- return;
3451
- }
3452
- logger.error('get blocklet meta from registry failed', { did, error });
3453
- })); // prettier-ignore
3548
+ const item = await StoreUtil.getBlockletMeta({ did: bundleDid, registryUrl });
3549
+
3550
+ if (!item) {
3551
+ return null;
3552
+ }
3553
+
3554
+ versions = [{ did, version: item.version }];
3454
3555
 
3455
- versions = await Promise.all(tasks);
3456
3556
  this.cachedBlockletVersions.set(did, versions);
3457
3557
  }
3458
3558
 
@@ -3505,6 +3605,38 @@ class BlockletManager extends BaseBlockletManager {
3505
3605
 
3506
3606
  return versions[0];
3507
3607
  }
3608
+
3609
+ async _runPreInstallHook(blocklet, context) {
3610
+ const nodeEnvironments = await states.node.getEnvironments();
3611
+
3612
+ const preInstall = (b) =>
3613
+ hooks.preInstall(b.env.processId, {
3614
+ hooks: Object.assign(b.meta.hooks || {}, b.meta.scripts || {}),
3615
+ env: { ...nodeEnvironments },
3616
+ appDir: b.env.appDir,
3617
+ did: blocklet.meta.did, // root blocklet did
3618
+ notification: states.notification,
3619
+ context,
3620
+ });
3621
+
3622
+ await forEachBlocklet(blocklet, preInstall, { parallel: true });
3623
+ }
3624
+
3625
+ async _runPostInstallHook(blocklet, context) {
3626
+ const nodeEnvironments = await states.node.getEnvironments();
3627
+
3628
+ const postInstall = (b, { ancestors }) =>
3629
+ hooks.postInstall(b.env.processId, {
3630
+ hooks: Object.assign(b.meta.hooks || {}, b.meta.scripts || {}),
3631
+ env: getRuntimeEnvironments(b, nodeEnvironments, ancestors),
3632
+ appDir: b.env.appDir,
3633
+ did: blocklet.meta.did, // root blocklet did
3634
+ notification: states.notification,
3635
+ context,
3636
+ });
3637
+
3638
+ await forEachBlocklet(blocklet, postInstall, { parallel: true });
3639
+ }
3508
3640
  }
3509
3641
 
3510
3642
  module.exports = BlockletManager;