@foldkit/vite-plugin 0.3.2 → 0.4.0
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/dist/index.d.ts.map +1 -1
- package/dist/index.js +233 -149
- package/package.json +4 -4
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AA+BA,OAAO,KAAK,EAAE,MAAM,EAAkC,MAAM,MAAM,CAAA;AAGlE,6CAA6C;AAC7C,MAAM,MAAM,oBAAoB,GAAG,QAAQ,CAAC;IAC1C;;;;;OAKG;IACH,eAAe,CAAC,EAAE,MAAM,CAAA;CACzB,CAAC,CAAA;AAofF,eAAO,MAAM,OAAO,GAAI,UAAS,oBAAyB,KAAG,MAgC5D,CAAA"}
|
package/dist/index.js
CHANGED
|
@@ -1,49 +1,205 @@
|
|
|
1
|
-
import { Array, Effect,
|
|
1
|
+
import { Array, Console, Data, Effect, Exit, Fiber, HashMap, HashSet, Match as M, Option, Queue, Ref, Schema as S, Stream, pipe, } from 'effect';
|
|
2
2
|
import { EventFrame, RequestFrame, ResponseFrame, ResponseRuntimes, } from 'foldkit/devtools-protocol';
|
|
3
|
+
import { PreserveModelMessage, RequestModelMessage, RestoreModelMessage, } from 'foldkit/hmr-protocol';
|
|
3
4
|
import { WebSocketServer } from 'ws';
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
5
|
+
// NOTE: Vite's dep optimizer scans the consumer's source for `effect`
|
|
6
|
+
// imports and pre-bundles only those exports into a single `effect.js`
|
|
7
|
+
// blob. It does not follow imports through workspace/node_modules
|
|
8
|
+
// packages, so any `effect` namespace foldkit's compiled dist references
|
|
9
|
+
// that the consumer does not mention by name is missing from the blob
|
|
10
|
+
// and crashes at runtime in dev. The list below covers every top-level
|
|
11
|
+
// namespace foldkit imports from bare `'effect'`. Over-inclusion is
|
|
12
|
+
// harmless; under-inclusion is the bug. Kept in sync with foldkit's
|
|
13
|
+
// source by `scripts/check-effect-prebundle.ts` (runs in `pnpm check`).
|
|
14
|
+
const FORCE_INCLUDED_EFFECT_NAMESPACES = [
|
|
15
|
+
'effect/Array',
|
|
16
|
+
'effect/Cause',
|
|
17
|
+
'effect/Clock',
|
|
18
|
+
'effect/Context',
|
|
19
|
+
'effect/Data',
|
|
20
|
+
'effect/DateTime',
|
|
21
|
+
'effect/Duration',
|
|
22
|
+
'effect/Effect',
|
|
23
|
+
'effect/Equal',
|
|
24
|
+
'effect/Equivalence',
|
|
25
|
+
'effect/Exit',
|
|
26
|
+
'effect/Fiber',
|
|
27
|
+
'effect/Function',
|
|
28
|
+
'effect/Hash',
|
|
29
|
+
'effect/HashMap',
|
|
30
|
+
'effect/HashSet',
|
|
31
|
+
'effect/Layer',
|
|
32
|
+
'effect/Match',
|
|
33
|
+
'effect/Number',
|
|
34
|
+
'effect/Option',
|
|
35
|
+
'effect/Order',
|
|
36
|
+
'effect/Predicate',
|
|
37
|
+
'effect/PubSub',
|
|
38
|
+
'effect/Queue',
|
|
39
|
+
'effect/Record',
|
|
40
|
+
'effect/Ref',
|
|
41
|
+
'effect/Result',
|
|
42
|
+
'effect/Scheduler',
|
|
43
|
+
'effect/Schema',
|
|
44
|
+
'effect/SchemaIssue',
|
|
45
|
+
'effect/SchemaTransformation',
|
|
46
|
+
'effect/Stream',
|
|
47
|
+
'effect/String',
|
|
48
|
+
'effect/Struct',
|
|
49
|
+
'effect/SubscriptionRef',
|
|
50
|
+
'effect/Types',
|
|
51
|
+
];
|
|
52
|
+
const Event = Data.taggedEnum();
|
|
53
|
+
const makeState = Effect.gen(function* () {
|
|
54
|
+
const preservedModels = yield* Ref.make(HashMap.empty());
|
|
55
|
+
const connectedRuntimes = yield* Ref.make(HashMap.empty());
|
|
56
|
+
const mcpClients = yield* Ref.make(HashSet.empty());
|
|
57
|
+
const clientConnections = yield* Ref.make(HashMap.empty());
|
|
58
|
+
const trackedClients = yield* Ref.make(HashSet.empty());
|
|
59
|
+
const state = {
|
|
60
|
+
preservedModels,
|
|
61
|
+
connectedRuntimes,
|
|
62
|
+
mcpClients,
|
|
63
|
+
clientConnections,
|
|
64
|
+
trackedClients,
|
|
18
65
|
};
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
66
|
+
return state;
|
|
67
|
+
});
|
|
68
|
+
const encodeResponseFrameJson = S.encodeUnknownSync(S.fromJsonString(ResponseFrame));
|
|
69
|
+
// HANDLERS
|
|
70
|
+
const handlePreserveModelReceived = (state, payload) => Exit.match(S.decodeUnknownExit(PreserveModelMessage)(payload), {
|
|
71
|
+
onFailure: error => Console.warn('[foldkit:hmr] failed to decode preserve-model payload', error),
|
|
72
|
+
onSuccess: ({ id, model }) => {
|
|
73
|
+
const entry = { model, isHmrReload: false };
|
|
74
|
+
return Ref.update(state.preservedModels, HashMap.set(id, entry));
|
|
75
|
+
},
|
|
76
|
+
});
|
|
77
|
+
const handleRequestModelReceived = (server, state, payload) => Exit.match(S.decodeUnknownExit(RequestModelMessage)(payload), {
|
|
78
|
+
onFailure: error => Console.warn('[foldkit:hmr] failed to decode request-model payload', error),
|
|
79
|
+
onSuccess: ({ id }) => Effect.gen(function* () {
|
|
80
|
+
const current = yield* Ref.get(state.preservedModels);
|
|
81
|
+
const sendRestore = (model) => Effect.sync(() => server.ws.send('foldkit:restore-model', S.encodeUnknownSync(RestoreModelMessage)(RestoreModelMessage.make({ id, model }))));
|
|
82
|
+
yield* Option.match(HashMap.get(current, id), {
|
|
83
|
+
onNone: () => sendRestore(undefined),
|
|
84
|
+
onSome: entry => {
|
|
85
|
+
if (entry.isHmrReload) {
|
|
86
|
+
const served = { ...entry, isHmrReload: false };
|
|
87
|
+
return Ref.update(state.preservedModels, HashMap.set(id, served)).pipe(Effect.flatMap(() => sendRestore(entry.model)));
|
|
88
|
+
}
|
|
89
|
+
return Ref.update(state.preservedModels, HashMap.remove(id)).pipe(Effect.flatMap(() => sendRestore(undefined)));
|
|
90
|
+
},
|
|
91
|
+
});
|
|
92
|
+
}),
|
|
93
|
+
});
|
|
94
|
+
const handleHotUpdateFired = (state) => Ref.update(state.preservedModels, current => HashMap.map(current, entry => ({ ...entry, isHmrReload: true })));
|
|
95
|
+
const handleBrowserEventFrameReceived = (state, data, client) => Exit.match(S.decodeUnknownExit(EventFrame)(data), {
|
|
96
|
+
onFailure: error => Console.warn('[foldkit:devTools] failed to decode browser event frame', error),
|
|
97
|
+
onSuccess: frame => M.value(frame.event).pipe(M.tagsExhaustive({
|
|
98
|
+
EventConnected: event => handleConnectedEvent(state, event, client),
|
|
99
|
+
EventDisconnected: event => handleDisconnectedEvent(state, event),
|
|
100
|
+
})),
|
|
101
|
+
});
|
|
102
|
+
const handleConnectedEvent = (state, event, client) => Effect.gen(function* () {
|
|
103
|
+
yield* Ref.update(state.connectedRuntimes, HashMap.set(event.runtime.connectionId, event.runtime));
|
|
104
|
+
yield* Ref.update(state.clientConnections, currentMap => {
|
|
105
|
+
const existing = HashMap.get(currentMap, client).pipe(Option.getOrElse(() => HashSet.empty()));
|
|
106
|
+
return HashMap.set(currentMap, client, HashSet.add(existing, event.runtime.connectionId));
|
|
24
107
|
});
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
108
|
+
yield* Console.log(`[foldkit:devTools] runtime connected: ${event.runtime.connectionId} (${event.runtime.title})`);
|
|
109
|
+
});
|
|
110
|
+
const handleDisconnectedEvent = (state, event) => Effect.gen(function* () {
|
|
111
|
+
yield* Ref.update(state.connectedRuntimes, HashMap.remove(event.connectionId));
|
|
112
|
+
yield* Console.log(`[foldkit:devTools] runtime disconnected: ${event.connectionId}`);
|
|
113
|
+
});
|
|
114
|
+
const pruneRuntime = (state, connectionId) => Effect.gen(function* () {
|
|
115
|
+
yield* Ref.update(state.connectedRuntimes, HashMap.remove(connectionId));
|
|
116
|
+
yield* Console.log(`[foldkit:devTools] runtime pruned (socket close): ${connectionId}`);
|
|
117
|
+
});
|
|
118
|
+
const pruneRuntimesForClient = (state, connectionIds) => Effect.forEach(Array.fromIterable(connectionIds), connectionId => pruneRuntime(state, connectionId), { discard: true });
|
|
119
|
+
const handleViteClientClosed = (state, client) => Effect.gen(function* () {
|
|
120
|
+
const connections = yield* Ref.get(state.clientConnections);
|
|
121
|
+
yield* Option.match(HashMap.get(connections, client), {
|
|
122
|
+
onNone: () => Effect.void,
|
|
123
|
+
onSome: connectionIds => pruneRuntimesForClient(state, connectionIds),
|
|
31
124
|
});
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
125
|
+
yield* Ref.update(state.clientConnections, HashMap.remove(client));
|
|
126
|
+
yield* Ref.update(state.trackedClients, HashSet.remove(client));
|
|
127
|
+
});
|
|
128
|
+
const handleBrowserResponseFrameReceived = (state, data) => Exit.match(S.decodeUnknownExit(ResponseFrame)(data), {
|
|
129
|
+
onFailure: error => Console.warn('[foldkit:devTools] failed to decode browser response frame', error),
|
|
130
|
+
onSuccess: frame => broadcastResponseToMcpClients(state, frame),
|
|
131
|
+
});
|
|
132
|
+
const broadcastResponseToMcpClients = (state, frame) => Effect.gen(function* () {
|
|
133
|
+
const clients = yield* Ref.get(state.mcpClients);
|
|
134
|
+
const payload = encodeResponseFrameJson(frame);
|
|
135
|
+
yield* Effect.sync(() => {
|
|
136
|
+
for (const client of clients) {
|
|
137
|
+
if (client.readyState === client.OPEN) {
|
|
138
|
+
client.send(payload);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
35
141
|
});
|
|
36
|
-
|
|
37
|
-
|
|
142
|
+
});
|
|
143
|
+
const handleMcpClientConnected = (state, client) => Effect.gen(function* () {
|
|
144
|
+
yield* Ref.update(state.mcpClients, HashSet.add(client));
|
|
145
|
+
const total = HashSet.size(yield* Ref.get(state.mcpClients));
|
|
146
|
+
yield* Console.log(`[foldkit:devTools] MCP client connected (${total} total)`);
|
|
147
|
+
});
|
|
148
|
+
const handleMcpClientDisconnected = (state, client) => Effect.gen(function* () {
|
|
149
|
+
yield* Ref.update(state.mcpClients, HashSet.remove(client));
|
|
150
|
+
const remaining = HashSet.size(yield* Ref.get(state.mcpClients));
|
|
151
|
+
yield* Console.log(`[foldkit:devTools] MCP client disconnected (${remaining} remaining)`);
|
|
152
|
+
});
|
|
153
|
+
const handleMcpRequestReceived = (server, state, client, raw) => Exit.match(S.decodeUnknownExit(S.fromJsonString(RequestFrame))(raw), {
|
|
154
|
+
onFailure: error => Console.warn('[foldkit:devTools] failed to decode MCP request frame', error),
|
|
155
|
+
onSuccess: frame => M.value(frame.request).pipe(M.tag('RequestListRuntimes', () => replyListRuntimes(state, client, frame.id)), M.orElse(() => forwardRequestToBrowsers(server, frame))),
|
|
156
|
+
});
|
|
157
|
+
const replyListRuntimes = (state, client, requestId) => Effect.gen(function* () {
|
|
158
|
+
const runtimes = pipe(yield* Ref.get(state.connectedRuntimes), HashMap.values, Array.fromIterable);
|
|
159
|
+
const responseFrame = {
|
|
160
|
+
id: requestId,
|
|
161
|
+
response: ResponseRuntimes({ runtimes }),
|
|
162
|
+
};
|
|
163
|
+
yield* Effect.sync(() => {
|
|
164
|
+
if (client.readyState === client.OPEN) {
|
|
165
|
+
client.send(encodeResponseFrameJson(responseFrame));
|
|
166
|
+
}
|
|
38
167
|
});
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
168
|
+
});
|
|
169
|
+
const forwardRequestToBrowsers = (server, frame) => Effect.sync(() => server.ws.send('foldkit:devTools:request', S.encodeUnknownSync(RequestFrame)(frame)));
|
|
170
|
+
// EVENT DISPATCH
|
|
171
|
+
const dispatchEvent = (server, state, event) => M.value(event).pipe(M.tagsExhaustive({
|
|
172
|
+
PreserveModelReceived: ({ payload }) => handlePreserveModelReceived(state, payload),
|
|
173
|
+
RequestModelReceived: ({ payload }) => handleRequestModelReceived(server, state, payload),
|
|
174
|
+
BrowserEventFrameReceived: ({ data, client }) => handleBrowserEventFrameReceived(state, data, client),
|
|
175
|
+
BrowserResponseFrameReceived: ({ data }) => handleBrowserResponseFrameReceived(state, data),
|
|
176
|
+
ViteClientClosed: ({ client }) => handleViteClientClosed(state, client),
|
|
177
|
+
HotUpdateFired: () => handleHotUpdateFired(state),
|
|
178
|
+
McpClientConnected: ({ client }) => handleMcpClientConnected(state, client),
|
|
179
|
+
McpClientDisconnected: ({ client }) => handleMcpClientDisconnected(state, client),
|
|
180
|
+
McpRequestReceived: ({ client, raw }) => handleMcpRequestReceived(server, state, client, raw),
|
|
181
|
+
}));
|
|
182
|
+
// VITE WS BRIDGE
|
|
183
|
+
const ensureClientTracked = (state, client, enqueue) => Effect.gen(function* () {
|
|
184
|
+
const tracked = yield* Ref.get(state.trackedClients);
|
|
185
|
+
if (HashSet.has(tracked, client)) {
|
|
186
|
+
return;
|
|
42
187
|
}
|
|
43
|
-
|
|
44
|
-
|
|
188
|
+
yield* Ref.update(state.trackedClients, HashSet.add(client));
|
|
189
|
+
yield* Effect.sync(() => client.socket.on('close', () => enqueue(Event.ViteClientClosed({ client }))));
|
|
190
|
+
});
|
|
191
|
+
const registerViteWsHandlers = (server, state, enqueue) => Effect.sync(() => {
|
|
192
|
+
server.ws.on('foldkit:preserve-model', payload => enqueue(Event.PreserveModelReceived({ payload })));
|
|
193
|
+
server.ws.on('foldkit:request-model', payload => enqueue(Event.RequestModelReceived({ payload })));
|
|
194
|
+
server.ws.on('foldkit:devTools:event', (data, client) => {
|
|
195
|
+
Effect.runFork(ensureClientTracked(state, client, enqueue));
|
|
196
|
+
enqueue(Event.BrowserEventFrameReceived({ data, client }));
|
|
197
|
+
});
|
|
198
|
+
server.ws.on('foldkit:devTools:response', (data) => enqueue(Event.BrowserResponseFrameReceived({ data })));
|
|
199
|
+
});
|
|
200
|
+
// MCP RELAY
|
|
201
|
+
const startMcpRelay = (port, enqueue) => Effect.acquireRelease(Effect.sync(() => {
|
|
45
202
|
const wss = new WebSocketServer({ port });
|
|
46
|
-
mcpWebSocketServer = wss;
|
|
47
203
|
wss.on('error', error => {
|
|
48
204
|
if ('code' in error && error.code === 'EADDRINUSE') {
|
|
49
205
|
console.error(`\n[foldkit:devTools] Port ${port} is already in use, so the DevTools MCP relay could not start.\n` +
|
|
@@ -54,20 +210,11 @@ const startMcpWebSocketServer = (port) => {
|
|
|
54
210
|
else {
|
|
55
211
|
console.error(`[foldkit:devTools] MCP relay failed to start on port ${port}; continuing without the relay`, error);
|
|
56
212
|
}
|
|
57
|
-
mcpWebSocketServer = null;
|
|
58
213
|
});
|
|
59
214
|
wss.on('connection', client => {
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
client.on('message', raw => {
|
|
64
|
-
handleMcpMessage(client, raw.toString());
|
|
65
|
-
});
|
|
66
|
-
client.on('close', () => {
|
|
67
|
-
Effect.runSync(Ref.update(mcpClientsRef, HashSet.remove(client)));
|
|
68
|
-
const remaining = HashSet.size(Effect.runSync(Ref.get(mcpClientsRef)));
|
|
69
|
-
console.log(`[foldkit:devTools] MCP client disconnected (${remaining} remaining)`);
|
|
70
|
-
});
|
|
215
|
+
enqueue(Event.McpClientConnected({ client }));
|
|
216
|
+
client.on('message', raw => enqueue(Event.McpRequestReceived({ client, raw: raw.toString() })));
|
|
217
|
+
client.on('close', () => enqueue(Event.McpClientDisconnected({ client })));
|
|
71
218
|
client.on('error', error => {
|
|
72
219
|
console.error('[foldkit:devTools] MCP client error', error);
|
|
73
220
|
});
|
|
@@ -75,110 +222,47 @@ const startMcpWebSocketServer = (port) => {
|
|
|
75
222
|
wss.on('listening', () => {
|
|
76
223
|
console.log(`[foldkit:devTools] MCP relay listening on ws://localhost:${port}`);
|
|
77
224
|
});
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
HashSet.forEach(clients, client => {
|
|
82
|
-
client.close();
|
|
83
|
-
});
|
|
84
|
-
Effect.runSync(Ref.set(mcpClientsRef, HashSet.empty()));
|
|
85
|
-
mcpWebSocketServer?.close();
|
|
86
|
-
mcpWebSocketServer = null;
|
|
225
|
+
return wss;
|
|
226
|
+
}), wss => Effect.sync(() => {
|
|
227
|
+
wss.close();
|
|
87
228
|
console.log('[foldkit:devTools] MCP relay stopped');
|
|
88
|
-
};
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
})),
|
|
99
|
-
});
|
|
100
|
-
};
|
|
101
|
-
const ensureClientTracked = (client) => {
|
|
102
|
-
const tracked = Effect.runSync(Ref.get(trackedClientsRef));
|
|
103
|
-
if (HashSet.has(tracked, client)) {
|
|
104
|
-
return;
|
|
229
|
+
}));
|
|
230
|
+
// PROGRAM
|
|
231
|
+
const main = (server, events, options) => Effect.gen(function* () {
|
|
232
|
+
const state = yield* makeState;
|
|
233
|
+
const enqueue = (event) => {
|
|
234
|
+
Queue.offerUnsafe(events, event);
|
|
235
|
+
};
|
|
236
|
+
yield* registerViteWsHandlers(server, state, enqueue);
|
|
237
|
+
if (options.devToolsMcpPort !== undefined) {
|
|
238
|
+
yield* startMcpRelay(options.devToolsMcpPort, enqueue);
|
|
105
239
|
}
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
const
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
240
|
+
yield* Stream.fromQueue(events).pipe(Stream.runForEach(event => dispatchEvent(server, state, event)));
|
|
241
|
+
});
|
|
242
|
+
// PLUGIN ENTRY
|
|
243
|
+
export const foldkit = (options = {}) => {
|
|
244
|
+
const events = Effect.runSync(Queue.unbounded());
|
|
245
|
+
return {
|
|
246
|
+
name: 'foldkit-hmr',
|
|
247
|
+
apply: 'serve',
|
|
248
|
+
config: () => ({
|
|
249
|
+
optimizeDeps: {
|
|
250
|
+
include: [...FORCE_INCLUDED_EFFECT_NAMESPACES],
|
|
251
|
+
},
|
|
115
252
|
}),
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
const decoded = S.decodeUnknownEither(ResponseFrame)(data);
|
|
122
|
-
Either.match(decoded, {
|
|
123
|
-
onLeft: error => {
|
|
124
|
-
console.warn('[foldkit:devTools] failed to decode browser response frame', error);
|
|
253
|
+
configureServer: server => {
|
|
254
|
+
const fiber = Effect.runFork(Effect.scoped(main(server, events, options)));
|
|
255
|
+
server.httpServer?.on('close', () => {
|
|
256
|
+
Effect.runFork(Fiber.interrupt(fiber));
|
|
257
|
+
});
|
|
125
258
|
},
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
return HashMap.set(currentMap, client, HashSet.add(existing, event.runtime.connectionId));
|
|
134
|
-
}));
|
|
135
|
-
console.log(`[foldkit:devTools] runtime connected: ${event.runtime.connectionId} (${event.runtime.title})`);
|
|
136
|
-
};
|
|
137
|
-
const handleDisconnectedEvent = (event) => {
|
|
138
|
-
Effect.runSync(Ref.update(connectedRuntimesRef, HashMap.remove(event.connectionId)));
|
|
139
|
-
console.log(`[foldkit:devTools] runtime disconnected: ${event.connectionId}`);
|
|
140
|
-
};
|
|
141
|
-
const broadcastToMcpClients = (payload) => {
|
|
142
|
-
const clients = Effect.runSync(Ref.get(mcpClientsRef));
|
|
143
|
-
HashSet.forEach(clients, client => {
|
|
144
|
-
if (client.readyState === client.OPEN) {
|
|
145
|
-
client.send(payload);
|
|
146
|
-
}
|
|
147
|
-
});
|
|
148
|
-
};
|
|
149
|
-
const forwardResponseToMcpClients = (responseFrame) => {
|
|
150
|
-
broadcastToMcpClients(JSON.stringify(responseFrame));
|
|
151
|
-
};
|
|
152
|
-
const handleMcpMessage = (client, raw) => {
|
|
153
|
-
const decoded = S.decodeUnknownEither(S.parseJson(RequestFrame))(raw);
|
|
154
|
-
Either.match(decoded, {
|
|
155
|
-
onLeft: error => {
|
|
156
|
-
console.warn('[foldkit:devTools] failed to decode MCP request frame', error);
|
|
259
|
+
handleHotUpdate: ({ server, modules, }) => {
|
|
260
|
+
if (modules.length === 0) {
|
|
261
|
+
return;
|
|
262
|
+
}
|
|
263
|
+
server.ws.send({ type: 'full-reload' });
|
|
264
|
+
Queue.offerUnsafe(events, Event.HotUpdateFired());
|
|
265
|
+
return [];
|
|
157
266
|
},
|
|
158
|
-
onRight: frame => Match.value(frame.request).pipe(Match.tag('RequestListRuntimes', () => replyListRuntimes(client, frame.id)), Match.orElse(() => forwardRequestToBrowsers(frame))),
|
|
159
|
-
});
|
|
160
|
-
};
|
|
161
|
-
const replyListRuntimes = (client, requestId) => {
|
|
162
|
-
const runtimes = pipe(Effect.runSync(Ref.get(connectedRuntimesRef)), HashMap.values, Array.fromIterable);
|
|
163
|
-
const responseFrame = {
|
|
164
|
-
id: requestId,
|
|
165
|
-
response: ResponseRuntimes({ runtimes }),
|
|
166
267
|
};
|
|
167
|
-
if (client.readyState === client.OPEN) {
|
|
168
|
-
client.send(JSON.stringify(responseFrame));
|
|
169
|
-
}
|
|
170
|
-
};
|
|
171
|
-
const forwardRequestToBrowsers = (frame) => {
|
|
172
|
-
if (viteServer === null) {
|
|
173
|
-
return;
|
|
174
|
-
}
|
|
175
|
-
viteServer.ws.send('foldkit:devTools:request', frame);
|
|
176
|
-
};
|
|
177
|
-
const handleHotUpdate = ({ server, modules, }) => {
|
|
178
|
-
if (modules.length === 0) {
|
|
179
|
-
return;
|
|
180
|
-
}
|
|
181
|
-
isHmrReload = true;
|
|
182
|
-
server.ws.send({ type: 'full-reload' });
|
|
183
|
-
return [];
|
|
184
268
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@foldkit/vite-plugin",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "Vite plugin for Foldkit hot module reloading with state preservation",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
"dist"
|
|
17
17
|
],
|
|
18
18
|
"peerDependencies": {
|
|
19
|
-
"effect": "
|
|
19
|
+
"effect": "4.0.0-beta.59",
|
|
20
20
|
"foldkit": "^0",
|
|
21
21
|
"vite": "^7.0.0"
|
|
22
22
|
},
|
|
@@ -25,10 +25,10 @@
|
|
|
25
25
|
},
|
|
26
26
|
"devDependencies": {
|
|
27
27
|
"@types/ws": "^8.5.13",
|
|
28
|
-
"effect": "
|
|
28
|
+
"effect": "4.0.0-beta.59",
|
|
29
29
|
"typescript": "^6.0.2",
|
|
30
30
|
"vite": "^7.3.1",
|
|
31
|
-
"foldkit": "0.
|
|
31
|
+
"foldkit": "0.82.0"
|
|
32
32
|
},
|
|
33
33
|
"keywords": [
|
|
34
34
|
"vite",
|