diff --git a/internal/execute/tsctests/tscwatch_test.go b/internal/execute/tsctests/tscwatch_test.go
index 7650fcea7e9..fdf7ec0302e 100644
--- a/internal/execute/tsctests/tscwatch_test.go
+++ b/internal/execute/tsctests/tscwatch_test.go
@@ -1,6 +1,7 @@
package tsctests
import (
+ "fmt"
"strings"
"testing"
@@ -9,6 +10,30 @@ import (
func TestWatch(t *testing.T) {
t.Parallel()
+ bunDependencyTest := func() *tscInput {
+ files := FileMap{}
+ var index strings.Builder
+ var fileNames strings.Builder
+ fileNames.WriteString(`"index.ts"`)
+ for i := range 12 {
+ name := fmt.Sprintf("pkg%d", i)
+ value := fmt.Sprintf("value%d", i)
+ index.WriteString(fmt.Sprintf(`import { %[1]s } from "./node_modules/.bun/%[2]s/index"; %[1]s;`, value, name))
+ index.WriteString("\n")
+ files["/home/src/workspaces/project/node_modules/.bun/"+name+"/index.ts"] = fmt.Sprintf("export const %s = %d;", value, i)
+ fileNames.WriteString(fmt.Sprintf(`, "node_modules/.bun/%s/index.ts"`, name))
+ }
+ files["/home/src/workspaces/project/index.ts"] = index.String()
+ files["/home/src/workspaces/project/tsconfig.json"] = fmt.Sprintf(`{
+ "compilerOptions": {},
+ "files": [%s]
+}`, fileNames.String())
+ return &tscInput{
+ subScenario: "watch handles many bun dependency files",
+ files: files,
+ commandLineArgs: []string{"--watch"},
+ }
+ }
testCases := []*tscInput{
{
subScenario: "watch with no tsconfig",
@@ -25,6 +50,7 @@ func TestWatch(t *testing.T) {
},
commandLineArgs: []string{"--watch", "--incremental"},
},
+ bunDependencyTest(),
{
subScenario: "watch skips build when no files change",
files: FileMap{
diff --git a/internal/fswatch/watcher.go b/internal/fswatch/watcher.go
index 1500417c3e4..f59c7e4795c 100644
--- a/internal/fswatch/watcher.go
+++ b/internal/fswatch/watcher.go
@@ -221,6 +221,8 @@ type watcher struct {
debounce *debounce // lazily created in getOrCreateDirWatch
}
+const recursiveConsolidateThreshold = 10
+
func (w *watcher) Name() string { return w.name }
func (w *watcher) String() string { return w.name }
func (w *watcher) Available() bool { return w.factory != nil }
@@ -266,6 +268,54 @@ func (w *watcher) getImpl() (watcherImpl, error) {
return impl, nil
}
+func (w *watcher) keyForDirWatch(dir string, recursive bool) string {
+ if recursive {
+ return dir + "\x00recursive"
+ }
+ return dir
+}
+
+func (w *watcher) findCoveringRecursiveWatchLocked(dir string) *dirWatch {
+ var best *dirWatch
+ for _, dw := range w.dirWatches {
+ if !dw.recursive || !isInDirectoryOrSelf(dw.dir, dir) {
+ continue
+ }
+ if best == nil || len(dw.dir) > len(best.dir) {
+ best = dw
+ }
+ }
+ return best
+}
+
+func (w *watcher) findConsolidationDirLocked(dir string) string {
+ if !w.HasFastRecursiveBackend() {
+ return ""
+ }
+ parent := filepath.Dir(dir)
+ for parent != dir && parent != "." {
+ if filepath.Dir(parent) == parent {
+ break
+ }
+ count := 1
+ for _, dw := range w.dirWatches {
+ if isInDirectoryOrSelf(parent, dw.dir) {
+ count++
+ }
+ }
+ if count >= recursiveConsolidateThreshold {
+ return parent
+ }
+ next := filepath.Dir(parent)
+ if next == parent {
+ break
+ }
+ dir = parent
+ parent = next
+ }
+ return ""
+}
+
func (w *watcher) getOrCreateDirWatch(dir string, physicalDir string, recursive bool) *dirWatch {
w.mu.Lock()
defer w.mu.Unlock()
@@ -275,10 +325,22 @@ func (w *watcher) getOrCreateDirWatch(dir string, physicalDir string, recursive
if w.debounce == nil {
w.debounce = newDebounce()
}
- key := dir
- if recursive {
- key = dir + "\x00recursive"
+
+ if w.HasFastRecursiveBackend() {
+ if dw := w.findCoveringRecursiveWatchLocked(dir); dw != nil {
+ return dw
+ }
+ if consolidationDir := w.findConsolidationDirLocked(dir); consolidationDir != "" {
+ dir = consolidationDir
+ physicalDir = physicalDirFor(dir)
+ recursive = true
+ if dw := w.findCoveringRecursiveWatchLocked(dir); dw != nil {
+ return dw
+ }
+ }
}
+
+ key := w.keyForDirWatch(dir, recursive)
if dw, ok := w.dirWatches[key]; ok {
return dw
}
@@ -291,10 +353,7 @@ func (w *watcher) getOrCreateDirWatch(dir string, physicalDir string, recursive
func (w *watcher) removeDirWatch(dw *dirWatch) {
w.mu.Lock()
defer w.mu.Unlock()
- key := dw.dir
- if dw.recursive {
- key = dw.dir + "\x00recursive"
- }
+ key := w.keyForDirWatch(dw.dir, dw.recursive)
if existing, ok := w.dirWatches[key]; ok && existing == dw {
delete(w.dirWatches, key)
dw.destroyDebounce()
@@ -321,7 +380,7 @@ func (w *watcher) WatchDirectory(dir string, fn WatchCallback, opts ...WatchOpti
}
dw := w.getOrCreateDirWatch(dir, physicalDir, sopts.recursive)
- id, _ := dw.watch(fn, sopts.ignore)
+ id, _ := dw.watch(dir, sopts.recursive, fn, sopts.ignore)
impl, err := w.getImpl()
if err != nil {
@@ -512,9 +571,11 @@ func (b *watcherBase) handleWatcherError(werr *dirWatchError) {
// ----- dirWatch: per-directory watch state -------------------------
type callback struct {
- id uint64
- fn WatchCallback
- ignore func(path string) bool
+ id uint64
+ dir string
+ recursive bool
+ fn WatchCallback
+ ignore func(path string) bool
}
// dirWatchError associates an error with a specific directory watch.
@@ -663,18 +724,21 @@ func (dw *dirWatch) triggerCallbacks() {
}
events, err := dw.events.drain()
cbs := slices.Clone(dw.callbacks)
- recursive := dw.recursive
dw.mu.Unlock()
for _, cb := range cbs {
cbEvents := events
- if cb.ignore != nil || !recursive {
+ if cb.ignore != nil || !cb.recursive || cb.dir != dw.dir {
filtered := make([]Event, 0, len(events))
for _, e := range events {
if cb.ignore != nil && cb.ignore(e.Path) {
continue
}
- if !recursive && !isDirectChild(dw.dir, e.Path) {
+ if cb.recursive {
+ if !isInDirectoryOrSelf(cb.dir, e.Path) {
+ continue
+ }
+ } else if !isDirectChild(cb.dir, e.Path) {
continue
}
filtered = append(filtered, e)
@@ -687,6 +751,17 @@ func (dw *dirWatch) triggerCallbacks() {
}
}
+func isInDirectoryOrSelf(dir, path string) bool {
+ if path == dir {
+ return true
+ }
+ if !strings.HasPrefix(path, dir) {
+ return false
+ }
+ rest := path[len(dir):]
+ return len(rest) > 0 && (rest[0] == '/' || rest[0] == filepath.Separator)
+}
+
// isDirectChild reports whether path is an immediate child of dir.
// Both paths must be absolute. Returns false for path == dir.
func isDirectChild(dir, path string) bool {
@@ -704,12 +779,12 @@ func isDirectChild(dir, path string) bool {
return len(rest) > 0 && !strings.ContainsRune(rest, '/') && !strings.ContainsRune(rest, filepath.Separator)
}
-func (dw *dirWatch) watch(fn WatchCallback, ignore func(path string) bool) (uint64, bool) {
+func (dw *dirWatch) watch(dir string, recursive bool, fn WatchCallback, ignore func(path string) bool) (uint64, bool) {
dw.mu.Lock()
defer dw.mu.Unlock()
dw.nextCBID++
id := dw.nextCBID
- dw.callbacks = append(dw.callbacks, callback{id: id, fn: fn, ignore: ignore})
+ dw.callbacks = append(dw.callbacks, callback{id: id, dir: dir, recursive: recursive, fn: fn, ignore: ignore})
return id, true
}
diff --git a/internal/fswatch/watcher_test.go b/internal/fswatch/watcher_test.go
index a051ff7c197..4093f3b6f8b 100644
--- a/internal/fswatch/watcher_test.go
+++ b/internal/fswatch/watcher_test.go
@@ -1313,6 +1313,108 @@ func TestSubscribeMultipleDifferentDirs(t *testing.T) {
})
}
+type countingWatcherImpl struct {
+ watcherBase
+ subscribed []*dirWatch
+ closed []*dirWatch
+}
+
+func newCountingWatcherImpl() *countingWatcherImpl {
+ impl := &countingWatcherImpl{}
+ impl.watcherBase.init(impl)
+ return impl
+}
+
+func (b *countingWatcherImpl) start() error {
+ b.notifyStarted()
+ return nil
+}
+
+func (b *countingWatcherImpl) subscribe(w *dirWatch) error {
+ b.subscribed = append(b.subscribed, w)
+ return nil
+}
+
+func (b *countingWatcherImpl) closeWatch(w *dirWatch) error {
+ b.closed = append(b.closed, w)
+ return nil
+}
+
+func TestFastRecursiveWatcherConsolidatesSiblingDirectories(t *testing.T) {
+ t.Parallel()
+
+ root := t.TempDir()
+ parent := filepath.Join(root, "node_modules", ".bun")
+ if err := os.MkdirAll(parent, 0o755); err != nil {
+ t.Fatal(err)
+ }
+
+ var impl *countingWatcherImpl
+ watcherImpl := &watcher{
+ name: "fsevents",
+ factory: func() watcherImpl {
+ impl = newCountingWatcherImpl()
+ return impl
+ },
+ }
+
+ var subs []Watch
+ for i := range recursiveConsolidateThreshold + 2 {
+ dir := filepath.Join(parent, fmt.Sprintf("pkg%d", i))
+ if err := os.MkdirAll(dir, 0o755); err != nil {
+ t.Fatal(err)
+ }
+ sub, err := watcherImpl.WatchDirectory(dir, func([]Event, error) {})
+ if err != nil {
+ t.Fatal(err)
+ }
+ subs = append(subs, sub)
+ }
+ t.Cleanup(func() {
+ for _, sub := range subs {
+ _ = sub.Close()
+ }
+ })
+
+ if got := len(impl.subscribed); got != recursiveConsolidateThreshold {
+ t.Fatalf("expected %d subscriptions after consolidation, got %d", recursiveConsolidateThreshold, got)
+ }
+ consolidated := impl.subscribed[len(impl.subscribed)-1]
+ if consolidated.dir != parent || !consolidated.recursive {
+ t.Fatalf("expected consolidated recursive watch on %s, got dir=%s recursive=%v", parent, consolidated.dir, consolidated.recursive)
+ }
+
+ watcherImpl.mu.Lock()
+ _, hasPkgWatch := watcherImpl.dirWatches[watcherImpl.keyForDirWatch(filepath.Join(parent, "pkg11"), false)]
+ watcherImpl.mu.Unlock()
+ if hasPkgWatch {
+ t.Fatal("expected later package watch to reuse consolidated parent instead of creating its own stream")
+ }
+}
+
+func TestConsolidatedChildWatchFiltersAgainstRequestedDir(t *testing.T) {
+ t.Parallel()
+
+ parent := filepath.Join(t.TempDir(), "parent")
+ child := filepath.Join(parent, "child")
+ sibling := filepath.Join(parent, "sibling")
+ dw := newDirectWatcher(t, parent)
+
+ var got []Event
+ dw.watch(child, false, func(events []Event, err error) {
+ if err != nil {
+ t.Fatal(err)
+ }
+ got = append(got, events...)
+ }, nil)
+ dw.events.update(filepath.Join(child, "file.ts"))
+ dw.events.update(filepath.Join(child, "nested", "file.ts"))
+ dw.events.update(filepath.Join(sibling, "file.ts"))
+ dw.triggerCallbacks()
+
+ assertEventSequence(t, got, []wantEvent{{EventUpdate, filepath.Join(child, "file.ts")}})
+}
+
// ----- errors ------------------------------------------------------------
func TestSubscribeMissingDirError(t *testing.T) {
diff --git a/internal/lsp/lspwatcher/lspwatcher.go b/internal/lsp/lspwatcher/lspwatcher.go
index 6b5fbc6544f..0bcf09325b7 100644
--- a/internal/lsp/lspwatcher/lspwatcher.go
+++ b/internal/lsp/lspwatcher/lspwatcher.go
@@ -25,6 +25,7 @@ const throttleWindow = 75 * time.Millisecond
type watcherBackend interface {
WatchDirectory(dir string, fn fswatch.WatchCallback, opts ...fswatch.WatchOption) (io.Closer, error)
+ HasFastRecursiveBackend() bool
}
type defaultWatcherBackend struct {
@@ -35,6 +36,10 @@ func (d defaultWatcherBackend) WatchDirectory(dir string, fn fswatch.WatchCallba
return d.watcher.WatchDirectory(dir, fn, opts...)
}
+func (d defaultWatcherBackend) HasFastRecursiveBackend() bool {
+ return d.watcher.HasFastRecursiveBackend()
+}
+
// Watcher manages a set of file system subscriptions identified by
// WatcherID strings (matching the LSP server's project.WatcherID type).
// Events are delivered to onChanges in batches as `*lsproto.FileEvent`,
@@ -286,7 +291,11 @@ func (w *watch) reconcile(emitSyntheticCreates bool) error {
if !w.watchingTarget && w.subscription != nil && w.watchedDirectory == ancestorDirectory {
return nil // already watching the correct ancestor
}
- subscription, err := watcher.backend.WatchDirectory(ancestorDirectory, w.ancestorCallback())
+ var options []fswatch.WatchOption
+ if watcher.backend.HasFastRecursiveBackend() {
+ options = append(options, fswatch.WithRecursive())
+ }
+ subscription, err := watcher.backend.WatchDirectory(ancestorDirectory, w.ancestorCallback(), options...)
if err != nil {
return err
}
diff --git a/internal/lsp/lspwatcher/lspwatcher_test.go b/internal/lsp/lspwatcher/lspwatcher_test.go
index 90ad3a010cc..b8f6ae88292 100644
--- a/internal/lsp/lspwatcher/lspwatcher_test.go
+++ b/internal/lsp/lspwatcher/lspwatcher_test.go
@@ -171,6 +171,7 @@ type fakeBackend struct {
closed map[string]int
optCount map[string]int
failDirs map[string]error
+ fast bool
}
func newFakeBackend() *fakeBackend {
@@ -199,6 +200,10 @@ func (f *fakeBackend) WatchDirectory(dir string, fn fswatch.WatchCallback, opts
}}, nil
}
+func (f *fakeBackend) HasFastRecursiveBackend() bool {
+ return f.fast
+}
+
// watchedDirs returns the directories currently subscribed, for assertions.
func (f *fakeBackend) watchedDirs() []string {
f.mu.Lock()
diff --git a/testdata/baselines/reference/tscWatch/commandLineWatch/watch-handles-many-bun-dependency-files.js b/testdata/baselines/reference/tscWatch/commandLineWatch/watch-handles-many-bun-dependency-files.js
new file mode 100644
index 00000000000..dbd33c9fbaf
--- /dev/null
+++ b/testdata/baselines/reference/tscWatch/commandLineWatch/watch-handles-many-bun-dependency-files.js
@@ -0,0 +1,175 @@
+currentDirectory::/home/src/workspaces/project
+useCaseSensitiveFileNames::true
+Input::
+//// [/home/src/workspaces/project/index.ts] *new*
+import { value0 } from "./node_modules/.bun/pkg0/index"; value0;
+import { value1 } from "./node_modules/.bun/pkg1/index"; value1;
+import { value2 } from "./node_modules/.bun/pkg2/index"; value2;
+import { value3 } from "./node_modules/.bun/pkg3/index"; value3;
+import { value4 } from "./node_modules/.bun/pkg4/index"; value4;
+import { value5 } from "./node_modules/.bun/pkg5/index"; value5;
+import { value6 } from "./node_modules/.bun/pkg6/index"; value6;
+import { value7 } from "./node_modules/.bun/pkg7/index"; value7;
+import { value8 } from "./node_modules/.bun/pkg8/index"; value8;
+import { value9 } from "./node_modules/.bun/pkg9/index"; value9;
+import { value10 } from "./node_modules/.bun/pkg10/index"; value10;
+import { value11 } from "./node_modules/.bun/pkg11/index"; value11;
+
+//// [/home/src/workspaces/project/node_modules/.bun/pkg0/index.ts] *new*
+export const value0 = 0;
+//// [/home/src/workspaces/project/node_modules/.bun/pkg1/index.ts] *new*
+export const value1 = 1;
+//// [/home/src/workspaces/project/node_modules/.bun/pkg10/index.ts] *new*
+export const value10 = 10;
+//// [/home/src/workspaces/project/node_modules/.bun/pkg11/index.ts] *new*
+export const value11 = 11;
+//// [/home/src/workspaces/project/node_modules/.bun/pkg2/index.ts] *new*
+export const value2 = 2;
+//// [/home/src/workspaces/project/node_modules/.bun/pkg3/index.ts] *new*
+export const value3 = 3;
+//// [/home/src/workspaces/project/node_modules/.bun/pkg4/index.ts] *new*
+export const value4 = 4;
+//// [/home/src/workspaces/project/node_modules/.bun/pkg5/index.ts] *new*
+export const value5 = 5;
+//// [/home/src/workspaces/project/node_modules/.bun/pkg6/index.ts] *new*
+export const value6 = 6;
+//// [/home/src/workspaces/project/node_modules/.bun/pkg7/index.ts] *new*
+export const value7 = 7;
+//// [/home/src/workspaces/project/node_modules/.bun/pkg8/index.ts] *new*
+export const value8 = 8;
+//// [/home/src/workspaces/project/node_modules/.bun/pkg9/index.ts] *new*
+export const value9 = 9;
+//// [/home/src/workspaces/project/tsconfig.json] *new*
+{
+ "compilerOptions": {},
+ "files": ["index.ts", "node_modules/.bun/pkg0/index.ts", "node_modules/.bun/pkg1/index.ts", "node_modules/.bun/pkg2/index.ts", "node_modules/.bun/pkg3/index.ts", "node_modules/.bun/pkg4/index.ts", "node_modules/.bun/pkg5/index.ts", "node_modules/.bun/pkg6/index.ts", "node_modules/.bun/pkg7/index.ts", "node_modules/.bun/pkg8/index.ts", "node_modules/.bun/pkg9/index.ts", "node_modules/.bun/pkg10/index.ts", "node_modules/.bun/pkg11/index.ts"]
+}
+
+tsgo --watch
+ExitStatus:: Success
+Output::
+[2J[3J[H[[90mHH:MM:SS AM[0m] Starting compilation in watch mode...
+
+[[90mHH:MM:SS AM[0m] Found 0 errors. Watching for file changes.
+
+//// [/home/src/tslibs/TS/Lib/lib.es2025.full.d.ts] *Lib*
+///
+interface Boolean {}
+interface Function {}
+interface CallableFunction {}
+interface NewableFunction {}
+interface IArguments {}
+interface Number { toExponential: any; }
+interface Object {}
+interface RegExp {}
+interface String { charAt: any; }
+interface Array { length: number; [n: number]: T; }
+interface ReadonlyArray {}
+interface SymbolConstructor {
+ (desc?: string | number): symbol;
+ for(name: string): symbol;
+ readonly toStringTag: symbol;
+}
+declare var Symbol: SymbolConstructor;
+interface Symbol {
+ readonly [Symbol.toStringTag]: string;
+}
+declare const console: { log(msg: any): void; };
+//// [/home/src/workspaces/project/index.js] *new*
+import { value0 } from "./node_modules/.bun/pkg0/index";
+value0;
+import { value1 } from "./node_modules/.bun/pkg1/index";
+value1;
+import { value2 } from "./node_modules/.bun/pkg2/index";
+value2;
+import { value3 } from "./node_modules/.bun/pkg3/index";
+value3;
+import { value4 } from "./node_modules/.bun/pkg4/index";
+value4;
+import { value5 } from "./node_modules/.bun/pkg5/index";
+value5;
+import { value6 } from "./node_modules/.bun/pkg6/index";
+value6;
+import { value7 } from "./node_modules/.bun/pkg7/index";
+value7;
+import { value8 } from "./node_modules/.bun/pkg8/index";
+value8;
+import { value9 } from "./node_modules/.bun/pkg9/index";
+value9;
+import { value10 } from "./node_modules/.bun/pkg10/index";
+value10;
+import { value11 } from "./node_modules/.bun/pkg11/index";
+value11;
+
+//// [/home/src/workspaces/project/node_modules/.bun/pkg0/index.js] *new*
+export const value0 = 0;
+
+//// [/home/src/workspaces/project/node_modules/.bun/pkg1/index.js] *new*
+export const value1 = 1;
+
+//// [/home/src/workspaces/project/node_modules/.bun/pkg10/index.js] *new*
+export const value10 = 10;
+
+//// [/home/src/workspaces/project/node_modules/.bun/pkg11/index.js] *new*
+export const value11 = 11;
+
+//// [/home/src/workspaces/project/node_modules/.bun/pkg2/index.js] *new*
+export const value2 = 2;
+
+//// [/home/src/workspaces/project/node_modules/.bun/pkg3/index.js] *new*
+export const value3 = 3;
+
+//// [/home/src/workspaces/project/node_modules/.bun/pkg4/index.js] *new*
+export const value4 = 4;
+
+//// [/home/src/workspaces/project/node_modules/.bun/pkg5/index.js] *new*
+export const value5 = 5;
+
+//// [/home/src/workspaces/project/node_modules/.bun/pkg6/index.js] *new*
+export const value6 = 6;
+
+//// [/home/src/workspaces/project/node_modules/.bun/pkg7/index.js] *new*
+export const value7 = 7;
+
+//// [/home/src/workspaces/project/node_modules/.bun/pkg8/index.js] *new*
+export const value8 = 8;
+
+//// [/home/src/workspaces/project/node_modules/.bun/pkg9/index.js] *new*
+export const value9 = 9;
+
+
+Watch Registrations::
+Directory watches::
+ /home/src/tslibs/TS/Lib
+ /home/src/workspaces/project
+ /home/src/workspaces/project/node_modules
+ /home/src/workspaces/project/node_modules/.bun
+ /home/src/workspaces/project/node_modules/.bun/pkg0
+ /home/src/workspaces/project/node_modules/.bun/pkg1
+ /home/src/workspaces/project/node_modules/.bun/pkg10
+ /home/src/workspaces/project/node_modules/.bun/pkg11
+ /home/src/workspaces/project/node_modules/.bun/pkg2
+ /home/src/workspaces/project/node_modules/.bun/pkg3
+ /home/src/workspaces/project/node_modules/.bun/pkg4
+ /home/src/workspaces/project/node_modules/.bun/pkg5
+ /home/src/workspaces/project/node_modules/.bun/pkg6
+ /home/src/workspaces/project/node_modules/.bun/pkg7
+ /home/src/workspaces/project/node_modules/.bun/pkg8
+ /home/src/workspaces/project/node_modules/.bun/pkg9
+tsconfig.json::
+SemanticDiagnostics::
+*refresh* /home/src/tslibs/TS/Lib/lib.es2025.full.d.ts
+*refresh* /home/src/workspaces/project/node_modules/.bun/pkg0/index.ts
+*refresh* /home/src/workspaces/project/node_modules/.bun/pkg1/index.ts
+*refresh* /home/src/workspaces/project/node_modules/.bun/pkg2/index.ts
+*refresh* /home/src/workspaces/project/node_modules/.bun/pkg3/index.ts
+*refresh* /home/src/workspaces/project/node_modules/.bun/pkg4/index.ts
+*refresh* /home/src/workspaces/project/node_modules/.bun/pkg5/index.ts
+*refresh* /home/src/workspaces/project/node_modules/.bun/pkg6/index.ts
+*refresh* /home/src/workspaces/project/node_modules/.bun/pkg7/index.ts
+*refresh* /home/src/workspaces/project/node_modules/.bun/pkg8/index.ts
+*refresh* /home/src/workspaces/project/node_modules/.bun/pkg9/index.ts
+*refresh* /home/src/workspaces/project/node_modules/.bun/pkg10/index.ts
+*refresh* /home/src/workspaces/project/node_modules/.bun/pkg11/index.ts
+*refresh* /home/src/workspaces/project/index.ts
+Signatures::