Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changeset/watch-include-entry.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@e2b/python-sdk": minor
"e2b": minor
---

Add an `includeEntry`/`include_entry` option to filesystem directory watching. When enabled, each `FilesystemEvent` carries the affected entry's `EntryInfo` (best-effort; left unset for events where the path no longer exists, such as remove/rename-away). Requires envd 0.6.2 or later; older sandboxes ignore the option and leave `entry` unset.
25 changes: 24 additions & 1 deletion packages/js-sdk/src/envd/filesystem/filesystem_pb.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import type { Message } from '@bufbuild/protobuf'
export const file_filesystem_filesystem: GenFile =
/*@__PURE__*/
fileDesc(
'ChtmaWxlc3lzdGVtL2ZpbGVzeXN0ZW0ucHJvdG8SCmZpbGVzeXN0ZW0iMgoLTW92ZVJlcXVlc3QSDgoGc291cmNlGAEgASgJEhMKC2Rlc3RpbmF0aW9uGAIgASgJIjQKDE1vdmVSZXNwb25zZRIkCgVlbnRyeRgBIAEoCzIVLmZpbGVzeXN0ZW0uRW50cnlJbmZvIh4KDk1ha2VEaXJSZXF1ZXN0EgwKBHBhdGgYASABKAkiNwoPTWFrZURpclJlc3BvbnNlEiQKBWVudHJ5GAEgASgLMhUuZmlsZXN5c3RlbS5FbnRyeUluZm8iHQoNUmVtb3ZlUmVxdWVzdBIMCgRwYXRoGAEgASgJIhAKDlJlbW92ZVJlc3BvbnNlIhsKC1N0YXRSZXF1ZXN0EgwKBHBhdGgYASABKAkiNAoMU3RhdFJlc3BvbnNlEiQKBWVudHJ5GAEgASgLMhUuZmlsZXN5c3RlbS5FbnRyeUluZm8i/QEKCUVudHJ5SW5mbxIMCgRuYW1lGAEgASgJEiIKBHR5cGUYAiABKA4yFC5maWxlc3lzdGVtLkZpbGVUeXBlEgwKBHBhdGgYAyABKAkSDAoEc2l6ZRgEIAEoAxIMCgRtb2RlGAUgASgNEhMKC3Blcm1pc3Npb25zGAYgASgJEg0KBW93bmVyGAcgASgJEg0KBWdyb3VwGAggASgJEjEKDW1vZGlmaWVkX3RpbWUYCSABKAsyGi5nb29nbGUucHJvdG9idWYuVGltZXN0YW1wEhsKDnN5bWxpbmtfdGFyZ2V0GAogASgJSACIAQFCEQoPX3N5bWxpbmtfdGFyZ2V0Ii0KDkxpc3REaXJSZXF1ZXN0EgwKBHBhdGgYASABKAkSDQoFZGVwdGgYAiABKA0iOQoPTGlzdERpclJlc3BvbnNlEiYKB2VudHJpZXMYASADKAsyFS5maWxlc3lzdGVtLkVudHJ5SW5mbyIyCg9XYXRjaERpclJlcXVlc3QSDAoEcGF0aBgBIAEoCRIRCglyZWN1cnNpdmUYAiABKAgiRAoPRmlsZXN5c3RlbUV2ZW50EgwKBG5hbWUYASABKAkSIwoEdHlwZRgCIAEoDjIVLmZpbGVzeXN0ZW0uRXZlbnRUeXBlIuABChBXYXRjaERpclJlc3BvbnNlEjgKBXN0YXJ0GAEgASgLMicuZmlsZXN5c3RlbS5XYXRjaERpclJlc3BvbnNlLlN0YXJ0RXZlbnRIABIxCgpmaWxlc3lzdGVtGAIgASgLMhsuZmlsZXN5c3RlbS5GaWxlc3lzdGVtRXZlbnRIABI7CglrZWVwYWxpdmUYAyABKAsyJi5maWxlc3lzdGVtLldhdGNoRGlyUmVzcG9uc2UuS2VlcEFsaXZlSAAaDAoKU3RhcnRFdmVudBoLCglLZWVwQWxpdmVCBwoFZXZlbnQiNwoUQ3JlYXRlV2F0Y2hlclJlcXVlc3QSDAoEcGF0aBgBIAEoCRIRCglyZWN1cnNpdmUYAiABKAgiKwoVQ3JlYXRlV2F0Y2hlclJlc3BvbnNlEhIKCndhdGNoZXJfaWQYASABKAkiLQoXR2V0V2F0Y2hlckV2ZW50c1JlcXVlc3QSEgoKd2F0Y2hlcl9pZBgBIAEoCSJHChhHZXRXYXRjaGVyRXZlbnRzUmVzcG9uc2USKwoGZXZlbnRzGAEgAygLMhsuZmlsZXN5c3RlbS5GaWxlc3lzdGVtRXZlbnQiKgoUUmVtb3ZlV2F0Y2hlclJlcXVlc3QSEgoKd2F0Y2hlcl9pZBgBIAEoCSIXChVSZW1vdmVXYXRjaGVyUmVzcG9uc2UqUgoIRmlsZVR5cGUSGQoVRklMRV9UWVBFX1VOU1BFQ0lGSUVEEAASEgoORklMRV9UWVBFX0ZJTEUQARIXChNGSUxFX1RZUEVfRElSRUNUT1JZEAIqmAEKCUV2ZW50VHlwZRIaChZFVkVOVF9UWVBFX1VOU1BFQ0lGSUVEEAASFQoRRVZFTlRfVFlQRV9DUkVBVEUQARIUChBFVkVOVF9UWVBFX1dSSVRFEAISFQoRRVZFTlRfVFlQRV9SRU1PVkUQAxIVChFFVkVOVF9UWVBFX1JFTkFNRRAEEhQKEEVWRU5UX1RZUEVfQ0hNT0QQBTKfBQoKRmlsZXN5c3RlbRI5CgRTdGF0EhcuZmlsZXN5c3RlbS5TdGF0UmVxdWVzdBoYLmZpbGVzeXN0ZW0uU3RhdFJlc3BvbnNlEkIKB01ha2VEaXISGi5maWxlc3lzdGVtLk1ha2VEaXJSZXF1ZXN0GhsuZmlsZXN5c3RlbS5NYWtlRGlyUmVzcG9uc2USOQoETW92ZRIXLmZpbGVzeXN0ZW0uTW92ZVJlcXVlc3QaGC5maWxlc3lzdGVtLk1vdmVSZXNwb25zZRJCCgdMaXN0RGlyEhouZmlsZXN5c3RlbS5MaXN0RGlyUmVxdWVzdBobLmZpbGVzeXN0ZW0uTGlzdERpclJlc3BvbnNlEj8KBlJlbW92ZRIZLmZpbGVzeXN0ZW0uUmVtb3ZlUmVxdWVzdBoaLmZpbGVzeXN0ZW0uUmVtb3ZlUmVzcG9uc2USRwoIV2F0Y2hEaXISGy5maWxlc3lzdGVtLldhdGNoRGlyUmVxdWVzdBocLmZpbGVzeXN0ZW0uV2F0Y2hEaXJSZXNwb25zZTABElQKDUNyZWF0ZVdhdGNoZXISIC5maWxlc3lzdGVtLkNyZWF0ZVdhdGNoZXJSZXF1ZXN0GiEuZmlsZXN5c3RlbS5DcmVhdGVXYXRjaGVyUmVzcG9uc2USXQoQR2V0V2F0Y2hlckV2ZW50cxIjLmZpbGVzeXN0ZW0uR2V0V2F0Y2hlckV2ZW50c1JlcXVlc3QaJC5maWxlc3lzdGVtLkdldFdhdGNoZXJFdmVudHNSZXNwb25zZRJUCg1SZW1vdmVXYXRjaGVyEiAuZmlsZXN5c3RlbS5SZW1vdmVXYXRjaGVyUmVxdWVzdBohLmZpbGVzeXN0ZW0uUmVtb3ZlV2F0Y2hlclJlc3BvbnNlQmkKDmNvbS5maWxlc3lzdGVtQg9GaWxlc3lzdGVtUHJvdG9QAaICA0ZYWKoCCkZpbGVzeXN0ZW3KAgpGaWxlc3lzdGVt4gIWRmlsZXN5c3RlbVxHUEJNZXRhZGF0YeoCCkZpbGVzeXN0ZW1iBnByb3RvMw',
'ChtmaWxlc3lzdGVtL2ZpbGVzeXN0ZW0ucHJvdG8SCmZpbGVzeXN0ZW0iMgoLTW92ZVJlcXVlc3QSDgoGc291cmNlGAEgASgJEhMKC2Rlc3RpbmF0aW9uGAIgASgJIjQKDE1vdmVSZXNwb25zZRIkCgVlbnRyeRgBIAEoCzIVLmZpbGVzeXN0ZW0uRW50cnlJbmZvIh4KDk1ha2VEaXJSZXF1ZXN0EgwKBHBhdGgYASABKAkiNwoPTWFrZURpclJlc3BvbnNlEiQKBWVudHJ5GAEgASgLMhUuZmlsZXN5c3RlbS5FbnRyeUluZm8iHQoNUmVtb3ZlUmVxdWVzdBIMCgRwYXRoGAEgASgJIhAKDlJlbW92ZVJlc3BvbnNlIhsKC1N0YXRSZXF1ZXN0EgwKBHBhdGgYASABKAkiNAoMU3RhdFJlc3BvbnNlEiQKBWVudHJ5GAEgASgLMhUuZmlsZXN5c3RlbS5FbnRyeUluZm8i/QEKCUVudHJ5SW5mbxIMCgRuYW1lGAEgASgJEiIKBHR5cGUYAiABKA4yFC5maWxlc3lzdGVtLkZpbGVUeXBlEgwKBHBhdGgYAyABKAkSDAoEc2l6ZRgEIAEoAxIMCgRtb2RlGAUgASgNEhMKC3Blcm1pc3Npb25zGAYgASgJEg0KBW93bmVyGAcgASgJEg0KBWdyb3VwGAggASgJEjEKDW1vZGlmaWVkX3RpbWUYCSABKAsyGi5nb29nbGUucHJvdG9idWYuVGltZXN0YW1wEhsKDnN5bWxpbmtfdGFyZ2V0GAogASgJSACIAQFCEQoPX3N5bWxpbmtfdGFyZ2V0Ii0KDkxpc3REaXJSZXF1ZXN0EgwKBHBhdGgYASABKAkSDQoFZGVwdGgYAiABKA0iOQoPTGlzdERpclJlc3BvbnNlEiYKB2VudHJpZXMYASADKAsyFS5maWxlc3lzdGVtLkVudHJ5SW5mbyJJCg9XYXRjaERpclJlcXVlc3QSDAoEcGF0aBgBIAEoCRIRCglyZWN1cnNpdmUYAiABKAgSFQoNaW5jbHVkZV9lbnRyeRgDIAEoCCJ5Cg9GaWxlc3lzdGVtRXZlbnQSDAoEbmFtZRgBIAEoCRIjCgR0eXBlGAIgASgOMhUuZmlsZXN5c3RlbS5FdmVudFR5cGUSKQoFZW50cnkYAyABKAsyFS5maWxlc3lzdGVtLkVudHJ5SW5mb0gAiAEBQggKBl9lbnRyeSLgAQoQV2F0Y2hEaXJSZXNwb25zZRI4CgVzdGFydBgBIAEoCzInLmZpbGVzeXN0ZW0uV2F0Y2hEaXJSZXNwb25zZS5TdGFydEV2ZW50SAASMQoKZmlsZXN5c3RlbRgCIAEoCzIbLmZpbGVzeXN0ZW0uRmlsZXN5c3RlbUV2ZW50SAASOwoJa2VlcGFsaXZlGAMgASgLMiYuZmlsZXN5c3RlbS5XYXRjaERpclJlc3BvbnNlLktlZXBBbGl2ZUgAGgwKClN0YXJ0RXZlbnQaCwoJS2VlcEFsaXZlQgcKBWV2ZW50Ik4KFENyZWF0ZVdhdGNoZXJSZXF1ZXN0EgwKBHBhdGgYASABKAkSEQoJcmVjdXJzaXZlGAIgASgIEhUKDWluY2x1ZGVfZW50cnkYAyABKAgiKwoVQ3JlYXRlV2F0Y2hlclJlc3BvbnNlEhIKCndhdGNoZXJfaWQYASABKAkiLQoXR2V0V2F0Y2hlckV2ZW50c1JlcXVlc3QSEgoKd2F0Y2hlcl9pZBgBIAEoCSJHChhHZXRXYXRjaGVyRXZlbnRzUmVzcG9uc2USKwoGZXZlbnRzGAEgAygLMhsuZmlsZXN5c3RlbS5GaWxlc3lzdGVtRXZlbnQiKgoUUmVtb3ZlV2F0Y2hlclJlcXVlc3QSEgoKd2F0Y2hlcl9pZBgBIAEoCSIXChVSZW1vdmVXYXRjaGVyUmVzcG9uc2UqUgoIRmlsZVR5cGUSGQoVRklMRV9UWVBFX1VOU1BFQ0lGSUVEEAASEgoORklMRV9UWVBFX0ZJTEUQARIXChNGSUxFX1RZUEVfRElSRUNUT1JZEAIqmAEKCUV2ZW50VHlwZRIaChZFVkVOVF9UWVBFX1VOU1BFQ0lGSUVEEAASFQoRRVZFTlRfVFlQRV9DUkVBVEUQARIUChBFVkVOVF9UWVBFX1dSSVRFEAISFQoRRVZFTlRfVFlQRV9SRU1PVkUQAxIVChFFVkVOVF9UWVBFX1JFTkFNRRAEEhQKEEVWRU5UX1RZUEVfQ0hNT0QQBTKfBQoKRmlsZXN5c3RlbRI5CgRTdGF0EhcuZmlsZXN5c3RlbS5TdGF0UmVxdWVzdBoYLmZpbGVzeXN0ZW0uU3RhdFJlc3BvbnNlEkIKB01ha2VEaXISGi5maWxlc3lzdGVtLk1ha2VEaXJSZXF1ZXN0GhsuZmlsZXN5c3RlbS5NYWtlRGlyUmVzcG9uc2USOQoETW92ZRIXLmZpbGVzeXN0ZW0uTW92ZVJlcXVlc3QaGC5maWxlc3lzdGVtLk1vdmVSZXNwb25zZRJCCgdMaXN0RGlyEhouZmlsZXN5c3RlbS5MaXN0RGlyUmVxdWVzdBobLmZpbGVzeXN0ZW0uTGlzdERpclJlc3BvbnNlEj8KBlJlbW92ZRIZLmZpbGVzeXN0ZW0uUmVtb3ZlUmVxdWVzdBoaLmZpbGVzeXN0ZW0uUmVtb3ZlUmVzcG9uc2USRwoIV2F0Y2hEaXISGy5maWxlc3lzdGVtLldhdGNoRGlyUmVxdWVzdBocLmZpbGVzeXN0ZW0uV2F0Y2hEaXJSZXNwb25zZTABElQKDUNyZWF0ZVdhdGNoZXISIC5maWxlc3lzdGVtLkNyZWF0ZVdhdGNoZXJSZXF1ZXN0GiEuZmlsZXN5c3RlbS5DcmVhdGVXYXRjaGVyUmVzcG9uc2USXQoQR2V0V2F0Y2hlckV2ZW50cxIjLmZpbGVzeXN0ZW0uR2V0V2F0Y2hlckV2ZW50c1JlcXVlc3QaJC5maWxlc3lzdGVtLkdldFdhdGNoZXJFdmVudHNSZXNwb25zZRJUCg1SZW1vdmVXYXRjaGVyEiAuZmlsZXN5c3RlbS5SZW1vdmVXYXRjaGVyUmVxdWVzdBohLmZpbGVzeXN0ZW0uUmVtb3ZlV2F0Y2hlclJlc3BvbnNlQmkKDmNvbS5maWxlc3lzdGVtQg9GaWxlc3lzdGVtUHJvdG9QAaICA0ZYWKoCCkZpbGVzeXN0ZW3KAgpGaWxlc3lzdGVt4gIWRmlsZXN5c3RlbVxHUEJNZXRhZGF0YeoCCkZpbGVzeXN0ZW1iBnByb3RvMw',
[file_google_protobuf_timestamp]
)

Expand Down Expand Up @@ -291,6 +291,13 @@ export type WatchDirRequest = Message<'filesystem.WatchDirRequest'> & {
* @generated from field: bool recursive = 2;
*/
recursive: boolean

/**
* If true, each FilesystemEvent includes the EntryInfo of the affected entry, when available.
*
* @generated from field: bool include_entry = 3;
*/
includeEntry: boolean
}

/**
Expand All @@ -314,6 +321,15 @@ export type FilesystemEvent = Message<'filesystem.FilesystemEvent'> & {
* @generated from field: filesystem.EventType type = 2;
*/
type: EventType

/**
* Info of the entry that triggered the event. Only populated when include_entry
* was requested and the entry could be stat-ed (e.g. not set for remove/rename-away
* events, where the entry no longer exists at this path).
*
* @generated from field: optional filesystem.EntryInfo entry = 3;
*/
entry?: EntryInfo
}

/**
Expand Down Expand Up @@ -406,6 +422,13 @@ export type CreateWatcherRequest =
* @generated from field: bool recursive = 2;
*/
recursive: boolean

/**
* If true, each FilesystemEvent includes the EntryInfo of the affected entry, when available.
*
* @generated from field: bool include_entry = 3;
*/
includeEntry: boolean
}

/**
Expand Down
76 changes: 37 additions & 39 deletions packages/js-sdk/src/sandbox/filesystem/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import { authenticationHeader, handleRpcError } from '../../envd/rpc'

import { EnvdApiClient } from '../../envd/api'
import {
EntryInfo as FsEntryInfo,
Filesystem as FilesystemService,
FileType as FsFileType,
} from '../../envd/filesystem/filesystem_pb'
Expand Down Expand Up @@ -153,6 +154,24 @@ function mapModifiedTime(modifiedTime: Timestamp | undefined) {
)
}

/**
* Map a protobuf `EntryInfo` to the SDK `EntryInfo`.
*/
export function mapEntryInfo(entry: FsEntryInfo): EntryInfo {
return {
name: entry.name,
type: mapFileType(entry.type),
path: entry.path,
size: Number(entry.size),
mode: entry.mode,
permissions: entry.permissions,
owner: entry.owner,
group: entry.group,
modifiedTime: mapModifiedTime(entry.modifiedTime),
symlinkTarget: entry.symlinkTarget,
}
}

/**
* Options for the sandbox filesystem operations.
*/
Expand Down Expand Up @@ -218,6 +237,16 @@ export interface WatchOpts extends FilesystemRequestOpts {
* Watch the directory recursively
*/
recursive?: boolean
/**
* Include the {@link EntryInfo} of the affected entry in each {@link FilesystemEvent}.
*
* The entry is populated best-effort and may be `undefined` for events where the
* entry no longer exists at the path (e.g. remove or rename-away events).
*
* Requires envd 0.6.2 or later. When the sandbox's envd version doesn't support it,
* the option is ignored and `event.entry` stays `undefined`.
*/
includeEntry?: boolean
}

/**
Expand Down Expand Up @@ -577,22 +606,12 @@ export class Filesystem {
const entries: EntryInfo[] = []

for (const e of res.entries) {
const type = mapFileType(e.type)

if (type) {
entries.push({
name: e.name,
type,
path: e.path,
size: Number(e.size),
mode: e.mode,
permissions: e.permissions,
owner: e.owner,
group: e.group,
modifiedTime: mapModifiedTime(e.modifiedTime),
symlinkTarget: e.symlinkTarget,
})
// Skip entries with an unknown file type.
if (!mapFileType(e.type)) {
continue
}

entries.push(mapEntryInfo(e))
}

return entries
Expand Down Expand Up @@ -668,18 +687,7 @@ export class Filesystem {
throw new Error('Expected to receive information about moved object')
}

return {
name: entry.name,
type: mapFileType(entry.type),
path: entry.path,
size: Number(entry.size),
mode: entry.mode,
permissions: entry.permissions,
owner: entry.owner,
group: entry.group,
modifiedTime: mapModifiedTime(entry.modifiedTime),
symlinkTarget: entry.symlinkTarget,
}
return mapEntryInfo(entry)
} catch (err) {
throw handleFilesystemRpcError(err)
}
Expand Down Expand Up @@ -771,18 +779,7 @@ export class Filesystem {
)
}

return {
name: res.entry.name,
type: mapFileType(res.entry.type),
path: res.entry.path,
size: Number(res.entry.size),
mode: res.entry.mode,
permissions: res.entry.permissions,
owner: res.entry.owner,
group: res.entry.group,
modifiedTime: mapModifiedTime(res.entry.modifiedTime),
symlinkTarget: res.entry.symlinkTarget,
}
return mapEntryInfo(res.entry)
} catch (err) {
throw handleFilesystemRpcError(err)
}
Expand Down Expand Up @@ -827,6 +824,7 @@ export class Filesystem {
{
path,
recursive: opts?.recursive ?? this.defaultWatchRecursive,
includeEntry: opts?.includeEntry ?? false,
},
{
headers: {
Expand Down
12 changes: 12 additions & 0 deletions packages/js-sdk/src/sandbox/filesystem/watchHandle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {
EventType,
WatchDirResponse,
} from '../../envd/filesystem/filesystem_pb'
import { EntryInfo, mapEntryInfo } from './index'

/**
* Sandbox filesystem event types.
Expand Down Expand Up @@ -57,6 +58,14 @@ export interface FilesystemEvent {
* Filesystem operation event type.
*/
type: FilesystemEventType
/**
* Information about the entry that triggered the event.
*
* Only populated when the watch was started with `includeEntry: true` and the
* sandbox's envd version supports it. It may be `undefined` for events where the
* entry no longer exists at the path (e.g. remove or rename-away events).
*/
entry?: EntryInfo
}

/**
Expand Down Expand Up @@ -106,6 +115,9 @@ export class WatchHandle {
this.onEvent?.({
name: event.value.name,
type: eventType,
entry: event.value.entry
? mapEntryInfo(event.value.entry)
: undefined,
})
}
this.onExit?.()
Expand Down
39 changes: 39 additions & 0 deletions packages/js-sdk/tests/sandbox/files/watch.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
import { isDebug, sandboxTest } from '../../setup.js'
import {
FileNotFoundError,
FilesystemEvent,
FilesystemEventType,
FileType,
SandboxError,
} from '../../../src'

Expand Down Expand Up @@ -138,6 +140,43 @@
}
)

sandboxTest('watch directory changes with entry info', async ({ sandbox }) => {
const dirname = 'test_watch_dir_entry'
const filename = 'test_watch.txt'
const content = 'This file will be watched.'
const newContent = 'This file has been modified.'

await sandbox.files.makeDir(dirname)
await sandbox.files.write(`${dirname}/${filename}`, content)

let resolveEvent: (event: FilesystemEvent) => void
const eventPromise = new Promise<FilesystemEvent>((resolve) => {
resolveEvent = resolve
})

const handle = await sandbox.files.watchDir(
dirname,
async (event) => {
if (event.type === FilesystemEventType.WRITE && event.name === filename) {
resolveEvent(event)
}
},
{ includeEntry: true }
)

await sandbox.files.write(`${dirname}/${filename}`, newContent)

const event = await eventPromise

// The entry is populated best-effort for events where the path still exists.
expect(event.entry).toBeDefined()

Check failure on line 172 in packages/js-sdk/tests/sandbox/files/watch.test.ts

View workflow job for this annotation

GitHub Actions / Production / JS SDK Tests / JS SDK - Build and test (windows-latest)

[unit] tests/sandbox/files/watch.test.ts > watch directory changes with entry info

AssertionError: expected undefined to be defined ❯ tests/sandbox/files/watch.test.ts:172:23

Check failure on line 172 in packages/js-sdk/tests/sandbox/files/watch.test.ts

View workflow job for this annotation

GitHub Actions / Production / JS SDK Tests / JS SDK - Build and test (ubuntu-22.04)

[unit] tests/sandbox/files/watch.test.ts > watch directory changes with entry info

AssertionError: expected undefined to be defined ❯ tests/sandbox/files/watch.test.ts:172:23
expect(event.entry?.name).toBe(filename)
expect(event.entry?.path).toBe(`/home/user/${dirname}/${filename}`)
expect(event.entry?.type).toBe(FileType.FILE)

await handle.stop()
})

sandboxTest('watch non-existing directory', async ({ sandbox }) => {
const dirname = 'non_existing_watch_dir'

Expand Down
Loading
Loading