@concavejs/devtools 0.0.1-alpha.10
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/README.md +263 -0
- package/dist/client-CRYWrjNv.js +3590 -0
- package/dist/client.d.ts +7 -0
- package/dist/client.js +6 -0
- package/dist/extension/page-agent.d.ts +8 -0
- package/dist/extension/panel.d.ts +14 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.js +486 -0
- package/dist/interceptor/index.d.ts +5 -0
- package/dist/interceptor/websocket-interceptor.d.ts +68 -0
- package/dist/overlay/ActivityPanel.d.ts +18 -0
- package/dist/overlay/ContextMenu.d.ts +23 -0
- package/dist/overlay/DataInspector.d.ts +12 -0
- package/dist/overlay/DevToolbar.d.ts +19 -0
- package/dist/overlay/LogsPanel.d.ts +10 -0
- package/dist/overlay/NetworkPanel.d.ts +9 -0
- package/dist/overlay/PerformancePanel.d.ts +10 -0
- package/dist/overlay/SearchField.d.ts +9 -0
- package/dist/overlay/SettingsPanel.d.ts +9 -0
- package/dist/overlay/SubscriptionsPanel.d.ts +9 -0
- package/dist/overlay/TimelinePanel.d.ts +9 -0
- package/dist/overlay/TraceViewer.d.ts +7 -0
- package/dist/overlay/utils.d.ts +30 -0
- package/dist/standalone.d.ts +11 -0
- package/dist/store/event-store.d.ts +191 -0
- package/dist/store/safe-storage.d.ts +7 -0
- package/dist/types.d.ts +211 -0
- package/dist/version.d.ts +10 -0
- package/dist/vite-plugin/index.d.ts +16 -0
- package/dist/vite-plugin.js +668 -0
- package/package.json +62 -0
package/dist/client.d.ts
ADDED
package/dist/client.js
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Page Agent — Thin IIFE injected into the page context.
|
|
3
|
+
*
|
|
4
|
+
* Intercepts WebSocket traffic using the same WebSocketInterceptor,
|
|
5
|
+
* but instead of a full EventStore it posts events to the content script
|
|
6
|
+
* via window.postMessage. Buffers the last 500 events for late-opening panels.
|
|
7
|
+
*/
|
|
8
|
+
export {};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DevTools Panel Entry — React app rendered inside the Chrome DevTools "Concave" tab.
|
|
3
|
+
*
|
|
4
|
+
* Uses chrome.devtools.inspectedWindow.reload({ injectedScript }) to install a
|
|
5
|
+
* WebSocket interceptor BEFORE any page scripts run. This guarantees full
|
|
6
|
+
* interception without any permissions beyond the devtools_page manifest key.
|
|
7
|
+
*
|
|
8
|
+
* Flow:
|
|
9
|
+
* 1. Panel opens → try eval() injection (catches new WS connections)
|
|
10
|
+
* 2. If page already has a connection → show "Reload" button
|
|
11
|
+
* 3. Reload uses inspectedWindow.reload({ injectedScript }) — runs before page JS
|
|
12
|
+
* 4. On navigation → re-inject via eval()
|
|
13
|
+
*/
|
|
14
|
+
import "../devtools.css";
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Concave DevTools
|
|
3
|
+
*
|
|
4
|
+
* Client-side development tools for Convex/Concave applications
|
|
5
|
+
*/
|
|
6
|
+
export type { DevToolsEvent, QueryEvent, MutationEvent, ActionEvent, SubscriptionEvent, LogEvent, AuthEvent, ActiveSubscription, PerformanceMetrics, EventType, EventStatus, SubscriptionStatus, } from "./types";
|
|
7
|
+
export { EventStore, getGlobalEventStore } from "./store/event-store";
|
|
8
|
+
export type { EventListener, StoreSnapshot, ExportedSession } from "./store/event-store";
|
|
9
|
+
export { WebSocketInterceptor } from "./interceptor/websocket-interceptor";
|
|
10
|
+
export { DevToolbar } from "./overlay/DevToolbar";
|
|
11
|
+
export { SubscriptionsPanel } from "./overlay/SubscriptionsPanel";
|
|
12
|
+
export { PerformancePanel } from "./overlay/PerformancePanel";
|
|
13
|
+
export { LogsPanel } from "./overlay/LogsPanel";
|
|
14
|
+
export { SettingsPanel } from "./overlay/SettingsPanel";
|
|
15
|
+
export { DataInspector } from "./overlay/DataInspector";
|
|
16
|
+
export { initDevTools } from "./client";
|
|
17
|
+
export { default as concaveDevTools } from "./vite-plugin/index";
|
|
18
|
+
export type { DevToolsOptions } from "./vite-plugin/index";
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,486 @@
|
|
|
1
|
+
import { b as M, D as k, E as A, L as D, P as $, a as C, S as R, g as W, i as O } from "./client-CRYWrjNv.js";
|
|
2
|
+
import { concaveDevTools as N } from "./vite-plugin.js";
|
|
3
|
+
function P(c) {
|
|
4
|
+
const t = c.split(".");
|
|
5
|
+
if (t.length < 2) return null;
|
|
6
|
+
try {
|
|
7
|
+
const n = t[1].replace(/-/g, "+").replace(/_/g, "/"), e = n + "=".repeat((4 - n.length % 4) % 4), o = JSON.parse(atob(e)), s = ["iss", "sub", "aud", "exp", "iat", "nbf"], i = {};
|
|
8
|
+
for (const a of s) {
|
|
9
|
+
const d = o[a];
|
|
10
|
+
d !== void 0 && (i[a] = d);
|
|
11
|
+
}
|
|
12
|
+
return typeof i.exp == "number" && (i.expIso = new Date(i.exp * 1e3).toISOString()), typeof i.iat == "number" && (i.iatIso = new Date(i.iat * 1e3).toISOString()), Object.keys(i).length > 0 ? i : null;
|
|
13
|
+
} catch {
|
|
14
|
+
return null;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
const v = /* @__PURE__ */ Symbol.for("__concaveDevToolsInternalWsListener"), m = /* @__PURE__ */ Symbol.for("__concaveDevToolsWsDelayMs");
|
|
18
|
+
function q() {
|
|
19
|
+
const c = window.__concaveDevToolsLatencyConfig;
|
|
20
|
+
if (!c) return 0;
|
|
21
|
+
const t = (s) => {
|
|
22
|
+
const i = Number(s);
|
|
23
|
+
return !Number.isFinite(i) || i <= 0 ? 0 : Math.min(i, 5e3);
|
|
24
|
+
}, n = t(c.latencyMs);
|
|
25
|
+
let e = t(c.jitterMs);
|
|
26
|
+
if (n <= 0 && e <= 0) return 0;
|
|
27
|
+
n > 0 && e > n && (e = n);
|
|
28
|
+
const o = e > 0 ? (Math.random() * 2 - 1) * e : 0;
|
|
29
|
+
return Math.max(0, n + o);
|
|
30
|
+
}
|
|
31
|
+
function T(c) {
|
|
32
|
+
const t = c[m];
|
|
33
|
+
if (typeof t == "number" && Number.isFinite(t) && t >= 0)
|
|
34
|
+
return t;
|
|
35
|
+
const n = q();
|
|
36
|
+
try {
|
|
37
|
+
Object.defineProperty(c, m, {
|
|
38
|
+
value: n,
|
|
39
|
+
configurable: !0
|
|
40
|
+
});
|
|
41
|
+
} catch {
|
|
42
|
+
c[m] = n;
|
|
43
|
+
}
|
|
44
|
+
return n;
|
|
45
|
+
}
|
|
46
|
+
function E(c, t, n) {
|
|
47
|
+
if (c[v]) {
|
|
48
|
+
c.call(t, n);
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
let e = 0;
|
|
52
|
+
try {
|
|
53
|
+
e = T(n);
|
|
54
|
+
} catch {
|
|
55
|
+
e = 0;
|
|
56
|
+
}
|
|
57
|
+
if (e <= 0) {
|
|
58
|
+
c.call(t, n);
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
setTimeout(() => {
|
|
62
|
+
c.call(t, n);
|
|
63
|
+
}, e);
|
|
64
|
+
}
|
|
65
|
+
class L {
|
|
66
|
+
eventStore;
|
|
67
|
+
originalWebSocket;
|
|
68
|
+
pendingQueries = /* @__PURE__ */ new Map();
|
|
69
|
+
pendingMutations = /* @__PURE__ */ new Map();
|
|
70
|
+
pendingActions = /* @__PURE__ */ new Map();
|
|
71
|
+
pendingAuthTokenType = null;
|
|
72
|
+
/** Tracks the most recent mutation event ID for causality linking */
|
|
73
|
+
lastMutationEventId = null;
|
|
74
|
+
lastMutationTimestamp = 0;
|
|
75
|
+
constructor(t) {
|
|
76
|
+
this.eventStore = t, this.originalWebSocket = window.WebSocket;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Install the WebSocket interceptor
|
|
80
|
+
*/
|
|
81
|
+
install() {
|
|
82
|
+
const t = this;
|
|
83
|
+
window.__concaveDevToolsLatencyConfig || (window.__concaveDevToolsLatencyConfig = { latencyMs: 0, jitterMs: 0 });
|
|
84
|
+
const n = this.originalWebSocket;
|
|
85
|
+
class e extends n {
|
|
86
|
+
constructor(s, i) {
|
|
87
|
+
super(s, i);
|
|
88
|
+
const a = this.addEventListener.bind(this), d = this.removeEventListener.bind(this), g = /* @__PURE__ */ new WeakMap();
|
|
89
|
+
this.addEventListener = (u, r, h) => {
|
|
90
|
+
if (u === "message" && typeof r == "function") {
|
|
91
|
+
let l = g.get(r);
|
|
92
|
+
l || (l = (y) => {
|
|
93
|
+
try {
|
|
94
|
+
const f = T(y);
|
|
95
|
+
t.handleServerMessage(y.data, f);
|
|
96
|
+
} catch (f) {
|
|
97
|
+
console.warn("[DevTools] Error parsing server message:", f);
|
|
98
|
+
}
|
|
99
|
+
E(r, this, y);
|
|
100
|
+
}, l[v] = !0, g.set(r, l)), a("message", l, h);
|
|
101
|
+
} else
|
|
102
|
+
a(u, r, h);
|
|
103
|
+
}, this.removeEventListener = (u, r, h) => {
|
|
104
|
+
if (u === "message" && typeof r == "function") {
|
|
105
|
+
const l = g.get(r);
|
|
106
|
+
d("message", l ?? r, h);
|
|
107
|
+
} else
|
|
108
|
+
d(u, r, h);
|
|
109
|
+
};
|
|
110
|
+
let b = null, p = null;
|
|
111
|
+
Object.defineProperty(this, "onmessage", {
|
|
112
|
+
get: () => b,
|
|
113
|
+
set: (u) => {
|
|
114
|
+
if (p && (d("message", p), p = null), b = u, u) {
|
|
115
|
+
const r = (h) => {
|
|
116
|
+
try {
|
|
117
|
+
const l = T(h);
|
|
118
|
+
t.handleServerMessage(h.data, l);
|
|
119
|
+
} catch (l) {
|
|
120
|
+
console.warn("[DevTools] Error parsing server message:", l);
|
|
121
|
+
}
|
|
122
|
+
E(u, this, h);
|
|
123
|
+
};
|
|
124
|
+
r[v] = !0, p = r, a("message", r);
|
|
125
|
+
}
|
|
126
|
+
},
|
|
127
|
+
configurable: !0,
|
|
128
|
+
enumerable: !0
|
|
129
|
+
});
|
|
130
|
+
const I = this.send.bind(this);
|
|
131
|
+
this.send = (u) => {
|
|
132
|
+
try {
|
|
133
|
+
typeof u == "string" && t.handleClientMessage(u);
|
|
134
|
+
} catch (r) {
|
|
135
|
+
console.warn("[DevTools] Error parsing client message:", r);
|
|
136
|
+
}
|
|
137
|
+
I(u);
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
window.WebSocket = e, console.log("[DevTools] WebSocket interceptor installed");
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Uninstall the interceptor
|
|
145
|
+
*/
|
|
146
|
+
uninstall() {
|
|
147
|
+
window.WebSocket = this.originalWebSocket;
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Handle client → server messages
|
|
151
|
+
*/
|
|
152
|
+
handleClientMessage(t) {
|
|
153
|
+
try {
|
|
154
|
+
const n = JSON.parse(t);
|
|
155
|
+
switch (console.log("[DevTools] Client message:", n.type, n), n.type) {
|
|
156
|
+
case "ModifyQuerySet":
|
|
157
|
+
this.handleModifyQuerySet(n.modifications);
|
|
158
|
+
break;
|
|
159
|
+
case "Mutation":
|
|
160
|
+
this.handleMutationRequest(n);
|
|
161
|
+
break;
|
|
162
|
+
case "Action":
|
|
163
|
+
this.handleActionRequest(n);
|
|
164
|
+
break;
|
|
165
|
+
case "Connect":
|
|
166
|
+
this.handleConnectMessage(n);
|
|
167
|
+
break;
|
|
168
|
+
case "Authenticate":
|
|
169
|
+
this.handleAuthenticateMessage(n);
|
|
170
|
+
break;
|
|
171
|
+
}
|
|
172
|
+
} catch (n) {
|
|
173
|
+
console.warn("[DevTools] Failed to parse client message:", n);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Handle server → client messages
|
|
178
|
+
*/
|
|
179
|
+
handleServerMessage(t, n = 0) {
|
|
180
|
+
try {
|
|
181
|
+
const e = JSON.parse(t);
|
|
182
|
+
switch (console.log("[DevTools] Server message:", e.type, e), e.type) {
|
|
183
|
+
case "Transition":
|
|
184
|
+
this.handleTransition(e, n);
|
|
185
|
+
break;
|
|
186
|
+
case "MutationResponse":
|
|
187
|
+
this.handleMutationResponse(e, n);
|
|
188
|
+
break;
|
|
189
|
+
case "ActionResponse":
|
|
190
|
+
this.handleActionResponse(e, n);
|
|
191
|
+
break;
|
|
192
|
+
case "AuthError":
|
|
193
|
+
this.handleAuthErrorResponse(e);
|
|
194
|
+
break;
|
|
195
|
+
}
|
|
196
|
+
} catch (e) {
|
|
197
|
+
console.warn("[DevTools] Failed to parse server message:", e);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Handle query set modifications (subscriptions)
|
|
202
|
+
*/
|
|
203
|
+
handleModifyQuerySet(t) {
|
|
204
|
+
const n = Date.now();
|
|
205
|
+
for (const e of t)
|
|
206
|
+
if (e.type === "Add") {
|
|
207
|
+
this.pendingQueries.set(e.queryId, {
|
|
208
|
+
queryId: e.queryId,
|
|
209
|
+
udfPath: e.udfPath,
|
|
210
|
+
args: e.args,
|
|
211
|
+
componentPath: e.componentPath,
|
|
212
|
+
startTime: n
|
|
213
|
+
});
|
|
214
|
+
const o = {
|
|
215
|
+
id: `sub-${e.queryId}-${n}`,
|
|
216
|
+
timestamp: n,
|
|
217
|
+
type: "subscription",
|
|
218
|
+
queryId: e.queryId,
|
|
219
|
+
udfPath: e.udfPath,
|
|
220
|
+
args: e.args,
|
|
221
|
+
componentPath: e.componentPath,
|
|
222
|
+
status: "added"
|
|
223
|
+
};
|
|
224
|
+
this.eventStore.addEvent(o);
|
|
225
|
+
const s = {
|
|
226
|
+
id: `query-pending-${e.queryId}-${n}`,
|
|
227
|
+
timestamp: n,
|
|
228
|
+
type: "query",
|
|
229
|
+
queryId: e.queryId,
|
|
230
|
+
udfPath: e.udfPath,
|
|
231
|
+
args: e.args,
|
|
232
|
+
componentPath: e.componentPath,
|
|
233
|
+
status: "pending"
|
|
234
|
+
};
|
|
235
|
+
this.eventStore.addEvent(s);
|
|
236
|
+
} else if (e.type === "Remove") {
|
|
237
|
+
const o = this.pendingQueries.get(e.queryId);
|
|
238
|
+
if (this.pendingQueries.delete(e.queryId), o) {
|
|
239
|
+
const s = {
|
|
240
|
+
id: `sub-${e.queryId}-${n}`,
|
|
241
|
+
timestamp: n,
|
|
242
|
+
type: "subscription",
|
|
243
|
+
queryId: e.queryId,
|
|
244
|
+
udfPath: o.udfPath,
|
|
245
|
+
args: o.args,
|
|
246
|
+
componentPath: o.componentPath,
|
|
247
|
+
status: "removed"
|
|
248
|
+
};
|
|
249
|
+
this.eventStore.addEvent(s);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
/**
|
|
254
|
+
* Handle mutation requests
|
|
255
|
+
*/
|
|
256
|
+
handleMutationRequest(t) {
|
|
257
|
+
const n = Date.now();
|
|
258
|
+
this.pendingMutations.set(t.requestId, {
|
|
259
|
+
requestId: t.requestId,
|
|
260
|
+
udfPath: t.udfPath,
|
|
261
|
+
args: t.args,
|
|
262
|
+
componentPath: t.componentPath,
|
|
263
|
+
startTime: n
|
|
264
|
+
});
|
|
265
|
+
const e = {
|
|
266
|
+
id: `mutation-pending-${t.requestId}-${n}`,
|
|
267
|
+
timestamp: n,
|
|
268
|
+
type: "mutation",
|
|
269
|
+
requestId: t.requestId,
|
|
270
|
+
udfPath: t.udfPath,
|
|
271
|
+
args: t.args,
|
|
272
|
+
componentPath: t.componentPath,
|
|
273
|
+
status: "pending"
|
|
274
|
+
};
|
|
275
|
+
this.eventStore.addEvent(e);
|
|
276
|
+
}
|
|
277
|
+
/**
|
|
278
|
+
* Handle action requests
|
|
279
|
+
*/
|
|
280
|
+
handleActionRequest(t) {
|
|
281
|
+
const n = Date.now();
|
|
282
|
+
this.pendingActions.set(t.requestId, {
|
|
283
|
+
requestId: t.requestId,
|
|
284
|
+
udfPath: t.udfPath,
|
|
285
|
+
args: t.args,
|
|
286
|
+
componentPath: t.componentPath,
|
|
287
|
+
startTime: n
|
|
288
|
+
});
|
|
289
|
+
const e = {
|
|
290
|
+
id: `action-pending-${t.requestId}-${n}`,
|
|
291
|
+
timestamp: n,
|
|
292
|
+
type: "action",
|
|
293
|
+
requestId: t.requestId,
|
|
294
|
+
udfPath: t.udfPath,
|
|
295
|
+
args: t.args,
|
|
296
|
+
componentPath: t.componentPath,
|
|
297
|
+
status: "pending"
|
|
298
|
+
};
|
|
299
|
+
this.eventStore.addEvent(e);
|
|
300
|
+
}
|
|
301
|
+
handleConnectMessage(t) {
|
|
302
|
+
const n = {};
|
|
303
|
+
typeof t.connectionCount == "number" && (n.connectionCount = t.connectionCount), typeof t.lastCloseReason == "string" && (n.lastCloseReason = t.lastCloseReason), typeof t.clientTs == "number" && (n.clientTs = t.clientTs), typeof t.sessionId == "string" && (n.sessionId = t.sessionId), this.emitAuthEvent({
|
|
304
|
+
direction: "client",
|
|
305
|
+
messageType: "Connect",
|
|
306
|
+
status: "success",
|
|
307
|
+
details: n
|
|
308
|
+
});
|
|
309
|
+
}
|
|
310
|
+
handleAuthenticateMessage(t) {
|
|
311
|
+
const n = {};
|
|
312
|
+
if (typeof t.baseVersion == "number" && (n.baseVersion = t.baseVersion), typeof t.tokenType == "string" && (n.tokenType = t.tokenType), typeof t.value == "string" && t.value.length > 0) {
|
|
313
|
+
const e = t.value;
|
|
314
|
+
n.tokenLength = e.length, n.tokenPreview = e.length > 24 ? `${e.slice(0, 12)}…${e.slice(-10)}` : e;
|
|
315
|
+
const o = P(e);
|
|
316
|
+
o && (n.jwtClaims = o);
|
|
317
|
+
}
|
|
318
|
+
this.pendingAuthTokenType = t.tokenType ?? "Unknown", this.emitAuthEvent({
|
|
319
|
+
direction: "client",
|
|
320
|
+
messageType: "Authenticate",
|
|
321
|
+
status: "success",
|
|
322
|
+
tokenType: t.tokenType,
|
|
323
|
+
details: n
|
|
324
|
+
});
|
|
325
|
+
}
|
|
326
|
+
/**
|
|
327
|
+
* Handle state transitions (query updates)
|
|
328
|
+
*/
|
|
329
|
+
handleTransition(t, n = 0) {
|
|
330
|
+
const e = Date.now();
|
|
331
|
+
this.pendingAuthTokenType && (this.emitAuthEvent({
|
|
332
|
+
direction: "server",
|
|
333
|
+
messageType: "Authenticated",
|
|
334
|
+
status: "success",
|
|
335
|
+
tokenType: this.pendingAuthTokenType,
|
|
336
|
+
details: { modificationCount: t.modifications.length }
|
|
337
|
+
}), this.pendingAuthTokenType = null);
|
|
338
|
+
for (const o of t.modifications)
|
|
339
|
+
if (o.type === "QueryUpdated") {
|
|
340
|
+
const s = this.pendingQueries.get(o.queryId);
|
|
341
|
+
if (!s) continue;
|
|
342
|
+
const i = e - s.startTime, a = this.resolveOperationTimings(i, n), d = this.lastMutationEventId && e - this.lastMutationTimestamp < 2e3 ? this.lastMutationEventId : void 0, g = {
|
|
343
|
+
id: `query-${o.queryId}-${e}`,
|
|
344
|
+
timestamp: e,
|
|
345
|
+
type: "query",
|
|
346
|
+
queryId: o.queryId,
|
|
347
|
+
udfPath: s.udfPath,
|
|
348
|
+
args: s.args,
|
|
349
|
+
componentPath: s.componentPath,
|
|
350
|
+
status: "success",
|
|
351
|
+
result: o.value,
|
|
352
|
+
logLines: o.logLines,
|
|
353
|
+
trace: o.trace,
|
|
354
|
+
...a,
|
|
355
|
+
triggeredBy: d
|
|
356
|
+
};
|
|
357
|
+
this.eventStore.addEvent(g), Array.isArray(o.logLines) && o.logLines.length > 0 && this.emitLogEvents(o.logLines, g.id);
|
|
358
|
+
} else if (o.type === "QueryFailed") {
|
|
359
|
+
const s = this.pendingQueries.get(o.queryId);
|
|
360
|
+
if (!s) continue;
|
|
361
|
+
const i = e - s.startTime, a = this.resolveOperationTimings(i, n), d = {
|
|
362
|
+
id: `query-${o.queryId}-${e}`,
|
|
363
|
+
timestamp: e,
|
|
364
|
+
type: "query",
|
|
365
|
+
queryId: o.queryId,
|
|
366
|
+
udfPath: s.udfPath,
|
|
367
|
+
args: s.args,
|
|
368
|
+
componentPath: s.componentPath,
|
|
369
|
+
status: "error",
|
|
370
|
+
error: o.errorMessage,
|
|
371
|
+
logLines: o.logLines,
|
|
372
|
+
...a
|
|
373
|
+
};
|
|
374
|
+
this.eventStore.addEvent(d), Array.isArray(o.logLines) && o.logLines.length > 0 && this.emitLogEvents(o.logLines, d.id);
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
/**
|
|
378
|
+
* Handle mutation responses
|
|
379
|
+
*/
|
|
380
|
+
handleMutationResponse(t, n = 0) {
|
|
381
|
+
const e = this.pendingMutations.get(t.requestId);
|
|
382
|
+
if (!e) return;
|
|
383
|
+
const o = Date.now(), s = o - e.startTime, i = this.resolveOperationTimings(s, n), a = {
|
|
384
|
+
id: `mutation-${t.requestId}-${o}`,
|
|
385
|
+
timestamp: o,
|
|
386
|
+
type: "mutation",
|
|
387
|
+
requestId: t.requestId,
|
|
388
|
+
udfPath: e.udfPath,
|
|
389
|
+
args: e.args,
|
|
390
|
+
componentPath: e.componentPath,
|
|
391
|
+
status: t.success ? "success" : "error",
|
|
392
|
+
result: t.success ? t.result : void 0,
|
|
393
|
+
error: t.success ? void 0 : t.result,
|
|
394
|
+
logLines: t.logLines,
|
|
395
|
+
trace: t.trace,
|
|
396
|
+
...i
|
|
397
|
+
};
|
|
398
|
+
this.eventStore.addEvent(a), this.lastMutationEventId = a.id, this.lastMutationTimestamp = o, t.logLines && t.logLines.length > 0 && this.emitLogEvents(t.logLines, a.id), this.pendingMutations.delete(t.requestId);
|
|
399
|
+
}
|
|
400
|
+
/**
|
|
401
|
+
* Handle action responses
|
|
402
|
+
*/
|
|
403
|
+
handleActionResponse(t, n = 0) {
|
|
404
|
+
const e = this.pendingActions.get(t.requestId);
|
|
405
|
+
if (!e) return;
|
|
406
|
+
const o = Date.now(), s = o - e.startTime, i = this.resolveOperationTimings(s, n), a = {
|
|
407
|
+
id: `action-${t.requestId}-${o}`,
|
|
408
|
+
timestamp: o,
|
|
409
|
+
type: "action",
|
|
410
|
+
requestId: t.requestId,
|
|
411
|
+
udfPath: e.udfPath,
|
|
412
|
+
args: e.args,
|
|
413
|
+
componentPath: e.componentPath,
|
|
414
|
+
status: t.success ? "success" : "error",
|
|
415
|
+
result: t.success ? t.result : void 0,
|
|
416
|
+
error: t.success ? void 0 : t.result,
|
|
417
|
+
logLines: t.logLines,
|
|
418
|
+
trace: t.trace,
|
|
419
|
+
...i
|
|
420
|
+
};
|
|
421
|
+
this.eventStore.addEvent(a), t.logLines && t.logLines.length > 0 && this.emitLogEvents(t.logLines, a.id), this.pendingActions.delete(t.requestId);
|
|
422
|
+
}
|
|
423
|
+
handleAuthErrorResponse(t) {
|
|
424
|
+
const n = {};
|
|
425
|
+
typeof t.baseVersion == "number" && (n.baseVersion = t.baseVersion), typeof t.authUpdateAttempted == "boolean" && (n.authUpdateAttempted = t.authUpdateAttempted), this.emitAuthEvent({
|
|
426
|
+
direction: "server",
|
|
427
|
+
messageType: "AuthError",
|
|
428
|
+
status: "error",
|
|
429
|
+
tokenType: this.pendingAuthTokenType ?? void 0,
|
|
430
|
+
error: t.error ?? "Authentication failed",
|
|
431
|
+
details: n
|
|
432
|
+
}), this.pendingAuthTokenType = null;
|
|
433
|
+
}
|
|
434
|
+
resolveOperationTimings(t, n) {
|
|
435
|
+
const e = Number.isFinite(t) && t >= 0 ? t : 0, o = Number.isFinite(n) && n > 0 ? n : 0;
|
|
436
|
+
return {
|
|
437
|
+
duration: e,
|
|
438
|
+
simulatedDelayMs: o,
|
|
439
|
+
endToEndDurationMs: e + o
|
|
440
|
+
};
|
|
441
|
+
}
|
|
442
|
+
emitAuthEvent(t) {
|
|
443
|
+
const n = Date.now(), e = {
|
|
444
|
+
id: `auth-${t.direction}-${t.messageType}-${n}-${Math.random().toString(36).slice(2, 8)}`,
|
|
445
|
+
timestamp: n,
|
|
446
|
+
type: "auth",
|
|
447
|
+
direction: t.direction,
|
|
448
|
+
messageType: t.messageType,
|
|
449
|
+
status: t.status,
|
|
450
|
+
tokenType: t.tokenType,
|
|
451
|
+
error: t.error,
|
|
452
|
+
details: t.details
|
|
453
|
+
};
|
|
454
|
+
this.eventStore.addEvent(e);
|
|
455
|
+
}
|
|
456
|
+
/**
|
|
457
|
+
* Extract and emit log events from logLines
|
|
458
|
+
*/
|
|
459
|
+
emitLogEvents(t, n) {
|
|
460
|
+
const e = Date.now();
|
|
461
|
+
for (const o of t) {
|
|
462
|
+
const s = o.match(/^\[(log|info|warn|error)\]\s+(.+)$/i), i = s ? s[1].toLowerCase() : "log", a = s ? s[2] : o, d = {
|
|
463
|
+
id: `log-${e}-${Math.random()}`,
|
|
464
|
+
timestamp: e,
|
|
465
|
+
type: "log",
|
|
466
|
+
level: i,
|
|
467
|
+
message: a,
|
|
468
|
+
relatedEventId: n
|
|
469
|
+
};
|
|
470
|
+
this.eventStore.addEvent(d);
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
export {
|
|
475
|
+
M as DataInspector,
|
|
476
|
+
k as DevToolbar,
|
|
477
|
+
A as EventStore,
|
|
478
|
+
D as LogsPanel,
|
|
479
|
+
$ as PerformancePanel,
|
|
480
|
+
C as SettingsPanel,
|
|
481
|
+
R as SubscriptionsPanel,
|
|
482
|
+
L as WebSocketInterceptor,
|
|
483
|
+
N as concaveDevTools,
|
|
484
|
+
W as getGlobalEventStore,
|
|
485
|
+
O as initDevTools
|
|
486
|
+
};
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WebSocket Interceptor for Convex Sync Protocol
|
|
3
|
+
*
|
|
4
|
+
* Intercepts WebSocket messages to capture queries, mutations, actions, subscriptions,
|
|
5
|
+
* and authentication protocol events.
|
|
6
|
+
*/
|
|
7
|
+
import type { EventStore } from "../store/event-store";
|
|
8
|
+
export declare class WebSocketInterceptor {
|
|
9
|
+
private eventStore;
|
|
10
|
+
private originalWebSocket;
|
|
11
|
+
private pendingQueries;
|
|
12
|
+
private pendingMutations;
|
|
13
|
+
private pendingActions;
|
|
14
|
+
private pendingAuthTokenType;
|
|
15
|
+
/** Tracks the most recent mutation event ID for causality linking */
|
|
16
|
+
private lastMutationEventId;
|
|
17
|
+
private lastMutationTimestamp;
|
|
18
|
+
constructor(eventStore: EventStore);
|
|
19
|
+
/**
|
|
20
|
+
* Install the WebSocket interceptor
|
|
21
|
+
*/
|
|
22
|
+
install(): void;
|
|
23
|
+
/**
|
|
24
|
+
* Uninstall the interceptor
|
|
25
|
+
*/
|
|
26
|
+
uninstall(): void;
|
|
27
|
+
/**
|
|
28
|
+
* Handle client → server messages
|
|
29
|
+
*/
|
|
30
|
+
private handleClientMessage;
|
|
31
|
+
/**
|
|
32
|
+
* Handle server → client messages
|
|
33
|
+
*/
|
|
34
|
+
private handleServerMessage;
|
|
35
|
+
/**
|
|
36
|
+
* Handle query set modifications (subscriptions)
|
|
37
|
+
*/
|
|
38
|
+
private handleModifyQuerySet;
|
|
39
|
+
/**
|
|
40
|
+
* Handle mutation requests
|
|
41
|
+
*/
|
|
42
|
+
private handleMutationRequest;
|
|
43
|
+
/**
|
|
44
|
+
* Handle action requests
|
|
45
|
+
*/
|
|
46
|
+
private handleActionRequest;
|
|
47
|
+
private handleConnectMessage;
|
|
48
|
+
private handleAuthenticateMessage;
|
|
49
|
+
/**
|
|
50
|
+
* Handle state transitions (query updates)
|
|
51
|
+
*/
|
|
52
|
+
private handleTransition;
|
|
53
|
+
/**
|
|
54
|
+
* Handle mutation responses
|
|
55
|
+
*/
|
|
56
|
+
private handleMutationResponse;
|
|
57
|
+
/**
|
|
58
|
+
* Handle action responses
|
|
59
|
+
*/
|
|
60
|
+
private handleActionResponse;
|
|
61
|
+
private handleAuthErrorResponse;
|
|
62
|
+
private resolveOperationTimings;
|
|
63
|
+
private emitAuthEvent;
|
|
64
|
+
/**
|
|
65
|
+
* Extract and emit log events from logLines
|
|
66
|
+
*/
|
|
67
|
+
private emitLogEvents;
|
|
68
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Activity Panel - Unified view of queries, mutations, and actions
|
|
3
|
+
* with focused list-first debugging UX
|
|
4
|
+
*/
|
|
5
|
+
import type { EventStore } from "../store/event-store";
|
|
6
|
+
interface ActivityPanelProps {
|
|
7
|
+
eventStore: EventStore;
|
|
8
|
+
panelConnectionState?: {
|
|
9
|
+
waitingForConnection: boolean;
|
|
10
|
+
needsReload: boolean;
|
|
11
|
+
onReload: () => void;
|
|
12
|
+
agentAttached?: boolean;
|
|
13
|
+
socketSeen?: boolean;
|
|
14
|
+
captureLive?: boolean;
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
export declare function ActivityPanel({ eventStore, panelConnectionState }: ActivityPanelProps): import("react/jsx-runtime").JSX.Element;
|
|
18
|
+
export {};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ContextMenu - Native-style right-click context menu for DevTools panels
|
|
3
|
+
*/
|
|
4
|
+
export interface MenuItem {
|
|
5
|
+
label: string;
|
|
6
|
+
action: () => void;
|
|
7
|
+
shortcut?: string;
|
|
8
|
+
disabled?: boolean;
|
|
9
|
+
separator?: boolean;
|
|
10
|
+
}
|
|
11
|
+
export interface ContextMenuProps {
|
|
12
|
+
x: number;
|
|
13
|
+
y: number;
|
|
14
|
+
items: MenuItem[];
|
|
15
|
+
onClose: () => void;
|
|
16
|
+
}
|
|
17
|
+
export declare function ContextMenu({ x, y, items, onClose }: ContextMenuProps): import("react/jsx-runtime").JSX.Element;
|
|
18
|
+
/** State type for panels using context menus */
|
|
19
|
+
export interface ContextMenuState {
|
|
20
|
+
x: number;
|
|
21
|
+
y: number;
|
|
22
|
+
items: MenuItem[];
|
|
23
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Data Inspector - Advanced JSON viewer with syntax highlighting, search, and diff
|
|
3
|
+
*/
|
|
4
|
+
import type { JSONValue } from "../types";
|
|
5
|
+
interface DataInspectorProps {
|
|
6
|
+
data: JSONValue;
|
|
7
|
+
label?: string;
|
|
8
|
+
onCopy?: () => void;
|
|
9
|
+
maxHeight?: number;
|
|
10
|
+
}
|
|
11
|
+
export declare function DataInspector({ data, label, onCopy, maxHeight, }: DataInspectorProps): import("react/jsx-runtime").JSX.Element;
|
|
12
|
+
export {};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Main DevTools Overlay Component
|
|
3
|
+
*/
|
|
4
|
+
import type { EventStore } from "../store/event-store";
|
|
5
|
+
interface DevToolbarProps {
|
|
6
|
+
eventStore: EventStore;
|
|
7
|
+
position?: "bottom-right" | "bottom-left" | "top-right" | "top-left";
|
|
8
|
+
mode?: "overlay" | "panel";
|
|
9
|
+
panelConnectionState?: {
|
|
10
|
+
waitingForConnection: boolean;
|
|
11
|
+
needsReload: boolean;
|
|
12
|
+
onReload: () => void;
|
|
13
|
+
agentAttached?: boolean;
|
|
14
|
+
socketSeen?: boolean;
|
|
15
|
+
captureLive?: boolean;
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
export declare function DevToolbar({ eventStore, position, mode, panelConnectionState, }: DevToolbarProps): import("react/jsx-runtime").JSX.Element;
|
|
19
|
+
export {};
|