multiple file watch fixes.
This commit is contained in:
parent
77ec29e250
commit
cd243c1b9e
5 changed files with 80 additions and 16 deletions
|
|
@ -2,6 +2,15 @@
|
||||||
|
|
||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
|
|
||||||
|
## [0.6.2] - Slifer (2026-03-24)
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- File watcher system with WebSocket-based live sync for external vault changes
|
||||||
|
- Real-time detection of file create, modify, delete, and rename operations
|
||||||
|
- Echo guard to suppress events from local operations (prevents feedback loops)
|
||||||
|
- Automatic reconnection with exponential backoff for WebSocket client
|
||||||
|
|
||||||
## [0.6.1] - Slifer (2026-03-24)
|
## [0.6.1] - Slifer (2026-03-24)
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "ignis",
|
"name": "ignis",
|
||||||
"version": "0.6.1",
|
"version": "0.6.2",
|
||||||
"private": true,
|
"private": true,
|
||||||
"description": "An Electron shim and server bridge for running Obsidian in a browser.",
|
"description": "An Electron shim and server bridge for running Obsidian in a browser.",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|
|
||||||
|
|
@ -279,9 +279,12 @@ router.delete("/unlink", async (req, res) => {
|
||||||
|
|
||||||
res.json({ ok: true });
|
res.json({ ok: true });
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
const status = e.code === "ENOENT" ? 404 : 500;
|
if (e.code === "ENOENT") {
|
||||||
|
// File already gone - desired outcome achieved
|
||||||
res.status(status).json({ error: e.message, code: e.code });
|
res.json({ ok: true });
|
||||||
|
} else {
|
||||||
|
res.status(500).json({ error: e.message, code: e.code });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,29 +11,65 @@ export function createFsWatch(transport) {
|
||||||
if (!watchers.has(path)) {
|
if (!watchers.has(path)) {
|
||||||
watchers.set(path, new Set());
|
watchers.set(path, new Set());
|
||||||
}
|
}
|
||||||
watchers.get(path).add(listener);
|
|
||||||
|
|
||||||
// TODO: send watch subscription to server via transport
|
// Wrapper that holds both direct listener and .on() listeners
|
||||||
|
const entry = {
|
||||||
|
direct: typeof listener === "function" ? listener : null,
|
||||||
|
eventListeners: new Map(), // event name -> Set<fn>
|
||||||
|
call(eventType, filename) {
|
||||||
|
if (this.direct) {
|
||||||
|
this.direct(eventType, filename);
|
||||||
|
}
|
||||||
|
const fns = this.eventListeners.get("change");
|
||||||
|
if (fns) {
|
||||||
|
for (const fn of fns) {
|
||||||
|
try {
|
||||||
|
fn(eventType, filename);
|
||||||
|
} catch (e) {
|
||||||
|
console.error("[shim:fs:watch] Listener error:", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
watchers.get(path).add(entry);
|
||||||
|
|
||||||
// Return a watcher-like object
|
// Return a watcher-like object
|
||||||
return {
|
return {
|
||||||
close() {
|
close() {
|
||||||
const set = watchers.get(path);
|
const set = watchers.get(path);
|
||||||
if (set) {
|
if (set) {
|
||||||
set.delete(listener);
|
set.delete(entry);
|
||||||
|
|
||||||
if (set.size === 0) {
|
if (set.size === 0) {
|
||||||
watchers.delete(path);
|
watchers.delete(path);
|
||||||
// TODO: send unwatch to server
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
on() {
|
on(event, fn) {
|
||||||
|
if (!entry.eventListeners.has(event)) {
|
||||||
|
entry.eventListeners.set(event, new Set());
|
||||||
|
}
|
||||||
|
|
||||||
|
entry.eventListeners.get(event).add(fn);
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
},
|
},
|
||||||
once() {
|
once(event, fn) {
|
||||||
return this;
|
const wrapped = (...args) => {
|
||||||
|
this.removeListener(event, wrapped);
|
||||||
|
fn(...args);
|
||||||
|
};
|
||||||
|
|
||||||
|
return this.on(event, wrapped);
|
||||||
},
|
},
|
||||||
removeListener() {
|
removeListener(event, fn) {
|
||||||
|
const fns = entry.eventListeners.get(event);
|
||||||
|
|
||||||
|
if (fns) {
|
||||||
|
fns.delete(fn);
|
||||||
|
}
|
||||||
return this;
|
return this;
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
@ -41,12 +77,27 @@ export function createFsWatch(transport) {
|
||||||
|
|
||||||
// Internal: called when transport receives a file-change event
|
// Internal: called when transport receives a file-change event
|
||||||
_dispatch(eventType, filePath) {
|
_dispatch(eventType, filePath) {
|
||||||
|
const normFile = (filePath || "").replace(/^\/+/, "");
|
||||||
|
let matched = false;
|
||||||
|
|
||||||
for (const [watchPath, listeners] of watchers) {
|
for (const [watchPath, listeners] of watchers) {
|
||||||
if (filePath === watchPath || filePath.startsWith(watchPath + "/")) {
|
const normWatch = (watchPath || "").replace(/^\/+/, "");
|
||||||
const relativeName = filePath.slice(watchPath.length + 1) || filePath;
|
// Empty normWatch means root watcher - matches everything
|
||||||
for (const fn of listeners) {
|
const isMatch =
|
||||||
|
normWatch === "" ||
|
||||||
|
normFile === normWatch ||
|
||||||
|
normFile.startsWith(normWatch + "/");
|
||||||
|
|
||||||
|
if (isMatch) {
|
||||||
|
matched = true;
|
||||||
|
const relativeName =
|
||||||
|
normWatch === ""
|
||||||
|
? normFile
|
||||||
|
: normFile.slice(normWatch.length + 1) || normFile;
|
||||||
|
|
||||||
|
for (const entry of listeners) {
|
||||||
try {
|
try {
|
||||||
fn(eventType, relativeName);
|
entry.call(eventType, relativeName);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error("[shim:fs:watch] Listener error:", e);
|
console.error("[shim:fs:watch] Listener error:", e);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
import { installRequire } from "./require.js";
|
import { installRequire } from "./require.js";
|
||||||
import { installGlobals } from "./globals.js";
|
import { installGlobals } from "./globals.js";
|
||||||
import { initialize } from "./init.js";
|
import { initialize } from "./init.js";
|
||||||
|
import { fsShim } from "./fs/index.js";
|
||||||
|
|
||||||
installGlobals(); // process, Buffer, window overrides (before require so Buffer is available)
|
installGlobals(); // process, Buffer, window overrides (before require so Buffer is available)
|
||||||
installRequire(); // shim registry, window.require
|
installRequire(); // shim registry, window.require
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue