refactor(fs)!: drop notify's serialization-compat-6, flatten WatchEvent (#3609)

`WatchEvent` now follows notify v8's format: the event kind is flattened
into the event, with `type` holding the top-level kind and `kind`/`mode`
refining it, e.g. `{ type: 'modify', kind: 'data', mode: 'content' }`
instead of `{ type: { modify: { kind: 'data', mode: 'content' } } }`.
`attrs` is now typed as `WatchEventAttributes` and its `flag` is
`rescan` instead of `Rescan`.
This commit is contained in:
Lucas Fernandes Nogueira
2026-09-22 06:05:51 -03:00
committed by GitHub
parent a315a07f36
commit 8083107a0a
4 changed files with 81 additions and 14 deletions
+6
View File
@@ -0,0 +1,6 @@
---
"fs": major
"fs-js": major
---
**Breaking:** The `notify` crate's `serialization-compat-6` feature is no longer enabled, so `WatchEvent` now follows notify v8's format: the event kind is flattened into the event, with `type` holding the top-level kind (`any`, `access`, `create`, `modify`, `remove` or `other`) and `kind`/`mode` refining it. For instance `{ type: { modify: { kind: 'data', mode: 'content' } } }` is now `{ type: 'modify', kind: 'data', mode: 'content' }`, and `attrs.flag` is `rescan` instead of `Rescan`. `WatchEvent.attrs` is now typed as `WatchEventAttributes`.
+1 -5
View File
@@ -38,11 +38,7 @@ url = { workspace = true }
log = { workspace = true }
anyhow = "1"
glob = { workspace = true }
# TODO: Remove `serialization-compat-6` in v3
notify = { version = "8", optional = true, features = [
"serde",
"serialization-compat-6",
] }
notify = { version = "8", optional = true, features = ["serde"] }
notify-debouncer-full = { version = "0.6", optional = true }
dunce = { workspace = true }
percent-encoding = "2"
+43 -9
View File
@@ -1184,24 +1184,57 @@ interface DebouncedWatchOptions extends WatchOptions {
}
/**
* Additional attributes of a {@linkcode WatchEvent}.
*
* @since 3.0.0
*/
interface WatchEventAttributes {
/** Tracker ID that groups related events, e.g. both sides of a rename. */
tracker?: number
/**
* `rescan` means some events may have been missed, so any file or folder might have been modified.
*/
flag?: 'rescan'
/** Short string identifying the details of an `other` event. */
info?: string
/** Short string identifying the backend that generated the event. */
source?: string
}
/**
* A file system event.
*
* The event kind is flattened into the event: `type` is the top-level kind and,
* for `access`, `create`, `modify` and `remove` events, `kind` (and `mode` when available)
* refines it.
*
* @example
* ```typescript
* import { watch } from '@tauri-apps/plugin-fs';
* await watch('/path/to/file', (event) => {
* if (event.type === 'modify' && event.kind === 'data') {
* console.log('data changed', event.paths, event.mode);
* }
* });
* ```
*
* @since 2.0.0
*/
interface WatchEvent {
type: WatchEventKind
type WatchEvent = WatchEventKind & {
paths: string[]
attrs: unknown
attrs: WatchEventAttributes
}
/**
* @since 2.0.0
*/
type WatchEventKind =
| 'any'
| { access: WatchEventKindAccess }
| { create: WatchEventKindCreate }
| { modify: WatchEventKindModify }
| { remove: WatchEventKindRemove }
| 'other'
| { type: 'any' }
| ({ type: 'access' } & WatchEventKindAccess)
| ({ type: 'create' } & WatchEventKindCreate)
| ({ type: 'modify' } & WatchEventKindModify)
| ({ type: 'remove' } & WatchEventKindRemove)
| { type: 'other' }
/**
* @since 2.0.0
@@ -1437,6 +1470,7 @@ export type {
WatchOptions,
DebouncedWatchOptions,
WatchEvent,
WatchEventAttributes,
WatchEventKind,
WatchEventKindAccess,
WatchEventKindCreate,
+31
View File
@@ -102,3 +102,34 @@ pub fn watch<R: Runtime>(
Ok(rid)
}
#[cfg(test)]
mod tests {
use notify::{
event::{DataChange, ModifyKind},
Event, EventKind,
};
// the `WatchEvent` type of the JavaScript API relies on this format
#[test]
fn event_kind_is_flattened_into_the_event() {
let event = Event::new(EventKind::Modify(ModifyKind::Data(DataChange::Content)))
.add_path("/tmp/file".into());
assert_eq!(
serde_json::to_value(event).unwrap(),
serde_json::json!({
"type": "modify",
"kind": "data",
"mode": "content",
"paths": ["/tmp/file"],
"attrs": {}
})
);
let event = Event::new(EventKind::Any);
assert_eq!(
serde_json::to_value(event).unwrap(),
serde_json::json!({ "type": "any", "paths": [], "attrs": {} })
);
}
}