@abtnode/core 1.6.1 → 1.6.5

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.
@@ -3,7 +3,7 @@ const { BlockletGroup } = require('@blocklet/meta/lib/constants');
3
3
  const joinURL = require('url-join');
4
4
  const get = require('lodash/get');
5
5
  const pick = require('lodash/pick');
6
- const { BLOCKLET_STORE_API_PREFIX } = require('@abtnode/constant');
6
+ const { BLOCKLET_STORE_API_PREFIX, BLOCKLET_STORE_META_PATH } = require('@abtnode/constant');
7
7
 
8
8
  const { name } = require('../../package.json');
9
9
 
@@ -170,7 +170,7 @@ BlockletRegistry.validateRegistryURL = async (registry) => {
170
170
 
171
171
  BlockletRegistry.getRegistryMeta = async (registry) => {
172
172
  try {
173
- const url = joinURL(registry, BLOCKLET_STORE_API_PREFIX, `/registry.json?__t__=${Date.now()}`);
173
+ const url = joinURL(registry, BLOCKLET_STORE_META_PATH, `?__t__=${Date.now()}`);
174
174
  const { data } = await request.get(url);
175
175
 
176
176
  if (!data) {
package/lib/index.js CHANGED
@@ -1,4 +1,5 @@
1
- const { listProviders } = require('@abtnode/router-provider');
1
+ const fs = require('fs');
2
+ const path = require('path');
2
3
  const md5 = require('@abtnode/util/lib/md5');
3
4
  const Cron = require('@abtnode/cron');
4
5
 
@@ -9,6 +10,7 @@ const {
9
10
  fromBlockletSource,
10
11
  toBlockletSource,
11
12
  } = require('@blocklet/meta/lib/constants');
13
+ const { listProviders } = require('@abtnode/router-provider');
12
14
 
13
15
  const RoutingSnapshot = require('./states/routing-snapshot');
14
16
  const BlockletRegistry = require('./blocklet/registry');
@@ -53,6 +55,11 @@ function ABTNode(options) {
53
55
  throw new Error('Can not initialize ABTNode without dataDir');
54
56
  }
55
57
 
58
+ const ekFile = path.join(options.dataDir, '.sock');
59
+ if (fs.existsSync(ekFile)) {
60
+ options.dek = fs.readFileSync(ekFile);
61
+ }
62
+
56
63
  if (typeof options.daemon === 'undefined') {
57
64
  options.daemon = false;
58
65
  }
@@ -0,0 +1,59 @@
1
+ /* eslint-disable no-await-in-loop */
2
+ const fs = require('fs');
3
+ const path = require('path');
4
+ const crypto = require('crypto');
5
+ const yaml = require('js-yaml');
6
+ const cloneDeep = require('lodash/cloneDeep');
7
+ const security = require('@abtnode/util/lib/security');
8
+
9
+ module.exports = async ({ states, configFile, dataDir }) => {
10
+ if (process.env.CI || process.env.NODE_ENV === 'test') {
11
+ return;
12
+ }
13
+
14
+ const file = path.join(dataDir, '.sock');
15
+ if (fs.existsSync(file)) {
16
+ return;
17
+ }
18
+
19
+ try {
20
+ fs.writeFileSync(file, crypto.randomBytes(32), { encoding: 'binary', mode: '0600' });
21
+
22
+ const config = yaml.safeLoad(fs.readFileSync(configFile).toString(), { json: true });
23
+ config.node.sk = security.encrypt(config.node.sk, config.node.did, fs.readFileSync(file));
24
+ fs.writeFileSync(configFile, yaml.dump(config));
25
+ await states.node.updateNodeInfo({ sk: config.node.sk });
26
+
27
+ const items = await states.blockletExtras.find();
28
+ for (const item of items) {
29
+ const newConfigs = cloneDeep(item.configs || []).map((c) => {
30
+ if (c.secure) {
31
+ c.value = security.encrypt(c.value, item.did, fs.readFileSync(file));
32
+ }
33
+
34
+ return c;
35
+ });
36
+
37
+ const newChildren = cloneDeep(item.children || []).map((x) => {
38
+ return {
39
+ ...x,
40
+ configs: cloneDeep(x.configs || []).map((c) => {
41
+ if (c.secure) {
42
+ c.value = security.encrypt(c.value, item.did, fs.readFileSync(file));
43
+ }
44
+
45
+ return c;
46
+ }),
47
+ };
48
+ });
49
+
50
+ await states.blockletExtras.update({ did: item.did }, { $set: { configs: newConfigs, children: newChildren } });
51
+ }
52
+
53
+ states.node.compactDatafile();
54
+ states.blockletExtras.compactDatafile();
55
+ } catch (err) {
56
+ console.error(err);
57
+ throw err;
58
+ }
59
+ };
@@ -0,0 +1,60 @@
1
+ /* eslint-disable no-await-in-loop */
2
+ const fs = require('fs');
3
+ const path = require('path');
4
+ const uniq = require('lodash/uniq');
5
+ const cloneDeep = require('lodash/cloneDeep');
6
+ const security = require('@abtnode/util/lib/security');
7
+ const { forEachBlocklet } = require('@blocklet/meta/lib/util');
8
+
9
+ module.exports = async ({ states, dataDir }) => {
10
+ if (process.env.CI || process.env.NODE_ENV === 'test') {
11
+ return;
12
+ }
13
+
14
+ const file = path.join(dataDir, '.sock');
15
+ if (fs.existsSync(file) === false) {
16
+ return;
17
+ }
18
+
19
+ try {
20
+ const extras = await states.blockletExtras.find();
21
+ const blocklets = await states.blocklet.find();
22
+ for (const blocklet of blocklets) {
23
+ forEachBlocklet(
24
+ blocklet,
25
+ (b) => {
26
+ let configKeys;
27
+ const extra = extras.find((x) => x.did === blocklet.meta.did);
28
+ const rootKeys = (extra.configs || []).map((x) => x.key);
29
+ if (b.meta.did === blocklet.meta.did) {
30
+ configKeys = rootKeys;
31
+ } else {
32
+ const childExtra = (extra.children || []).find((x) => x.did === b.meta.did);
33
+ const childKeys = childExtra ? childExtra.configs.map((x) => x.key) : [];
34
+ configKeys = uniq([...rootKeys, ...childKeys]);
35
+ }
36
+
37
+ b.environments = cloneDeep(b.environments || [])
38
+ .filter((x) => configKeys.includes(x.key) === false)
39
+ .map((c) => {
40
+ if (c.key === 'BLOCKLET_APP_SK') {
41
+ c.value = security.encrypt(c.value, b.meta.did, fs.readFileSync(file));
42
+ }
43
+
44
+ return c;
45
+ });
46
+ },
47
+ { sync: true }
48
+ );
49
+
50
+ await states.blocklet.updateById(blocklet._id, {
51
+ $set: { environments: blocklet.environments, children: blocklet.children, migratedFrom: '1.6.5' },
52
+ });
53
+ }
54
+
55
+ states.blocklet.compactDatafile();
56
+ } catch (err) {
57
+ console.error(err);
58
+ throw err;
59
+ }
60
+ };
@@ -1,4 +1,5 @@
1
1
  const get = require('lodash/get');
2
+ const pick = require('lodash/pick');
2
3
  const cloneDeep = require('lodash/cloneDeep');
3
4
  const {
4
5
  DOMAIN_FOR_DEFAULT_SITE,
@@ -9,7 +10,6 @@ const {
9
10
  BLOCKLET_SITE_GROUP_SUFFIX,
10
11
  } = require('@abtnode/constant');
11
12
  const { BLOCKLET_UI_INTERFACES } = require('@blocklet/meta/lib/constants');
12
- const { pick } = require('lodash');
13
13
  const logger = require('@abtnode/logger')('@abtnode/core:router');
14
14
 
15
15
  const expandSites = (sites = []) => {
@@ -191,6 +191,14 @@ class BaseState extends EventEmitter {
191
191
  insert(...args) {
192
192
  return this.asyncDB.insert(...args);
193
193
  }
194
+
195
+ compactDatafile() {
196
+ this.db.persistence.compactDatafile((err) => {
197
+ if (err) {
198
+ console.error(`failed to compact datafile: ${this.filename}`, err);
199
+ }
200
+ });
201
+ }
194
202
  }
195
203
 
196
204
  /**
@@ -6,7 +6,9 @@ const camelCase = require('lodash/camelCase');
6
6
 
7
7
  const BaseState = require('./base');
8
8
 
9
- const { mergeConfigs } = require('../blocklet/extras');
9
+ const { mergeConfigs, parseConfigs } = require('../blocklet/extras');
10
+
11
+ const noop = (k) => (v) => v[k];
10
12
 
11
13
  class BlockletExtrasState extends BaseState {
12
14
  constructor(baseDir, options = {}) {
@@ -17,12 +19,13 @@ class BlockletExtrasState extends BaseState {
17
19
  {
18
20
  name: 'configs',
19
21
  beforeSet: mergeConfigs,
22
+ afterGet: parseConfigs,
20
23
  },
21
24
 
22
25
  // setting
23
26
  {
24
27
  name: 'settings',
25
- beforeSet: (old, cur) => {
28
+ beforeSet: ({ old, cur }) => {
26
29
  const merged = { ...old, ...cur };
27
30
  Object.keys(merged).forEach((key) => {
28
31
  if (merged[key] === undefined || merged[key] === null) {
@@ -67,22 +70,23 @@ class BlockletExtrasState extends BaseState {
67
70
 
68
71
  generateGetFn(extra) {
69
72
  return async (did) => {
70
- const { name } = extra;
73
+ const { dek } = this.options;
74
+ const { name, afterGet = noop('data') } = extra;
71
75
  const item = await this.asyncDB.findOne({ did });
72
- return item ? item[name] : item;
76
+ return afterGet({ data: item ? item[name] : item, did, dek });
73
77
  };
74
78
  }
75
79
 
76
80
  generateSetFn(extra) {
77
81
  return async (did, data) => {
78
- const { name, beforeSet } = extra;
79
- const hasBeforeSet = beforeSet && typeof beforeSet === 'function';
82
+ const { dek } = this.options;
83
+ const { name, beforeSet = noop('cur') } = extra;
80
84
  const item = await this.asyncDB.findOne({ did });
81
85
 
82
86
  if (!item) {
83
87
  const insertData = {
84
88
  did,
85
- [name]: hasBeforeSet ? beforeSet(undefined, data) : data,
89
+ [name]: beforeSet({ old: undefined, cur: data, did, dek }),
86
90
  };
87
91
 
88
92
  const info = await this.asyncDB.insert(insertData);
@@ -93,7 +97,7 @@ class BlockletExtrasState extends BaseState {
93
97
  const itemNameValue = item[name];
94
98
  const updated = await this.update(item._id, {
95
99
  $set: {
96
- [name]: hasBeforeSet ? beforeSet(itemNameValue, data) : data,
100
+ [name]: beforeSet({ old: itemNameValue, cur: data, did, dek }),
97
101
  },
98
102
  });
99
103
  return updated[name];
@@ -132,22 +136,23 @@ class BlockletExtrasState extends BaseState {
132
136
 
133
137
  generateGetChildFn(extra) {
134
138
  return async (did, childDid) => {
135
- const { name } = extra;
139
+ const { dek } = this.options;
140
+ const { name, afterGet = noop('data') } = extra;
136
141
  const item = await this.asyncDB.findOne({ did });
137
142
  const children = (item || {}).children || [];
138
143
  const subItem = (children || []).find((x) => x.did === childDid);
139
- return subItem ? subItem[name] : null;
144
+ return afterGet({ data: subItem ? subItem[name] : null, did, dek });
140
145
  };
141
146
  }
142
147
 
143
148
  generateSetChildFn(extra) {
144
149
  return async (did, childDid, data) => {
145
- const { name, beforeSet } = extra;
146
- const hasBeforeSet = beforeSet && typeof beforeSet === 'function';
150
+ const { dek } = this.options;
151
+ const { name, beforeSet = noop('cur') } = extra;
147
152
  const item = await this.asyncDB.findOne({ did });
148
153
 
149
154
  if (!item) {
150
- const newData = hasBeforeSet ? beforeSet(undefined, data) : data;
155
+ const newData = beforeSet({ old: undefined, cur: data, did, dek });
151
156
  const insertData = {
152
157
  did,
153
158
  children: [
@@ -168,7 +173,7 @@ class BlockletExtrasState extends BaseState {
168
173
  const subItem = (children || []).find((x) => x.did === childDid);
169
174
 
170
175
  if (!subItem) {
171
- const newData = hasBeforeSet ? beforeSet(undefined, data) : data;
176
+ const newData = beforeSet({ old: undefined, cur: data, did, dek });
172
177
  await this.update(item._id, {
173
178
  $addToSet: {
174
179
  children: {
@@ -182,7 +187,7 @@ class BlockletExtrasState extends BaseState {
182
187
  return newData;
183
188
  }
184
189
 
185
- const newData = hasBeforeSet ? beforeSet(subItem[name], data) : data;
190
+ const newData = beforeSet({ old: subItem[name], cur: data, did, dek });
186
191
 
187
192
  children.forEach((x) => {
188
193
  if (x.did === childDid) {
@@ -1,3 +1,4 @@
1
+ /* eslint-disable no-async-promise-executor */
1
2
  /* eslint-disable no-await-in-loop */
2
3
  /* eslint-disable function-paren-newline */
3
4
  /* eslint-disable no-underscore-dangle */
@@ -5,6 +6,7 @@ const omit = require('lodash/omit');
5
6
  const uniq = require('lodash/uniq');
6
7
  const detectPort = require('detect-port');
7
8
  const Lock = require('@abtnode/util/lib/lock');
9
+ const security = require('@abtnode/util/lib/security');
8
10
  const { fixPerson, fixInterfaces } = require('@blocklet/meta/lib/fix');
9
11
  const {
10
12
  BlockletStatus,
@@ -27,6 +29,40 @@ const getMaxPort = (ports = {}) => Math.max(Object.values(ports).map(Number));
27
29
  const getExternalPortsFromMeta = (meta) =>
28
30
  (meta.interfaces || []).map((x) => x.port && x.port.external).filter(Boolean);
29
31
 
32
+ const formatBlocklet = (blocklet, phase, dek) => {
33
+ forEachBlocklet(
34
+ blocklet,
35
+ (b) => {
36
+ if (b.meta) {
37
+ fixPerson(b.meta);
38
+ fixInterfaces(b.meta);
39
+ }
40
+
41
+ b.children = b.children || [];
42
+
43
+ if (!b.environments || !b.meta || !dek) {
44
+ return;
45
+ }
46
+
47
+ ['BLOCKLET_APP_SK'].forEach((key) => {
48
+ const env = b.environments.find((x) => x.key === key);
49
+ if (!env) {
50
+ return;
51
+ }
52
+ if (phase === 'onUpdate' && env.value.indexOf('0x') === 0) {
53
+ env.value = security.encrypt(env.value, b.meta.did, dek);
54
+ }
55
+ if (phase === 'onRead' && env.value.indexOf('0x') === -1) {
56
+ env.value = security.decrypt(env.value, b.meta.did, dek);
57
+ }
58
+ });
59
+ },
60
+ { sync: true }
61
+ );
62
+
63
+ return blocklet;
64
+ };
65
+
30
66
  class BlockletState extends BaseState {
31
67
  /**
32
68
  * Creates an instance of BlockletState
@@ -48,14 +84,7 @@ class BlockletState extends BaseState {
48
84
  return reject(err);
49
85
  }
50
86
 
51
- if (doc) {
52
- // TODO: this only exists for backward compatible
53
- fixPerson(doc.meta);
54
- fixInterfaces(doc.meta);
55
- doc.children = doc.children || [];
56
- }
57
-
58
- return resolve(doc);
87
+ return resolve(doc ? formatBlocklet(doc, 'onRead', this.options.dek) : null);
59
88
  });
60
89
  });
61
90
  }
@@ -70,14 +99,7 @@ class BlockletState extends BaseState {
70
99
  return reject(err);
71
100
  }
72
101
 
73
- return resolve(
74
- docs.filter(Boolean).map((doc) => {
75
- // TODO: this only exists for backward compatible
76
- fixPerson(doc.meta);
77
- fixInterfaces(doc.meta);
78
- return doc;
79
- })
80
- );
102
+ return resolve(docs.filter(Boolean).map((doc) => formatBlocklet(doc, 'onRead', this.options.dek)));
81
103
  });
82
104
  });
83
105
  }
@@ -114,7 +136,6 @@ class BlockletState extends BaseState {
114
136
  } = {}) {
115
137
  return this.getBlocklet(did).then(
116
138
  (doc) =>
117
- // eslint-disable-next-line no-async-promise-executor
118
139
  new Promise(async (resolve, reject) => {
119
140
  if (doc) {
120
141
  reject(new Error('Blocklet already added'));
@@ -165,6 +186,26 @@ class BlockletState extends BaseState {
165
186
  );
166
187
  }
167
188
 
189
+ updateBlocklet(did, updates) {
190
+ return this.getBlocklet(did).then(
191
+ (doc) =>
192
+ new Promise(async (resolve, reject) => {
193
+ if (!doc) {
194
+ reject(new Error('Blocklet does not exist'));
195
+ return;
196
+ }
197
+
198
+ try {
199
+ const formatted = formatBlocklet(updates, 'onUpdate', this.options.dek);
200
+ const newDoc = await this.updateById(doc._id, { $set: formatted });
201
+ resolve(newDoc);
202
+ } catch (err) {
203
+ reject(err);
204
+ }
205
+ })
206
+ );
207
+ }
208
+
168
209
  upgradeBlocklet({ meta, source, deployedFrom, children } = {}) {
169
210
  return this.getBlocklet(meta.did).then(
170
211
  (doc) =>
@@ -2,6 +2,7 @@
2
2
  const omit = require('lodash/omit');
3
3
  const isEqual = require('lodash/isEqual');
4
4
  const isEmpty = require('lodash/isEmpty');
5
+ const security = require('@abtnode/util/lib/security');
5
6
  const { isFromPublicKey } = require('@arcblock/did');
6
7
  const logger = require('@abtnode/logger')('@abtnode/core:node');
7
8
  const { ROUTER_PROVIDER_NONE, NODE_MODES, DISK_ALERT_THRESHOLD_PERCENT } = require('@abtnode/constant');
@@ -58,6 +59,7 @@ class NodeState extends BaseState {
58
59
  */
59
60
  read() {
60
61
  return new Promise((resolve, reject) => {
62
+ const { nodeDid, dek } = this.options;
61
63
  this.db.findOne({ did: this.options.nodeDid }, (err, record) => {
62
64
  if (err) {
63
65
  // eslint-disable-next-line no-console
@@ -66,6 +68,10 @@ class NodeState extends BaseState {
66
68
  }
67
69
 
68
70
  if (record) {
71
+ if (dek) {
72
+ record.sk = security.decrypt(record.sk, record.did, dek);
73
+ }
74
+
69
75
  return resolve(record);
70
76
  }
71
77
 
@@ -74,7 +80,6 @@ class NodeState extends BaseState {
74
80
  description,
75
81
  nodeSk,
76
82
  nodePk,
77
- nodeDid,
78
83
  nodeOwner,
79
84
  port,
80
85
  version,
@@ -100,7 +105,7 @@ class NodeState extends BaseState {
100
105
  name,
101
106
  description,
102
107
  pk: nodePk,
103
- sk: nodeSk,
108
+ sk: dek ? security.encrypt(nodeSk, nodeDid, dek) : nodeSk,
104
109
  did: nodeDid,
105
110
  initialized,
106
111
  version,
@@ -192,7 +192,6 @@ const fillBlockletConfigs = (blocklet, configs) => {
192
192
  acc[x.key] = x.value;
193
193
  return acc;
194
194
  }, {});
195
- blocklet.userEnvironments = blocklet.configObj; // deprecated
196
195
  blocklet.environmentObj = (blocklet.environments || []).reduce((acc, x) => {
197
196
  acc[x.key] = x.value;
198
197
  return acc;
@@ -308,6 +307,7 @@ const getRuntimeEnvironments = (blocklet, nodeEnvironments) => {
308
307
 
309
308
  return {
310
309
  ...blocklet.environmentObj,
310
+ ...blocklet.configObj,
311
311
  ...nodeEnvironments,
312
312
  ...safeNodeEnvironments,
313
313
  };
@@ -798,7 +798,7 @@ const getRuntimeInfo = async (appId) => {
798
798
  const proc = await getProcessInfo(appId);
799
799
  return {
800
800
  pid: proc.pid,
801
- uptime: proc.pm2_env ? Number(proc.pm2_env.pm_uptime) : 0,
801
+ uptime: proc.pm2_env ? +new Date() - Number(proc.pm2_env.pm_uptime) : 0,
802
802
  memoryUsage: proc.monit.memory,
803
803
  cpuUsage: proc.monit.cpu,
804
804
  status: proc.pm2_env ? proc.pm2_env.status : null,
@@ -3,7 +3,7 @@ const { DEFAULT_IP_DNS_DOMAIN_SUFFIX } = require('@abtnode/constant');
3
3
 
4
4
  const SLOT_FOR_IP_DNS_SITE = '888-888-888-888';
5
5
 
6
- const formatName = (name) => slugify(name.replace(/^[@./-]/, '').replace(/[@./]/g, '-'));
6
+ const formatName = (name) => slugify(name.replace(/^[@./-]/, '').replace(/[@./_]/g, '-'));
7
7
 
8
8
  const hiddenInterfaceNames = ['public', 'publicUrl'];
9
9
 
@@ -10,7 +10,8 @@ const isSatisfied = (requirements, throwOnUnsatisfied = true) => {
10
10
  }
11
11
 
12
12
  const { platform: actualOs, arch: actualCpu } = process;
13
- const { os: expectedOs, cpu: expectedCpu, abtnode: expectedVersion } = Object.assign(defaults, requirements);
13
+ const { os: expectedOs, cpu: expectedCpu, abtnode, server } = Object.assign(defaults, requirements);
14
+ const expectedVersion = server || abtnode;
14
15
 
15
16
  const isOsSatisfied =
16
17
  expectedOs === '*' || expectedOs === actualOs || (Array.isArray(expectedOs) && expectedOs.includes(actualOs));
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "1.6.1",
6
+ "version": "1.6.5",
7
7
  "description": "",
8
8
  "main": "lib/index.js",
9
9
  "files": [
@@ -19,26 +19,26 @@
19
19
  "author": "wangshijun <wangshijun2010@gmail.com> (http://github.com/wangshijun)",
20
20
  "license": "MIT",
21
21
  "dependencies": {
22
- "@abtnode/constant": "1.6.1",
23
- "@abtnode/cron": "1.6.1",
24
- "@abtnode/logger": "1.6.1",
25
- "@abtnode/queue": "1.6.1",
26
- "@abtnode/rbac": "1.6.1",
27
- "@abtnode/router-provider": "1.6.1",
28
- "@abtnode/static-server": "1.6.1",
29
- "@abtnode/timemachine": "1.6.1",
30
- "@abtnode/util": "1.6.1",
31
- "@arcblock/did": "^1.13.65",
32
- "@arcblock/event-hub": "1.13.65",
22
+ "@abtnode/constant": "1.6.5",
23
+ "@abtnode/cron": "1.6.5",
24
+ "@abtnode/logger": "1.6.5",
25
+ "@abtnode/queue": "1.6.5",
26
+ "@abtnode/rbac": "1.6.5",
27
+ "@abtnode/router-provider": "1.6.5",
28
+ "@abtnode/static-server": "1.6.5",
29
+ "@abtnode/timemachine": "1.6.5",
30
+ "@abtnode/util": "1.6.5",
31
+ "@arcblock/did": "^1.13.77",
32
+ "@arcblock/event-hub": "1.13.77",
33
33
  "@arcblock/pm2-events": "^0.0.5",
34
- "@arcblock/vc": "^1.13.65",
35
- "@blocklet/meta": "1.6.1",
34
+ "@arcblock/vc": "^1.13.77",
35
+ "@blocklet/meta": "1.6.5",
36
36
  "@fidm/x509": "^1.2.1",
37
37
  "@nedb/core": "^1.2.2",
38
38
  "@nedb/multi": "^1.2.2",
39
- "@ocap/mcrypto": "^1.13.65",
40
- "@ocap/util": "^1.13.65",
41
- "@ocap/wallet": "^1.13.65",
39
+ "@ocap/mcrypto": "^1.13.77",
40
+ "@ocap/util": "^1.13.77",
41
+ "@ocap/wallet": "^1.13.77",
42
42
  "@slack/webhook": "^5.0.3",
43
43
  "axios": "^0.21.4",
44
44
  "axon": "^2.0.3",
@@ -73,5 +73,5 @@
73
73
  "express": "^4.17.1",
74
74
  "jest": "^27.3.1"
75
75
  },
76
- "gitHead": "5be6776ba9bbfdbff4000fdab7dcb77686b3a9e8"
76
+ "gitHead": "59da906682f8122797a932640aebbad5a30dfcc3"
77
77
  }