@jxsuite/server 0.28.5 → 0.30.1
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 +9 -8
- package/src/claude-session.ts +2 -2
- package/src/code-api.ts +11 -6
- package/src/refactor/apply.ts +215 -0
- package/src/refactor/fs-events.ts +71 -0
- package/src/refactor/index.ts +11 -0
- package/src/refactor/paths.ts +187 -0
- package/src/refactor/refs.ts +236 -0
- package/src/refactor/watcher.ts +70 -0
- package/src/resolve.ts +41 -12
- package/src/server.ts +26 -14
- package/src/studio-api.ts +199 -91
- package/src/watch.ts +33 -2
package/src/watch.ts
CHANGED
|
@@ -3,7 +3,9 @@
|
|
|
3
3
|
import { watch as chokidarWatch } from "chokidar";
|
|
4
4
|
import { relative } from "node:path";
|
|
5
5
|
import { rebuild } from "./build.ts";
|
|
6
|
+
import { coalesceFsEvents, toFsEvent } from "./refactor/fs-events.ts";
|
|
6
7
|
import type { BuildEntry } from "./types.ts";
|
|
8
|
+
import type { FsEventPayload } from "./refactor/fs-events.ts";
|
|
7
9
|
|
|
8
10
|
const DEFAULT_IGNORE = [
|
|
9
11
|
"**/node_modules/**",
|
|
@@ -86,6 +88,17 @@ export function createWatcher(
|
|
|
86
88
|
}
|
|
87
89
|
}
|
|
88
90
|
|
|
91
|
+
/**
|
|
92
|
+
* Send a _named_ SSE event. The preview iframe only listens to the default (unnamed) `onmessage`,
|
|
93
|
+
* so named events (e.g. "fs") reach the studio shell without triggering a preview reload.
|
|
94
|
+
*/
|
|
95
|
+
function broadcastEvent(event: string, payload: unknown) {
|
|
96
|
+
const frame = `event: ${event}\ndata: ${JSON.stringify(payload)}\n\n`;
|
|
97
|
+
for (const send of clients) {
|
|
98
|
+
send(frame);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
89
102
|
function handleSSE() {
|
|
90
103
|
/** @type {((msg: string) => void) | undefined} */
|
|
91
104
|
let send: ((msg: string) => void) | undefined;
|
|
@@ -121,6 +134,9 @@ export function createWatcher(
|
|
|
121
134
|
}
|
|
122
135
|
|
|
123
136
|
let timer: ReturnType<typeof setTimeout> | null = null;
|
|
137
|
+
let fsTimer: ReturnType<typeof setTimeout> | null = null;
|
|
138
|
+
let fsBuffer: FsEventPayload[] = [];
|
|
139
|
+
const fsDebounceMs = 40;
|
|
124
140
|
const watcher = chokidarWatch(root, {
|
|
125
141
|
awaitWriteFinish: {
|
|
126
142
|
pollInterval: 10,
|
|
@@ -131,11 +147,26 @@ export function createWatcher(
|
|
|
131
147
|
ignored: (watchedPath) => shouldIgnore(watchedPath, ignore),
|
|
132
148
|
});
|
|
133
149
|
|
|
134
|
-
watcher.on("all", (
|
|
150
|
+
watcher.on("all", (eventType, changedPath) => {
|
|
135
151
|
const filename = relative(root, changedPath);
|
|
136
152
|
if (!filename || filename.startsWith("..")) {
|
|
137
153
|
return;
|
|
138
154
|
}
|
|
155
|
+
|
|
156
|
+
// Structured FS events for the studio sidebar (coalesced + batched), emitted as a named "fs"
|
|
157
|
+
// SSE event so the preview iframe ignores them while the studio shell subscribes to them.
|
|
158
|
+
const fsEvent = toFsEvent(eventType, root, changedPath);
|
|
159
|
+
if (fsEvent) {
|
|
160
|
+
fsBuffer = coalesceFsEvents(fsBuffer, fsEvent);
|
|
161
|
+
clearTimeout(fsTimer ?? undefined);
|
|
162
|
+
fsTimer = setTimeout(() => {
|
|
163
|
+
if (fsBuffer.length > 0) {
|
|
164
|
+
broadcastEvent("fs", { events: fsBuffer });
|
|
165
|
+
fsBuffer = [];
|
|
166
|
+
}
|
|
167
|
+
}, fsDebounceMs);
|
|
168
|
+
}
|
|
169
|
+
|
|
139
170
|
clearTimeout(timer ?? undefined);
|
|
140
171
|
timer = setTimeout(async () => {
|
|
141
172
|
if (builds.length > 0) {
|
|
@@ -155,5 +186,5 @@ export function createWatcher(
|
|
|
155
186
|
}, debounceMs);
|
|
156
187
|
});
|
|
157
188
|
|
|
158
|
-
return { broadcast, handleSSE, watcher };
|
|
189
|
+
return { broadcast, broadcastEvent, handleSSE, watcher };
|
|
159
190
|
}
|