@oh-my-pi/pi-coding-agent 16.4.2 → 16.4.3
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/CHANGELOG.md +42 -0
- package/dist/cli.js +17514 -17799
- package/dist/types/advisor/config.d.ts +4 -3
- package/dist/types/commit/agentic/agent.d.ts +1 -0
- package/dist/types/config/settings-schema.d.ts +28 -0
- package/dist/types/extensibility/plugins/legacy-pi-compat.d.ts +10 -22
- package/dist/types/lsp/deferred-diagnostics.d.ts +11 -0
- package/dist/types/modes/components/move-overlay.d.ts +1 -1
- package/dist/types/modes/components/status-line/component.d.ts +3 -0
- package/dist/types/modes/components/status-line/types.d.ts +3 -0
- package/dist/types/modes/interactive-mode.d.ts +8 -0
- package/dist/types/modes/types.d.ts +2 -0
- package/dist/types/session/agent-session.d.ts +16 -0
- package/dist/types/task/executor.d.ts +31 -0
- package/dist/types/tools/__tests__/vibe-render.test.d.ts +1 -0
- package/dist/types/tools/browser/cmux/cmux-tab.d.ts +3 -1
- package/dist/types/tools/browser/cmux/rpc.d.ts +20 -0
- package/dist/types/tools/browser/run-output.d.ts +25 -0
- package/dist/types/tools/browser/tab-worker.d.ts +16 -0
- package/dist/types/tools/index.d.ts +1 -0
- package/dist/types/tools/vibe.d.ts +161 -0
- package/dist/types/utils/changelog.d.ts +35 -1
- package/dist/types/vibe/runtime.d.ts +124 -0
- package/dist/types/vibe/state.d.ts +4 -0
- package/dist/types/web/search/provider.d.ts +2 -0
- package/dist/types/web/search/providers/bing.d.ts +14 -0
- package/dist/types/web/search/providers/browser-headers.d.ts +9 -0
- package/dist/types/web/search/providers/browser-page.d.ts +32 -0
- package/dist/types/web/search/providers/ecosia.d.ts +14 -0
- package/dist/types/web/search/providers/google.d.ts +13 -0
- package/dist/types/web/search/providers/mojeek.d.ts +14 -0
- package/dist/types/web/search/providers/public.d.ts +37 -0
- package/dist/types/web/search/providers/startpage.d.ts +14 -0
- package/dist/types/web/search/providers/yahoo.d.ts +14 -0
- package/dist/types/web/search/types.d.ts +28 -0
- package/package.json +15 -18
- package/scripts/build-binary.ts +56 -73
- package/scripts/bundle-dist.ts +36 -40
- package/scripts/compile-binary.ts +68 -0
- package/scripts/generate-docs-index.ts +3 -93
- package/scripts/legacy-pi-virtual-module.ts +192 -0
- package/src/advisor/__tests__/advisor.test.ts +13 -0
- package/src/advisor/__tests__/config.test.ts +36 -0
- package/src/advisor/config.ts +11 -8
- package/src/commit/agentic/agent.ts +4 -0
- package/src/commit/agentic/index.ts +46 -21
- package/src/edit/index.ts +10 -76
- package/src/exec/non-interactive-env.ts +0 -1
- package/src/extensibility/plugins/legacy-pi-compat.ts +328 -162
- package/src/extensibility/plugins/legacy-pi-virtual-modules.d.ts +4 -0
- package/src/internal-urls/docs-index.ts +2 -1
- package/src/internal-urls/skill-protocol.ts +1 -1
- package/src/lsp/deferred-diagnostics.ts +66 -0
- package/src/main.ts +13 -13
- package/src/mcp/transports/stdio.test.ts +45 -1
- package/src/mcp/transports/stdio.ts +6 -3
- package/src/modes/acp/acp-agent.ts +65 -1
- package/src/modes/acp/acp-event-mapper.ts +5 -0
- package/src/modes/acp/acp-mode.ts +11 -0
- package/src/modes/components/__tests__/move-overlay.test.ts +16 -1
- package/src/modes/components/advisor-config.ts +15 -7
- package/src/modes/components/move-overlay.ts +2 -3
- package/src/modes/components/status-line/component.ts +6 -0
- package/src/modes/components/status-line/segments.ts +6 -0
- package/src/modes/components/status-line/types.ts +3 -0
- package/src/modes/controllers/command-controller.ts +8 -10
- package/src/modes/interactive-mode.ts +117 -1
- package/src/modes/types.ts +2 -0
- package/src/prompts/system/eager-task.md +2 -2
- package/src/prompts/system/system-prompt.md +9 -2
- package/src/prompts/system/vibe-mode-active.md +23 -0
- package/src/prompts/tools/browser.md +3 -3
- package/src/prompts/tools/grep.md +1 -1
- package/src/prompts/tools/vibe-kill.md +3 -0
- package/src/prompts/tools/vibe-list.md +3 -0
- package/src/prompts/tools/vibe-send.md +9 -0
- package/src/prompts/tools/vibe-spawn.md +10 -0
- package/src/prompts/tools/vibe-turn-result.md +19 -0
- package/src/prompts/tools/vibe-wait.md +8 -0
- package/src/sdk.ts +5 -0
- package/src/session/agent-session.ts +103 -13
- package/src/session/snapcompact-inline.ts +3 -19
- package/src/slash-commands/builtin-registry.ts +24 -8
- package/src/task/agents.ts +0 -2
- package/src/task/executor.ts +105 -0
- package/src/tools/__tests__/vibe-render.test.ts +210 -0
- package/src/tools/bash-skill-urls.ts +1 -1
- package/src/tools/browser/cmux/cmux-tab.ts +46 -48
- package/src/tools/browser/cmux/rpc.ts +50 -0
- package/src/tools/browser/run-output.ts +76 -0
- package/src/tools/browser/tab-worker.ts +264 -129
- package/src/tools/glob.ts +20 -6
- package/src/tools/index.ts +1 -0
- package/src/tools/read.ts +17 -9
- package/src/tools/renderers.ts +6 -0
- package/src/tools/vibe.ts +608 -0
- package/src/tools/write.ts +15 -2
- package/src/utils/changelog.ts +106 -9
- package/src/utils/git.ts +0 -1
- package/src/utils/title-generator.ts +70 -7
- package/src/vibe/runtime.ts +710 -0
- package/src/vibe/state.ts +4 -0
- package/src/web/search/index.ts +14 -6
- package/src/web/search/provider.ts +37 -1
- package/src/web/search/providers/bing.ts +197 -0
- package/src/web/search/providers/browser-headers.ts +92 -0
- package/src/web/search/providers/browser-page.ts +123 -0
- package/src/web/search/providers/duckduckgo.ts +13 -34
- package/src/web/search/providers/ecosia.ts +178 -0
- package/src/web/search/providers/google.ts +193 -0
- package/src/web/search/providers/mojeek.ts +206 -0
- package/src/web/search/providers/public.ts +201 -0
- package/src/web/search/providers/startpage.ts +213 -0
- package/src/web/search/providers/yahoo.ts +179 -0
- package/src/web/search/types.ts +35 -0
- package/dist/types/extensibility/plugins/legacy-pi-bundled-keys.d.ts +0 -10
- package/dist/types/extensibility/plugins/legacy-pi-bundled-registry.d.ts +0 -10
- package/scripts/generate-legacy-pi-bundled-registry.ts +0 -420
- package/src/extensibility/plugins/legacy-pi-bundled-keys.ts +0 -1011
- package/src/extensibility/plugins/legacy-pi-bundled-registry.ts +0 -3430
- package/src/internal-urls/docs-index.generated.txt +0 -2
- package/src/prompts/agents/plan.md +0 -47
|
@@ -1,82 +1,62 @@
|
|
|
1
|
+
/// <reference path="./legacy-pi-virtual-modules.d.ts" />
|
|
1
2
|
import * as fs from "node:fs";
|
|
2
3
|
import { isBuiltin } from "node:module";
|
|
3
4
|
import * as path from "node:path";
|
|
4
5
|
import * as url from "node:url";
|
|
5
6
|
import { isCompiledBinary, stripWindowsExtendedLengthPathPrefix } from "@oh-my-pi/pi-utils";
|
|
6
7
|
import { registerPluginCacheInvalidator } from "../../discovery/helpers";
|
|
7
|
-
import { BUNDLED_PI_REGISTRY_KEYS } from "./legacy-pi-bundled-keys";
|
|
8
8
|
|
|
9
9
|
const IS_COMPILED_BINARY = isCompiledBinary();
|
|
10
10
|
|
|
11
|
-
// === Bundled host
|
|
11
|
+
// === Bundled host modules (issue #3423) ===
|
|
12
12
|
//
|
|
13
13
|
// Bun 1.3.14 stopped exposing `--compile` extras through any filesystem-style
|
|
14
14
|
// API: `fs.existsSync`, `Bun.file().exists()`, `Bun.resolveSync`, and even
|
|
15
15
|
// `import("/$bunfs/...")` / `import("file:///$bunfs/...")` all fail for the
|
|
16
|
-
// embedded entries
|
|
17
|
-
//
|
|
18
|
-
// `@(scope)/pi-*` imports to a `file:///$bunfs/...` URL therefore breaks
|
|
19
|
-
// every legacy extension in compiled mode (issue #3423; see also issues
|
|
20
|
-
// #3329, #2168). Bun.plugin `onResolve` for bare specifiers also no longer
|
|
21
|
-
// fires for transitive imports inside runtime-loaded extensions, so the
|
|
22
|
-
// fallback hook in `installLegacyPiSpecifierShim()` cannot rescue them.
|
|
16
|
+
// embedded entries. Bun.plugin `onResolve` also no longer fires for transitive
|
|
17
|
+
// imports inside runtime-loaded extensions.
|
|
23
18
|
//
|
|
24
|
-
//
|
|
25
|
-
//
|
|
26
|
-
//
|
|
27
|
-
//
|
|
28
|
-
//
|
|
29
|
-
//
|
|
30
|
-
//
|
|
31
|
-
// through Bun's standard URL loader).
|
|
32
|
-
//
|
|
33
|
-
// The registry lives in a sibling file (`legacy-pi-bundled-registry.ts`)
|
|
34
|
-
// loaded via a conditional dynamic import: its transitive deps include the
|
|
35
|
-
// coding-agent root which pulls in generated artifacts (e.g.
|
|
36
|
-
// `export/html/tool-views.generated.js`) that only exist after a build, so a
|
|
37
|
-
// static import would crash every dev/test run that touches
|
|
38
|
-
// `legacy-pi-compat.ts`. This is the documented "conditional platform code"
|
|
39
|
-
// exception to the static-import rule.
|
|
19
|
+
// Compiled builds therefore keep live JS-heap references to the host packages
|
|
20
|
+
// and serve requested surfaces through `omp-legacy-pi-bundled:<key>` synthetic
|
|
21
|
+
// modules. `scripts/legacy-pi-virtual-module.ts` derives the static import edges
|
|
22
|
+
// from current package exports inside a Bun build plugin: no generated source
|
|
23
|
+
// or duplicate key list exists on disk. Runtime extension loading stays lazy —
|
|
24
|
+
// the virtual module is evaluated only when an extension requests a host
|
|
25
|
+
// package — but the compiler still sees every possible edge at build time.
|
|
40
26
|
const BUNDLED_VIRTUAL_SCHEME = "omp-legacy-pi-bundled:";
|
|
41
27
|
const BUNDLED_VIRTUAL_NAMESPACE = "omp-legacy-pi-bundled";
|
|
42
|
-
const
|
|
43
|
-
const
|
|
28
|
+
const BUNDLED_MODULES_GLOBAL = "__ompLegacyPiBundledModules";
|
|
29
|
+
const TYPEBOX_BUNDLED_MODULE_KEY = "typebox";
|
|
44
30
|
|
|
45
|
-
type
|
|
31
|
+
type BundledModules = Readonly<Record<string, Readonly<Record<string, unknown>>>>;
|
|
46
32
|
|
|
47
|
-
let
|
|
33
|
+
let bundledModulesPromise: Promise<BundledModules> | null = null;
|
|
48
34
|
|
|
49
35
|
/**
|
|
50
|
-
* Lazy-load the
|
|
51
|
-
*
|
|
52
|
-
*
|
|
53
|
-
* `globalThis` is the bridge, not laziness: each synthesized
|
|
54
|
-
* `omp-legacy-pi-bundled:<key>` module is a *separate* ES module Bun compiles
|
|
55
|
-
* from a source string, so it cannot close over the registry in this file's
|
|
56
|
-
* lexical scope and the live (non-serializable) function/object exports cannot
|
|
57
|
-
* be inlined — the only runtime channel back to the host objects is a global.
|
|
36
|
+
* Lazy-load the build-supplied host modules and stash them on `globalThis` for
|
|
37
|
+
* the synthetic module source emitted by `synthesizeBundledModuleSource`.
|
|
58
38
|
*
|
|
59
|
-
*
|
|
60
|
-
*
|
|
61
|
-
*
|
|
39
|
+
* `globalThis` is the bridge: each `omp-legacy-pi-bundled:<key>` source string
|
|
40
|
+
* becomes a separate ES module and cannot close over this file's lexical scope.
|
|
41
|
+
* The dynamic import is intentional conditional build code. Dev/test runs
|
|
42
|
+
* never execute it; binary builds resolve the literal through the in-memory
|
|
43
|
+
* plugin in `scripts/legacy-pi-virtual-module.ts`.
|
|
62
44
|
*/
|
|
63
|
-
function
|
|
45
|
+
function ensureBundledModulesLoaded(): Promise<BundledModules> {
|
|
64
46
|
if (!IS_COMPILED_BINARY) {
|
|
65
|
-
return Promise.reject(
|
|
66
|
-
new Error("omp:legacy-pi-shim: bundled registry is only available in compiled-binary mode"),
|
|
67
|
-
);
|
|
47
|
+
return Promise.reject(new Error("omp:legacy-pi-shim: bundled modules are only available in compiled mode"));
|
|
68
48
|
}
|
|
69
|
-
if (!
|
|
70
|
-
|
|
71
|
-
(globalThis
|
|
72
|
-
return
|
|
49
|
+
if (!bundledModulesPromise) {
|
|
50
|
+
bundledModulesPromise = import("omp-legacy-pi-modules").then(module => {
|
|
51
|
+
Reflect.set(globalThis, BUNDLED_MODULES_GLOBAL, module.BUNDLED_PI_MODULES);
|
|
52
|
+
return module.BUNDLED_PI_MODULES;
|
|
73
53
|
});
|
|
74
54
|
}
|
|
75
|
-
return
|
|
55
|
+
return bundledModulesPromise;
|
|
76
56
|
}
|
|
77
57
|
|
|
78
|
-
function
|
|
79
|
-
return `${BUNDLED_VIRTUAL_SCHEME}${
|
|
58
|
+
function bundledModuleVirtualSpecifier(moduleKey: string): string {
|
|
59
|
+
return `${BUNDLED_VIRTUAL_SCHEME}${moduleKey}`;
|
|
80
60
|
}
|
|
81
61
|
|
|
82
62
|
function isBundledVirtualSpecifier(value: string): boolean {
|
|
@@ -84,19 +64,16 @@ function isBundledVirtualSpecifier(value: string): boolean {
|
|
|
84
64
|
}
|
|
85
65
|
|
|
86
66
|
/**
|
|
87
|
-
* Build
|
|
88
|
-
*
|
|
89
|
-
* and emits a string of ES exports rooted in `globalThis[BUNDLED_REGISTRY_GLOBAL]`.
|
|
90
|
-
* `synthesizeBundledModuleSource` wraps this with the lazy registry load —
|
|
91
|
-
* tests use this sync helper directly to assert export-shape preservation.
|
|
67
|
+
* Build a synthetic ES module for one live bundled namespace. Every export
|
|
68
|
+
* reads through the global bridge; no bunfs path or copied package is involved.
|
|
92
69
|
*/
|
|
93
|
-
function
|
|
94
|
-
const mod =
|
|
70
|
+
function synthesizeBundledModuleSourceFromModules(moduleKey: string, modules: BundledModules): string {
|
|
71
|
+
const mod = modules[moduleKey];
|
|
95
72
|
if (!mod) {
|
|
96
|
-
throw new Error(`omp:legacy-pi-shim: no bundled module registered for ${
|
|
73
|
+
throw new Error(`omp:legacy-pi-shim: no bundled module registered for ${moduleKey}`);
|
|
97
74
|
}
|
|
98
75
|
const lines: string[] = [
|
|
99
|
-
`const __omp_bundled = globalThis[${JSON.stringify(
|
|
76
|
+
`const __omp_bundled = globalThis[${JSON.stringify(BUNDLED_MODULES_GLOBAL)}][${JSON.stringify(moduleKey)}];`,
|
|
100
77
|
];
|
|
101
78
|
let hasDefault = false;
|
|
102
79
|
for (const exportName in mod) {
|
|
@@ -114,36 +91,25 @@ function synthesizeBundledModuleSourceFromRegistry(registryKey: string, registry
|
|
|
114
91
|
}
|
|
115
92
|
|
|
116
93
|
/**
|
|
117
|
-
* Build the synthetic
|
|
118
|
-
* `omp-legacy-pi-bundled:<key>` import.
|
|
119
|
-
* so legacy extensions see the same named/default exports they would have
|
|
120
|
-
* gotten from a real `file://` load — without touching the inaccessible bunfs
|
|
121
|
-
* filesystem.
|
|
94
|
+
* Build the synthetic source served for one
|
|
95
|
+
* `omp-legacy-pi-bundled:<key>` import.
|
|
122
96
|
*/
|
|
123
|
-
async function synthesizeBundledModuleSource(
|
|
124
|
-
const
|
|
125
|
-
return
|
|
97
|
+
async function synthesizeBundledModuleSource(moduleKey: string): Promise<string> {
|
|
98
|
+
const modules = await ensureBundledModulesLoaded();
|
|
99
|
+
return synthesizeBundledModuleSourceFromModules(moduleKey, modules);
|
|
126
100
|
}
|
|
127
101
|
|
|
128
|
-
/**
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
*/
|
|
133
|
-
export function __synthesizeLegacyPiBundledSourceWithRegistry(
|
|
134
|
-
registryKey: string,
|
|
135
|
-
registry: Readonly<Record<string, Readonly<Record<string, unknown>>>>,
|
|
102
|
+
/** Test seam for the virtual module's named/default export forwarding. */
|
|
103
|
+
export function __synthesizeLegacyPiBundledSourceWithModules(
|
|
104
|
+
moduleKey: string,
|
|
105
|
+
modules: Readonly<Record<string, Readonly<Record<string, unknown>>>>,
|
|
136
106
|
): string {
|
|
137
|
-
return
|
|
107
|
+
return synthesizeBundledModuleSourceFromModules(moduleKey, modules);
|
|
138
108
|
}
|
|
139
109
|
|
|
140
|
-
/**
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
* function writes to, so a rename can't break extension loads silently.
|
|
144
|
-
*/
|
|
145
|
-
export function __getLegacyPiBundledRegistryGlobal(): string {
|
|
146
|
-
return BUNDLED_REGISTRY_GLOBAL;
|
|
110
|
+
/** Test seam for the global bridge key shared with synthetic module source. */
|
|
111
|
+
export function __getLegacyPiBundledModulesGlobal(): string {
|
|
112
|
+
return BUNDLED_MODULES_GLOBAL;
|
|
147
113
|
}
|
|
148
114
|
|
|
149
115
|
// Canonical scope for in-process pi packages. Plugins published against any of
|
|
@@ -207,6 +173,9 @@ const nodePackageRootCache = new Map<string, Promise<string | null>>();
|
|
|
207
173
|
const packageManifestCache = new Map<string, Promise<Record<string, unknown> | null>>();
|
|
208
174
|
const bareDependencyResolutionCache = new Map<string, Promise<string | null>>();
|
|
209
175
|
const realpathCache = new Map<string, Promise<string>>();
|
|
176
|
+
const nativeAddonResolutionCache = new Map<string, Promise<string | null>>();
|
|
177
|
+
const nativeAddonRequireScanCache = new Map<string, Promise<boolean>>();
|
|
178
|
+
const nativeAddonLoaderModulePaths = new Set<string>();
|
|
210
179
|
|
|
211
180
|
function clearLegacyPiResolutionCaches(): void {
|
|
212
181
|
resolvedSpecifierFallbacks.clear();
|
|
@@ -215,6 +184,9 @@ function clearLegacyPiResolutionCaches(): void {
|
|
|
215
184
|
nodePackageRootCache.clear();
|
|
216
185
|
packageManifestCache.clear();
|
|
217
186
|
bareDependencyResolutionCache.clear();
|
|
187
|
+
nativeAddonResolutionCache.clear();
|
|
188
|
+
nativeAddonRequireScanCache.clear();
|
|
189
|
+
nativeAddonLoaderModulePaths.clear();
|
|
218
190
|
realpathCache.clear();
|
|
219
191
|
}
|
|
220
192
|
|
|
@@ -232,7 +204,7 @@ const TYPEBOX_SPECIFIER_FILTER = /^(?:@sinclair\/typebox|typebox)$/;
|
|
|
232
204
|
|
|
233
205
|
// Compat-shim path resolution. In compiled-binary mode every bundled surface
|
|
234
206
|
// is served through the `omp-legacy-pi-bundled:` virtual namespace (see the
|
|
235
|
-
//
|
|
207
|
+
// bundled-module block above) — bunfs paths are unreachable on Bun 1.3.14+, so the
|
|
236
208
|
// pre-#3423 helpers that derived `/$bunfs/root/...` paths from
|
|
237
209
|
// `import.meta.dir` are gone. Dev / source-link / installed-package modes
|
|
238
210
|
// still need a real filesystem path for the source shims, which
|
|
@@ -298,7 +270,7 @@ export function __resolveTypeBoxShimPath(
|
|
|
298
270
|
pathExistsSync: (p: string) => boolean = fs.existsSync,
|
|
299
271
|
): string | null {
|
|
300
272
|
if (isCompiled) {
|
|
301
|
-
return
|
|
273
|
+
return bundledModuleVirtualSpecifier(TYPEBOX_BUNDLED_MODULE_KEY);
|
|
302
274
|
}
|
|
303
275
|
return pathExistsSync(sourcePath) ? sourcePath : null;
|
|
304
276
|
}
|
|
@@ -314,18 +286,18 @@ const TYPEBOX_SHIM_PATH = __resolveTypeBoxShimPath(IS_COMPILED_BINARY, sourceShi
|
|
|
314
286
|
// imports such as `@oh-my-pi/pi-ai/oauth` continue to resolve directly
|
|
315
287
|
// against the bundled pi-ai package.
|
|
316
288
|
const LEGACY_PI_AI_SHIM_PATH = IS_COMPILED_BINARY
|
|
317
|
-
?
|
|
289
|
+
? bundledModuleVirtualSpecifier(`${CANONICAL_PI_SCOPE}/pi-ai`)
|
|
318
290
|
: sourceShimPath("legacy-pi-ai-shim.ts");
|
|
319
291
|
|
|
320
292
|
// The coding-agent's own `./src/index.ts` cannot be listed as an extra
|
|
321
293
|
// `bun --compile` entrypoint alongside the CLI entry without breaking binary
|
|
322
294
|
// startup (issue #1474 follow-up). In compiled-binary mode the legacy
|
|
323
295
|
// `@(scope)/pi-coding-agent` root therefore resolves through the bundled
|
|
324
|
-
//
|
|
325
|
-
//
|
|
296
|
+
// module shim; in dev / source-link / installed-package mode it points at the
|
|
297
|
+
// sibling source shim whose distinct file path avoids the #1474 collision
|
|
326
298
|
// while still re-exporting the canonical package surface.
|
|
327
299
|
const LEGACY_PI_CODING_AGENT_SHIM_PATH = IS_COMPILED_BINARY
|
|
328
|
-
?
|
|
300
|
+
? bundledModuleVirtualSpecifier(`${CANONICAL_PI_SCOPE}/pi-coding-agent`)
|
|
329
301
|
: sourceShimPath("legacy-pi-coding-agent-shim.ts");
|
|
330
302
|
|
|
331
303
|
// Package-root overrides. Shim entries (`pi-ai`, `pi-coding-agent`) always
|
|
@@ -346,8 +318,8 @@ const LEGACY_PI_CODING_AGENT_SHIM_PATH = IS_COMPILED_BINARY
|
|
|
346
318
|
/**
|
|
347
319
|
* Drop overrides whose filesystem targets are missing so they can fall
|
|
348
320
|
* through to the canonical-resolution path. Virtual `omp-legacy-pi-bundled:`
|
|
349
|
-
* entries always pass —
|
|
350
|
-
* compiled
|
|
321
|
+
* entries always pass — live bundled module references are the source of truth
|
|
322
|
+
* in compiled mode where bunfs paths are unreachable (issue #3423).
|
|
351
323
|
*
|
|
352
324
|
* `pathExistsSync` defaults to `fs.existsSync`; tests inject a stub to
|
|
353
325
|
* simulate the missing-entrypoint failure mode without touching the real FS.
|
|
@@ -356,41 +328,59 @@ export function __validateLegacyPiPackageRootOverrides(
|
|
|
356
328
|
candidates: Record<string, string>,
|
|
357
329
|
pathExistsSync: (p: string) => boolean = fs.existsSync,
|
|
358
330
|
): Record<string, string> {
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
)
|
|
363
|
-
|
|
331
|
+
const valid: Record<string, string> = {};
|
|
332
|
+
for (const key in candidates) {
|
|
333
|
+
const candidate = candidates[key];
|
|
334
|
+
if (candidate && (isBundledVirtualSpecifier(candidate) || pathExistsSync(candidate))) {
|
|
335
|
+
valid[key] = candidate;
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
return valid;
|
|
364
339
|
}
|
|
365
340
|
|
|
366
341
|
/**
|
|
367
342
|
* Compute the override map keyed by every canonical specifier the host serves
|
|
368
343
|
* directly: the pi-ai / pi-coding-agent roots (compat shims that re-attach
|
|
369
|
-
* legacy helpers) plus, in compiled
|
|
370
|
-
*
|
|
371
|
-
*
|
|
372
|
-
* `@(scope)/pi-ai/oauth` and friends from falling through to the extension's
|
|
373
|
-
* own — possibly absent — peer install when bunfs filesystem walks fail
|
|
374
|
-
* (issue #3442 follow-up to #3423). Exported as a test seam so the
|
|
375
|
-
* compiled-binary branch is verifiable from dev tests.
|
|
344
|
+
* legacy helpers) plus, in compiled mode, every build-supplied module key.
|
|
345
|
+
* Subpath coverage stops `@(scope)/pi-ai/oauth` and friends from falling
|
|
346
|
+
* through to the extension's absent peer install when bunfs walks fail.
|
|
376
347
|
*/
|
|
377
|
-
export function __buildLegacyPiPackageRootOverrides(
|
|
348
|
+
export function __buildLegacyPiPackageRootOverrides(
|
|
349
|
+
isCompiled: boolean,
|
|
350
|
+
bundledModuleKeys: Iterable<string> = [],
|
|
351
|
+
): Record<string, string> {
|
|
378
352
|
const candidates: Record<string, string> = {
|
|
379
353
|
[`${CANONICAL_PI_SCOPE}/pi-ai`]: LEGACY_PI_AI_SHIM_PATH,
|
|
380
354
|
[`${CANONICAL_PI_SCOPE}/pi-coding-agent`]: LEGACY_PI_CODING_AGENT_SHIM_PATH,
|
|
381
355
|
};
|
|
382
356
|
if (isCompiled) {
|
|
383
|
-
for (const key of
|
|
384
|
-
// Shim-bearing roots
|
|
385
|
-
//
|
|
386
|
-
if (key in candidates || key ===
|
|
387
|
-
candidates[key] =
|
|
357
|
+
for (const key of bundledModuleKeys) {
|
|
358
|
+
// Shim-bearing roots already map to their compat surfaces; TypeBox
|
|
359
|
+
// has a dedicated TYPEBOX_SHIM_PATH route.
|
|
360
|
+
if (key in candidates || key === TYPEBOX_BUNDLED_MODULE_KEY) continue;
|
|
361
|
+
candidates[key] = bundledModuleVirtualSpecifier(key);
|
|
388
362
|
}
|
|
389
363
|
}
|
|
390
364
|
return __validateLegacyPiPackageRootOverrides(candidates);
|
|
391
365
|
}
|
|
392
366
|
|
|
393
|
-
|
|
367
|
+
// Seeded with compat roots at module init; first compiled extension load adds
|
|
368
|
+
// every key supplied by the in-memory build module.
|
|
369
|
+
let legacyPiPackageRootOverrides = __buildLegacyPiPackageRootOverrides(IS_COMPILED_BINARY);
|
|
370
|
+
let legacyPiOverridesReadyPromise: Promise<void> | null = null;
|
|
371
|
+
|
|
372
|
+
/** Complete compiled-mode overrides once from the lazily evaluated host modules. */
|
|
373
|
+
function ensureLegacyPiOverridesReady(): Promise<void> {
|
|
374
|
+
if (!IS_COMPILED_BINARY) {
|
|
375
|
+
return Promise.resolve();
|
|
376
|
+
}
|
|
377
|
+
if (!legacyPiOverridesReadyPromise) {
|
|
378
|
+
legacyPiOverridesReadyPromise = ensureBundledModulesLoaded().then(modules => {
|
|
379
|
+
legacyPiPackageRootOverrides = __buildLegacyPiPackageRootOverrides(true, Object.keys(modules));
|
|
380
|
+
});
|
|
381
|
+
}
|
|
382
|
+
return legacyPiOverridesReadyPromise;
|
|
383
|
+
}
|
|
394
384
|
|
|
395
385
|
let isLegacyPiSpecifierShimInstalled = false;
|
|
396
386
|
|
|
@@ -428,7 +418,7 @@ function getResolvedSpecifier(specifier: string): string {
|
|
|
428
418
|
* specifiers.
|
|
429
419
|
*/
|
|
430
420
|
function resolveCanonicalPiSpecifier(remappedSpecifier: string): string {
|
|
431
|
-
const override =
|
|
421
|
+
const override = legacyPiPackageRootOverrides[remappedSpecifier];
|
|
432
422
|
if (override) {
|
|
433
423
|
return override;
|
|
434
424
|
}
|
|
@@ -438,7 +428,7 @@ function resolveCanonicalPiSpecifier(remappedSpecifier: string): string {
|
|
|
438
428
|
function toImportSpecifier(resolvedPath: string): string {
|
|
439
429
|
// Virtual `omp-legacy-pi-bundled:` specifiers are served by the synthetic
|
|
440
430
|
// onLoad in `installLegacyPiSpecifierShim()`; wrapping them as `file://`
|
|
441
|
-
// would corrupt the scheme
|
|
431
|
+
// would corrupt the scheme.
|
|
442
432
|
if (isBundledVirtualSpecifier(resolvedPath)) {
|
|
443
433
|
return resolvedPath;
|
|
444
434
|
}
|
|
@@ -492,6 +482,9 @@ async function rewriteLegacyExtensionSource(
|
|
|
492
482
|
importerPath: string,
|
|
493
483
|
mtimeTag: string | null = null,
|
|
494
484
|
): Promise<string> {
|
|
485
|
+
// Compiled mode completes the override map from the build-supplied module
|
|
486
|
+
// keys on first use; every rewrite path must see the full map.
|
|
487
|
+
await ensureLegacyPiOverridesReady();
|
|
495
488
|
const withPi = rewriteLegacyPiImports(source);
|
|
496
489
|
// When the TypeBox shim is missing (release build dropped the entrypoint —
|
|
497
490
|
// issue #3414), leave bare specifiers untouched so Bun resolves a real
|
|
@@ -506,10 +499,11 @@ async function rewriteLegacyExtensionSource(
|
|
|
506
499
|
: withPi;
|
|
507
500
|
const withPkg = await rewriteExtensionPackageImports(withTypeBox, importerPath, mtimeTag);
|
|
508
501
|
const withBare = await rewriteExtensionBareImports(withPkg, importerPath, mtimeTag);
|
|
502
|
+
const withNativeAddons = await rewriteExtensionNativeAddonRequires(withBare, importerPath);
|
|
509
503
|
if (!mtimeTag) {
|
|
510
|
-
return
|
|
504
|
+
return withNativeAddons;
|
|
511
505
|
}
|
|
512
|
-
return
|
|
506
|
+
return withNativeAddons.replace(
|
|
513
507
|
RELATIVE_GRAPH_IMPORT_SPECIFIER_REGEX,
|
|
514
508
|
(_match, prefix: string, specifier: string, suffix: string) => `${prefix}${specifier}?mtime=${mtimeTag}${suffix}`,
|
|
515
509
|
);
|
|
@@ -950,6 +944,118 @@ async function resolveExtensionBareDependencyUncached(specifier: string, importe
|
|
|
950
944
|
return resolveNodePackageDependency(specifier, importerPath);
|
|
951
945
|
}
|
|
952
946
|
|
|
947
|
+
const NATIVE_ADDON_EXTENSION = ".node";
|
|
948
|
+
|
|
949
|
+
// Match CommonJS require calls so bare native-addon specifiers can be pinned
|
|
950
|
+
// to absolute paths. Only requires whose resolution lands on a `.node` addon
|
|
951
|
+
// are rewritten; everything else stays on Bun's native resolver.
|
|
952
|
+
const NATIVE_ADDON_REQUIRE_SPECIFIER_REGEX = /(\brequire\s*\(\s*["'])([^"'()\s]+)(["']\s*\))/g;
|
|
953
|
+
|
|
954
|
+
/**
|
|
955
|
+
* Resolve a bare specifier whose target is a native `.node` addon — either a
|
|
956
|
+
* package subpath ending in `.node`, or a package whose `main` points at an
|
|
957
|
+
* addon (the napi-rs per-platform package convention, e.g.
|
|
958
|
+
* `@yuuang/ffi-rs-darwin-arm64` → `ffi-rs.darwin-arm64.node`). Returns the
|
|
959
|
+
* addon's absolute realpath, or null when the specifier is not a native addon.
|
|
960
|
+
*/
|
|
961
|
+
async function resolveExtensionNativeAddon(specifier: string, importerPath: string): Promise<string | null> {
|
|
962
|
+
if (!isBareExtensionDependencySpecifier(specifier)) {
|
|
963
|
+
return null;
|
|
964
|
+
}
|
|
965
|
+
|
|
966
|
+
const cacheKey = `${specifier}\0${path.resolve(path.dirname(importerPath))}`;
|
|
967
|
+
const cached = nativeAddonResolutionCache.get(cacheKey);
|
|
968
|
+
if (cached) return cached;
|
|
969
|
+
|
|
970
|
+
const promise = resolveExtensionNativeAddonUncached(specifier, importerPath);
|
|
971
|
+
nativeAddonResolutionCache.set(cacheKey, promise);
|
|
972
|
+
return promise;
|
|
973
|
+
}
|
|
974
|
+
|
|
975
|
+
async function resolveExtensionNativeAddonUncached(specifier: string, importerPath: string): Promise<string | null> {
|
|
976
|
+
const parsed = splitBarePackageSpecifier(specifier);
|
|
977
|
+
if (!parsed) return null;
|
|
978
|
+
const packageRoot = await findNodePackageRoot(parsed.name, importerPath);
|
|
979
|
+
if (!packageRoot) return null;
|
|
980
|
+
|
|
981
|
+
let target: string | null = null;
|
|
982
|
+
if (parsed.subpath !== null) {
|
|
983
|
+
target = parsed.subpath.endsWith(NATIVE_ADDON_EXTENSION) ? path.join(packageRoot, parsed.subpath) : null;
|
|
984
|
+
} else {
|
|
985
|
+
const manifest = await readPackageManifest(packageRoot);
|
|
986
|
+
const main = manifest?.main;
|
|
987
|
+
target =
|
|
988
|
+
typeof main === "string" && main.endsWith(NATIVE_ADDON_EXTENSION) ? path.resolve(packageRoot, main) : null;
|
|
989
|
+
}
|
|
990
|
+
if (!target || !(await pathExists(target))) {
|
|
991
|
+
return null;
|
|
992
|
+
}
|
|
993
|
+
return realpathOrSelf(target);
|
|
994
|
+
}
|
|
995
|
+
|
|
996
|
+
/**
|
|
997
|
+
* Rewrite bare `require()` specifiers that resolve to native `.node` addons
|
|
998
|
+
* into absolute-path requires. In `bun build --compile` binaries, Bun's bare
|
|
999
|
+
* resolution fails for packages whose `main` is a `.node` addon ("Cannot find
|
|
1000
|
+
* module '@scope/pkg-<platform>'") even when the package sits in the
|
|
1001
|
+
* extension's own node_modules; requiring the addon by absolute path works.
|
|
1002
|
+
*/
|
|
1003
|
+
async function rewriteExtensionNativeAddonRequires(source: string, importerPath: string): Promise<string> {
|
|
1004
|
+
let rewritten = "";
|
|
1005
|
+
let lastIndex = 0;
|
|
1006
|
+
for (const match of source.matchAll(NATIVE_ADDON_REQUIRE_SPECIFIER_REGEX)) {
|
|
1007
|
+
const matchIndex = match.index;
|
|
1008
|
+
if (matchIndex === undefined) continue;
|
|
1009
|
+
|
|
1010
|
+
const [fullMatch, prefix, specifier, suffix] = match;
|
|
1011
|
+
if (!prefix || !specifier || !suffix) continue;
|
|
1012
|
+
|
|
1013
|
+
const resolved = await resolveExtensionNativeAddon(specifier, importerPath);
|
|
1014
|
+
if (!resolved) continue;
|
|
1015
|
+
|
|
1016
|
+
rewritten += source.slice(lastIndex, matchIndex);
|
|
1017
|
+
// Forward slashes keep Windows paths valid inside single- or double-quoted literals.
|
|
1018
|
+
rewritten += `${prefix}${stripWindowsExtendedLengthPathPrefix(resolved).replaceAll("\\", "/")}${suffix}`;
|
|
1019
|
+
lastIndex = matchIndex + fullMatch.length;
|
|
1020
|
+
}
|
|
1021
|
+
|
|
1022
|
+
if (lastIndex === 0) {
|
|
1023
|
+
return source;
|
|
1024
|
+
}
|
|
1025
|
+
return `${rewritten}${source.slice(lastIndex)}`;
|
|
1026
|
+
}
|
|
1027
|
+
|
|
1028
|
+
/**
|
|
1029
|
+
* Whether a module's source contains a bare require that resolves to a native
|
|
1030
|
+
* `.node` addon — i.e. a napi-rs style loader that must be hooked into the
|
|
1031
|
+
* extension graph so {@link rewriteExtensionNativeAddonRequires} can pin its
|
|
1032
|
+
* platform-package requires to absolute paths.
|
|
1033
|
+
*/
|
|
1034
|
+
async function moduleRequiresNativeAddon(modulePath: string): Promise<boolean> {
|
|
1035
|
+
const cached = nativeAddonRequireScanCache.get(modulePath);
|
|
1036
|
+
if (cached) return cached;
|
|
1037
|
+
|
|
1038
|
+
const promise = moduleRequiresNativeAddonUncached(modulePath);
|
|
1039
|
+
nativeAddonRequireScanCache.set(modulePath, promise);
|
|
1040
|
+
return promise;
|
|
1041
|
+
}
|
|
1042
|
+
|
|
1043
|
+
async function moduleRequiresNativeAddonUncached(modulePath: string): Promise<boolean> {
|
|
1044
|
+
let source: string;
|
|
1045
|
+
try {
|
|
1046
|
+
source = await Bun.file(modulePath).text();
|
|
1047
|
+
} catch {
|
|
1048
|
+
return false;
|
|
1049
|
+
}
|
|
1050
|
+
for (const match of source.matchAll(NATIVE_ADDON_REQUIRE_SPECIFIER_REGEX)) {
|
|
1051
|
+
const specifier = match[2];
|
|
1052
|
+
if (specifier && (await resolveExtensionNativeAddon(specifier, modulePath))) {
|
|
1053
|
+
return true;
|
|
1054
|
+
}
|
|
1055
|
+
}
|
|
1056
|
+
return false;
|
|
1057
|
+
}
|
|
1058
|
+
|
|
953
1059
|
async function rewriteExtensionBareImports(
|
|
954
1060
|
source: string,
|
|
955
1061
|
importerPath: string,
|
|
@@ -986,7 +1092,9 @@ function escapeRegExp(value: string): string {
|
|
|
986
1092
|
// `imports` aliases such as `#src/*`, and extension-local bare dependency
|
|
987
1093
|
// entries. Bare imports inside node_modules dependencies remain native Bun
|
|
988
1094
|
// resolutions; once the dependency entry is hooked, its relative children are
|
|
989
|
-
// still collected and rewritten with the reload mtime tag.
|
|
1095
|
+
// still collected and rewritten with the reload mtime tag. `require()` calls
|
|
1096
|
+
// are scanned too so CJS entries and napi-rs loaders reached without an
|
|
1097
|
+
// import statement still join the graph.
|
|
990
1098
|
const EXTENSION_GRAPH_SPECIFIER_REGEX = /((?:from\s+|import\s+|import\s*\(\s*)["'])([^"'()\s]+)(["'])/g;
|
|
991
1099
|
|
|
992
1100
|
// Extension source realpaths already covered by an installed load-time hook for
|
|
@@ -1027,6 +1135,10 @@ async function realpathOrSelfUncached(p: string): Promise<string> {
|
|
|
1027
1135
|
* Extension-local bare dependency entries are also included so their relative
|
|
1028
1136
|
* children receive the reload mtime tag; bare imports inside those dependencies
|
|
1029
1137
|
* remain native Bun resolutions to avoid taking over full third-party graphs.
|
|
1138
|
+
* CommonJS dependency entries stay native too, with one exception: napi-rs
|
|
1139
|
+
* style loaders whose bare requires resolve to `.node` addons are hooked so
|
|
1140
|
+
* their requires can be pinned to absolute paths (unresolvable by bare
|
|
1141
|
+
* specifier inside `bun build --compile` binaries).
|
|
1030
1142
|
*/
|
|
1031
1143
|
async function collectExtensionModules(entryRealPath: string): Promise<Map<string, string>> {
|
|
1032
1144
|
const modules = new Map<string, string>();
|
|
@@ -1047,14 +1159,24 @@ async function collectExtensionModules(entryRealPath: string): Promise<Map<strin
|
|
|
1047
1159
|
let source: string;
|
|
1048
1160
|
try {
|
|
1049
1161
|
source = await Bun.file(file).text();
|
|
1162
|
+
if (nativeAddonLoaderModulePaths.has(file)) {
|
|
1163
|
+
// CJS requires cannot await an async onLoad hook. Resolve and
|
|
1164
|
+
// rewrite native-addon paths before installing its sync hook.
|
|
1165
|
+
source = await rewriteExtensionNativeAddonRequires(source, file);
|
|
1166
|
+
}
|
|
1050
1167
|
} catch {
|
|
1051
1168
|
continue;
|
|
1052
1169
|
}
|
|
1053
1170
|
modules.set(file, source);
|
|
1054
1171
|
const dir = path.dirname(file);
|
|
1172
|
+
const specifiers = new Set<string>();
|
|
1055
1173
|
for (const match of source.matchAll(EXTENSION_GRAPH_SPECIFIER_REGEX)) {
|
|
1056
|
-
|
|
1057
|
-
|
|
1174
|
+
if (match[2]) specifiers.add(match[2]);
|
|
1175
|
+
}
|
|
1176
|
+
for (const match of source.matchAll(NATIVE_ADDON_REQUIRE_SPECIFIER_REGEX)) {
|
|
1177
|
+
if (match[2]) specifiers.add(match[2]);
|
|
1178
|
+
}
|
|
1179
|
+
for (const specifier of specifiers) {
|
|
1058
1180
|
try {
|
|
1059
1181
|
let resolved: string | null = null;
|
|
1060
1182
|
let nextFollowsBareDependencies = followBareDependencies;
|
|
@@ -1079,10 +1201,17 @@ async function collectExtensionModules(entryRealPath: string): Promise<Map<strin
|
|
|
1079
1201
|
dependencyExtension === ".cjs" ||
|
|
1080
1202
|
dependencyExtension === ".cts" ||
|
|
1081
1203
|
((dependencyExtension === ".js" || dependencyExtension === ".jsx") && manifest?.type !== "module");
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1204
|
+
const isHookableEntry = Boolean(dependencyEntry && hasSourceModuleExtension(dependencyEntry));
|
|
1205
|
+
const hookCommonJsEntry =
|
|
1206
|
+
isHookableEntry && isCommonJsEntry && dependencyEntry
|
|
1207
|
+
? await moduleRequiresNativeAddon(dependencyEntry)
|
|
1208
|
+
: false;
|
|
1209
|
+
if (isHookableEntry && dependencyEntry && (!isCommonJsEntry || hookCommonJsEntry)) {
|
|
1210
|
+
resolved = await realpathOrSelf(dependencyEntry);
|
|
1211
|
+
if (hookCommonJsEntry) {
|
|
1212
|
+
nativeAddonLoaderModulePaths.add(resolved);
|
|
1213
|
+
}
|
|
1214
|
+
}
|
|
1086
1215
|
nextFollowsBareDependencies = false;
|
|
1087
1216
|
}
|
|
1088
1217
|
if (resolved && !modules.has(resolved)) {
|
|
@@ -1100,39 +1229,71 @@ async function collectExtensionModules(entryRealPath: string): Promise<Map<strin
|
|
|
1100
1229
|
}
|
|
1101
1230
|
|
|
1102
1231
|
/**
|
|
1103
|
-
* Install
|
|
1104
|
-
*
|
|
1105
|
-
*
|
|
1106
|
-
*
|
|
1107
|
-
* discovered modules instead of widening an existing filter to unrelated files.
|
|
1232
|
+
* Install exact-path load hooks for the current extension graph. ESM/TS source
|
|
1233
|
+
* retains the async rewrite path. Native-addon CJS loaders use a synchronous
|
|
1234
|
+
* hook with source pre-rewritten during graph collection; Bun rejects a CJS
|
|
1235
|
+
* `require()` whose onLoad callback returns a promise.
|
|
1108
1236
|
*/
|
|
1109
|
-
function installExtensionGraphHook(
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1237
|
+
function installExtensionGraphHook(
|
|
1238
|
+
entryRealPath: string,
|
|
1239
|
+
modules: Map<string, string>,
|
|
1240
|
+
): { asyncModules: Map<string, string>; syncCommonJsModules: Map<string, string> } {
|
|
1241
|
+
const asyncModules = new Map<string, string>();
|
|
1242
|
+
const syncCommonJsModules = new Map<string, string>();
|
|
1243
|
+
for (const [modulePath, source] of modules) {
|
|
1244
|
+
const destination = nativeAddonLoaderModulePaths.has(modulePath) ? syncCommonJsModules : asyncModules;
|
|
1245
|
+
destination.set(modulePath, source);
|
|
1246
|
+
}
|
|
1247
|
+
|
|
1248
|
+
if (asyncModules.size > 0) {
|
|
1249
|
+
const alternation = [...asyncModules.keys()].map(escapeRegExp).join("|");
|
|
1250
|
+
const filter = new RegExp(`^(?:${alternation})(?:\\?mtime=\\d+)?$`);
|
|
1251
|
+
const hookId = Bun.hash(`${entryRealPath}\0async\0${[...asyncModules.keys()].join("\0")}`).toString(36);
|
|
1252
|
+
Bun.plugin({
|
|
1253
|
+
name: `omp:legacy-pi-ext:${hookId}`,
|
|
1254
|
+
setup(build) {
|
|
1255
|
+
build.onLoad({ filter, namespace: "file" }, async args => {
|
|
1256
|
+
const queryIndex = args.path.indexOf("?mtime=");
|
|
1257
|
+
const sourcePath = queryIndex >= 0 ? args.path.slice(0, queryIndex) : args.path;
|
|
1258
|
+
const mtimeTag = queryIndex >= 0 ? args.path.slice(queryIndex + "?mtime=".length) : null;
|
|
1259
|
+
const cached = asyncModules.get(sourcePath);
|
|
1260
|
+
let raw: string;
|
|
1261
|
+
if (cached !== undefined) {
|
|
1262
|
+
// consume-once: preserves ?mtime edit-pickup for re-imports
|
|
1263
|
+
asyncModules.delete(sourcePath);
|
|
1264
|
+
raw = cached;
|
|
1265
|
+
} else {
|
|
1266
|
+
raw = await Bun.file(sourcePath).text();
|
|
1267
|
+
}
|
|
1268
|
+
return {
|
|
1269
|
+
contents: await rewriteLegacyExtensionSource(raw, sourcePath, mtimeTag),
|
|
1270
|
+
loader: getLoader(sourcePath),
|
|
1271
|
+
};
|
|
1272
|
+
});
|
|
1273
|
+
},
|
|
1274
|
+
});
|
|
1275
|
+
}
|
|
1276
|
+
|
|
1277
|
+
if (syncCommonJsModules.size > 0) {
|
|
1278
|
+
const alternation = [...syncCommonJsModules.keys()].map(escapeRegExp).join("|");
|
|
1279
|
+
const filter = new RegExp(`^(?:${alternation})(?:\\?mtime=\\d+)?$`);
|
|
1280
|
+
const hookId = Bun.hash(`${entryRealPath}\0sync-cjs\0${[...syncCommonJsModules.keys()].join("\0")}`).toString(36);
|
|
1281
|
+
Bun.plugin({
|
|
1282
|
+
name: `omp:legacy-pi-ext:${hookId}`,
|
|
1283
|
+
setup(build) {
|
|
1284
|
+
build.onLoad({ filter, namespace: "file" }, args => {
|
|
1285
|
+
const queryIndex = args.path.indexOf("?mtime=");
|
|
1286
|
+
const sourcePath = queryIndex >= 0 ? args.path.slice(0, queryIndex) : args.path;
|
|
1287
|
+
const source = syncCommonJsModules.get(sourcePath);
|
|
1288
|
+
if (source === undefined) {
|
|
1289
|
+
throw new Error(`Missing pre-rewritten CommonJS extension source: ${sourcePath}`);
|
|
1290
|
+
}
|
|
1291
|
+
return { contents: source, loader: getLoader(sourcePath) };
|
|
1292
|
+
});
|
|
1293
|
+
},
|
|
1294
|
+
});
|
|
1295
|
+
}
|
|
1296
|
+
return { asyncModules, syncCommonJsModules };
|
|
1136
1297
|
}
|
|
1137
1298
|
|
|
1138
1299
|
/**
|
|
@@ -1140,10 +1301,10 @@ function installExtensionGraphHook(entryRealPath: string, modules: Map<string, s
|
|
|
1140
1301
|
* rewrite hook. The entry graph can grow across reloads, so each call collects
|
|
1141
1302
|
* the current graph and registers hooks for paths not covered by earlier loads.
|
|
1142
1303
|
*
|
|
1143
|
-
* Returns
|
|
1144
|
-
* the
|
|
1304
|
+
* Returns a clearable handle to drop cached sources that weren't consumed
|
|
1305
|
+
* during the initial load; `undefined` when no new modules were discovered.
|
|
1145
1306
|
*/
|
|
1146
|
-
async function ensureExtensionGraphHook(entryRealPath: string): Promise<
|
|
1307
|
+
async function ensureExtensionGraphHook(entryRealPath: string): Promise<{ clear(): void } | undefined> {
|
|
1147
1308
|
const currentModules = await collectExtensionModules(entryRealPath);
|
|
1148
1309
|
let hookedModules = extensionGraphHookModules.get(entryRealPath);
|
|
1149
1310
|
if (!hookedModules) {
|
|
@@ -1161,11 +1322,16 @@ async function ensureExtensionGraphHook(entryRealPath: string): Promise<Map<stri
|
|
|
1161
1322
|
return undefined;
|
|
1162
1323
|
}
|
|
1163
1324
|
|
|
1164
|
-
installExtensionGraphHook(entryRealPath, pendingModules);
|
|
1325
|
+
const { asyncModules, syncCommonJsModules } = installExtensionGraphHook(entryRealPath, pendingModules);
|
|
1165
1326
|
for (const modulePath of pendingModules.keys()) {
|
|
1166
1327
|
hookedModules.add(modulePath);
|
|
1167
1328
|
}
|
|
1168
|
-
return
|
|
1329
|
+
return {
|
|
1330
|
+
clear() {
|
|
1331
|
+
asyncModules.clear();
|
|
1332
|
+
syncCommonJsModules.clear();
|
|
1333
|
+
},
|
|
1334
|
+
};
|
|
1169
1335
|
}
|
|
1170
1336
|
|
|
1171
1337
|
/**
|
|
@@ -1184,6 +1350,7 @@ export async function loadLegacyPiModule(resolvedPath: string): Promise<unknown>
|
|
|
1184
1350
|
// `bun link`/pnpm installs) so the rewrite filter matches the path Bun
|
|
1185
1351
|
// actually hands the hook.
|
|
1186
1352
|
const entryRealPath = await realpathOrSelf(path.resolve(resolvedPath));
|
|
1353
|
+
await ensureLegacyPiOverridesReady();
|
|
1187
1354
|
const pendingSources = await ensureExtensionGraphHook(entryRealPath);
|
|
1188
1355
|
try {
|
|
1189
1356
|
// Dynamic import is required: legacy extension entry paths are user/plugin supplied at runtime.
|
|
@@ -1261,9 +1428,8 @@ export function installLegacyPiSpecifierShim(): void {
|
|
|
1261
1428
|
setup(build) {
|
|
1262
1429
|
build.onResolve({ filter: LEGACY_PI_SPECIFIER_FILTER, namespace: "file" }, resolveLegacyPiSpecifier);
|
|
1263
1430
|
build.onResolve({ filter: TYPEBOX_SPECIFIER_FILTER, namespace: "file" }, resolveTypeBoxSpecifier);
|
|
1264
|
-
// Compiled
|
|
1265
|
-
//
|
|
1266
|
-
// in place of unreachable `file:///$bunfs/...` URLs (issue #3423).
|
|
1431
|
+
// Compiled mode serves `omp-legacy-pi-bundled:<key>` imports from
|
|
1432
|
+
// live host module references. No bunfs path leaves this loader.
|
|
1267
1433
|
build.onLoad({ filter: /.*/, namespace: BUNDLED_VIRTUAL_NAMESPACE }, async args => {
|
|
1268
1434
|
return { contents: await synthesizeBundledModuleSource(args.path), loader: "js" };
|
|
1269
1435
|
});
|