@cueloop/daemon 0.1.0-alpha.106 → 0.1.0-alpha.108
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/package.json +4 -4
- package/src/diff-watcher.test.ts +46 -0
- package/src/diff-watcher.ts +10 -6
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cueloop/daemon",
|
|
3
|
-
"version": "0.1.0-alpha.
|
|
3
|
+
"version": "0.1.0-alpha.108",
|
|
4
4
|
"description": "The cueloop session daemon and its client: socket protocol, atomic store, resumable waits",
|
|
5
5
|
"homepage": "https://github.com/mmurakaru/cueloop#readme",
|
|
6
6
|
"bugs": {
|
|
@@ -44,10 +44,10 @@
|
|
|
44
44
|
"access": "public"
|
|
45
45
|
},
|
|
46
46
|
"dependencies": {
|
|
47
|
-
"@cueloop/extension-api": "0.1.0-alpha.
|
|
48
|
-
"@cueloop/schema": "0.1.0-alpha.
|
|
47
|
+
"@cueloop/extension-api": "0.1.0-alpha.108",
|
|
48
|
+
"@cueloop/schema": "0.1.0-alpha.108",
|
|
49
49
|
"@pierre/diffs": "^1.5.2",
|
|
50
|
-
"effect": "^4.0.
|
|
50
|
+
"effect": "^4.0.5",
|
|
51
51
|
"smol-toml": "^1.9.1",
|
|
52
52
|
"valibot": "^1.5.0"
|
|
53
53
|
},
|
package/src/diff-watcher.test.ts
CHANGED
|
@@ -117,3 +117,49 @@ test("replacing a watch during reconciliation stops the old directory traversal"
|
|
|
117
117
|
fs.rmSync(repo, { recursive: true, force: true });
|
|
118
118
|
}
|
|
119
119
|
});
|
|
120
|
+
|
|
121
|
+
/** A watch listener as the runtime calls it: Bun on Linux can pass an undefined filename. */
|
|
122
|
+
type RuntimeWatchListener = (event: fs.WatchEventType, filename: string | null | undefined) => void;
|
|
123
|
+
|
|
124
|
+
test("a change without a filename still refreshes and watches a new directory", async () => {
|
|
125
|
+
const repo = fs.mkdtempSync(join(tmpdir(), "cueloop-watch-unnamed-"));
|
|
126
|
+
const watch = fs.watch;
|
|
127
|
+
const listeners = new Map<string, RuntimeWatchListener>();
|
|
128
|
+
let refreshes = 0;
|
|
129
|
+
const startup = Promise.withResolvers<void>();
|
|
130
|
+
const change = Promise.withResolvers<void>();
|
|
131
|
+
const watcher = new DiffWatcher(() => {
|
|
132
|
+
refreshes++;
|
|
133
|
+
|
|
134
|
+
if (refreshes === 1) startup.resolve();
|
|
135
|
+
else change.resolve();
|
|
136
|
+
});
|
|
137
|
+
// SAFETY: DiffWatcher always calls the string-filename, three-argument watch overload; the
|
|
138
|
+
// listener is typed as the runtime calls it, which may pass an undefined filename.
|
|
139
|
+
const spy = spyOn(fs, "watch").mockImplementation(((
|
|
140
|
+
path: fs.PathLike,
|
|
141
|
+
options: fs.WatchOptions,
|
|
142
|
+
listener: RuntimeWatchListener,
|
|
143
|
+
) => {
|
|
144
|
+
listeners.set(String(path), listener);
|
|
145
|
+
|
|
146
|
+
return watch(path, options, () => {});
|
|
147
|
+
}) as typeof fs.watch);
|
|
148
|
+
|
|
149
|
+
try {
|
|
150
|
+
watcher.trackDiffRepo(repo, "session");
|
|
151
|
+
await startup.promise;
|
|
152
|
+
const onRootChange = listeners.get(repo);
|
|
153
|
+
|
|
154
|
+
expect(onRootChange).toBeDefined();
|
|
155
|
+
fs.mkdirSync(join(repo, "fresh"));
|
|
156
|
+
expect(() => onRootChange?.("rename", undefined)).not.toThrow();
|
|
157
|
+
await change.promise;
|
|
158
|
+
expect(refreshes).toBe(2);
|
|
159
|
+
expect(listeners.has(join(repo, "fresh"))).toBe(true);
|
|
160
|
+
} finally {
|
|
161
|
+
watcher.close();
|
|
162
|
+
spy.mockRestore();
|
|
163
|
+
fs.rmSync(repo, { recursive: true, force: true });
|
|
164
|
+
}
|
|
165
|
+
});
|
package/src/diff-watcher.ts
CHANGED
|
@@ -65,7 +65,7 @@ function spawnGit(args: string[], cwd: string): { exitCode: number; stdout: Buff
|
|
|
65
65
|
}
|
|
66
66
|
|
|
67
67
|
/** The git-metadata filenames whose change means `git diff HEAD` moved: a checkout, a commit, or a reset. */
|
|
68
|
-
function isRefChange(filename: string): boolean {
|
|
68
|
+
function isRefChange(filename: string | null): boolean {
|
|
69
69
|
return filename === "HEAD" || filename === "packed-refs";
|
|
70
70
|
}
|
|
71
71
|
|
|
@@ -198,9 +198,12 @@ export class DiffWatcher {
|
|
|
198
198
|
if (ALWAYS_IGNORED_DIRS.has(basename(dir)) || repoWatch.ignored.has(dir)) return true;
|
|
199
199
|
|
|
200
200
|
const opened = this.watchInto(repoWatch.handles, dir, { recursive: false }, (filename) => {
|
|
201
|
-
this.scheduleRepoRefresh(repoRoot);
|
|
202
201
|
// a newly created subdirectory needs its own watch (a recursive watch got this for free)
|
|
203
|
-
this.extendWatch(repoRoot, join(dir, filename));
|
|
202
|
+
if (filename !== null) this.extendWatch(repoRoot, join(dir, filename));
|
|
203
|
+
// no name for the change: rescan the tree on the next refresh so a new directory is not missed
|
|
204
|
+
else repoWatch.reconcilePending = true;
|
|
205
|
+
|
|
206
|
+
this.scheduleRepoRefresh(repoRoot);
|
|
204
207
|
});
|
|
205
208
|
|
|
206
209
|
if (!opened) return false;
|
|
@@ -278,17 +281,18 @@ export class DiffWatcher {
|
|
|
278
281
|
* Open one watcher and add it to `handles`; returns whether it opened. A
|
|
279
282
|
* watcher error (the path is deleted mid-review) is swallowed - hot-reload is
|
|
280
283
|
* best-effort and must never take down the daemon. The inherited EventEmitter
|
|
281
|
-
* `on` is absent from Bun's FSWatcher type.
|
|
284
|
+
* `on` is absent from Bun's FSWatcher type. The platform may omit the changed
|
|
285
|
+
* name (Bun on Linux passes undefined, Node passes null); `onFile` then gets null.
|
|
282
286
|
*/
|
|
283
287
|
private watchInto(
|
|
284
288
|
handles: FSWatcher[],
|
|
285
289
|
path: string,
|
|
286
290
|
options: { recursive: boolean },
|
|
287
|
-
onFile: (filename: string) => void,
|
|
291
|
+
onFile: (filename: string | null) => void,
|
|
288
292
|
): boolean {
|
|
289
293
|
try {
|
|
290
294
|
const handle = watch(path, options, (_event, filename) => {
|
|
291
|
-
|
|
295
|
+
onFile(filename == null ? null : filename.toString());
|
|
292
296
|
});
|
|
293
297
|
|
|
294
298
|
handle.on("error", () => {});
|