@bash0816/claude-code 2.1.245 → 2.1.251

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.241
40
+ npm install -g @bash0816/claude-code@2.1.248
41
41
  ```
42
42
 
43
43
  ## Update / 更新
@@ -95,6 +95,51 @@ like `disable` or `Y` will silently turn protection off. Use exactly `0` to be s
95
95
  `1`/`true`/`yes`/`on`(大文字小文字を区別しません)以外の値は全て機能を再有効化してしまうため、
96
96
  `disable` や `Y` のような typo でも保護が黙って外れます。安全のため厳密に `0` を指定してください。
97
97
 
98
+ ### Tool Search Disabled by Default / Tool Search は既定で無効化
99
+
100
+ This Termux wrapper disables the upstream's automatic tool discovery feature (`ENABLE_TOOL_SEARCH=false`
101
+ by default) to prevent unnecessary dynamic tool loading in normal conversations. Without this setting,
102
+ the model may spontaneously activate `ToolSearch` and trigger `WebSearch`/`WebFetch` calls, consuming
103
+ your conversation turn limit (`--max-turns`).
104
+
105
+ この Termux wrapper は、upstream の自動 tool discovery 機能を既定で無効化しています
106
+ (`ENABLE_TOOL_SEARCH=false`)。この設定がないと、モデルが通常の会話で自発的に `ToolSearch` を活動化させ、
107
+ `WebSearch`/`WebFetch` を呼び出し、会話の turn 上限(`--max-turns`)を消費するおそれがあります。
108
+
109
+ If you want to re-enable tool search (for example, if you explicitly use `--tools` and want the model
110
+ to load additional tools dynamically), set the variable before launching:
111
+
112
+ tool search を再有効化したい場合(例えば `--tools` を明示的に指定して、モデルが追加の tool を
113
+ 動的にロードしてほしい場合)、起動前に環境変数を設定してください:
114
+
115
+ ```sh
116
+ ENABLE_TOOL_SEARCH=true claude
117
+ ```
118
+
119
+ or:
120
+
121
+ または:
122
+
123
+ ```sh
124
+ ENABLE_TOOL_SEARCH=auto claude
125
+ ```
126
+
127
+ **Note on `settings.json`:** If you want to configure this in `settings.json`, place `ENABLE_TOOL_SEARCH` in
128
+ the `env` block with the value `force`. The environment variable default (`false`) takes precedence over `true`,
129
+ so use the `env` block to force-enable it:
130
+
131
+ ```json
132
+ { "env": { "ENABLE_TOOL_SEARCH": "force" } }
133
+ ```
134
+
135
+ **`settings.json` を使う場合の注意:** `settings.json` で設定する場合、`env` ブロック内に `ENABLE_TOOL_SEARCH`
136
+ を置いて、値を `force` にしてください。環境変数の既定値(`false`)が `true` より優先されるため、
137
+ force-enable するには `env` ブロックを使う必要があります:
138
+
139
+ ```json
140
+ { "env": { "ENABLE_TOOL_SEARCH": "force" } }
141
+ ```
142
+
98
143
  ## Policy / 方針
99
144
 
100
145
  - Only audited versions in `config/claude-native-audited-versions.json` can run.
package/bin/claude CHANGED
@@ -16,6 +16,16 @@ NODE="${MAGI_NODE:-node}"
16
16
  if [ -n "${MAGI_NODE:-}" ]; then
17
17
  export PATH="$(dirname -- "$NODE"):$PATH"
18
18
  fi
19
+
20
+ if ! "$NODE" -e '
21
+ const [major, minor] = process.versions.node.split(".").map(Number);
22
+ const ok = major > 23 || (major === 23 && minor >= 8) || (major === 22 && minor >= 15);
23
+ process.exit(ok ? 0 : 1);
24
+ '; then
25
+ echo "Error: claude-code requires Node.js >=22.15.0 <23.0.0 or >=23.8.0 (module.registerHooks() API + zlib zstd support). Detected: $("$NODE" --version)" >&2
26
+ exit 1
27
+ fi
28
+
19
29
  CONFIG_FILE="${PACKAGE_DIR}/config/claude-native-audited-versions.json"
20
30
  PACKAGE_VERSION=$("$NODE" -e 'const fs=require("fs"); const path=require("path"); const p=JSON.parse(fs.readFileSync(path.join(process.argv[1],"package.json"),"utf8")); process.stdout.write(p.version)' "${PACKAGE_DIR}")
21
31
  CLAUDE_VERSION="${CLAUDE_TERMUX_CLAUDE_VERSION:-${PACKAGE_VERSION}}"
@@ -777,7 +777,173 @@
777
777
  "entry_format": "esm-chunked",
778
778
  "tarball_integrity": "sha512-Qbn5HnZbYeW4GdifVkDGfcVKqj3/f3U9sfVd3LEaUZh08CcS8D/ptJ76zuBfQUGHJHfToAdNVfak86uJ84b1Dg==",
779
779
  "tarball_sha256": "668662e7b5d91a93cff6c75736e60f3d5d3bed4cbe077ae4feefc63ad1253f4d",
780
- "status": "offset_discovered"
780
+ "cycle_hoists": [],
781
+ "status": "termux_verified"
782
+ },
783
+ "2.1.248": {
784
+ "wrapper_spec": "@anthropic-ai/claude-code@2.1.248",
785
+ "native_spec": "@anthropic-ai/claude-code-linux-arm64@2.1.248",
786
+ "entry_format": "esm-chunked",
787
+ "tarball_integrity": "sha512-2qwjgGuzlVfkJC7UWUjl86hiv5Ok5vtVxAZ/5fpdhzQKuBLlxrJJu3z5tVI+VrO/x/hcqx5kJuy9/O9K4eT+Kg==",
788
+ "tarball_sha256": "bd2e2b6ebace34039b073b0f586d521ec09c895fa2a39de8d1c86423ae5884ca",
789
+ "status": "termux_verified",
790
+ "num_modules": 1946,
791
+ "byte_count": 136124175,
792
+ "cycle_hoists": [
793
+ {
794
+ "file": "chunk-vmw9kxhv.js",
795
+ "targetModule": "chunk-y0jj307t.js",
796
+ "expectedOccurrences": 1,
797
+ "assertProperties": []
798
+ },
799
+ {
800
+ "file": "chunk-9n7t2tbt.js",
801
+ "targetModule": "chunk-ppd18hq1.js",
802
+ "expectedOccurrences": 1,
803
+ "assertProperties": [
804
+ "MonitorTool"
805
+ ]
806
+ },
807
+ {
808
+ "file": "chunk-9n7t2tbt.js",
809
+ "targetModule": "chunk-830s9480.js",
810
+ "expectedOccurrences": 1,
811
+ "assertProperties": [
812
+ "EndConversationTool"
813
+ ]
814
+ },
815
+ {
816
+ "file": "chunk-9n7t2tbt.js",
817
+ "targetModule": "chunk-m4hx5z9g.js",
818
+ "expectedOccurrences": 1,
819
+ "assertProperties": [
820
+ "ArtifactTool"
821
+ ]
822
+ },
823
+ {
824
+ "file": "chunk-9n7t2tbt.js",
825
+ "targetModule": "chunk-eg121wbt.js",
826
+ "expectedOccurrences": 1,
827
+ "assertProperties": [
828
+ "WorkflowTool"
829
+ ]
830
+ },
831
+ {
832
+ "file": "chunk-9n7t2tbt.js",
833
+ "targetModule": "chunk-prrm838s.js",
834
+ "expectedOccurrences": 1,
835
+ "assertProperties": []
836
+ },
837
+ {
838
+ "file": "chunk-9n7t2tbt.js",
839
+ "targetModule": "chunk-exvwsc2a.js",
840
+ "expectedOccurrences": 2,
841
+ "assertProperties": []
842
+ },
843
+ {
844
+ "file": "chunk-9n7t2tbt.js",
845
+ "targetModule": "chunk-s61yn21a.js",
846
+ "expectedOccurrences": 1,
847
+ "assertProperties": [
848
+ "default"
849
+ ]
850
+ },
851
+ {
852
+ "file": "chunk-9n7t2tbt.js",
853
+ "targetModule": "chunk-040m9a9s.js",
854
+ "expectedOccurrences": 3,
855
+ "assertProperties": [
856
+ "getWorkflowCommands",
857
+ "invalidateWorkflowCache",
858
+ "warmWorkflows"
859
+ ]
860
+ }
861
+ ]
862
+ },
863
+ "2.1.251": {
864
+ "wrapper_spec": "@anthropic-ai/claude-code@2.1.251",
865
+ "native_spec": "@anthropic-ai/claude-code-linux-arm64@2.1.251",
866
+ "entry_format": "esm-chunked",
867
+ "tarball_integrity": "sha512-h4EXRyV25yJfez40cTIoBlJptJqccP8nIDPdDMO9TDPUzZ6e5Y3OO9AgrfdMRAgccaTRsKfJenh7lA2D40nzdw==",
868
+ "tarball_sha256": "50760f283bf289a8bfb6f25ab86276808fd038b8f724ba5eaffb9b195483846d",
869
+ "status": "offset_discovered",
870
+ "num_modules": 1971,
871
+ "byte_count": 126850982,
872
+ "cycle_hoists": [
873
+ {
874
+ "file": "chunk-5yrgn0qs.js",
875
+ "targetModule": "chunk-j108xzrz.js",
876
+ "expectedOccurrences": 1,
877
+ "assertProperties": []
878
+ },
879
+ {
880
+ "file": "chunk-82xr73g2.js",
881
+ "targetModule": "chunk-kqaa82qj.js",
882
+ "expectedOccurrences": 1,
883
+ "assertProperties": [
884
+ "MonitorTool"
885
+ ]
886
+ },
887
+ {
888
+ "file": "chunk-82xr73g2.js",
889
+ "targetModule": "chunk-q6fxy93d.js",
890
+ "expectedOccurrences": 1,
891
+ "assertProperties": [
892
+ "EndConversationTool"
893
+ ]
894
+ },
895
+ {
896
+ "file": "chunk-82xr73g2.js",
897
+ "targetModule": "chunk-dxpcqxe7.js",
898
+ "expectedOccurrences": 1,
899
+ "assertProperties": [
900
+ "ArtifactTool"
901
+ ]
902
+ },
903
+ {
904
+ "file": "chunk-82xr73g2.js",
905
+ "targetModule": "chunk-be1dqpgt.js",
906
+ "expectedOccurrences": 1,
907
+ "assertProperties": [
908
+ "WorkflowTool"
909
+ ]
910
+ },
911
+ {
912
+ "file": "chunk-82xr73g2.js",
913
+ "targetModule": "chunk-er5txax0.js",
914
+ "expectedOccurrences": 1,
915
+ "assertProperties": []
916
+ },
917
+ {
918
+ "file": "chunk-82xr73g2.js",
919
+ "targetModule": "chunk-nh8esxyc.js",
920
+ "expectedOccurrences": 2,
921
+ "assertProperties": []
922
+ },
923
+ {
924
+ "file": "chunk-82xr73g2.js",
925
+ "targetModule": "chunk-4agvbmxr.js",
926
+ "expectedOccurrences": 1,
927
+ "assertProperties": [
928
+ "default"
929
+ ]
930
+ },
931
+ {
932
+ "file": "chunk-82xr73g2.js",
933
+ "targetModule": "chunk-4sfywf4e.js",
934
+ "expectedOccurrences": 3,
935
+ "assertProperties": [
936
+ "getWorkflowCommands",
937
+ "invalidateWorkflowCache",
938
+ "warmWorkflows"
939
+ ]
940
+ }
941
+ ],
942
+ "cycle_hoists_skipped_assets": [
943
+ "chart.umd.min.js",
944
+ "hljsBundle.generated.min.js",
945
+ "mermaid.min.js"
946
+ ]
781
947
  }
782
948
  }
783
949
  }
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "manifest_version": 1,
3
3
  "package_name": "@bash0816/claude-code",
4
- "latest_audited_version": "2.1.241",
5
- "latest_candidate_version": "2.1.245",
6
- "previous_stable_version": "2.1.240",
4
+ "latest_audited_version": "2.1.248",
5
+ "latest_candidate_version": "2.1.251",
6
+ "previous_stable_version": "2.1.245",
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"
9
9
  }
@@ -1,20 +1,105 @@
1
1
  import { pathToFileURL, fileURLToPath } from 'node:url';
2
2
  import { existsSync, readFileSync } from 'node:fs';
3
- import { createRequire } from 'node:module';
3
+ import { createRequire, syncBuiltinESMExports } from 'node:module';
4
4
  import path from 'node:path';
5
5
 
6
+ const require = createRequire(import.meta.url);
7
+
6
8
  let PROCESS_OWNED_DIR = null;
7
9
  let SOURCE_BIN = null;
8
10
  let CHILD_PROCESS_GUARD_PATH = null;
9
11
  let VM_GUARD_PATH = null;
10
12
  let WS_STUB_PATH = null;
11
13
 
14
+ let CYCLE_HOISTS = [];
15
+
16
+ let REEXTRACT = null;
17
+ let reExtractConsecFailures = 0;
18
+ let lastReExtractMs = 0;
19
+ const MAX_CONSEC_FAILURES = 3;
20
+ const REEXTRACT_THROTTLE_MS = 3000;
21
+
22
+ function recoverMissing(realPath, now = Date.now()) {
23
+ if (existsSync(realPath)) return true;
24
+ if (!REEXTRACT || !SOURCE_BIN) return false;
25
+ if (reExtractConsecFailures >= MAX_CONSEC_FAILURES) return false;
26
+ if (now - lastReExtractMs < REEXTRACT_THROTTLE_MS) return existsSync(realPath);
27
+ lastReExtractMs = now;
28
+ try {
29
+ if (!existsSync(SOURCE_BIN)) { reExtractConsecFailures += 1; return false; }
30
+ REEXTRACT(SOURCE_BIN, PROCESS_OWNED_DIR);
31
+ console.error('[claude-code] recovered missing extracted module(s) by re-extracting from the native binary');
32
+ } catch (e) {
33
+ reExtractConsecFailures += 1;
34
+ console.error('[claude-code] re-extraction failed: ' + (e && e.message ? e.message : String(e)));
35
+ return false;
36
+ }
37
+ const ok = existsSync(realPath);
38
+ reExtractConsecFailures = ok ? 0 : reExtractConsecFailures + 1;
39
+ return ok;
40
+ }
41
+
42
+ function resolveBunfsPath(id) {
43
+ if (typeof id !== 'string' || !id.startsWith('/$bunfs/root/')) return null;
44
+ const rel = id.slice('/$bunfs/root/'.length);
45
+ if (rel.includes('..') || path.isAbsolute(rel)) {
46
+ throw new Error(`bunfs: rejected specifier ${id}`);
47
+ }
48
+ const real = path.resolve(PROCESS_OWNED_DIR, rel);
49
+ if (path.relative(PROCESS_OWNED_DIR, real).startsWith('..')) {
50
+ throw new Error(`bunfs: path escapes process-owned dir: ${id}`);
51
+ }
52
+ return real;
53
+ }
54
+
12
55
  export function initialize(data) {
13
56
  PROCESS_OWNED_DIR = data.processOwnedDir;
14
57
  SOURCE_BIN = data.sourceBin;
15
58
  CHILD_PROCESS_GUARD_PATH = data.childProcessGuardPath;
16
59
  VM_GUARD_PATH = data.vmGuardPath;
17
60
  WS_STUB_PATH = data.wsStubPath;
61
+ CYCLE_HOISTS = Array.isArray(data.cycleHoists) ? data.cycleHoists : [];
62
+ REEXTRACT = typeof data.reExtract === 'function' ? data.reExtract : null;
63
+ globalThis.__bunfsRecoverMissing = recoverMissing;
64
+ globalThis.__bunfsResolvePath = resolveBunfsPath;
65
+ // 回復状態のリセット (テスト隔離・再 initialize 対応)
66
+ reExtractConsecFailures = 0;
67
+ lastReExtractMs = 0;
68
+ }
69
+
70
+ function tryHoistCycleBreakingImports(filePath, source) {
71
+ const rel = path.relative(PROCESS_OWNED_DIR, filePath);
72
+ const records = CYCLE_HOISTS.filter((r) => r.file === rel);
73
+ if (records.length === 0) return null;
74
+
75
+ let hoistedImportLines = '';
76
+ let newSource = source;
77
+ let varIndex = 0;
78
+ const targetToVar = new Map();
79
+
80
+ for (const record of records) {
81
+ const literal = `import.meta.require("/$bunfs/root/${record.targetModule}")`;
82
+ const occurrences = newSource.split(literal).length - 1;
83
+ if (occurrences !== record.expectedOccurrences) continue;
84
+
85
+ const real = path.resolve(PROCESS_OWNED_DIR, record.targetModule);
86
+ if (path.relative(PROCESS_OWNED_DIR, real).startsWith('..')) continue;
87
+ if (!existsSync(real) && !recoverMissing(real)) continue;
88
+
89
+ let varName = targetToVar.get(record.targetModule);
90
+ if (!varName) {
91
+ varName = `__bunfsHoisted_${varIndex++}`;
92
+ targetToVar.set(record.targetModule, varName);
93
+ hoistedImportLines += `import * as ${varName} from ${JSON.stringify(pathToFileURL(real).href)};\n`;
94
+ if (record.assertProperties && record.assertProperties.length > 0) {
95
+ hoistedImportLines += `__bunfsAssertHoistedProps(${varName}, ${JSON.stringify(record.targetModule)}, ${JSON.stringify(record.assertProperties)});\n`;
96
+ }
97
+ }
98
+ newSource = newSource.replaceAll(literal, varName);
99
+ }
100
+
101
+ if (!targetToVar.size) return null;
102
+ return { hoistedImportLine: hoistedImportLines, source: newSource };
18
103
  }
19
104
 
20
105
  function buildImportMetaRequirePolyfillPrelude(anchorUrl) {
@@ -22,35 +107,89 @@ function buildImportMetaRequirePolyfillPrelude(anchorUrl) {
22
107
  `import __bunfsGuardedChildProcess from ${JSON.stringify(pathToFileURL(CHILD_PROCESS_GUARD_PATH).href)};\n` +
23
108
  `import __bunfsGuardedVm from ${JSON.stringify(pathToFileURL(VM_GUARD_PATH).href)};\n` +
24
109
  `import { createRequire as __bunfsCreateRequire } from "node:module";\n` +
110
+ `import __bunfsMetaRequirePath from "node:path";\n` +
111
+ `import { existsSync as __bunfsMetaRequireExistsSync } from "node:fs";\n` +
112
+ `import { readFileSync as __bunfsMetaRequireReadFileSync } from "node:fs";\n` +
113
+ `function __bunfsAssertHoistedProps(ns, targetModule, propNames) {\n` +
114
+ ` for (const p of propNames) {\n` +
115
+ ` let v;\n` +
116
+ ` try {\n` +
117
+ ` v = ns[p];\n` +
118
+ ` } catch (e) {\n` +
119
+ ` if (e instanceof ReferenceError) {\n` +
120
+ ` throw new Error("bunfs cycle-hoist: accessing \\"" + p + "\\" on " + targetModule + " threw ReferenceError (TDZ) - evaluation-order regression");\n` +
121
+ ` }\n` +
122
+ ` throw e;\n` +
123
+ ` }\n` +
124
+ ` if (v === undefined) {\n` +
125
+ ` throw new Error("bunfs cycle-hoist: \\"" + p + "\\" is undefined after hoisting from " + targetModule + " (evaluation-order regression?)");\n` +
126
+ ` }\n` +
127
+ ` }\n` +
128
+ `}\n` +
25
129
  `const __bunfsRealRequire = __bunfsCreateRequire(${JSON.stringify(anchorUrl)});\n` +
130
+ `const __bunfsOwnedDir = ${JSON.stringify(PROCESS_OWNED_DIR)};\n` +
26
131
  `const __bunfsMetaRequire = (id) => {\n` +
27
132
  ` if (id === "child_process" || id === "node:child_process") return __bunfsGuardedChildProcess;\n` +
28
133
  ` if (id === "vm" || id === "node:vm") return __bunfsGuardedVm;\n` +
134
+ ` if (typeof id === "string" && id.startsWith("/$bunfs/root/")) {\n` +
135
+ ` let real;\n` +
136
+ ` try {\n` +
137
+ ` real = globalThis.__bunfsResolvePath(id);\n` +
138
+ ` } catch (_eResolve) {\n` +
139
+ ` throw new Error("bunfs meta-require: " + _eResolve.message);\n` +
140
+ ` }\n` +
141
+ ` if (!__bunfsMetaRequireExistsSync(real)) {\n` +
142
+ ` const _rec = (typeof globalThis.__bunfsRecoverMissing === "function") && globalThis.__bunfsRecoverMissing(real);\n` +
143
+ ` if (!_rec) throw new Error("bunfs meta-require: missing extracted module " + id + " -> " + real);\n` +
144
+ ` }\n` +
145
+ ` const ext = __bunfsMetaRequirePath.extname(real);\n` +
146
+ ` if (ext === ".md" || ext === ".txt") {\n` +
147
+ ` try { return __bunfsMetaRequireReadFileSync(real, "utf8"); }\n` +
148
+ ` catch (_e2) {\n` +
149
+ ` if (_e2 && _e2.code === "ENOENT" && !__bunfsMetaRequireExistsSync(real) && typeof globalThis.__bunfsRecoverMissing === "function" && globalThis.__bunfsRecoverMissing(real)) {\n` +
150
+ ` return __bunfsMetaRequireReadFileSync(real, "utf8");\n` +
151
+ ` }\n` +
152
+ ` throw _e2;\n` +
153
+ ` }\n` +
154
+ ` }\n` +
155
+ ` try {\n` +
156
+ ` return __bunfsRealRequire(real);\n` +
157
+ ` } catch (_e) {\n` +
158
+ ` if (!__bunfsMetaRequireExistsSync(real) && typeof globalThis.__bunfsRecoverMissing === "function" && globalThis.__bunfsRecoverMissing(real)) {\n` +
159
+ ` return __bunfsRealRequire(real);\n` +
160
+ ` }\n` +
161
+ ` throw _e;\n` +
162
+ ` }\n` +
163
+ ` }\n` +
29
164
  ` return __bunfsRealRequire(id);\n` +
30
165
  `};\n`
31
166
  );
32
167
  }
33
168
 
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' };
169
+ export function resolve(specifier, context, nextResolve) {
170
+ const parentURL = context && context.parentURL;
171
+ const ownedPrefix = pathToFileURL(PROCESS_OWNED_DIR + path.sep).href;
172
+ const fromChunk = typeof parentURL === 'string' && parentURL.startsWith(ownedPrefix);
173
+
174
+ if (fromChunk) {
175
+ if (specifier === 'child_process' || specifier === 'node:child_process') {
176
+ return { url: pathToFileURL(CHILD_PROCESS_GUARD_PATH).href, shortCircuit: true, format: 'module' };
177
+ }
178
+ if (specifier === 'vm' || specifier === 'node:vm') {
179
+ return { url: pathToFileURL(VM_GUARD_PATH).href, shortCircuit: true, format: 'module' };
180
+ }
181
+ if (specifier === 'ws') {
182
+ return { url: pathToFileURL(WS_STUB_PATH).href, shortCircuit: true, format: 'module' };
183
+ }
43
184
  }
44
185
  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}`);
186
+ let real;
187
+ try {
188
+ real = resolveBunfsPath(specifier);
189
+ } catch (e) {
190
+ throw new Error(`bunfs resolve: ${e.message}`);
52
191
  }
53
- if (!existsSync(real)) {
192
+ if (!existsSync(real) && !recoverMissing(real)) {
54
193
  throw new Error(`bunfs resolve: missing extracted module ${specifier} -> ${real}`);
55
194
  }
56
195
  return { url: pathToFileURL(real).href, shortCircuit: true, format: 'module' };
@@ -58,17 +197,91 @@ export async function resolve(specifier, context, nextResolve) {
58
197
  return nextResolve(specifier, context);
59
198
  }
60
199
 
61
- export async function load(url, context, nextLoad) {
200
+ export function load(url, context, nextLoad) {
62
201
  const ownedPrefix = pathToFileURL(PROCESS_OWNED_DIR + path.sep).href;
63
202
  if (!url.startsWith(ownedPrefix)) {
64
203
  return nextLoad(url, context);
65
204
  }
66
205
  const filePath = fileURLToPath(url);
67
- let source = readFileSync(filePath, 'utf8');
206
+ let source;
207
+ try {
208
+ source = readFileSync(filePath, 'utf8');
209
+ } catch (e) {
210
+ // filePath 自身が消えている場合のみ回復 (エラーコードだけに依存しない)
211
+ if (e && e.code === 'ENOENT' && !existsSync(filePath) && recoverMissing(filePath)) {
212
+ source = readFileSync(filePath, 'utf8');
213
+ } else {
214
+ throw e;
215
+ }
216
+ }
217
+
218
+ let hoistedImportLine = '';
219
+ const hoistResult = tryHoistCycleBreakingImports(filePath, source);
220
+ if (hoistResult) {
221
+ hoistedImportLine = hoistResult.hoistedImportLine;
222
+ source = hoistResult.source;
223
+ }
224
+
68
225
  if (source.includes('import.meta.require')) {
69
226
  const anchorUrl = pathToFileURL(SOURCE_BIN).href;
70
- source = buildImportMetaRequirePolyfillPrelude(anchorUrl) +
227
+ source = hoistedImportLine +
228
+ buildImportMetaRequirePolyfillPrelude(anchorUrl) +
71
229
  source.replaceAll('import.meta.require', '__bunfsMetaRequire');
230
+ } else if (hoistedImportLine) {
231
+ source = hoistedImportLine + source;
72
232
  }
73
233
  return { format: 'module', source, shortCircuit: true };
74
234
  }
235
+
236
+ let FS_PATCHED = false;
237
+ function installFsBunfsInterception() {
238
+ if (FS_PATCHED) return;
239
+ const fsMod = require('node:fs');
240
+ const zlib = require('node:zlib');
241
+ if (typeof zlib.zstdDecompressSync !== 'function' || typeof zlib.zstdDecompress !== 'function') {
242
+ throw new Error('bunfs: Node.js zlib zstd support not found. Please upgrade to Node.js >=23.8.0 or >=22.15.0 (LTS).');
243
+ }
244
+ const origReadFileSync = fsMod.readFileSync;
245
+ const origReadFile = fsMod.readFile;
246
+ const origPromisesReadFile = fsMod.promises.readFile;
247
+
248
+ function resolveOrRecover(p) {
249
+ const real = resolveBunfsPath(p);
250
+ if (real === null) return null;
251
+ if (!existsSync(real)) {
252
+ const recovered = (typeof globalThis.__bunfsRecoverMissing === 'function') && globalThis.__bunfsRecoverMissing(real);
253
+ if (!recovered) throw new Error(`bunfs fs-intercept: missing extracted asset ${p} -> ${real}`);
254
+ }
255
+ const realOwned = fsMod.realpathSync(PROCESS_OWNED_DIR);
256
+ const realTarget = fsMod.realpathSync(real);
257
+ if (path.relative(realOwned, realTarget).startsWith('..')) {
258
+ throw new Error(`bunfs fs-intercept: resolved path escapes owned dir via symlink: ${p}`);
259
+ }
260
+ return real;
261
+ }
262
+
263
+ try {
264
+ fsMod.readFileSync = function (p, ...rest) {
265
+ const real = typeof p === 'string' ? resolveOrRecover(p) : null;
266
+ return Reflect.apply(origReadFileSync, this, [real !== null ? real : p, ...rest]);
267
+ };
268
+ fsMod.readFile = function (p, ...rest) {
269
+ const real = typeof p === 'string' ? resolveOrRecover(p) : null;
270
+ return Reflect.apply(origReadFile, this, [real !== null ? real : p, ...rest]);
271
+ };
272
+ fsMod.promises.readFile = function (p, ...rest) {
273
+ const real = typeof p === 'string' ? resolveOrRecover(p) : null;
274
+ return Reflect.apply(origPromisesReadFile, this, [real !== null ? real : p, ...rest]);
275
+ };
276
+ syncBuiltinESMExports();
277
+ } catch (e) {
278
+ fsMod.readFileSync = origReadFileSync;
279
+ fsMod.readFile = origReadFile;
280
+ fsMod.promises.readFile = origPromisesReadFile;
281
+ try { syncBuiltinESMExports(); } catch { /* 復元目的、失敗しても re-throw を優先 */ }
282
+ throw e;
283
+ }
284
+ FS_PATCHED = true;
285
+ }
286
+
287
+ export { recoverMissing, resolveBunfsPath, installFsBunfsInterception };