@abtnode/core 1.6.16 → 1.6.20

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.
@@ -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
  };
package/lib/event.js CHANGED
@@ -132,7 +132,7 @@ module.exports = ({
132
132
  onEvent(name, blocklet);
133
133
  };
134
134
 
135
- const handleCLIEvent = (eventName) => {
135
+ const handleServerEvent = (eventName) => {
136
136
  const [, status] = eventName.split('.');
137
137
  onEvent(eventName, {
138
138
  title: `Blocklet Server ${status}`,
@@ -209,7 +209,8 @@ module.exports = ({
209
209
 
210
210
  events.handleBlockletAdd = handleBlockletAdd;
211
211
  events.handleBlockletRemove = handleBlockletRemove;
212
- events.handleCLIEvent = handleCLIEvent;
212
+ events.handleServerEvent = handleServerEvent;
213
+ events.onEvent = onEvent;
213
214
 
214
215
  return events;
215
216
  };
package/lib/index.js CHANGED
@@ -197,16 +197,17 @@ function ABTNode(options) {
197
197
  // Blocklet manager
198
198
  installBlocklet: blockletManager.install.bind(blockletManager),
199
199
  installBlockletFromVc: blockletManager.installBlockletFromVc.bind(blockletManager),
200
+ installComponent: blockletManager.installComponent.bind(blockletManager),
200
201
  startBlocklet: blockletManager.start.bind(blockletManager),
201
202
  stopBlocklet: blockletManager.stop.bind(blockletManager),
202
203
  reloadBlocklet: blockletManager.reload.bind(blockletManager),
203
204
  restartBlocklet: blockletManager.restart.bind(blockletManager),
204
205
  deleteBlocklet: blockletManager.delete.bind(blockletManager),
206
+ deleteComponent: blockletManager.deleteComponent.bind(blockletManager),
205
207
  cancelDownloadBlocklet: blockletManager.cancelDownload.bind(blockletManager),
206
208
  upgradeBlocklet: blockletManager.upgrade.bind(blockletManager),
207
209
  configBlocklet: blockletManager.config.bind(blockletManager),
208
210
  devBlocklet: blockletManager.dev.bind(blockletManager),
209
- getBlockletStatus: blockletManager.getStatus.bind(blockletManager),
210
211
  checkChildBlockletsForUpdates: blockletManager.checkChildrenForUpdates.bind(blockletManager),
211
212
  updateChildBlocklets: blockletManager.updateChildren.bind(blockletManager),
212
213
  getLatestBlockletVersion: blockletManager.getLatestBlockletVersion.bind(blockletManager),
@@ -0,0 +1,48 @@
1
+ /* eslint-disable no-continue */
2
+ /* eslint-disable no-await-in-loop */
3
+ /* eslint-disable no-underscore-dangle */
4
+
5
+ module.exports = async ({ states, printInfo }) => {
6
+ printInfo('Try to update blocklet to 1.6.17...');
7
+ const blockletState = states.blocklet;
8
+
9
+ const blocklets = await blockletState.getBlocklets();
10
+ for (const blocklet of blocklets) {
11
+ if (!blocklet) {
12
+ continue;
13
+ }
14
+
15
+ if (!blocklet.children || !blocklet.children.length) {
16
+ continue;
17
+ }
18
+
19
+ const meta = blocklet.meta || {};
20
+ const { did } = meta;
21
+ if (!did) {
22
+ continue;
23
+ }
24
+
25
+ const children = (blocklet.children || []).map((child) => {
26
+ if (child.mountPoint) {
27
+ return child;
28
+ }
29
+
30
+ const config = (meta.children || []).find((x) => x.name === child.meta.name);
31
+
32
+ if (
33
+ config &&
34
+ config.mountPoints &&
35
+ config.mountPoints[0] &&
36
+ config.mountPoints[0].root &&
37
+ config.mountPoints[0].root.prefix
38
+ ) {
39
+ child.mountPoint = config.mountPoints[0].root.prefix;
40
+ }
41
+ printInfo(`Set mountPoint: ${child.mountPoint} to child ${child.meta.name} in ${blocklet.meta.name}`);
42
+
43
+ return child;
44
+ });
45
+
46
+ await blockletState.update(blocklet._id, { $set: { children } });
47
+ }
48
+ };
@@ -317,7 +317,7 @@ const ensureWellknownRule = async (sites) => {
317
317
  rules.forEach((rule) => {
318
318
  if (
319
319
  rule.to.type !== ROUTING_RULE_TYPES.BLOCKLET || // is not blocklet
320
- rule.to.did !== rule.to.realDid // is a component endpoint
320
+ (rule.to.did !== rule.to.realDid && rule.from.pathPrefix !== '/') // is a component endpoint in sub path
321
321
  ) {
322
322
  return;
323
323
  }
@@ -660,7 +660,9 @@ module.exports = function getRouterHelpers({ dataDirs, routingSnapshot, routerMa
660
660
 
661
661
  const dashboardDomain = get(info, 'routing.dashboardDomain', '');
662
662
  const didDomain = `${info.did.toLowerCase()}.${info.didDomain}`;
663
- const dashboardAliasDomains = [dashboardDomain, didDomain]
663
+ let dashboardAliasDomains = [dashboardDomain, didDomain];
664
+
665
+ dashboardAliasDomains = dashboardAliasDomains
664
666
  .filter((item) => item && !isExistsInAlias(item))
665
667
  .map((item) => ({ value: item, isProtected: true }));
666
668
 
@@ -746,18 +748,18 @@ module.exports = function getRouterHelpers({ dataDirs, routingSnapshot, routerMa
746
748
 
747
749
  const existSite = await states.site.findOne({ domain: domainGroup });
748
750
  if (!existSite) {
749
- const ipEchoDnsDomain = getIpDnsDomainForBlocklet(blocklet, webInterface, nodeInfo.did);
750
- const appIdEnv = blocklet.environments.find((e) => e.key === 'BLOCKLET_APP_ID');
751
- const domainAliases = [{ value: ipEchoDnsDomain, isProtected: true }];
751
+ const domainAliases = [];
752
752
 
753
753
  const didDomain = getDidDomainForBlocklet({
754
- appId: appIdEnv.value,
754
+ name: blocklet.meta.name,
755
+ daemonDid: nodeInfo.did,
755
756
  didDomain: nodeInfo.didDomain,
756
757
  });
757
758
 
758
- if (blocklet.mode !== 'development') {
759
- domainAliases.push({ value: didDomain, isProtected: true });
760
- }
759
+ const ipEchoDnsDomain = getIpDnsDomainForBlocklet(blocklet, webInterface, nodeInfo.did);
760
+
761
+ // let didDomain in front of ipEchoDnsDomain
762
+ domainAliases.push({ value: didDomain, isProtected: true }, { value: ipEchoDnsDomain, isProtected: true });
761
763
 
762
764
  await routerManager.addRoutingSite(
763
765
  {
@@ -27,6 +27,7 @@ const {
27
27
  BLOCKLET_BUNDLE_FOLDER,
28
28
  BLOCKLET_DYNAMIC_PATH_PREFIX,
29
29
  BLOCKLET_INTERFACE_TYPE_WEB,
30
+ BlockletGroup,
30
31
  } = require('@blocklet/meta/lib/constants');
31
32
 
32
33
  const {
@@ -37,6 +38,7 @@ const {
37
38
  validateUpdateSite,
38
39
  } = require('../validators/router');
39
40
  const { getProviderFromNodeInfo, findInterfaceByName, isCLI, findInterfacePortByName } = require('../util');
41
+ const { findWebInterface } = require('../util/blocklet');
40
42
  const { attachInterfaceUrls, ensureLatestInfo } = require('./helper');
41
43
  const Router = require('./index');
42
44
  const states = require('../states');
@@ -219,10 +221,10 @@ class RouterManager extends EventEmitter {
219
221
  }
220
222
  }
221
223
 
222
- const updateResult = await states.site.update(
223
- { _id: id },
224
- { $push: { domainAliases: { value: domainAlias, isProtected: false } } }
225
- );
224
+ // let custom domain in front of protected domain
225
+ const domainAliases = [{ value: domainAlias, isProtected: false }, ...dbSite.domainAliases];
226
+
227
+ const updateResult = await states.site.update({ _id: id }, { $set: { domainAliases } });
226
228
  logger.debug('add domain alias update result', { id, updateResult, domainAlias });
227
229
 
228
230
  const newSite = await states.site.findOne({ _id: id });
@@ -679,49 +681,51 @@ class RouterManager extends EventEmitter {
679
681
 
680
682
  // get child rules
681
683
  const blocklet = await states.blocklet.getBlocklet(rule.to.did);
682
- for (const childMeta of blocklet.meta.children || []) {
683
- for (const mountPoint of childMeta.mountPoints) {
684
- const child = blocklet.children.find((b) => b.meta.name === childMeta.name);
685
-
686
- if (!child) {
687
- logger.error(`Child blocklet ${childMeta.name} does not exist`);
688
- // eslint-disable-next-line no-continue
689
- continue;
690
- }
684
+ for (const child of blocklet.children || []) {
685
+ const { mountPoint } = child;
686
+ if (!mountPoint) {
687
+ logger.error(`mountPoint of child ${child.meta.name} does not exist`);
688
+ // eslint-disable-next-line no-continue
689
+ continue;
690
+ }
691
691
 
692
- if (mountPoint.root.interfaceName === rule.to.interfaceName) {
693
- const pathPrefix = path.join(rule.from.pathPrefix, mountPoint.root.prefix);
694
- const isRootPath = pathPrefix === rule.from.pathPrefix;
695
- if (isRootPath) {
696
- occupied = true;
697
- }
692
+ const childWebInterface = findWebInterface(child);
693
+ if (!childWebInterface) {
694
+ logger.error(`web interface of child ${child.meta.name} does not exist`);
695
+ // eslint-disable-next-line no-continue
696
+ continue;
697
+ }
698
698
 
699
- // if is root path, child rule become root rule
700
- const childRule = {
701
- id: isRootPath ? rule.id : uuid.v4(),
702
- groupId: rule.id,
703
- from: {
704
- pathPrefix: normalizePathPrefix(pathPrefix),
705
- groupPathPrefix: rule.from.pathPrefix,
706
- },
707
- to: {
708
- type: ROUTING_RULE_TYPES.BLOCKLET,
709
- port: findInterfacePortByName(child, mountPoint.child.interfaceName),
710
- did: rule.to.did, // root blocklet did
711
- interfaceName: rule.to.interfaceName, // root blocklet interface
712
- realDid: child.meta.did, // child blocklet did
713
- realInterfaceName: mountPoint.child.interfaceName,
714
- },
715
- isProtected: isRootPath ? rule.isProtected : true,
716
- };
717
-
718
- rules.push(childRule);
719
- }
699
+ const pathPrefix = path.join(rule.from.pathPrefix, mountPoint);
700
+ const isRootPath = pathPrefix === rule.from.pathPrefix;
701
+ if (isRootPath) {
702
+ occupied = true;
720
703
  }
704
+
705
+ // if is root path, child rule become root rule
706
+ const childRule = {
707
+ id: isRootPath ? rule.id : uuid.v4(),
708
+ groupId: rule.id,
709
+ from: {
710
+ pathPrefix: normalizePathPrefix(pathPrefix),
711
+ groupPathPrefix: rule.from.pathPrefix,
712
+ },
713
+ to: {
714
+ type: ROUTING_RULE_TYPES.BLOCKLET,
715
+ port: findInterfacePortByName(child, childWebInterface.name),
716
+ did: rule.to.did, // root blocklet did
717
+ interfaceName: rule.to.interfaceName, // root blocklet interface
718
+ realDid: child.meta.did, // child blocklet did
719
+ realInterfaceName: childWebInterface.name,
720
+ },
721
+ isProtected: isRootPath ? rule.isProtected : true,
722
+ };
723
+
724
+ rules.push(childRule);
721
725
  }
722
726
 
723
727
  // get root rule
724
- if (!occupied) {
728
+ if (!occupied && blocklet.meta.group !== BlockletGroup.gateway) {
725
729
  rules.push(rule);
726
730
  }
727
731
 
@@ -3,6 +3,7 @@
3
3
  /* eslint-disable consistent-return */
4
4
  const logger = require('@abtnode/logger')('state-blocklet-extras');
5
5
  const camelCase = require('lodash/camelCase');
6
+ const get = require('lodash/get');
6
7
 
7
8
  const BaseState = require('./base');
8
9
 
@@ -36,16 +37,28 @@ class BlockletExtrasState extends BaseState {
36
37
  },
37
38
  },
38
39
  ];
40
+
41
+ this.childExtras = this.extras.filter((x) => ['configs'].includes(x.name));
42
+
39
43
  this.generateExtraFns();
40
44
  }
41
45
 
46
+ delete(did) {
47
+ return this.remove({ did });
48
+ }
49
+
42
50
  generateExtraFns() {
43
- const methods = ['get', 'set', 'del'];
51
+ const methods = ['get', 'set', 'del', 'list'];
44
52
  methods.forEach((method) => {
45
53
  this.extras.forEach((extra) => {
46
54
  const fn = camelCase(`${method} ${extra.name}`); // getConfigs, getRules
47
55
  this[fn] = this.generateExtraFn(method, extra);
56
+ });
57
+ });
48
58
 
59
+ const childMethods = ['get', 'set', 'del'];
60
+ childMethods.forEach((method) => {
61
+ this.childExtras.forEach((extra) => {
49
62
  const childFn = camelCase(`${method} child ${extra.name}`); // getChildConfigs, getChildRules
50
63
  this[childFn] = this.generateExtraChildFn(method, extra);
51
64
  });
@@ -66,14 +79,22 @@ class BlockletExtrasState extends BaseState {
66
79
  if (method === 'del') {
67
80
  return this.generateDelFn(extra);
68
81
  }
82
+
83
+ if (method === 'list') {
84
+ return this.generateListFn(extra);
85
+ }
69
86
  }
70
87
 
71
88
  generateGetFn(extra) {
72
- return async (did) => {
89
+ return async (did, path, defaultValue) => {
73
90
  const { dek } = this.options;
74
91
  const { name, afterGet = noop('data') } = extra;
75
92
  const item = await this.asyncDB.findOne({ did });
76
- return afterGet({ data: item ? item[name] : item, did, dek });
93
+ const data = afterGet({ data: item ? item[name] : item, did, dek });
94
+ if (!path) {
95
+ return data;
96
+ }
97
+ return get(data, path, defaultValue);
77
98
  };
78
99
  }
79
100
 
@@ -118,6 +139,21 @@ class BlockletExtrasState extends BaseState {
118
139
  };
119
140
  }
120
141
 
142
+ generateListFn(extra) {
143
+ return async () => {
144
+ const { dek } = this.options;
145
+ const { name, afterGet = noop('data') } = extra;
146
+ const docs = await this.asyncDB.find({});
147
+ const list = docs
148
+ .filter((x) => x[name])
149
+ .map((x) => ({
150
+ did: x.did,
151
+ [name]: afterGet({ data: x[name], did: x.did, dek }),
152
+ }));
153
+ return list;
154
+ };
155
+ }
156
+
121
157
  // generate extra child functions
122
158
 
123
159
  generateExtraChildFn(method, extra) {
@@ -20,7 +20,7 @@ const {
20
20
  const logger = require('@abtnode/logger')('state-blocklet');
21
21
 
22
22
  const BaseState = require('./base');
23
- const { forEachBlocklet } = require('../util/blocklet');
23
+ const { forEachBlocklet, checkDuplicateComponents } = require('../util/blocklet');
24
24
  const { validateBlockletMeta } = require('../util');
25
25
 
26
26
  const lock = new Lock('blocklet-port-assign-lock');
@@ -80,7 +80,11 @@ class BlockletState extends BaseState {
80
80
 
81
81
  getBlocklet(did) {
82
82
  return new Promise((resolve, reject) => {
83
- this.db.findOne({ 'meta.did': did }, (err, doc) => {
83
+ if (!did) {
84
+ resolve(null);
85
+ }
86
+
87
+ this.db.findOne({ $or: [{ 'meta.did': did }, { appDid: did }] }, (err, doc) => {
84
88
  if (err) {
85
89
  return reject(err);
86
90
  }
@@ -131,9 +135,9 @@ class BlockletState extends BaseState {
131
135
  meta,
132
136
  source = BlockletSource.registry,
133
137
  status = BlockletStatus.added,
134
- deployedFrom,
138
+ deployedFrom = '',
135
139
  mode = BLOCKLET_MODES.PRODUCTION,
136
- childrenMeta = [],
140
+ children: rawChildren = [],
137
141
  } = {}) {
138
142
  return this.getBlocklet(did).then(
139
143
  (doc) =>
@@ -153,13 +157,14 @@ class BlockletState extends BaseState {
153
157
 
154
158
  const ports = await this.getBlockletPorts({ interfaces: sanitized.interfaces || [] });
155
159
 
156
- const children = await this.fillChildrenPorts(this.getChildrenFromMetas(childrenMeta), {
160
+ const children = await this.fillChildrenPorts(rawChildren, {
157
161
  defaultPort: getMaxPort(ports),
158
162
  });
159
163
 
160
164
  // add to db
161
165
  this.db.insert(
162
166
  {
167
+ appDid: null, // will updated later when updating blocklet environments
163
168
  mode,
164
169
  meta: omit(sanitized, ['htmlAst']),
165
170
  status,
@@ -207,7 +212,7 @@ class BlockletState extends BaseState {
207
212
  );
208
213
  }
209
214
 
210
- upgradeBlocklet({ meta, source, deployedFrom, children } = {}) {
215
+ upgradeBlocklet({ meta, source, deployedFrom = '', children } = {}) {
211
216
  return this.getBlocklet(meta.did).then(
212
217
  (doc) =>
213
218
  // eslint-disable-next-line no-async-promise-executor
@@ -391,13 +396,20 @@ class BlockletState extends BaseState {
391
396
  return result;
392
397
  }
393
398
 
394
- async setBlockletStatus(did, status) {
399
+ /**
400
+ * @param {String} did blocklet did
401
+ * @param {BlockletStatus} status blocklet status
402
+ *
403
+ * children status only different with parent before blocklet installation
404
+ * @param {Array<{did}>} children
405
+ */
406
+ async setBlockletStatus(did, status, { children } = {}) {
395
407
  if (typeof status === 'undefined') {
396
408
  throw new Error('Unsupported blocklet status');
397
409
  }
398
410
 
399
411
  const doc = await this.getBlocklet(did);
400
- if (doc.status === status) {
412
+ if (doc.status === status && !children) {
401
413
  return formatBlocklet(doc, 'onRead', this.options.dek);
402
414
  }
403
415
 
@@ -412,14 +424,32 @@ class BlockletState extends BaseState {
412
424
  updates.stoppedAt = new Date();
413
425
  }
414
426
 
427
+ // update children status
428
+ updates.children = doc.children.map((child) => {
429
+ if (children === 'all') {
430
+ child.status = status;
431
+ return child;
432
+ }
433
+
434
+ if (!children) {
435
+ if (![BlockletStatus.waiting, BlockletStatus.upgrading, BlockletStatus.installing].includes(status)) {
436
+ child.status = status;
437
+ }
438
+
439
+ return child;
440
+ }
441
+
442
+ const inputChild = children.find((x) => x.did === child.meta.did);
443
+ if (inputChild) {
444
+ child.status = status;
445
+ }
446
+ return child;
447
+ });
448
+
415
449
  await this.update(doc._id, { $set: updates });
416
450
  return formatBlocklet({ ...doc, ...updates }, 'onRead', this.options.dek);
417
451
  }
418
452
 
419
- getChildrenFromMetas(childrenMeta) {
420
- return childrenMeta.map((x) => ({ meta: x }));
421
- }
422
-
423
453
  async fillChildrenPorts(children, { defaultPort = 0, oldChildren } = {}) {
424
454
  let _maxPort = defaultPort;
425
455
  for (const child of children || []) {
@@ -467,6 +497,48 @@ class BlockletState extends BaseState {
467
497
 
468
498
  return children;
469
499
  }
500
+
501
+ async addChildren(did, children) {
502
+ const parent = await this.getBlocklet(did);
503
+ if (!parent) {
504
+ throw new Error('Blocklet does not exist');
505
+ }
506
+
507
+ const oldChildren = parent.children || [];
508
+
509
+ const newChildren = [...oldChildren];
510
+ for (const child of children) {
511
+ const { meta, mountPoint, sourceUrl = '', source = '', deployedFrom = '' } = child;
512
+
513
+ if (!mountPoint) {
514
+ throw new Error(`mountPoint is required when adding component ${meta.title || meta.name}`);
515
+ }
516
+
517
+ if (meta.did === parent.meta.did) {
518
+ throw new Error('Cannot add self as a component');
519
+ }
520
+
521
+ checkDuplicateComponents([child], newChildren);
522
+
523
+ newChildren.push({
524
+ mountPoint,
525
+ meta,
526
+ sourceUrl,
527
+ source,
528
+ deployedFrom,
529
+ dynamic: true,
530
+ status: BlockletStatus.added,
531
+ });
532
+ }
533
+
534
+ // use upgradeBlocklet to assign ports to children and write new data to db
535
+ return this.upgradeBlocklet({
536
+ meta: parent.meta,
537
+ source: parent.source,
538
+ deployedFrom: parent.deployedFrom,
539
+ children: newChildren,
540
+ });
541
+ }
470
542
  }
471
543
 
472
544
  BlockletState.BlockletStatus = BlockletStatus;
@@ -49,7 +49,10 @@ class NodeState extends BaseState {
49
49
  }
50
50
 
51
51
  isInitialized(doc) {
52
- return !!doc.nodeOwner;
52
+ const isOwnerConnected = !!doc.nodeOwner;
53
+ const isControlledBy3rdParty =
54
+ !doc.enablePassportIssuance && Array.isArray(doc.trustedPassports) && doc.trustedPassports.length > 0;
55
+ return isOwnerConnected || isControlledBy3rdParty;
53
56
  }
54
57
 
55
58
  /**
@@ -92,13 +95,15 @@ class NodeState extends BaseState {
92
95
  launcherInfo,
93
96
  didRegistry,
94
97
  didDomain,
98
+ enablePassportIssuance = true,
99
+ trustedPassports = [],
95
100
  } = this.options;
96
101
 
97
102
  if (nodeOwner && !validateOwner(nodeOwner)) {
98
103
  return reject(new Error('Node owner is invalid'));
99
104
  }
100
105
 
101
- const initialized = this.isInitialized({ nodeOwner });
106
+ const initialized = this.isInitialized({ nodeOwner, enablePassportIssuance, trustedPassports });
102
107
 
103
108
  return getDefaultConfigs()
104
109
  .then((defaultConfigs) =>
@@ -126,6 +131,9 @@ class NodeState extends BaseState {
126
131
  launcherInfo: launcherInfo || undefined,
127
132
  didRegistry,
128
133
  didDomain,
134
+ enablePassportIssuance,
135
+ trustedPassports,
136
+ customBlockletNumber: 0,
129
137
  },
130
138
  async (e, data) => {
131
139
  if (e) {
@@ -262,6 +270,13 @@ class NodeState extends BaseState {
262
270
  getBlockletRegistry() {
263
271
  return this.read().then((info) => info.blockletRegistryList.find((item) => item.selected).url);
264
272
  }
273
+
274
+ async increaseCustomBlockletNumber() {
275
+ const { _id, customBlockletNumber = 0 } = await this.read();
276
+ const num = customBlockletNumber + 1;
277
+ await this.update(_id, { $set: { customBlockletNumber: num } });
278
+ return num;
279
+ }
265
280
  }
266
281
 
267
282
  module.exports = NodeState;