@foldkit/vite-plugin 0.3.1 → 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 +244 -150
- 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,174 +1,268 @@
|
|
|
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
|
-
|
|
49
|
-
|
|
204
|
+
if ('code' in error && error.code === 'EADDRINUSE') {
|
|
205
|
+
console.error(`\n[foldkit:devTools] Port ${port} is already in use, so the DevTools MCP relay could not start.\n` +
|
|
206
|
+
`[foldkit:devTools] This usually means another Foldkit project is already running and bound to this port.\n` +
|
|
207
|
+
`[foldkit:devTools] Until the port is freed, agents will not be able to connect to this app via the Foldkit DevTools MCP server.\n` +
|
|
208
|
+
`[foldkit:devTools] Stop the other project, or set a different \`devToolsMcpPort\` in this project's vite config.\n`);
|
|
209
|
+
}
|
|
210
|
+
else {
|
|
211
|
+
console.error(`[foldkit:devTools] MCP relay failed to start on port ${port}; continuing without the relay`, error);
|
|
212
|
+
}
|
|
50
213
|
});
|
|
51
214
|
wss.on('connection', client => {
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
client.on('message', raw => {
|
|
56
|
-
handleMcpMessage(client, raw.toString());
|
|
57
|
-
});
|
|
58
|
-
client.on('close', () => {
|
|
59
|
-
Effect.runSync(Ref.update(mcpClientsRef, HashSet.remove(client)));
|
|
60
|
-
const remaining = HashSet.size(Effect.runSync(Ref.get(mcpClientsRef)));
|
|
61
|
-
console.log(`[foldkit:devTools] MCP client disconnected (${remaining} remaining)`);
|
|
62
|
-
});
|
|
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 })));
|
|
63
218
|
client.on('error', error => {
|
|
64
219
|
console.error('[foldkit:devTools] MCP client error', error);
|
|
65
220
|
});
|
|
66
221
|
});
|
|
67
|
-
|
|
68
|
-
};
|
|
69
|
-
const stopMcpWebSocketServer = () => {
|
|
70
|
-
const clients = Effect.runSync(Ref.get(mcpClientsRef));
|
|
71
|
-
HashSet.forEach(clients, client => {
|
|
72
|
-
client.close();
|
|
222
|
+
wss.on('listening', () => {
|
|
223
|
+
console.log(`[foldkit:devTools] MCP relay listening on ws://localhost:${port}`);
|
|
73
224
|
});
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
225
|
+
return wss;
|
|
226
|
+
}), wss => Effect.sync(() => {
|
|
227
|
+
wss.close();
|
|
77
228
|
console.log('[foldkit:devTools] MCP relay stopped');
|
|
78
|
-
};
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
})),
|
|
89
|
-
});
|
|
90
|
-
};
|
|
91
|
-
const ensureClientTracked = (client) => {
|
|
92
|
-
const tracked = Effect.runSync(Ref.get(trackedClientsRef));
|
|
93
|
-
if (HashSet.has(tracked, client)) {
|
|
94
|
-
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);
|
|
95
239
|
}
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
const
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
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
|
+
},
|
|
105
252
|
}),
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
const decoded = S.decodeUnknownEither(ResponseFrame)(data);
|
|
112
|
-
Either.match(decoded, {
|
|
113
|
-
onLeft: error => {
|
|
114
|
-
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
|
+
});
|
|
115
258
|
},
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
return HashMap.set(currentMap, client, HashSet.add(existing, event.runtime.connectionId));
|
|
124
|
-
}));
|
|
125
|
-
console.log(`[foldkit:devTools] runtime connected: ${event.runtime.connectionId} (${event.runtime.title})`);
|
|
126
|
-
};
|
|
127
|
-
const handleDisconnectedEvent = (event) => {
|
|
128
|
-
Effect.runSync(Ref.update(connectedRuntimesRef, HashMap.remove(event.connectionId)));
|
|
129
|
-
console.log(`[foldkit:devTools] runtime disconnected: ${event.connectionId}`);
|
|
130
|
-
};
|
|
131
|
-
const broadcastToMcpClients = (payload) => {
|
|
132
|
-
const clients = Effect.runSync(Ref.get(mcpClientsRef));
|
|
133
|
-
HashSet.forEach(clients, client => {
|
|
134
|
-
if (client.readyState === client.OPEN) {
|
|
135
|
-
client.send(payload);
|
|
136
|
-
}
|
|
137
|
-
});
|
|
138
|
-
};
|
|
139
|
-
const forwardResponseToMcpClients = (responseFrame) => {
|
|
140
|
-
broadcastToMcpClients(JSON.stringify(responseFrame));
|
|
141
|
-
};
|
|
142
|
-
const handleMcpMessage = (client, raw) => {
|
|
143
|
-
const decoded = S.decodeUnknownEither(S.parseJson(RequestFrame))(raw);
|
|
144
|
-
Either.match(decoded, {
|
|
145
|
-
onLeft: error => {
|
|
146
|
-
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 [];
|
|
147
266
|
},
|
|
148
|
-
onRight: frame => Match.value(frame.request).pipe(Match.tag('RequestListRuntimes', () => replyListRuntimes(client, frame.id)), Match.orElse(() => forwardRequestToBrowsers(frame))),
|
|
149
|
-
});
|
|
150
|
-
};
|
|
151
|
-
const replyListRuntimes = (client, requestId) => {
|
|
152
|
-
const runtimes = pipe(Effect.runSync(Ref.get(connectedRuntimesRef)), HashMap.values, Array.fromIterable);
|
|
153
|
-
const responseFrame = {
|
|
154
|
-
id: requestId,
|
|
155
|
-
response: ResponseRuntimes({ runtimes }),
|
|
156
267
|
};
|
|
157
|
-
if (client.readyState === client.OPEN) {
|
|
158
|
-
client.send(JSON.stringify(responseFrame));
|
|
159
|
-
}
|
|
160
|
-
};
|
|
161
|
-
const forwardRequestToBrowsers = (frame) => {
|
|
162
|
-
if (viteServer === null) {
|
|
163
|
-
return;
|
|
164
|
-
}
|
|
165
|
-
viteServer.ws.send('foldkit:devTools:request', frame);
|
|
166
|
-
};
|
|
167
|
-
const handleHotUpdate = ({ server, modules, }) => {
|
|
168
|
-
if (modules.length === 0) {
|
|
169
|
-
return;
|
|
170
|
-
}
|
|
171
|
-
isHmrReload = true;
|
|
172
|
-
server.ws.send({ type: 'full-reload' });
|
|
173
|
-
return [];
|
|
174
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",
|