@bash0816/claude-code 2.1.241 → 2.1.245

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.
package/README.md CHANGED
@@ -37,7 +37,7 @@ npm が `claude` bin link を管理する状態になれば、通常の `npm ins
37
37
  Latest audited version / 最新監査済み版:
38
38
 
39
39
  ```sh
40
- npm install -g @bash0816/claude-code@2.1.240
40
+ npm install -g @bash0816/claude-code@2.1.241
41
41
  ```
42
42
 
43
43
  ## Update / 更新
package/bin/claude CHANGED
@@ -42,11 +42,18 @@ process.stdout.write(String(item[field]));
42
42
  ' "${CONFIG_FILE}" "${CLAUDE_VERSION}" "$1"
43
43
  }
44
44
 
45
- ENTRY_JS_OFFSET=$(json_field entry_js_offset)
46
- ENTRY_END_OFFSET=$(json_field entry_end_offset)
45
+ ENTRY_FORMAT=$(json_field entry_format || echo "legacy-cjs")
47
46
  CACHE_DIR="${CLAUDE_TERMUX_PACKAGE_CACHE:-${HOME}/.claude-termux-native-package}/versions/${CLAUDE_VERSION}"
48
47
  SOURCE_BIN="${CACHE_DIR}/app/node_modules/@anthropic-ai/claude-code-linux-arm64/claude"
49
48
 
49
+ if [ "${ENTRY_FORMAT}" = "esm-chunked" ]; then
50
+ ENTRY_JS_OFFSET=""
51
+ ENTRY_END_OFFSET=""
52
+ else
53
+ ENTRY_JS_OFFSET=$(json_field entry_js_offset)
54
+ ENTRY_END_OFFSET=$(json_field entry_end_offset)
55
+ fi
56
+
50
57
  if [ ! -f "${SOURCE_BIN}" ]; then
51
58
  "$NODE" "${PACKAGE_DIR}/lib/prepare-native.js" "${CLAUDE_VERSION}"
52
59
  fi
@@ -58,6 +65,7 @@ fi
58
65
 
59
66
  SOURCE_BIN="${SOURCE_BIN}" \
60
67
  WORKDIR="${CACHE_DIR}/launcher-workdir" \
68
+ ENTRY_FORMAT="${ENTRY_FORMAT}" \
61
69
  ENTRY_JS_OFFSET="${ENTRY_JS_OFFSET}" \
62
70
  ENTRY_END_OFFSET="${ENTRY_END_OFFSET}" \
63
71
  CURRENT_CLAUDE_VERSION="${CLAUDE_VERSION}" \
@@ -770,6 +770,14 @@
770
770
  "tarball_integrity": "sha512-tHsJhjwcoFyKTCQfYCclKjLtbnbbGKvcrzxUhMb6NDlPw9RrawmjSWUAdla7PtEVPaJsGkjGo4O2MQTlYIT+PA==",
771
771
  "tarball_sha256": "41de896e672667bc5b5060419d5a1ff5cfe15b77659d1418b10df5095e38419a",
772
772
  "status": "termux_verified"
773
+ },
774
+ "2.1.245": {
775
+ "wrapper_spec": "@anthropic-ai/claude-code@2.1.245",
776
+ "native_spec": "@anthropic-ai/claude-code-linux-arm64@2.1.245",
777
+ "entry_format": "esm-chunked",
778
+ "tarball_integrity": "sha512-Qbn5HnZbYeW4GdifVkDGfcVKqj3/f3U9sfVd3LEaUZh08CcS8D/ptJ76zuBfQUGHJHfToAdNVfak86uJ84b1Dg==",
779
+ "tarball_sha256": "668662e7b5d91a93cff6c75736e60f3d5d3bed4cbe077ae4feefc63ad1253f4d",
780
+ "status": "offset_discovered"
773
781
  }
774
782
  }
775
783
  }
@@ -2,7 +2,7 @@
2
2
  "manifest_version": 1,
3
3
  "package_name": "@bash0816/claude-code",
4
4
  "latest_audited_version": "2.1.241",
5
- "latest_candidate_version": "2.1.241",
5
+ "latest_candidate_version": "2.1.245",
6
6
  "previous_stable_version": "2.1.240",
7
7
  "stable_pinned_version": "2.1.220-2",
8
8
  "manifest_url": "https://raw.githubusercontent.com/bash0816/ClaudeCode-Termux/main/config/claude-termux-release-manifest.json"
@@ -0,0 +1,18 @@
1
+ import { createRequire } from 'node:module';
2
+
3
+ const require = createRequire(import.meta.url);
4
+ const realChildProcess = require('node:child_process');
5
+ const { createGuardedChildProcess } = require('./native-update-guard.js');
6
+ const guarded = createGuardedChildProcess(realChildProcess, (v) => process.stderr.write(v));
7
+
8
+ export const spawn = guarded.spawn;
9
+ export const execFile = guarded.execFile;
10
+ export const exec = guarded.exec;
11
+ export const spawnSync = guarded.spawnSync;
12
+ export const execFileSync = guarded.execFileSync;
13
+ export const execSync = guarded.execSync;
14
+ export const ChildProcess = realChildProcess.ChildProcess;
15
+ export const fork = realChildProcess.fork;
16
+ export const _forkChild = realChildProcess._forkChild;
17
+
18
+ export default Object.assign({}, realChildProcess, guarded);
@@ -0,0 +1,113 @@
1
+ 'use strict';
2
+
3
+ const test = require('node:test');
4
+ const assert = require('node:assert/strict');
5
+
6
+ test('bunfs-child-process-guard exports named exports for guarded methods', async () => {
7
+ const guard = await import('./bunfs-child-process-guard.mjs');
8
+
9
+ assert.equal(typeof guard.spawn, 'function');
10
+ assert.equal(typeof guard.execFile, 'function');
11
+ assert.equal(typeof guard.exec, 'function');
12
+ assert.equal(typeof guard.spawnSync, 'function');
13
+ assert.equal(typeof guard.execFileSync, 'function');
14
+ assert.equal(typeof guard.execSync, 'function');
15
+ });
16
+
17
+ test('bunfs-child-process-guard exports ChildProcess, fork, _forkChild', async () => {
18
+ const guard = await import('./bunfs-child-process-guard.mjs');
19
+
20
+ // ChildProcess is a class
21
+ assert.equal(typeof guard.ChildProcess, 'function');
22
+ // fork and _forkChild are functions
23
+ assert.equal(typeof guard.fork, 'function');
24
+ assert.equal(typeof guard._forkChild, 'function');
25
+ });
26
+
27
+ test('bunfs-child-process-guard provides default export with all methods', async () => {
28
+ const guard = await import('./bunfs-child-process-guard.mjs');
29
+ const defaultExport = guard.default;
30
+
31
+ assert.ok(defaultExport);
32
+ assert.equal(typeof defaultExport, 'object');
33
+ assert.equal(typeof defaultExport.spawn, 'function');
34
+ assert.equal(typeof defaultExport.execFile, 'function');
35
+ assert.equal(typeof defaultExport.exec, 'function');
36
+ assert.equal(typeof defaultExport.spawnSync, 'function');
37
+ assert.equal(typeof defaultExport.execFileSync, 'function');
38
+ assert.equal(typeof defaultExport.execSync, 'function');
39
+ assert.equal(typeof defaultExport.ChildProcess, 'function');
40
+ assert.equal(typeof defaultExport.fork, 'function');
41
+ assert.equal(typeof defaultExport._forkChild, 'function');
42
+ });
43
+
44
+ test('bunfs-child-process-guard blocks official package update via execFileSync', async () => {
45
+ const guard = await import('./bunfs-child-process-guard.mjs');
46
+
47
+ // Try to execute npm install of official package
48
+ try {
49
+ guard.execFileSync('npm', ['install', '-g', '@anthropic-ai/claude-code@latest']);
50
+ // If it doesn't throw, that's an error (should be blocked)
51
+ assert.fail('Expected execFileSync to block official package update');
52
+ } catch (err) {
53
+ // Should throw with CLAUDE_TERMUX_OFFICIAL_UPDATE_BLOCKED code
54
+ assert.ok(
55
+ err.code === 'CLAUDE_TERMUX_OFFICIAL_UPDATE_BLOCKED' ||
56
+ err.message.includes('disabled on Termux'),
57
+ `Expected block error, got: ${err.message}`,
58
+ );
59
+ }
60
+ });
61
+
62
+ test('bunfs-child-process-guard blocks official package update via execSync', async () => {
63
+ const guard = await import('./bunfs-child-process-guard.mjs');
64
+
65
+ // Try to execute npm install via exec
66
+ try {
67
+ guard.execSync('npm install -g @anthropic-ai/claude-code@latest');
68
+ // If it doesn't throw, that's an error (should be blocked)
69
+ assert.fail('Expected execSync to block official package update');
70
+ } catch (err) {
71
+ // Should throw with CLAUDE_TERMUX_OFFICIAL_UPDATE_BLOCKED code
72
+ assert.ok(
73
+ err.code === 'CLAUDE_TERMUX_OFFICIAL_UPDATE_BLOCKED' ||
74
+ err.message.includes('disabled on Termux'),
75
+ `Expected block error, got: ${err.message}`,
76
+ );
77
+ }
78
+ });
79
+
80
+ test('bunfs-child-process-guard allows harmless commands', async () => {
81
+ const guard = await import('./bunfs-child-process-guard.mjs');
82
+
83
+ // echo is a harmless command and should not be blocked
84
+ const result = guard.execFileSync('echo', ['hello']);
85
+ // Verify the command actually executed and produced output
86
+ assert.equal(result.toString().trim(), 'hello');
87
+ });
88
+
89
+ test('bunfs-child-process-guard spawn blocks official package install', async () => {
90
+ const guard = await import('./bunfs-child-process-guard.mjs');
91
+
92
+ // spawn should return a blocked child process (EventEmitter-like)
93
+ const child = guard.spawn('npm', ['install', '-g', '@anthropic-ai/claude-code']);
94
+
95
+ // Blocked spawn should have specific properties
96
+ assert.equal(child.stdout, null);
97
+ assert.equal(child.stderr, null);
98
+ assert.equal(child.stdin, null);
99
+ assert.equal(child.pid, 0);
100
+ assert.equal(child.killed, false);
101
+
102
+ // Verify it's event-like (has on method or can be used as event emitter)
103
+ assert.equal(typeof child.kill, 'function');
104
+ });
105
+
106
+ test('bunfs-child-process-guard does not block other package installs', async () => {
107
+ const guard = await import('./bunfs-child-process-guard.mjs');
108
+
109
+ // Installing a different package should not be blocked
110
+ const result = guard.execFileSync('echo', ['@bash0816/claude-code']);
111
+ // Verify the command actually executed and produced correct output
112
+ assert.equal(result.toString().trim(), '@bash0816/claude-code');
113
+ });
@@ -0,0 +1,74 @@
1
+ import { pathToFileURL, fileURLToPath } from 'node:url';
2
+ import { existsSync, readFileSync } from 'node:fs';
3
+ import { createRequire } from 'node:module';
4
+ import path from 'node:path';
5
+
6
+ let PROCESS_OWNED_DIR = null;
7
+ let SOURCE_BIN = null;
8
+ let CHILD_PROCESS_GUARD_PATH = null;
9
+ let VM_GUARD_PATH = null;
10
+ let WS_STUB_PATH = null;
11
+
12
+ export function initialize(data) {
13
+ PROCESS_OWNED_DIR = data.processOwnedDir;
14
+ SOURCE_BIN = data.sourceBin;
15
+ CHILD_PROCESS_GUARD_PATH = data.childProcessGuardPath;
16
+ VM_GUARD_PATH = data.vmGuardPath;
17
+ WS_STUB_PATH = data.wsStubPath;
18
+ }
19
+
20
+ function buildImportMetaRequirePolyfillPrelude(anchorUrl) {
21
+ return (
22
+ `import __bunfsGuardedChildProcess from ${JSON.stringify(pathToFileURL(CHILD_PROCESS_GUARD_PATH).href)};\n` +
23
+ `import __bunfsGuardedVm from ${JSON.stringify(pathToFileURL(VM_GUARD_PATH).href)};\n` +
24
+ `import { createRequire as __bunfsCreateRequire } from "node:module";\n` +
25
+ `const __bunfsRealRequire = __bunfsCreateRequire(${JSON.stringify(anchorUrl)});\n` +
26
+ `const __bunfsMetaRequire = (id) => {\n` +
27
+ ` if (id === "child_process" || id === "node:child_process") return __bunfsGuardedChildProcess;\n` +
28
+ ` if (id === "vm" || id === "node:vm") return __bunfsGuardedVm;\n` +
29
+ ` return __bunfsRealRequire(id);\n` +
30
+ `};\n`
31
+ );
32
+ }
33
+
34
+ export async function resolve(specifier, context, nextResolve) {
35
+ if (specifier === 'child_process' || specifier === 'node:child_process') {
36
+ return { url: pathToFileURL(CHILD_PROCESS_GUARD_PATH).href, shortCircuit: true, format: 'module' };
37
+ }
38
+ if (specifier === 'vm' || specifier === 'node:vm') {
39
+ return { url: pathToFileURL(VM_GUARD_PATH).href, shortCircuit: true, format: 'module' };
40
+ }
41
+ if (specifier === 'ws') {
42
+ return { url: pathToFileURL(WS_STUB_PATH).href, shortCircuit: true, format: 'module' };
43
+ }
44
+ if (specifier.startsWith('/$bunfs/root/')) {
45
+ const rel = specifier.slice('/$bunfs/root/'.length);
46
+ if (rel.includes('..') || path.isAbsolute(rel)) {
47
+ throw new Error(`bunfs resolve: rejected specifier ${specifier}`);
48
+ }
49
+ const real = path.resolve(PROCESS_OWNED_DIR, rel);
50
+ if (path.relative(PROCESS_OWNED_DIR, real).startsWith('..')) {
51
+ throw new Error(`bunfs resolve: path escapes process-owned dir: ${specifier}`);
52
+ }
53
+ if (!existsSync(real)) {
54
+ throw new Error(`bunfs resolve: missing extracted module ${specifier} -> ${real}`);
55
+ }
56
+ return { url: pathToFileURL(real).href, shortCircuit: true, format: 'module' };
57
+ }
58
+ return nextResolve(specifier, context);
59
+ }
60
+
61
+ export async function load(url, context, nextLoad) {
62
+ const ownedPrefix = pathToFileURL(PROCESS_OWNED_DIR + path.sep).href;
63
+ if (!url.startsWith(ownedPrefix)) {
64
+ return nextLoad(url, context);
65
+ }
66
+ const filePath = fileURLToPath(url);
67
+ let source = readFileSync(filePath, 'utf8');
68
+ if (source.includes('import.meta.require')) {
69
+ const anchorUrl = pathToFileURL(SOURCE_BIN).href;
70
+ source = buildImportMetaRequirePolyfillPrelude(anchorUrl) +
71
+ source.replaceAll('import.meta.require', '__bunfsMetaRequire');
72
+ }
73
+ return { format: 'module', source, shortCircuit: true };
74
+ }
@@ -0,0 +1,329 @@
1
+ 'use strict';
2
+
3
+ const test = require('node:test');
4
+ const assert = require('node:assert/strict');
5
+ const fs = require('node:fs');
6
+ const os = require('node:os');
7
+ const path = require('node:path');
8
+ const { pathToFileURL } = require('node:url');
9
+
10
+ // ESM test ファイルから CommonJS で import できないため、
11
+ // ここでは基本的な構造をテストする
12
+ test('bunfs-esm-loader module exports initialize, resolve, load functions', async () => {
13
+ // ESM モジュールを動的 import でテストする
14
+ const loader = await import('./bunfs-esm-loader.mjs');
15
+ assert.equal(typeof loader.initialize, 'function');
16
+ assert.equal(typeof loader.resolve, 'function');
17
+ assert.equal(typeof loader.load, 'function');
18
+ });
19
+
20
+ test('resolve() handles child_process and node:child_process specifiers', async () => {
21
+ const loader = await import('./bunfs-esm-loader.mjs');
22
+ const tempDir = path.join(os.tmpdir(), `bunfs-esm-loader-test-${process.pid}-${Date.now()}`);
23
+ fs.mkdirSync(tempDir, { recursive: true });
24
+ const guardPath = path.join(tempDir, 'guard.mjs');
25
+ fs.writeFileSync(guardPath, 'export default {};');
26
+
27
+ try {
28
+ loader.initialize({
29
+ processOwnedDir: tempDir,
30
+ sourceBin: '/dummy/bin',
31
+ childProcessGuardPath: guardPath,
32
+ vmGuardPath: path.join(tempDir, 'vm-guard.mjs'),
33
+ wsStubPath: path.join(tempDir, 'ws-stub.mjs'),
34
+ });
35
+
36
+ const nextResolve = async (spec, ctx) => ({ url: `unresolved:${spec}` });
37
+
38
+ // child_process should resolve to childProcessGuardPath
39
+ const result1 = await loader.resolve('child_process', {}, nextResolve);
40
+ assert.ok(result1.url.includes(guardPath));
41
+ assert.equal(result1.shortCircuit, true);
42
+
43
+ // node:child_process should also resolve to childProcessGuardPath
44
+ const result2 = await loader.resolve('node:child_process', {}, nextResolve);
45
+ assert.ok(result2.url.includes(guardPath));
46
+ assert.equal(result2.shortCircuit, true);
47
+ } finally {
48
+ fs.rmSync(tempDir, { recursive: true, force: true });
49
+ }
50
+ });
51
+
52
+ test('resolve() handles vm and node:vm specifiers', async () => {
53
+ const loader = await import('./bunfs-esm-loader.mjs');
54
+ const tempDir = path.join(os.tmpdir(), `bunfs-esm-loader-test-${process.pid}-${Date.now()}`);
55
+ fs.mkdirSync(tempDir, { recursive: true });
56
+ const vmGuardPath = path.join(tempDir, 'vm-guard.mjs');
57
+ fs.writeFileSync(vmGuardPath, 'export default {};');
58
+
59
+ try {
60
+ loader.initialize({
61
+ processOwnedDir: tempDir,
62
+ sourceBin: '/dummy/bin',
63
+ childProcessGuardPath: path.join(tempDir, 'guard.mjs'),
64
+ vmGuardPath: vmGuardPath,
65
+ wsStubPath: path.join(tempDir, 'ws-stub.mjs'),
66
+ });
67
+
68
+ const nextResolve = async (spec, ctx) => ({ url: `unresolved:${spec}` });
69
+
70
+ // vm should resolve to vmGuardPath
71
+ const result1 = await loader.resolve('vm', {}, nextResolve);
72
+ assert.ok(result1.url.includes(vmGuardPath));
73
+ assert.equal(result1.shortCircuit, true);
74
+
75
+ // node:vm should also resolve to vmGuardPath
76
+ const result2 = await loader.resolve('node:vm', {}, nextResolve);
77
+ assert.ok(result2.url.includes(vmGuardPath));
78
+ assert.equal(result2.shortCircuit, true);
79
+ } finally {
80
+ fs.rmSync(tempDir, { recursive: true, force: true });
81
+ }
82
+ });
83
+
84
+ test('resolve() handles ws specifier', async () => {
85
+ const loader = await import('./bunfs-esm-loader.mjs');
86
+ const tempDir = path.join(os.tmpdir(), `bunfs-esm-loader-test-${process.pid}-${Date.now()}`);
87
+ fs.mkdirSync(tempDir, { recursive: true });
88
+ const wsStubPath = path.join(tempDir, 'ws-stub.mjs');
89
+ fs.writeFileSync(wsStubPath, 'export default {};');
90
+
91
+ try {
92
+ loader.initialize({
93
+ processOwnedDir: tempDir,
94
+ sourceBin: '/dummy/bin',
95
+ childProcessGuardPath: path.join(tempDir, 'guard.mjs'),
96
+ vmGuardPath: path.join(tempDir, 'vm-guard.mjs'),
97
+ wsStubPath: wsStubPath,
98
+ });
99
+
100
+ const nextResolve = async (spec, ctx) => ({ url: `unresolved:${spec}` });
101
+
102
+ const result = await loader.resolve('ws', {}, nextResolve);
103
+ assert.ok(result.url.includes(wsStubPath));
104
+ assert.equal(result.shortCircuit, true);
105
+ } finally {
106
+ fs.rmSync(tempDir, { recursive: true, force: true });
107
+ }
108
+ });
109
+
110
+ test('resolve() resolves /$bunfs/root/ specifiers to real files in processOwnedDir', async () => {
111
+ const loader = await import('./bunfs-esm-loader.mjs');
112
+ const tempDir = path.join(os.tmpdir(), `bunfs-esm-loader-test-${process.pid}-${Date.now()}`);
113
+ fs.mkdirSync(tempDir, { recursive: true });
114
+
115
+ // Create a dummy file in processOwnedDir
116
+ const dummyFile = path.join(tempDir, 'foo.js');
117
+ fs.writeFileSync(dummyFile, 'export const foo = 1;');
118
+
119
+ try {
120
+ loader.initialize({
121
+ processOwnedDir: tempDir,
122
+ sourceBin: '/dummy/bin',
123
+ childProcessGuardPath: path.join(tempDir, 'guard.mjs'),
124
+ vmGuardPath: path.join(tempDir, 'vm-guard.mjs'),
125
+ wsStubPath: path.join(tempDir, 'ws-stub.mjs'),
126
+ });
127
+
128
+ const nextResolve = async (spec, ctx) => ({ url: `unresolved:${spec}` });
129
+
130
+ const result = await loader.resolve('/$bunfs/root/foo.js', {}, nextResolve);
131
+ assert.ok(result.url.includes('foo.js'));
132
+ assert.equal(result.shortCircuit, true);
133
+ assert.equal(result.format, 'module');
134
+ } finally {
135
+ fs.rmSync(tempDir, { recursive: true, force: true });
136
+ }
137
+ });
138
+
139
+ test('resolve() rejects path traversal with ..', async () => {
140
+ const loader = await import('./bunfs-esm-loader.mjs');
141
+ const tempDir = path.join(os.tmpdir(), `bunfs-esm-loader-test-${process.pid}-${Date.now()}`);
142
+ fs.mkdirSync(tempDir, { recursive: true });
143
+
144
+ try {
145
+ loader.initialize({
146
+ processOwnedDir: tempDir,
147
+ sourceBin: '/dummy/bin',
148
+ childProcessGuardPath: path.join(tempDir, 'guard.mjs'),
149
+ vmGuardPath: path.join(tempDir, 'vm-guard.mjs'),
150
+ wsStubPath: path.join(tempDir, 'ws-stub.mjs'),
151
+ });
152
+
153
+ const nextResolve = async (spec, ctx) => ({ url: `unresolved:${spec}` });
154
+
155
+ await assert.rejects(
156
+ () => loader.resolve('/$bunfs/root/../../etc/passwd', {}, nextResolve),
157
+ /rejected specifier|escapes/,
158
+ );
159
+ } finally {
160
+ fs.rmSync(tempDir, { recursive: true, force: true });
161
+ }
162
+ });
163
+
164
+ test('resolve() rejects absolute paths', async () => {
165
+ const loader = await import('./bunfs-esm-loader.mjs');
166
+ const tempDir = path.join(os.tmpdir(), `bunfs-esm-loader-test-${process.pid}-${Date.now()}`);
167
+ fs.mkdirSync(tempDir, { recursive: true });
168
+
169
+ try {
170
+ loader.initialize({
171
+ processOwnedDir: tempDir,
172
+ sourceBin: '/dummy/bin',
173
+ childProcessGuardPath: path.join(tempDir, 'guard.mjs'),
174
+ vmGuardPath: path.join(tempDir, 'vm-guard.mjs'),
175
+ wsStubPath: path.join(tempDir, 'ws-stub.mjs'),
176
+ });
177
+
178
+ const nextResolve = async (spec, ctx) => ({ url: `unresolved:${spec}` });
179
+
180
+ await assert.rejects(
181
+ () => loader.resolve('/$bunfs/root//etc/passwd', {}, nextResolve),
182
+ /rejected specifier|escapes/,
183
+ );
184
+ } finally {
185
+ fs.rmSync(tempDir, { recursive: true, force: true });
186
+ }
187
+ });
188
+
189
+ test('resolve() throws error for missing extracted module', async () => {
190
+ const loader = await import('./bunfs-esm-loader.mjs');
191
+ const tempDir = path.join(os.tmpdir(), `bunfs-esm-loader-test-${process.pid}-${Date.now()}`);
192
+ fs.mkdirSync(tempDir, { recursive: true });
193
+
194
+ try {
195
+ loader.initialize({
196
+ processOwnedDir: tempDir,
197
+ sourceBin: '/dummy/bin',
198
+ childProcessGuardPath: path.join(tempDir, 'guard.mjs'),
199
+ vmGuardPath: path.join(tempDir, 'vm-guard.mjs'),
200
+ wsStubPath: path.join(tempDir, 'ws-stub.mjs'),
201
+ });
202
+
203
+ const nextResolve = async (spec, ctx) => ({ url: `unresolved:${spec}` });
204
+
205
+ await assert.rejects(
206
+ () => loader.resolve('/$bunfs/root/nonexistent.js', {}, nextResolve),
207
+ /missing extracted module/,
208
+ );
209
+ } finally {
210
+ fs.rmSync(tempDir, { recursive: true, force: true });
211
+ }
212
+ });
213
+
214
+ test('resolve() calls nextResolve for unknown specifiers', async () => {
215
+ const loader = await import('./bunfs-esm-loader.mjs');
216
+ const tempDir = path.join(os.tmpdir(), `bunfs-esm-loader-test-${process.pid}-${Date.now()}`);
217
+ fs.mkdirSync(tempDir, { recursive: true });
218
+
219
+ try {
220
+ loader.initialize({
221
+ processOwnedDir: tempDir,
222
+ sourceBin: '/dummy/bin',
223
+ childProcessGuardPath: path.join(tempDir, 'guard.mjs'),
224
+ vmGuardPath: path.join(tempDir, 'vm-guard.mjs'),
225
+ wsStubPath: path.join(tempDir, 'ws-stub.mjs'),
226
+ });
227
+
228
+ let nextResolveCalled = false;
229
+ const nextResolve = async (spec, ctx) => {
230
+ nextResolveCalled = true;
231
+ return { url: `unresolved:${spec}` };
232
+ };
233
+
234
+ await loader.resolve('some-unknown-package', {}, nextResolve);
235
+ assert.equal(nextResolveCalled, true);
236
+ } finally {
237
+ fs.rmSync(tempDir, { recursive: true, force: true });
238
+ }
239
+ });
240
+
241
+ test('load() returns source as-is when import.meta.require is not present', async () => {
242
+ const loader = await import('./bunfs-esm-loader.mjs');
243
+ const tempDir = path.join(os.tmpdir(), `bunfs-esm-loader-test-${process.pid}-${Date.now()}`);
244
+ fs.mkdirSync(tempDir, { recursive: true });
245
+
246
+ const testFile = path.join(tempDir, 'test.js');
247
+ const sourceCode = 'export const x = 1;';
248
+ fs.writeFileSync(testFile, sourceCode);
249
+
250
+ try {
251
+ loader.initialize({
252
+ processOwnedDir: tempDir,
253
+ sourceBin: '/dummy/bin',
254
+ childProcessGuardPath: path.join(tempDir, 'guard.mjs'),
255
+ vmGuardPath: path.join(tempDir, 'vm-guard.mjs'),
256
+ wsStubPath: path.join(tempDir, 'ws-stub.mjs'),
257
+ });
258
+
259
+ const fileUrl = pathToFileURL(testFile).href;
260
+ const result = await loader.load(fileUrl, {}, async () => ({ source: 'fallback' }));
261
+
262
+ assert.equal(result.format, 'module');
263
+ assert.equal(result.source, sourceCode);
264
+ assert.equal(result.shortCircuit, true);
265
+ } finally {
266
+ fs.rmSync(tempDir, { recursive: true, force: true });
267
+ }
268
+ });
269
+
270
+ test('load() injects polyfill prelude when import.meta.require is present', async () => {
271
+ const loader = await import('./bunfs-esm-loader.mjs');
272
+ const tempDir = path.join(os.tmpdir(), `bunfs-esm-loader-test-${process.pid}-${Date.now()}`);
273
+ fs.mkdirSync(tempDir, { recursive: true });
274
+
275
+ const testFile = path.join(tempDir, 'test.js');
276
+ const sourceCode = 'const cp = import.meta.require("child_process");';
277
+ fs.writeFileSync(testFile, sourceCode);
278
+
279
+ try {
280
+ loader.initialize({
281
+ processOwnedDir: tempDir,
282
+ sourceBin: '/dummy/bin',
283
+ childProcessGuardPath: path.join(tempDir, 'guard.mjs'),
284
+ vmGuardPath: path.join(tempDir, 'vm-guard.mjs'),
285
+ wsStubPath: path.join(tempDir, 'ws-stub.mjs'),
286
+ });
287
+
288
+ const fileUrl = pathToFileURL(testFile).href;
289
+ const result = await loader.load(fileUrl, {}, async () => ({ source: 'fallback' }));
290
+
291
+ assert.equal(result.format, 'module');
292
+ assert.ok(result.source.includes('__bunfsMetaRequire'));
293
+ assert.ok(result.source.includes('import __bunfsGuardedChildProcess'));
294
+ assert.ok(result.source.includes('import __bunfsGuardedVm'));
295
+ // Check that import.meta.require was replaced with __bunfsMetaRequire
296
+ assert.ok(result.source.includes('__bunfsMetaRequire("child_process")'));
297
+ assert.ok(!result.source.includes('import.meta.require("child_process")'));
298
+ assert.equal(result.shortCircuit, true);
299
+ } finally {
300
+ fs.rmSync(tempDir, { recursive: true, force: true });
301
+ }
302
+ });
303
+
304
+ test('load() calls nextLoad for URLs outside processOwnedDir', async () => {
305
+ const loader = await import('./bunfs-esm-loader.mjs');
306
+ const tempDir = path.join(os.tmpdir(), `bunfs-esm-loader-test-${process.pid}-${Date.now()}`);
307
+ fs.mkdirSync(tempDir, { recursive: true });
308
+
309
+ try {
310
+ loader.initialize({
311
+ processOwnedDir: tempDir,
312
+ sourceBin: '/dummy/bin',
313
+ childProcessGuardPath: path.join(tempDir, 'guard.mjs'),
314
+ vmGuardPath: path.join(tempDir, 'vm-guard.mjs'),
315
+ wsStubPath: path.join(tempDir, 'ws-stub.mjs'),
316
+ });
317
+
318
+ let nextLoadCalled = false;
319
+ const nextLoad = async (url, ctx) => {
320
+ nextLoadCalled = true;
321
+ return { source: 'fallback', format: 'module' };
322
+ };
323
+
324
+ await loader.load('file:///some/other/path/module.js', {}, nextLoad);
325
+ assert.equal(nextLoadCalled, true);
326
+ } finally {
327
+ fs.rmSync(tempDir, { recursive: true, force: true });
328
+ }
329
+ });