@abtnode/core 1.16.6-beta-79e0bbcc → 1.16.6-beta-56be9f01

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.
@@ -7,8 +7,18 @@ const logger = require('@abtnode/logger')(`${require('../../package.json').name}
7
7
 
8
8
  const { getSafeEnv } = require('../util');
9
9
 
10
- const runUserHook = async (processId, hookName, args) => {
11
- const { appDir, hooks, env, exitOnError = true, silent = false, notification, did } = args;
10
+ const runUserHook = async (label, hookName, args) => {
11
+ const {
12
+ appDir,
13
+ hooks,
14
+ env,
15
+ exitOnError = true,
16
+ silent = false,
17
+ notification,
18
+ did,
19
+ output: outputFile,
20
+ error: errorFile,
21
+ } = args;
12
22
  const hook = get(hooks, `[${hookName}]`) || get(hooks, `[${camelCase(hookName)}]`);
13
23
 
14
24
  try {
@@ -16,10 +26,17 @@ const runUserHook = async (processId, hookName, args) => {
16
26
  return;
17
27
  }
18
28
 
19
- logger.info(`run hook:${hookName}:`, { processId, hook });
20
- await runScript(hook, [processId, hookName].join(':'), { cwd: appDir, env: getSafeEnv(env), silent });
29
+ logger.info(`run hook:${hookName}:`, { label, hook });
30
+ // FIXME @linchen timeout 应该动态设置或不设置
31
+ await runScript(hook, [label, hookName].join(':'), {
32
+ cwd: appDir,
33
+ env: getSafeEnv(env),
34
+ silent,
35
+ output: outputFile,
36
+ error: errorFile,
37
+ });
21
38
  } catch (error) {
22
- logger.error(`run ${hook} error:`, { processId, error });
39
+ logger.error(`run ${hook} error:`, { label, error });
23
40
 
24
41
  if (notification) {
25
42
  notification.create({
@@ -32,14 +49,14 @@ const runUserHook = async (processId, hookName, args) => {
32
49
  }
33
50
 
34
51
  if (exitOnError) {
35
- throw new Error(`Run [${processId} - ${hookName}] failed: ${error.message}`);
52
+ throw new Error(`Run [${label} - ${hookName}] failed: ${error.message}`);
36
53
  }
37
54
  }
38
55
  };
39
56
 
40
- const preInstall = (processId, ...args) => runUserHook(processId, 'pre-install', ...args);
41
- const postInstall = (processId, ...args) => runUserHook(processId, 'post-install', ...args);
42
- const preConfig = (processId, ...args) => runUserHook(processId, 'pre-config', ...args);
57
+ const preInstall = (label, ...args) => runUserHook(label, 'pre-install', ...args);
58
+ const postInstall = (label, ...args) => runUserHook(label, 'post-install', ...args);
59
+ const preConfig = (label, ...args) => runUserHook(label, 'pre-config', ...args);
43
60
  const preStart = async (blocklet, options) => {
44
61
  // check required environments
45
62
  let environments = get(blocklet, 'meta.environments', []);
@@ -52,11 +69,11 @@ const preStart = async (blocklet, options) => {
52
69
  throw new Error(`Required environments is not set: ${tmp.join(',')}`);
53
70
  }
54
71
 
55
- return runUserHook(blocklet.env.processId, 'pre-start', options);
72
+ return runUserHook(blocklet.meta.title, 'pre-start', options);
56
73
  };
57
74
 
58
- const postStart = (blocklet, ...args) => runUserHook(blocklet.env.processId, 'post-start', ...args);
59
- const preUninstall = (processId, ...args) => runUserHook(processId, 'pre-uninstall', ...args);
60
- const preStop = (processId, ...args) => runUserHook(processId, 'pre-stop', ...args);
75
+ const postStart = (blocklet, ...args) => runUserHook(blocklet.meta.title, 'post-start', ...args);
76
+ const preUninstall = (label, ...args) => runUserHook(label, 'pre-uninstall', ...args);
77
+ const preStop = (label, ...args) => runUserHook(label, 'pre-stop', ...args);
61
78
 
62
79
  module.exports = { preInstall, postInstall, preStart, postStart, preUninstall, preStop, preConfig };
@@ -98,7 +98,6 @@ const {
98
98
  checkDuplicateMountPoint,
99
99
  validateStore,
100
100
  isRotatingAppSk,
101
- isRotatingAppDid,
102
101
  checkVersionCompatibility,
103
102
  getBlockletKnownAs,
104
103
  updateBlockletFallbackLogo,
@@ -134,6 +133,11 @@ const { formatEnvironments, shouldUpdateBlockletStatus, getBlockletMeta, validat
134
133
 
135
134
  const statusLock = new Lock('blocklet-status-lock');
136
135
 
136
+ const getHooksOutputFiles = (blocklet) => ({
137
+ output: path.join(blocklet.env.logsDir, 'output.log'),
138
+ error: path.join(blocklet.env.logsDir, 'error.log'),
139
+ });
140
+
137
141
  const pm2StatusMap = {
138
142
  online: BlockletStatus.running,
139
143
  stop: BlockletStatus.stopped,
@@ -473,6 +477,7 @@ class BlockletManager extends BaseBlockletManager {
473
477
  hooks: Object.assign(b.meta.hooks || {}, b.meta.scripts || {}),
474
478
  env,
475
479
  did, // root blocklet did,
480
+ ...getHooksOutputFiles(b),
476
481
  });
477
482
 
478
483
  // start process
@@ -553,7 +558,7 @@ class BlockletManager extends BaseBlockletManager {
553
558
  const nodeEnvironments = await states.node.getEnvironments();
554
559
  await stopBlockletProcess(blocklet, {
555
560
  preStop: (b, { ancestors }) =>
556
- hooks.preStop(b.env.processId, {
561
+ hooks.preStop(b.meta.title, {
557
562
  appDir: b.env.appDir,
558
563
  hooks: Object.assign(b.meta.hooks || {}, b.meta.scripts || {}),
559
564
  env: getRuntimeEnvironments(b, nodeEnvironments, ancestors),
@@ -562,6 +567,7 @@ class BlockletManager extends BaseBlockletManager {
562
567
  context,
563
568
  exitOnError: false,
564
569
  silent,
570
+ ...getHooksOutputFiles(b),
565
571
  }),
566
572
  });
567
573
  } catch (error) {
@@ -689,7 +695,7 @@ class BlockletManager extends BaseBlockletManager {
689
695
  const nodeEnvironments = await states.node.getEnvironments();
690
696
  await deleteBlockletProcess(blocklet, {
691
697
  preDelete: (b, { ancestors }) =>
692
- hooks.preUninstall(b.env.processId, {
698
+ hooks.preUninstall(b.meta.title, {
693
699
  appDir: b.env.appDir,
694
700
  hooks: Object.assign(b.meta.hooks || {}, b.meta.scripts || {}),
695
701
  env: getRuntimeEnvironments(b, nodeEnvironments, ancestors),
@@ -697,6 +703,7 @@ class BlockletManager extends BaseBlockletManager {
697
703
  notification: states.notification,
698
704
  context,
699
705
  exitOnError: false,
706
+ ...getHooksOutputFiles(b),
700
707
  }),
701
708
  });
702
709
 
@@ -749,8 +756,7 @@ class BlockletManager extends BaseBlockletManager {
749
756
  fs.removeSync(logsDir);
750
757
 
751
758
  // Reset config in db
752
- await states.blockletExtras.remove({ did: blocklet.meta.did });
753
- await this._setConfigsFromMeta(did);
759
+ await this._resetExtras(blocklet.meta.did);
754
760
  await this._updateBlockletEnvironment(did);
755
761
  await this.resetSiteByDid(did, context);
756
762
  } else {
@@ -1029,7 +1035,9 @@ class BlockletManager extends BaseBlockletManager {
1029
1035
 
1030
1036
  const configObj = {};
1031
1037
  for (const x of newConfigs) {
1032
- if (x.custom === true) {
1038
+ if (['CHAIN_TYPE', 'BLOCKLET_APP_CHAIN_TYPE'].includes(x.key)) {
1039
+ throw new Error(`${x.key} should not be changed`);
1040
+ } else if (x.custom === true) {
1033
1041
  // custom key
1034
1042
  await environmentNameSchema.validateAsync(x.key);
1035
1043
  } else if (BLOCKLET_CONFIGURABLE_KEY[x.key] && x.key.startsWith('BLOCKLET_')) {
@@ -1038,7 +1046,6 @@ class BlockletManager extends BaseBlockletManager {
1038
1046
  logger.error(`Cannot set ${x.key} to child blocklet`, [dids]);
1039
1047
  throw new Error(`Cannot set ${x.key} to child blocklet`);
1040
1048
  }
1041
-
1042
1049
  await validateAppConfig(x, states);
1043
1050
  } else if (!BLOCKLET_CONFIGURABLE_KEY[x.key] && !isPreferenceKey(x)) {
1044
1051
  if (!(blocklet.meta.environments || []).some((y) => y.name === x.key)) {
@@ -1051,7 +1058,6 @@ class BlockletManager extends BaseBlockletManager {
1051
1058
  }
1052
1059
 
1053
1060
  const willAppSkChange = isRotatingAppSk(newConfigs, blocklet.configs, blocklet.externalSk);
1054
- const willAppDidChange = isRotatingAppDid(newConfigs, blocklet.configs, blocklet.externalSk);
1055
1061
 
1056
1062
  // NOTICE: cannot use appDid as did param because appDid will become old appDid in alsoKnownAs and cannot get blocklet by the old appDid
1057
1063
  if (willAppSkChange && rootDid !== rootMetaDid) {
@@ -1062,13 +1068,14 @@ class BlockletManager extends BaseBlockletManager {
1062
1068
  if (!skipHook) {
1063
1069
  const nodeEnvironments = await states.node.getEnvironments();
1064
1070
  // FIXME: we should also call preConfig for child blocklets
1065
- await hooks.preConfig(blocklet.env.processId, {
1071
+ await hooks.preConfig(blocklet.meta.title, {
1066
1072
  appDir: blocklet.env.appDir,
1067
1073
  hooks: Object.assign(blocklet.meta.hooks || {}, blocklet.meta.scripts || {}),
1068
1074
  exitOnError: true,
1069
1075
  env: { ...getRuntimeEnvironments(blocklet, nodeEnvironments, ancestors), ...configObj },
1070
1076
  did,
1071
1077
  context,
1078
+ ...getHooksOutputFiles(blocklet),
1072
1079
  });
1073
1080
  }
1074
1081
 
@@ -1120,7 +1127,7 @@ class BlockletManager extends BaseBlockletManager {
1120
1127
  // response
1121
1128
  const newState = await this.getBlocklet(rootDid);
1122
1129
 
1123
- if (willAppDidChange && !skipDidDocument) {
1130
+ if (willAppSkChange && !skipDidDocument) {
1124
1131
  await this._updateDidDocument(newState);
1125
1132
  }
1126
1133
 
@@ -1349,8 +1356,8 @@ class BlockletManager extends BaseBlockletManager {
1349
1356
  let shouldUpdateStatus = forceSync || shouldUpdateBlockletStatus(blocklet.status);
1350
1357
  if (isInProgress(blocklet.status)) {
1351
1358
  const uptime = Date.now() - new Date(blocklet.updatedAt).getTime();
1352
- // if uptime great than 30s, sync the status from pm2
1353
- if (uptime > 30 * 1000) {
1359
+ // FIXME @linchen 在彻底修复 blocklet status 稳定性之后, uptime check 应该去掉. 如果 hook 执行时间很长, uptime check 会影响 hook 执行过程中的 blocklet status
1360
+ if (uptime > 120 * 1000) {
1354
1361
  shouldUpdateStatus = true;
1355
1362
  }
1356
1363
  }
@@ -1866,6 +1873,8 @@ class BlockletManager extends BaseBlockletManager {
1866
1873
  }
1867
1874
 
1868
1875
  async _addBlocklet({ component, mode = BLOCKLET_MODES.PRODUCTION, name, did, title, description, skSource = '' }) {
1876
+ const environments = component?.meta?.environments || [];
1877
+
1869
1878
  const meta = {
1870
1879
  name,
1871
1880
  did,
@@ -1884,7 +1893,7 @@ class BlockletManager extends BaseBlockletManager {
1884
1893
  },
1885
1894
  ],
1886
1895
  specVersion: BLOCKLET_LATEST_SPEC_VERSION,
1887
- environments: component?.meta?.environments || [],
1896
+ environments,
1888
1897
  timeout: {
1889
1898
  start: process.env.NODE_ENV === 'test' ? 10 : 60,
1890
1899
  },
@@ -1934,6 +1943,18 @@ class BlockletManager extends BaseBlockletManager {
1934
1943
 
1935
1944
  const blocklet = await states.blocklet.addBlocklet(params);
1936
1945
 
1946
+ // set chain type
1947
+ const chainTypeEnv = {
1948
+ key: BLOCKLET_CONFIGURABLE_KEY.BLOCKLET_APP_CHAIN_TYPE,
1949
+ value: 'arcblock',
1950
+ shared: true,
1951
+ };
1952
+ const customChainType = environments.find((x) => x.name === 'CHAIN_TYPE')?.default;
1953
+ if (['eth', 'ethereum'].includes(customChainType)) {
1954
+ chainTypeEnv.value = 'ethereum';
1955
+ }
1956
+ await states.blockletExtras.setConfigs(blocklet.meta.did, [chainTypeEnv]);
1957
+
1937
1958
  return blocklet;
1938
1959
  }
1939
1960
 
@@ -2396,13 +2417,14 @@ class BlockletManager extends BaseBlockletManager {
2396
2417
  const nodeEnvironments = await states.node.getEnvironments();
2397
2418
 
2398
2419
  const preInstall = (b) =>
2399
- hooks.preInstall(b.env.processId, {
2420
+ hooks.preInstall(b.meta.title, {
2400
2421
  hooks: Object.assign(b.meta.hooks || {}, b.meta.scripts || {}),
2401
2422
  env: { ...nodeEnvironments },
2402
2423
  appDir: b.env.appDir,
2403
2424
  did: blocklet.meta.did, // root blocklet did
2404
2425
  notification: states.notification,
2405
2426
  context,
2427
+ ...getHooksOutputFiles(b),
2406
2428
  });
2407
2429
 
2408
2430
  await forEachBlocklet(blocklet, preInstall, { parallel: true });
@@ -2412,13 +2434,14 @@ class BlockletManager extends BaseBlockletManager {
2412
2434
  const nodeEnvironments = await states.node.getEnvironments();
2413
2435
 
2414
2436
  const postInstall = (b, { ancestors }) =>
2415
- hooks.postInstall(b.env.processId, {
2437
+ hooks.postInstall(b.meta.title, {
2416
2438
  hooks: Object.assign(b.meta.hooks || {}, b.meta.scripts || {}),
2417
2439
  env: getRuntimeEnvironments(b, nodeEnvironments, ancestors),
2418
2440
  appDir: b.env.appDir,
2419
2441
  did: blocklet.meta.did, // root blocklet did
2420
2442
  notification: states.notification,
2421
2443
  context,
2444
+ ...getHooksOutputFiles(b),
2422
2445
  });
2423
2446
 
2424
2447
  await forEachBlocklet(blocklet, postInstall, { parallel: true });
@@ -2744,7 +2767,8 @@ class BlockletManager extends BaseBlockletManager {
2744
2767
  const appConfigs = await states.blockletExtras.getConfigs([metaDid]);
2745
2768
 
2746
2769
  const chainConfigs = configs
2747
- .filter((x) => ['CHAIN_HOST', 'CHAIN_ID', 'CHAIN_TYPE'].includes(x.key))
2770
+ // CHAIN_TYPE should not changed after first set
2771
+ .filter((x) => ['CHAIN_HOST', 'CHAIN_ID'].includes(x.key))
2748
2772
  .filter((x) => {
2749
2773
  if (force) {
2750
2774
  return true;
@@ -2766,6 +2790,20 @@ class BlockletManager extends BaseBlockletManager {
2766
2790
  await states.blockletExtras.setConfigs(metaDid, items);
2767
2791
  }
2768
2792
  }
2793
+
2794
+ async _resetExtras(metaDid) {
2795
+ const did = metaDid;
2796
+ const configs = await states.blockletExtras.getConfigs(metaDid);
2797
+ const chainType = (configs || []).find((x) => x.key === 'BLOCKLET_APP_CHAIN_TYPE');
2798
+ await states.blockletExtras.remove({ did });
2799
+
2800
+ if (chainType) {
2801
+ await states.blockletExtras.setConfigs(metaDid, [chainType]);
2802
+ } else {
2803
+ logger.error(`chainType does not exist in app ${metaDid}`);
2804
+ }
2805
+ await this._setConfigsFromMeta(did);
2806
+ }
2769
2807
  }
2770
2808
 
2771
2809
  module.exports = BlockletManager;
@@ -71,10 +71,7 @@ const installApplicationFromBackup = async ({
71
71
  '_id',
72
72
  'createdAt',
73
73
  'updatedAt',
74
- 'controller.nftId',
75
- 'controller.nftOwner',
76
- 'controller.chainHost',
77
- 'controller.appMaxCount',
74
+ 'controller',
78
75
  ]);
79
76
 
80
77
  if (blockletState.meta.did !== extra.did) {
@@ -70,7 +70,7 @@ const installApplicationFromGeneral = async ({
70
70
  let component;
71
71
  if (componentSourceUrl) {
72
72
  const meta = await getBlockletMetaFromUrl(componentSourceUrl);
73
- const blockletWalletTypeEnv = (meta.environments || []).find((x) => x.name === 'BLOCKLET_WALLET_TYPE');
73
+ const blockletWalletTypeEnv = (meta.environments || []).find((x) => x.name === 'CHAIN_TYPE');
74
74
  if (blockletWalletTypeEnv) {
75
75
  blockletWalletType = blockletWalletTypeEnv.default;
76
76
  }
@@ -137,6 +137,7 @@ const migrateAppOnChain = async (blocklet, oldSk, newSk) => {
137
137
  }
138
138
 
139
139
  // ensure account changed
140
+ // 2023.04.26 eth type application need not be supported
140
141
  const type = blocklet.configObj?.BLOCKLET_WALLET_TYPE;
141
142
  const oldWallet = getBlockletWallet(oldSk, undefined, type);
142
143
  const newWallet = getBlockletWallet(newSk, undefined, type);
@@ -339,7 +339,7 @@ const getAppOverwrittenEnvironments = (blocklet, nodeInfo) => {
339
339
  result[x] = blocklet.configObj[x];
340
340
  });
341
341
 
342
- const keys = ['BLOCKLET_APP_SK', 'BLOCKLET_WALLET_TYPE'];
342
+ const keys = ['BLOCKLET_APP_SK', 'BLOCKLET_APP_CHAIN_TYPE'];
343
343
  const isAppDidRewritten = keys.some((key) => blocklet.configObj[key]);
344
344
  if (!isAppDidRewritten) {
345
345
  return result;
@@ -1609,29 +1609,6 @@ const isRotatingAppSk = (newConfigs, oldConfigs, externalSk) => {
1609
1609
  return false;
1610
1610
  };
1611
1611
 
1612
- const isRotatingAppDid = (newConfigs, oldConfigs, externalSk) => {
1613
- if (isRotatingAppSk(newConfigs, oldConfigs, externalSk)) {
1614
- return true;
1615
- }
1616
-
1617
- const newType = newConfigs.find((x) => BLOCKLET_CONFIGURABLE_KEY.BLOCKLET_WALLET_TYPE === x.key);
1618
- const oldType = oldConfigs.find((x) => BLOCKLET_CONFIGURABLE_KEY.BLOCKLET_WALLET_TYPE === x.key);
1619
- if (!newType) {
1620
- return false;
1621
- }
1622
-
1623
- if (!oldType) {
1624
- return true;
1625
- }
1626
-
1627
- // eslint-disable-next-line sonarjs/prefer-single-boolean-return
1628
- if (oldType !== newType) {
1629
- return true;
1630
- }
1631
-
1632
- return false;
1633
- };
1634
-
1635
1612
  /**
1636
1613
  * this function has side effect on config.value
1637
1614
  * @param {{ key: string, value?: string }} config
@@ -1683,8 +1660,8 @@ const validateAppConfig = async (config, states) => {
1683
1660
  x.value = await logoSchema.validateAsync(x.value);
1684
1661
  }
1685
1662
 
1686
- if (x.key === BLOCKLET_CONFIGURABLE_KEY.BLOCKLET_WALLET_TYPE) {
1687
- if (['default', 'eth'].includes(x.value) === false) {
1663
+ if (x.key === BLOCKLET_CONFIGURABLE_KEY.BLOCKLET_APP_CHAIN_TYPE) {
1664
+ if (['arcblock', 'ethereum'].includes(x.value) === false) {
1688
1665
  throw new Error('Invalid blocklet wallet type, only "default" and "eth" are supported');
1689
1666
  }
1690
1667
  }
@@ -1989,7 +1966,6 @@ module.exports = {
1989
1966
  validateAppConfig,
1990
1967
  isBlockletAppSkUsed,
1991
1968
  isRotatingAppSk,
1992
- isRotatingAppDid,
1993
1969
  checkDuplicateAppSk,
1994
1970
  checkDuplicateMountPoint,
1995
1971
  validateStore,
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "1.16.6-beta-79e0bbcc",
6
+ "version": "1.16.6-beta-56be9f01",
7
7
  "description": "",
8
8
  "main": "lib/index.js",
9
9
  "files": [
@@ -19,18 +19,18 @@
19
19
  "author": "wangshijun <wangshijun2010@gmail.com> (http://github.com/wangshijun)",
20
20
  "license": "MIT",
21
21
  "dependencies": {
22
- "@abtnode/auth": "1.16.6-beta-79e0bbcc",
23
- "@abtnode/certificate-manager": "1.16.6-beta-79e0bbcc",
24
- "@abtnode/constant": "1.16.6-beta-79e0bbcc",
25
- "@abtnode/cron": "1.16.6-beta-79e0bbcc",
26
- "@abtnode/db": "1.16.6-beta-79e0bbcc",
27
- "@abtnode/logger": "1.16.6-beta-79e0bbcc",
28
- "@abtnode/queue": "1.16.6-beta-79e0bbcc",
29
- "@abtnode/rbac": "1.16.6-beta-79e0bbcc",
30
- "@abtnode/router-provider": "1.16.6-beta-79e0bbcc",
31
- "@abtnode/static-server": "1.16.6-beta-79e0bbcc",
32
- "@abtnode/timemachine": "1.16.6-beta-79e0bbcc",
33
- "@abtnode/util": "1.16.6-beta-79e0bbcc",
22
+ "@abtnode/auth": "1.16.6-beta-56be9f01",
23
+ "@abtnode/certificate-manager": "1.16.6-beta-56be9f01",
24
+ "@abtnode/constant": "1.16.6-beta-56be9f01",
25
+ "@abtnode/cron": "1.16.6-beta-56be9f01",
26
+ "@abtnode/db": "1.16.6-beta-56be9f01",
27
+ "@abtnode/logger": "1.16.6-beta-56be9f01",
28
+ "@abtnode/queue": "1.16.6-beta-56be9f01",
29
+ "@abtnode/rbac": "1.16.6-beta-56be9f01",
30
+ "@abtnode/router-provider": "1.16.6-beta-56be9f01",
31
+ "@abtnode/static-server": "1.16.6-beta-56be9f01",
32
+ "@abtnode/timemachine": "1.16.6-beta-56be9f01",
33
+ "@abtnode/util": "1.16.6-beta-56be9f01",
34
34
  "@arcblock/did": "1.18.76",
35
35
  "@arcblock/did-auth": "1.18.76",
36
36
  "@arcblock/did-ext": "^1.18.76",
@@ -40,9 +40,9 @@
40
40
  "@arcblock/jwt": "^1.18.76",
41
41
  "@arcblock/pm2-events": "^0.0.5",
42
42
  "@arcblock/vc": "1.18.76",
43
- "@blocklet/constant": "1.16.6-beta-79e0bbcc",
44
- "@blocklet/meta": "1.16.6-beta-79e0bbcc",
45
- "@blocklet/sdk": "1.16.6-beta-79e0bbcc",
43
+ "@blocklet/constant": "1.16.6-beta-56be9f01",
44
+ "@blocklet/meta": "1.16.6-beta-56be9f01",
45
+ "@blocklet/sdk": "1.16.6-beta-56be9f01",
46
46
  "@did-space/client": "^0.2.87",
47
47
  "@fidm/x509": "^1.2.1",
48
48
  "@ocap/mcrypto": "1.18.76",
@@ -94,5 +94,5 @@
94
94
  "express": "^4.18.2",
95
95
  "jest": "^27.5.1"
96
96
  },
97
- "gitHead": "a14091393bf4c1a7b430155cf27343a0f23ffa2c"
97
+ "gitHead": "f93c24693ed27c4a2821ec33c345696afe6f001a"
98
98
  }