@abtnode/core 1.16.46 → 1.16.47-beta-20250710-123505-c50a3bdc

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.
@@ -908,21 +908,21 @@ class DiskBlockletManager extends BaseBlockletManager {
908
908
  async (b, { env }) => {
909
909
  const hookArgs = getHookArgs(b);
910
910
  const needRunDocker = await checkNeedRunDocker(b.meta, env, nodeInfo, isExternalBlocklet(blocklet));
911
- if (needRunDocker) {
912
- if (hookName === 'postStart' && b.meta.scripts?.postStart) {
913
- return dockerExec({
914
- blocklet,
915
- meta: b.meta,
916
- script: b.meta.scripts?.postStart,
917
- hookName,
918
- nodeInfo,
919
- env,
920
- ...hookArgs,
921
- timeout: 20000,
922
- });
923
- }
911
+ if (!b.meta.scripts?.[hookName]) {
924
912
  return null;
925
913
  }
914
+ if (needRunDocker) {
915
+ return dockerExec({
916
+ blocklet,
917
+ meta: b.meta,
918
+ script: b.meta.scripts?.[hookName],
919
+ hookName,
920
+ nodeInfo,
921
+ env,
922
+ ...hookArgs,
923
+ timeout: 20000,
924
+ });
925
+ }
926
926
  return hooks[hookName](b, {
927
927
  appDir: b.env.appDir,
928
928
  hooks: Object.assign(b.meta.hooks || {}, b.meta.scripts || {}),
@@ -4225,14 +4225,7 @@ class DiskBlockletManager extends BaseBlockletManager {
4225
4225
  blocklet,
4226
4226
  meta: b.meta,
4227
4227
  env: nextEnv,
4228
- script: [
4229
- b.meta.scripts?.preInstall,
4230
- 'export NODE_OPTIONS=--no-experimental-permission && pnpm config set store-dir /var/lib/blocklet/.pnpm-store --global && pnpm config set update-notifier false --global && pnpm install --shamefully-hoist --unsafe-perm --fetch-timeout=90000 --fetch-retries=5 || (echo "PNPM install Error, Retry with --force" && pnpm install --shamefully-hoist --unsafe-perm --fetch-timeout=90000 --fetch-retries=5 --force)',
4231
- b.meta.scripts?.preFlight,
4232
- 'node docker-exec/run-script.cjs',
4233
- ]
4234
- .filter(Boolean)
4235
- .join(' && '),
4228
+ script: ['node docker-exec/run-script.cjs'].filter(Boolean).join(' && '),
4236
4229
  hookName: 'migration',
4237
4230
  nodeInfo,
4238
4231
  runScriptDir: path.join(__dirname, '../migration-dist'),
@@ -4322,18 +4315,11 @@ class DiskBlockletManager extends BaseBlockletManager {
4322
4315
  const env = getRuntimeEnvironments(blocklet, nodeEnvironments, {});
4323
4316
  const needRunDocker = await checkNeedRunDocker(blocklet.meta, env, nodeInfo, isExternalBlocklet(blocklet));
4324
4317
 
4325
- if (!needRunDocker) {
4326
- // pre install
4327
- await this._runUserHook('preInstall', blocklet, context);
4328
- }
4329
-
4318
+ await this._runUserHook('preInstall', blocklet, context);
4330
4319
  // post install
4331
4320
  await this._runUserHook('postInstall', blocklet, context);
4332
4321
 
4333
- if (!needRunDocker) {
4334
- // pre flight
4335
- await this._runUserHook('preFlight', blocklet, context);
4336
- }
4322
+ await this._runUserHook('preFlight', blocklet, context);
4337
4323
 
4338
4324
  await this._runMigration({ needRunDocker, did, blocklet, oldBlocklet, componentDids });
4339
4325
 
@@ -4705,6 +4691,14 @@ class DiskBlockletManager extends BaseBlockletManager {
4705
4691
  const env = getRuntimeEnvironments(b, nodeEnvironments, ancestors);
4706
4692
  const needRunDocker = await checkNeedRunDocker(b.meta, env, nodeInfo, isExternalBlocklet(blocklet));
4707
4693
  const hookArgs = getHookArgs(b);
4694
+
4695
+ if (name === 'preInstall') {
4696
+ await installExternalDependencies({
4697
+ appDir: b.env.appDir,
4698
+ nodeInfo,
4699
+ });
4700
+ }
4701
+
4708
4702
  if (needRunDocker) {
4709
4703
  const script = b.meta.scripts?.[name];
4710
4704
  if (script && afterRunScripts.has(name)) {
@@ -4720,11 +4714,6 @@ class DiskBlockletManager extends BaseBlockletManager {
4720
4714
  }
4721
4715
  return null;
4722
4716
  }
4723
- if (name === 'preInstall') {
4724
- await installExternalDependencies({
4725
- appDir: b.env.appDir,
4726
- });
4727
- }
4728
4717
 
4729
4718
  return hooks[name](b.meta.title, {
4730
4719
  hooks: Object.assign(b.meta.hooks || {}, b.meta.scripts || {}),
@@ -2,6 +2,7 @@ const shelljs = require('shelljs');
2
2
  const semver = require('semver');
3
3
  const { ABT_NODE_KERNEL_OR_BLOCKLET_MODE } = require('@blocklet/constant');
4
4
  const logger = require('@abtnode/logger')('@abtnode/core:blocklet:engine');
5
+ const { BUN_VERSION } = require('../../util/ensure-bun');
5
6
 
6
7
  // eslint-disable-next-line no-underscore-dangle
7
8
  const _getVersion = (fn, name) => {
@@ -41,44 +42,55 @@ function describe() {
41
42
  };
42
43
  }
43
44
 
44
- const engineMap = new Map([
45
- [
46
- 'node',
47
- {
48
- name: 'node',
49
- displayName: 'Node.js',
50
- interpreter: 'node',
51
- description: 'Powered by Node.js',
52
- args:
53
- process.env.ABT_NODE_KERNEL_MODE === ABT_NODE_KERNEL_OR_BLOCKLET_MODE.PERFORMANT
54
- ? '--max-http-header-size=16384'
55
- : '--max-http-header-size=16384 --optimize_for_size',
56
- visible: true,
57
- version() {
58
- return _getVersion(() => process.version, this.name);
59
- },
60
- describe,
61
- isAvailable() {
62
- return _isAvailable(this.name);
63
- },
64
- },
65
- ],
66
- [
67
- 'binary',
68
- {
69
- name: 'binary',
70
- visible: false,
71
- displayName: 'Executable binary',
72
- interpreter: 'none',
73
- description: 'Executable binary',
74
- version() {
75
- return '';
76
- },
77
- describe,
78
- isAvailable: () => true,
79
- },
80
- ],
81
- ]);
45
+ const engineMap = new Map();
46
+
47
+ engineMap.set('node', {
48
+ name: 'node',
49
+ displayName: 'Node.js',
50
+ interpreter: 'node',
51
+ description: 'Powered by Node.js',
52
+ args:
53
+ process.env.ABT_NODE_KERNEL_MODE === ABT_NODE_KERNEL_OR_BLOCKLET_MODE.PERFORMANT
54
+ ? '--max-http-header-size=16384'
55
+ : '--max-http-header-size=16384 --optimize_for_size',
56
+ visible: true,
57
+ version() {
58
+ return _getVersion(() => process.version, this.name);
59
+ },
60
+ describe,
61
+ isAvailable() {
62
+ return _isAvailable(this.name);
63
+ },
64
+ });
65
+
66
+ engineMap.set('bun', {
67
+ name: 'bun',
68
+ displayName: 'Bun',
69
+ interpreter: 'bun',
70
+ description: 'Powered by Bun',
71
+ args: '',
72
+ visible: true,
73
+ version() {
74
+ return BUN_VERSION;
75
+ },
76
+ describe,
77
+ isAvailable() {
78
+ return _isAvailable(this.name);
79
+ },
80
+ });
81
+
82
+ engineMap.set('binary', {
83
+ name: 'binary',
84
+ visible: false,
85
+ displayName: 'Executable binary',
86
+ interpreter: 'none',
87
+ description: 'Executable binary',
88
+ version() {
89
+ return '';
90
+ },
91
+ describe,
92
+ isAvailable: () => true,
93
+ });
82
94
 
83
95
  const engineNames = [...engineMap.keys()];
84
96
 
@@ -38902,7 +38902,7 @@ module.exports = require("zlib");
38902
38902
  /***/ ((module) => {
38903
38903
 
38904
38904
  "use strict";
38905
- module.exports = /*#__PURE__*/JSON.parse('{"name":"@abtnode/core","publishConfig":{"access":"public"},"version":"1.16.45","description":"","main":"lib/index.js","files":["lib"],"scripts":{"lint":"eslint tests lib --ignore-pattern \'tests/assets/*\'","lint:fix":"eslint --fix tests lib","test":"node tools/jest.js","coverage":"npm run test -- --coverage"},"keywords":[],"author":"wangshijun <wangshijun2010@gmail.com> (http://github.com/wangshijun)","license":"Apache-2.0","dependencies":{"@abtnode/analytics":"1.16.45","@abtnode/auth":"1.16.45","@abtnode/certificate-manager":"1.16.45","@abtnode/client":"1.16.45","@abtnode/constant":"1.16.45","@abtnode/cron":"1.16.45","@abtnode/db-cache":"1.16.45","@abtnode/docker-utils":"1.16.45","@abtnode/logger":"1.16.45","@abtnode/models":"1.16.45","@abtnode/queue":"1.16.45","@abtnode/rbac":"1.16.45","@abtnode/router-provider":"1.16.45","@abtnode/static-server":"1.16.45","@abtnode/timemachine":"1.16.45","@abtnode/util":"1.16.45","@arcblock/did":"1.20.15","@arcblock/did-auth":"1.20.15","@arcblock/did-ext":"1.20.15","@arcblock/did-motif":"^1.1.14","@arcblock/did-util":"1.20.15","@arcblock/event-hub":"1.20.15","@arcblock/jwt":"1.20.15","@arcblock/pm2-events":"^0.0.5","@arcblock/validator":"1.20.15","@arcblock/vc":"1.20.15","@blocklet/constant":"1.16.45","@blocklet/did-space-js":"^1.1.5","@blocklet/env":"1.16.45","@blocklet/error":"^0.2.5","@blocklet/meta":"1.16.45","@blocklet/resolver":"1.16.45","@blocklet/sdk":"1.16.45","@blocklet/store":"1.16.45","@blocklet/theme":"^3.0.23","@fidm/x509":"^1.2.1","@ocap/mcrypto":"1.20.15","@ocap/util":"1.20.15","@ocap/wallet":"1.20.15","@slack/webhook":"^5.0.4","archiver":"^7.0.1","axios":"^1.7.9","axon":"^2.0.3","chalk":"^4.1.2","cross-spawn":"^7.0.3","dayjs":"^1.11.13","deep-diff":"^1.0.2","detect-port":"^1.5.1","envfile":"^7.1.0","escape-string-regexp":"^4.0.0","fast-glob":"^3.3.2","filesize":"^10.1.1","flat":"^5.0.2","fs-extra":"^11.2.0","get-port":"^5.1.1","hasha":"^5.2.2","is-base64":"^1.1.0","is-cidr":"4","is-ip":"3","is-url":"^1.2.4","joi":"17.12.2","joi-extension-semver":"^5.0.0","js-yaml":"^4.1.0","kill-port":"^2.0.1","lodash":"^4.17.21","node-stream-zip":"^1.15.0","p-all":"^3.0.0","p-limit":"^3.1.0","p-map":"^4.0.0","p-retry":"^4.6.2","p-wait-for":"^3.2.0","rate-limiter-flexible":"^5.0.5","read-last-lines":"^1.8.0","semver":"^7.6.3","sequelize":"^6.35.0","shelljs":"^0.8.5","slugify":"^1.6.6","ssri":"^8.0.1","stream-throttle":"^0.1.3","stream-to-promise":"^3.0.0","systeminformation":"^5.23.3","tail":"^2.2.4","tar":"^6.1.11","transliteration":"^2.3.5","ua-parser-js":"^1.0.2","ufo":"^1.5.3","uuid":"^9.0.1","valid-url":"^1.0.9","which":"^2.0.2","xbytes":"^1.8.0"},"devDependencies":{"expand-tilde":"^2.0.2","express":"^4.18.2","jest":"^29.7.0","unzipper":"^0.10.11"},"gitHead":"e5764f753181ed6a7c615cd4fc6682aacf0cb7cd"}');
38905
+ module.exports = /*#__PURE__*/JSON.parse('{"name":"@abtnode/core","publishConfig":{"access":"public"},"version":"1.16.46","description":"","main":"lib/index.js","files":["lib"],"scripts":{"lint":"eslint tests lib --ignore-pattern \'tests/assets/*\'","lint:fix":"eslint --fix tests lib","test":"node tools/jest.js","coverage":"npm run test -- --coverage"},"keywords":[],"author":"wangshijun <wangshijun2010@gmail.com> (http://github.com/wangshijun)","license":"Apache-2.0","dependencies":{"@abtnode/analytics":"1.16.46","@abtnode/auth":"1.16.46","@abtnode/certificate-manager":"1.16.46","@abtnode/client":"1.16.46","@abtnode/constant":"1.16.46","@abtnode/cron":"1.16.46","@abtnode/db-cache":"1.16.46","@abtnode/docker-utils":"1.16.46","@abtnode/logger":"1.16.46","@abtnode/models":"1.16.46","@abtnode/queue":"1.16.46","@abtnode/rbac":"1.16.46","@abtnode/router-provider":"1.16.46","@abtnode/static-server":"1.16.46","@abtnode/timemachine":"1.16.46","@abtnode/util":"1.16.46","@arcblock/did":"1.20.15","@arcblock/did-auth":"1.20.15","@arcblock/did-ext":"1.20.15","@arcblock/did-motif":"^1.1.14","@arcblock/did-util":"1.20.15","@arcblock/event-hub":"1.20.15","@arcblock/jwt":"1.20.15","@arcblock/pm2-events":"^0.0.5","@arcblock/validator":"1.20.15","@arcblock/vc":"1.20.15","@blocklet/constant":"1.16.46","@blocklet/did-space-js":"^1.1.5","@blocklet/env":"1.16.46","@blocklet/error":"^0.2.5","@blocklet/meta":"1.16.46","@blocklet/resolver":"1.16.46","@blocklet/sdk":"1.16.46","@blocklet/store":"1.16.46","@blocklet/theme":"^3.0.23","@fidm/x509":"^1.2.1","@ocap/mcrypto":"1.20.15","@ocap/util":"1.20.15","@ocap/wallet":"1.20.15","@slack/webhook":"^5.0.4","archiver":"^7.0.1","axios":"^1.7.9","axon":"^2.0.3","chalk":"^4.1.2","cross-spawn":"^7.0.3","dayjs":"^1.11.13","deep-diff":"^1.0.2","detect-port":"^1.5.1","envfile":"^7.1.0","escape-string-regexp":"^4.0.0","fast-glob":"^3.3.2","filesize":"^10.1.1","flat":"^5.0.2","fs-extra":"^11.2.0","get-port":"^5.1.1","hasha":"^5.2.2","is-base64":"^1.1.0","is-cidr":"4","is-ip":"3","is-url":"^1.2.4","joi":"17.12.2","joi-extension-semver":"^5.0.0","js-yaml":"^4.1.0","kill-port":"^2.0.1","lodash":"^4.17.21","node-stream-zip":"^1.15.0","p-all":"^3.0.0","p-limit":"^3.1.0","p-map":"^4.0.0","p-retry":"^4.6.2","p-wait-for":"^3.2.0","rate-limiter-flexible":"^5.0.5","read-last-lines":"^1.8.0","semver":"^7.6.3","sequelize":"^6.35.0","shelljs":"^0.8.5","slugify":"^1.6.6","ssri":"^8.0.1","stream-throttle":"^0.1.3","stream-to-promise":"^3.0.0","systeminformation":"^5.23.3","tail":"^2.2.4","tar":"^6.1.11","transliteration":"^2.3.5","ua-parser-js":"^1.0.2","ufo":"^1.5.3","uuid":"^9.0.1","valid-url":"^1.0.9","which":"^2.0.2","xbytes":"^1.8.0"},"devDependencies":{"expand-tilde":"^2.0.2","express":"^4.18.2","jest":"^29.7.0","unzipper":"^0.10.11"},"gitHead":"e5764f753181ed6a7c615cd4fc6682aacf0cb7cd"}');
38906
38906
 
38907
38907
  /***/ }),
38908
38908
 
@@ -152,7 +152,6 @@ class BlockletRuntimeMonitor extends EventEmitter {
152
152
  appMem += runtimeInfo.memoryUsage || 0;
153
153
  runningDocker = runningDocker || runtimeInfo.runningDocker;
154
154
  } catch (err) {
155
- console.error(err);
156
155
  // component status in db may not sync with pm2 when server has just started
157
156
  if (err.code !== 'BLOCKLET_PROCESS_404') {
158
157
  this.logger.error('failed to get blocklet runtime info', { processId, error: err });
@@ -128,10 +128,10 @@ const {
128
128
  const { installExternalDependencies } = require('./install-external-dependencies');
129
129
  const parseDockerOptionsFromPm2 = require('./docker/parse-docker-options-from-pm2');
130
130
  const dockerRemoveByName = require('./docker/docker-remove-by-name');
131
- const checkNeedRunDocker = require('./docker/check-need-run-docker');
132
131
  const getDockerRuntimeInfo = require('./docker/get-docker-runtime-info');
133
132
  const parseDockerName = require('./docker/parse-docker-name');
134
133
  const { createDockerNetwork } = require('./docker/docker-network');
134
+ const { ensureBun } = require('./ensure-bun');
135
135
 
136
136
  /**
137
137
  * get blocklet engine info, default is node
@@ -656,14 +656,10 @@ const startBlockletProcess = async (
656
656
  // get env
657
657
  const env = getRuntimeEnvironments(b, nodeEnvironments, ancestors);
658
658
  const startedAt = Date.now();
659
- const needRunDocker = await checkNeedRunDocker(b.meta, env, nodeInfo, isExternalBlocklet(blocklet));
660
659
 
661
- if (!needRunDocker) {
662
- await installExternalDependencies({ appDir: env?.BLOCKLET_APP_DIR });
663
- // run hook
664
- await preFlight(b, { env: { ...env } });
665
- await preStart(b, { env: { ...env } });
666
- }
660
+ await installExternalDependencies({ appDir: env?.BLOCKLET_APP_DIR, nodeInfo });
661
+ await preFlight(b, { env: { ...env } });
662
+ await preStart(b, { env: { ...env } });
667
663
 
668
664
  // kill process if port is occupied
669
665
  try {
@@ -748,6 +744,13 @@ const startBlockletProcess = async (
748
744
  serverSk: nodeEnvironments.ABT_NODE_SK,
749
745
  });
750
746
 
747
+ if (options.interpreter === 'bun') {
748
+ options.exec_interpreter = await ensureBun();
749
+ options.exec_mode = 'fork';
750
+ delete options.instances;
751
+ delete options.mergeLogs;
752
+ }
753
+
751
754
  const nextOptions =
752
755
  b.mode === BLOCKLET_MODES.DEVELOPMENT
753
756
  ? options
@@ -1,11 +1,11 @@
1
1
  const debianWrapDockerfile = require('./debian-wrap-dockerfile');
2
2
 
3
3
  // Define Dockerfile with dynamic UID/GID mapping
4
- const dockerfile = debianWrapDockerfile('arcblock/blocklet-base:0.0.1');
4
+ const dockerfile = debianWrapDockerfile('arcblock/blocklet-base:0.0.2');
5
5
 
6
6
  module.exports = {
7
7
  dockerfile,
8
- image: 'debian_node_v3',
8
+ image: 'debian_node_v4',
9
9
  shell: 'sh',
10
10
  network: '--network host',
11
11
  baseDir: '/var/lib/blocklet',
@@ -0,0 +1,85 @@
1
+ const path = require('path');
2
+ const fs = require('fs/promises');
3
+ const os = require('os');
4
+ const { DBCache, getAbtNodeRedisAndSQLiteUrl } = require('@abtnode/db-cache');
5
+ const promiseSpawn = require('@abtnode/util/lib/promise-spawn');
6
+ const logger = require('@abtnode/logger')('@abtnode/docker-exec-chown');
7
+ const parseDockerName = require('./parse-docker-name');
8
+ const { checkDockerHasImage } = require('./check-docker-has-image');
9
+ const { createDockerImage } = require('./create-docker-image');
10
+ const debianDockerfile = require('./debian-dockerfile');
11
+ const { getBunCacheDir } = require('../ensure-bun');
12
+
13
+ const lockFile = new DBCache(() => ({
14
+ prefix: 'docker-exec-chown-locks',
15
+ ttl: 1000 * 60 * 2,
16
+ ...getAbtNodeRedisAndSQLiteUrl(),
17
+ }));
18
+
19
+ const LAST_INSTALL_NODE_MODULES_OS = 'node_modules_os_lock';
20
+
21
+ const saveLastInstallOs = async (installDir, isDocker = false) => {
22
+ const lastInstallOsLockPath = path.join(installDir, LAST_INSTALL_NODE_MODULES_OS);
23
+ await fs.writeFile(lastInstallOsLockPath, isDocker ? 'Linux' : os.type());
24
+ };
25
+
26
+ const loadLastInstallOs = async (installDir) => {
27
+ const lastInstallOsLockPath = path.join(installDir, LAST_INSTALL_NODE_MODULES_OS);
28
+ try {
29
+ await fs.access(lastInstallOsLockPath);
30
+ const lastInstallOs = await fs.readFile(lastInstallOsLockPath, 'utf-8');
31
+ return lastInstallOs.trim();
32
+ } catch (_) {
33
+ return '';
34
+ }
35
+ };
36
+
37
+ const isSameOs = async (installDir, isDocker = false) => {
38
+ const lastInstallOs = await loadLastInstallOs(installDir);
39
+ if (!lastInstallOs) {
40
+ return false;
41
+ }
42
+ return lastInstallOs === (isDocker ? 'Linux' : os.type());
43
+ };
44
+
45
+ async function dockerInstallDependencies({ name, installDir }) {
46
+ const { image, baseDir } = debianDockerfile;
47
+
48
+ if (!(await checkDockerHasImage(image))) {
49
+ await createDockerImage({
50
+ appDir: path.join(process.env.ABT_NODE_DATA_DIR, 'tmp', 'docker'),
51
+ dataDir: path.join(process.env.ABT_NODE_DATA_DIR, 'tmp', 'docker'),
52
+ meta: {},
53
+ ports: [],
54
+ env: {},
55
+ doChown: false,
56
+ });
57
+ }
58
+
59
+ const bunCacheDir = await getBunCacheDir(true);
60
+
61
+ const needChangeDir = [
62
+ { source: installDir, target: `${baseDir}/blocklet` },
63
+ { source: bunCacheDir, target: `${baseDir}/bun-cache` },
64
+ ];
65
+
66
+ const volumes = needChangeDir.map((dir) => `-v ${dir.source}:${dir.target}:rw`).join(' ');
67
+
68
+ const realName = parseDockerName(name, 'docker-install-dependencies');
69
+ const startTime = Date.now();
70
+
71
+ await lockFile.acquire(realName);
72
+ try {
73
+ await promiseSpawn(
74
+ `docker rm -fv ${realName} > /dev/null 2>&1 || true && docker run --rm --name ${realName} ${volumes} --env BUN_INSTALL_CACHE_DIR=${needChangeDir[1].target} ${image} sh -c 'cd ${needChangeDir[0].target} && bun install'`,
75
+ {},
76
+ { timeout: 1000 * 120, retry: 3 }
77
+ );
78
+ } finally {
79
+ await lockFile.releaseLock(realName);
80
+ }
81
+
82
+ logger.info(`docker-install-dependencies ${name} cost time: ${Date.now() - startTime}ms`);
83
+ }
84
+
85
+ module.exports = { dockerInstallDependencies, isSameOs, saveLastInstallOs, loadLastInstallOs };
@@ -12,8 +12,9 @@ const { NODE_MODES } = require('@abtnode/constant');
12
12
  const promiseSpawn = require('@abtnode/util/lib/promise-spawn');
13
13
  const getLocalIPAddress = require('@abtnode/util/lib/get-local-ip-address');
14
14
  const { dockerCmdValidator } = require('@abtnode/docker-utils');
15
-
15
+ const { getBlockletEngine } = require('@blocklet/meta/lib/engine');
16
16
  const { isExternalBlocklet } = require('@blocklet/meta/lib/util');
17
+
17
18
  const parseDockerName = require('./parse-docker-name');
18
19
  const { createDockerImage } = require('./create-docker-image');
19
20
  const checkNeedRunDocker = require('./check-need-run-docker');
@@ -122,6 +123,7 @@ async function parseDockerOptionsFromPm2({
122
123
  NODE_MODES: options.env.NODE_MODES || (isServerless ? NODE_MODES.SERVERLESS : NODE_MODES.PRODUCTION),
123
124
  HTTP_PROXY: process.env.DOCKER_HTTP_PROXY || '',
124
125
  HTTPS_PROXY: process.env.DOCKER_HTTPS_PROXY || '',
126
+ BUN_INSTALL_CACHE_DIR: '/var/lib/blocklet/bun-cache',
125
127
  };
126
128
 
127
129
  nextOptions.env = { ...nextOptions.env, ...envDefaults };
@@ -185,21 +187,6 @@ async function parseDockerOptionsFromPm2({
185
187
 
186
188
  replaceEnvValue(dockerEnv, rootBlocklet, dockerNamePrefix);
187
189
 
188
- let nodeModulesName = 'node_modules';
189
- let installScript = '';
190
- const appDir = nextOptions.env.BLOCKLET_APP_DIR;
191
- const packageJsonPath = path.join(appDir, 'package.json');
192
- const compactJsPath = path.join(appDir, 'blocklet-compact.js');
193
-
194
- if (dockerInfo.installNodeModules && existsSync(packageJsonPath) && existsSync(compactJsPath)) {
195
- const packageJson = JSON.parse(await fsp.readFile(packageJsonPath, 'utf-8'));
196
- if (packageJson.dependencies || packageJson.devDependencies) {
197
- nodeModulesName = 'docker_node_modules_v4';
198
-
199
- installScript = `export NODE_OPTIONS=--no-experimental-permission && pnpm config set store-dir ${dockerInfo.pnpmStore || '/var/lib/blocklet/.pnpm-store'} --global && pnpm config set update-notifier false --global && pnpm install --shamefully-hoist --unsafe-perm --fetch-timeout=90000 --fetch-retries=5 || (echo "PNPM install Error, Retry with --force" && pnpm install --shamefully-hoist --unsafe-perm --fetch-timeout=90000 --fetch-retries=5 --force)`;
200
- }
201
- }
202
-
203
190
  // Replace all occurrences of serverDir with baseDir in dockerEnv
204
191
  let dockerEnvString = JSON.stringify(dockerEnv).replace(new RegExp(serverDir, 'g'), baseDir);
205
192
  if (dockerEnv.BLOCKLET_DOCKER_NETWORK) {
@@ -220,12 +207,14 @@ async function parseDockerOptionsFromPm2({
220
207
  let runScript = dockerInfo.script || '';
221
208
  if (!runScript && dockerInfo.installNodeModules) {
222
209
  const maxOldSpaceSize = Math.floor(Number(dockerEnv.BLOCKLET_DOCKER_MEMORY.replace('g', '')) * 0.85 * 1024);
223
- runScript = /(npm|yarn|pnpm|bun)/g.test(nextOptions.script)
224
- ? nextOptions.script
225
- : `node ${process.env.ABT_NODE_BLOCKLET_MODE === ABT_NODE_KERNEL_OR_BLOCKLET_MODE.PERFORMANT ? '' : '--optimize_for_size'} --max-old-space-size=${maxOldSpaceSize} --max-http-header-size=16384 ${nextOptions.script} -- BLOCKLET_NAME=${options.name}`;
210
+ const nodeInterpreter = `node ${process.env.ABT_NODE_BLOCKLET_MODE === ABT_NODE_KERNEL_OR_BLOCKLET_MODE.PERFORMANT ? '' : '--optimize_for_size'} --max-old-space-size=${maxOldSpaceSize} --max-http-header-size=16384 ${nextOptions.script} -- BLOCKLET_NAME=${options.name}`;
211
+ const bunInterpreter = `bun run ${nextOptions.script} -- BLOCKLET_NAME=${options.name}`;
212
+ const engine = getBlockletEngine(meta);
213
+ const runCommand = engine.interpreter === 'bun' ? bunInterpreter : nodeInterpreter;
214
+ runScript = /(npm|yarn|pnpm|bun)/g.test(nextOptions.script) ? nextOptions.script : runCommand;
226
215
  }
227
216
 
228
- const runScripts = ['preInstall', '--installScript--', 'postInstall', 'preFlight', 'preStart', '--runScript--']
217
+ const runScripts = ['preInstall', 'postInstall', 'preFlight', 'preStart', '--runScript--']
229
218
  .map((key) => {
230
219
  if (key === '--runScript--') {
231
220
  return overrideScript || runScript;
@@ -233,9 +222,6 @@ async function parseDockerOptionsFromPm2({
233
222
  if (overrideScript) {
234
223
  return '';
235
224
  }
236
- if (key === '--installScript--') {
237
- return installScript;
238
- }
239
225
  return meta.scripts?.[key] || '';
240
226
  })
241
227
  .filter(Boolean);
@@ -283,9 +269,11 @@ async function parseDockerOptionsFromPm2({
283
269
  { source: nextOptions.env.BLOCKLET_LOG_DIR, target: dockerEnv.BLOCKLET_LOG_DIR, canEdit: true },
284
270
  { source: nextOptions.env.BLOCKLET_CACHE_DIR, target: dockerEnv.BLOCKLET_CACHE_DIR, canEdit: true },
285
271
  {
286
- source: path.join(nextOptions.env.BLOCKLET_APP_DIR, nodeModulesName),
272
+ source: path.join(nextOptions.env.BLOCKLET_APP_DIR, 'node_modules'),
287
273
  target: path.join(dockerEnv.BLOCKLET_APP_DIR, 'node_modules'),
288
- canEdit: true,
274
+ // 因为要共享 node_modules,而每个 node_modules 在 bin 和 pnpm 里都是硬链接, 编辑会有安全问题
275
+ // 上个版本能编辑是因为每个组件的 pnpm-store 都是独立的, 但是这样也使得每个组件首次安装的时间无法得到优化, 只有二次启动时才能用到缓存
276
+ canEdit: false,
289
277
  },
290
278
  {
291
279
  source: path.join(nextOptions.env.BLOCKLET_APP_DATA_DIR, '.projects'),
@@ -297,20 +285,6 @@ async function parseDockerOptionsFromPm2({
297
285
  target: dockerEnv.BLOCKLET_SHARE_DIR,
298
286
  canEdit: true,
299
287
  },
300
- dockerInfo.pnpmStore && nextOptions.env.BLOCKLET_REAL_NAME && nextOptions.env.BLOCKLET_COMPONENT_DID
301
- ? {
302
- source: path.join(
303
- serverDir,
304
- 'tmp',
305
- 'pnpm-stores',
306
- nextOptions.env.BLOCKLET_REAL_NAME.split('/')[1],
307
- nextOptions.env.BLOCKLET_COMPONENT_DID,
308
- '.pnpm-store'
309
- ),
310
- target: dockerInfo.pnpmStore,
311
- canEdit: true,
312
- }
313
- : null,
314
288
  ].filter((v) => {
315
289
  if (!v) {
316
290
  return false;
@@ -0,0 +1,94 @@
1
+ const path = require('path');
2
+ const { spawn } = require('child_process');
3
+ const fsp = require('fs/promises');
4
+ const os = require('os');
5
+
6
+ const BUN_VERSION = '1.2.18';
7
+
8
+ const getRootDir = () => {
9
+ if (process.env.ABT_NODE_DATA_DIR) {
10
+ return process.env.ABT_NODE_DATA_DIR;
11
+ }
12
+ return path.join(os.tmpdir(), 'bun_install');
13
+ };
14
+
15
+ async function _ensureBun() {
16
+ const bunDir = path.join(getRootDir(), 'core', 'bun_install');
17
+ try {
18
+ await fsp.access(bunDir);
19
+ } catch {
20
+ await fsp.mkdir(bunDir, { recursive: true });
21
+ }
22
+
23
+ const installDir = path.join(bunDir, BUN_VERSION);
24
+ const binDir = path.join(installDir, 'bin');
25
+ const bunExec = path.join(binDir, process.platform === 'win32' ? 'bun.exe' : 'bun');
26
+
27
+ // If bun is already installed in this project, return its path
28
+ try {
29
+ await fsp.access(bunExec);
30
+ return bunExec;
31
+ } catch (_) {
32
+ //
33
+ }
34
+
35
+ // Create installation directory
36
+ await fsp.mkdir(installDir, { recursive: true });
37
+
38
+ // Run the official Bun installer script with BUN_INSTALL overridden
39
+ await new Promise((resolvePromise, reject) => {
40
+ const linuxInstall = ['-c', 'curl -fsSL https://bun.sh/install | bash'];
41
+ const windowsInstall = ['-c', 'powershell -c "irm bun.sh/install.ps1 | iex"'];
42
+ const installer = spawn('bash', process.platform === 'win32' ? windowsInstall : linuxInstall, {
43
+ env: { ...process.env, BUN_INSTALL: installDir, BUN_VERSION, SHELL: '/dev/null', HOME: installDir },
44
+ stdio: 'inherit',
45
+ });
46
+
47
+ installer.on('close', (code) => {
48
+ if (code !== 0) {
49
+ reject(new Error(`Bun installation failed with exit code ${code}`));
50
+ } else {
51
+ resolvePromise();
52
+ }
53
+ });
54
+
55
+ installer.on('error', (err) => {
56
+ reject(err);
57
+ });
58
+ });
59
+
60
+ return bunExec;
61
+ }
62
+
63
+ let bunPathPromise = null;
64
+
65
+ // eslint-disable-next-line require-await
66
+ const ensureBun = async () => {
67
+ if (bunPathPromise) {
68
+ return bunPathPromise;
69
+ }
70
+ bunPathPromise = _ensureBun();
71
+ return bunPathPromise;
72
+ };
73
+
74
+ const bunOptions = {
75
+ baseDockerOs: 'Linux',
76
+ };
77
+
78
+ const getBunCacheDir = async (isDocker = false) => {
79
+ const abtNodeDir = getRootDir();
80
+ // 如果不是linux, 缓存的目录区分 docker 和非 docker
81
+ let cacheDir = isDocker ? 'bun-cache-docker' : 'bun-cache';
82
+ if (os.type() === bunOptions.baseDockerOs) {
83
+ cacheDir = 'bun-cache';
84
+ }
85
+ const bunCacheDir = path.join(abtNodeDir, 'tmp', cacheDir);
86
+ try {
87
+ await fsp.access(bunCacheDir);
88
+ } catch (_) {
89
+ await fsp.mkdir(bunCacheDir, { recursive: true });
90
+ }
91
+ return bunCacheDir;
92
+ };
93
+
94
+ module.exports = { ensureBun, getBunCacheDir, BUN_VERSION, bunOptions };
@@ -1,12 +1,15 @@
1
1
  const { spawn } = require('child_process');
2
2
  const fs = require('fs-extra');
3
3
  const path = require('path');
4
+ const { ensureBun, getBunCacheDir } = require('./ensure-bun');
5
+ const checkDockerRunHistory = require('./docker/check-docker-run-history');
6
+ const { dockerInstallDependencies, saveLastInstallOs, isSameOs } = require('./docker/docker-install-dependenices');
4
7
 
5
8
  function isDependencyInstalled(appDir, dependency) {
6
9
  return fs.existsSync(path.resolve(appDir, 'node_modules', dependency));
7
10
  }
8
11
 
9
- function installExternalDependencies({ appDir, forceInstall = false } = {}) {
12
+ async function installExternalDependencies({ appDir, forceInstall = false, nodeInfo } = {}) {
10
13
  if (!appDir) {
11
14
  throw new Error('appDir is required');
12
15
  }
@@ -14,6 +17,12 @@ function installExternalDependencies({ appDir, forceInstall = false } = {}) {
14
17
  throw new Error(`not a correct appDir directory: ${appDir}`);
15
18
  }
16
19
 
20
+ const isUseDocker = checkDockerRunHistory(nodeInfo);
21
+
22
+ if (!(await isSameOs(appDir, isUseDocker))) {
23
+ fs.removeSync(path.join(appDir, 'node_modules'));
24
+ }
25
+
17
26
  // 读取 BLOCKLET_APP_DIR 的 package.json
18
27
  const packageJsonPath = path.resolve(appDir, 'package.json');
19
28
 
@@ -23,7 +32,7 @@ function installExternalDependencies({ appDir, forceInstall = false } = {}) {
23
32
 
24
33
  const packageJson = fs.readJsonSync(packageJsonPath);
25
34
 
26
- const { blockletExternalDependencies: externals = [], externalManager = 'npm' } = packageJson;
35
+ const { blockletExternalDependencies: externals = [] } = packageJson;
27
36
  if (!Array.isArray(externals) || !externals.length) {
28
37
  return;
29
38
  }
@@ -33,35 +42,48 @@ function installExternalDependencies({ appDir, forceInstall = false } = {}) {
33
42
  return;
34
43
  }
35
44
 
36
- // eslint-disable-next-line consistent-return
37
- return new Promise((resolve, reject) => {
38
- const child = spawn(externalManager, ['install'], {
39
- cwd: appDir,
40
- stdio: 'pipe',
41
- shell: true,
42
- env: {
43
- ...process.env,
44
- NODE_ENV: 'production',
45
- },
46
- });
45
+ const bunPath = await ensureBun();
46
+ const bunCacheDir = await getBunCacheDir();
47
47
 
48
- let errorOutput = '';
49
- child.stderr.on('data', (data) => {
50
- errorOutput += data.toString();
48
+ if (isUseDocker) {
49
+ await dockerInstallDependencies({
50
+ // appDir 后两个路径作为 docker name
51
+ name: appDir.split(path.sep).slice(-2).join(path.sep),
52
+ installDir: appDir,
51
53
  });
54
+ } else {
55
+ await new Promise((resolve, reject) => {
56
+ const child = spawn(bunPath, ['install'], {
57
+ cwd: appDir,
58
+ stdio: 'pipe',
59
+ shell: true,
60
+ env: {
61
+ ...process.env,
62
+ NODE_ENV: 'production',
63
+ BUN_INSTALL_CACHE_DIR: bunCacheDir,
64
+ },
65
+ });
52
66
 
53
- child.on('close', (code) => {
54
- if (code !== 0 && errorOutput.trim()) {
55
- reject(new Error(errorOutput));
56
- } else {
57
- resolve();
58
- }
59
- });
67
+ let errorOutput = '';
68
+ child.stderr.on('data', (data) => {
69
+ errorOutput += data.toString();
70
+ });
71
+
72
+ child.on('close', (code) => {
73
+ if (code !== 0 && errorOutput.trim()) {
74
+ reject(new Error(errorOutput));
75
+ } else {
76
+ resolve();
77
+ }
78
+ });
60
79
 
61
- child.on('error', (err) => {
62
- reject(err);
80
+ child.on('error', (err) => {
81
+ reject(err);
82
+ });
63
83
  });
64
- });
84
+ }
85
+
86
+ await saveLastInstallOs(appDir, isUseDocker);
65
87
  }
66
88
 
67
89
  module.exports = { installExternalDependencies };
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "1.16.46",
6
+ "version": "1.16.47-beta-20250710-123505-c50a3bdc",
7
7
  "description": "",
8
8
  "main": "lib/index.js",
9
9
  "files": [
@@ -19,22 +19,22 @@
19
19
  "author": "wangshijun <wangshijun2010@gmail.com> (http://github.com/wangshijun)",
20
20
  "license": "Apache-2.0",
21
21
  "dependencies": {
22
- "@abtnode/analytics": "1.16.46",
23
- "@abtnode/auth": "1.16.46",
24
- "@abtnode/certificate-manager": "1.16.46",
25
- "@abtnode/client": "1.16.46",
26
- "@abtnode/constant": "1.16.46",
27
- "@abtnode/cron": "1.16.46",
28
- "@abtnode/db-cache": "1.16.46",
29
- "@abtnode/docker-utils": "1.16.46",
30
- "@abtnode/logger": "1.16.46",
31
- "@abtnode/models": "1.16.46",
32
- "@abtnode/queue": "1.16.46",
33
- "@abtnode/rbac": "1.16.46",
34
- "@abtnode/router-provider": "1.16.46",
35
- "@abtnode/static-server": "1.16.46",
36
- "@abtnode/timemachine": "1.16.46",
37
- "@abtnode/util": "1.16.46",
22
+ "@abtnode/analytics": "1.16.47-beta-20250710-123505-c50a3bdc",
23
+ "@abtnode/auth": "1.16.47-beta-20250710-123505-c50a3bdc",
24
+ "@abtnode/certificate-manager": "1.16.47-beta-20250710-123505-c50a3bdc",
25
+ "@abtnode/client": "1.16.47-beta-20250710-123505-c50a3bdc",
26
+ "@abtnode/constant": "1.16.47-beta-20250710-123505-c50a3bdc",
27
+ "@abtnode/cron": "1.16.47-beta-20250710-123505-c50a3bdc",
28
+ "@abtnode/db-cache": "1.16.47-beta-20250710-123505-c50a3bdc",
29
+ "@abtnode/docker-utils": "1.16.47-beta-20250710-123505-c50a3bdc",
30
+ "@abtnode/logger": "1.16.47-beta-20250710-123505-c50a3bdc",
31
+ "@abtnode/models": "1.16.47-beta-20250710-123505-c50a3bdc",
32
+ "@abtnode/queue": "1.16.47-beta-20250710-123505-c50a3bdc",
33
+ "@abtnode/rbac": "1.16.47-beta-20250710-123505-c50a3bdc",
34
+ "@abtnode/router-provider": "1.16.47-beta-20250710-123505-c50a3bdc",
35
+ "@abtnode/static-server": "1.16.47-beta-20250710-123505-c50a3bdc",
36
+ "@abtnode/timemachine": "1.16.47-beta-20250710-123505-c50a3bdc",
37
+ "@abtnode/util": "1.16.47-beta-20250710-123505-c50a3bdc",
38
38
  "@arcblock/did": "1.20.15",
39
39
  "@arcblock/did-auth": "1.20.15",
40
40
  "@arcblock/did-ext": "1.20.15",
@@ -45,14 +45,14 @@
45
45
  "@arcblock/pm2-events": "^0.0.5",
46
46
  "@arcblock/validator": "1.20.15",
47
47
  "@arcblock/vc": "1.20.15",
48
- "@blocklet/constant": "1.16.46",
48
+ "@blocklet/constant": "1.16.47-beta-20250710-123505-c50a3bdc",
49
49
  "@blocklet/did-space-js": "^1.1.5",
50
- "@blocklet/env": "1.16.46",
50
+ "@blocklet/env": "1.16.47-beta-20250710-123505-c50a3bdc",
51
51
  "@blocklet/error": "^0.2.5",
52
- "@blocklet/meta": "1.16.46",
53
- "@blocklet/resolver": "1.16.46",
54
- "@blocklet/sdk": "1.16.46",
55
- "@blocklet/store": "1.16.46",
52
+ "@blocklet/meta": "1.16.47-beta-20250710-123505-c50a3bdc",
53
+ "@blocklet/resolver": "1.16.47-beta-20250710-123505-c50a3bdc",
54
+ "@blocklet/sdk": "1.16.47-beta-20250710-123505-c50a3bdc",
55
+ "@blocklet/store": "1.16.47-beta-20250710-123505-c50a3bdc",
56
56
  "@blocklet/theme": "^3.0.23",
57
57
  "@fidm/x509": "^1.2.1",
58
58
  "@ocap/mcrypto": "1.20.15",
@@ -116,5 +116,5 @@
116
116
  "jest": "^29.7.0",
117
117
  "unzipper": "^0.10.11"
118
118
  },
119
- "gitHead": "7539a231d36a17026dc7066b1d79e1e026337beb"
119
+ "gitHead": "91504e15ebe43ae5f2c12e6090783412d10e320d"
120
120
  }