@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.
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Logs Panel - Shows console logs from function executions
3
+ */
4
+ import type { EventStore } from "../store/event-store";
5
+ interface LogsPanelProps {
6
+ eventStore: EventStore;
7
+ onNavigate?: (tab: "activity" | "subscriptions" | "performance" | "logs" | "settings") => void;
8
+ }
9
+ export declare function LogsPanel({ eventStore, onNavigate }: LogsPanelProps): import("react/jsx-runtime").JSX.Element;
10
+ export {};
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Network Panel - Shows all client-server communication
3
+ */
4
+ import type { EventStore } from "../store/event-store";
5
+ interface NetworkPanelProps {
6
+ eventStore: EventStore;
7
+ }
8
+ export declare function NetworkPanel({ eventStore }: NetworkPanelProps): import("react/jsx-runtime").JSX.Element;
9
+ export {};
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Performance Panel - Shows performance metrics with sparkline trends
3
+ */
4
+ import type { EventStore } from "../store/event-store";
5
+ interface PerformancePanelProps {
6
+ eventStore: EventStore;
7
+ onNavigate?: (tab: "activity" | "subscriptions" | "performance" | "logs" | "settings") => void;
8
+ }
9
+ export declare function PerformancePanel({ eventStore, onNavigate }: PerformancePanelProps): import("react/jsx-runtime").JSX.Element;
10
+ export {};
@@ -0,0 +1,9 @@
1
+ import React from "react";
2
+ interface SearchFieldProps extends Omit<React.InputHTMLAttributes<HTMLInputElement>, "type" | "value" | "onChange"> {
3
+ value: string;
4
+ onValueChange: (value: string) => void;
5
+ onClear?: () => void;
6
+ className?: string;
7
+ }
8
+ export declare const SearchField: React.ForwardRefExoticComponent<SearchFieldProps & React.RefAttributes<HTMLInputElement>>;
9
+ export {};
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Settings Panel - Configure devtools behavior
3
+ */
4
+ import type { EventStore } from "../store/event-store";
5
+ interface SettingsPanelProps {
6
+ eventStore: EventStore;
7
+ }
8
+ export declare function SettingsPanel({ eventStore }: SettingsPanelProps): import("react/jsx-runtime").JSX.Element;
9
+ export {};
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Subscriptions Panel - Shows active query subscriptions
3
+ */
4
+ import type { EventStore } from "../store/event-store";
5
+ interface SubscriptionsPanelProps {
6
+ eventStore: EventStore;
7
+ }
8
+ export declare function SubscriptionsPanel({ eventStore }: SubscriptionsPanelProps): import("react/jsx-runtime").JSX.Element;
9
+ export {};
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Timeline Panel - Visual timeline of all events
3
+ */
4
+ import type { EventStore } from "../store/event-store";
5
+ interface TimelinePanelProps {
6
+ eventStore: EventStore;
7
+ }
8
+ export declare function TimelinePanel({ eventStore }: TimelinePanelProps): import("react/jsx-runtime").JSX.Element;
9
+ export {};
@@ -0,0 +1,7 @@
1
+ import type { UdfTrace } from "../types";
2
+ interface TraceViewerProps {
3
+ trace: UdfTrace;
4
+ maxHeight?: number;
5
+ }
6
+ export declare function TraceViewer({ trace, maxHeight }: TraceViewerProps): import("react/jsx-runtime").JSX.Element;
7
+ export {};
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Copy text to clipboard with fallback for restricted contexts (e.g. Chrome
3
+ * DevTools panels where the Clipboard API is blocked by permissions policy).
4
+ * Shows a brief "Copied!" toast on success.
5
+ */
6
+ export declare function copyToClipboard(text: string): void;
7
+ /**
8
+ * Check if the devtools overlay currently has keyboard focus.
9
+ * The devtools renders inside a Shadow DOM under #concave-devtools-host.
10
+ * When any element inside the shadow is focused, document.activeElement
11
+ * returns the shadow host element.
12
+ */
13
+ export declare function isDevToolsFocused(): boolean;
14
+ /**
15
+ * Returns the currently focused element inside the devtools shadow DOM,
16
+ * or null if nothing inside the devtools is focused.
17
+ * Use this instead of document.activeElement when comparing against refs
18
+ * to elements inside the shadow tree.
19
+ */
20
+ export declare function getDevToolsActiveElement(): Element | null;
21
+ export declare function unwrapArgs<T>(args: T): T;
22
+ export type InlinePreviewOptions = {
23
+ maxStringLength?: number;
24
+ stringTailLength?: number;
25
+ maxArrayItems?: number;
26
+ maxObjectEntries?: number;
27
+ maxDepth?: number;
28
+ };
29
+ export declare function formatInlinePreview(value: any, options?: InlinePreviewOptions, depth?: number): string;
30
+ export declare function formatJsonSnippet(value: any, maxLength?: number): string;
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Standalone DevTools Entry Point
3
+ *
4
+ * Self-contained bundle that can be loaded via <script> on any page.
5
+ * Renders inside a Shadow DOM for complete CSS/DOM isolation from the host page.
6
+ *
7
+ * When the Chrome extension's page-agent is already intercepting WebSockets,
8
+ * this skips installing its own interceptor and instead consumes events from
9
+ * page-agent via postMessage. This enables overlay + panel coexistence.
10
+ */
11
+ export {};
@@ -0,0 +1,191 @@
1
+ /**
2
+ * Client-side Event Store
3
+ *
4
+ * Maintains a circular buffer of events captured from WebSocket interception
5
+ * with persistence, export/import, and time-travel capabilities
6
+ */
7
+ import type { DevToolsEvent, ActiveSubscription, PerformanceMetrics } from "../types";
8
+ export type EventListener = (event: DevToolsEvent) => void;
9
+ export type FocusListener = (eventId: string | null) => void;
10
+ export type SettingsListener = (settings: DevToolsSettings) => void;
11
+ export interface DevToolsSettings {
12
+ persistEvents: boolean;
13
+ maxEvents: number;
14
+ captureLogLines: boolean;
15
+ autoPauseOnError: boolean;
16
+ wsLatencyMs: number;
17
+ wsJitterMs: number;
18
+ }
19
+ export interface StoreSnapshot {
20
+ timestamp: number;
21
+ events: DevToolsEvent[];
22
+ subscriptions: Array<[number, ActiveSubscription]>;
23
+ }
24
+ export interface ExportedSession {
25
+ version: string;
26
+ exportedAt: number;
27
+ events: DevToolsEvent[];
28
+ subscriptions: Array<[number, ActiveSubscription]>;
29
+ metadata: {
30
+ userAgent: string;
31
+ url: string;
32
+ };
33
+ }
34
+ export interface EventStoreOptions {
35
+ maxEvents?: number;
36
+ storageScope?: string;
37
+ enablePersistence?: boolean;
38
+ }
39
+ export declare class EventStore {
40
+ private events;
41
+ private maxEvents;
42
+ private listeners;
43
+ private focusListeners;
44
+ private settingsListeners;
45
+ private subscriptions;
46
+ private snapshots;
47
+ private maxSnapshots;
48
+ private isPaused;
49
+ private focusedEventId;
50
+ private readonly storageKey;
51
+ private readonly settingsKey;
52
+ private readonly persistenceEnabled;
53
+ private recentEventIds;
54
+ private persistTimer;
55
+ private settings;
56
+ private static readonly MIN_EVENTS;
57
+ private static readonly MAX_EVENTS;
58
+ private static readonly MAX_SIMULATED_LATENCY_MS;
59
+ constructor(options?: number | EventStoreOptions, legacyStorageScope?: string);
60
+ /**
61
+ * Add an event to the store
62
+ */
63
+ addEvent(event: DevToolsEvent): void;
64
+ /**
65
+ * Subscribe to new events
66
+ */
67
+ subscribe(listener: EventListener): () => void;
68
+ /**
69
+ * Subscribe to focus/navigation changes across panels
70
+ */
71
+ subscribeFocus(listener: FocusListener): () => void;
72
+ /**
73
+ * Subscribe to settings changes
74
+ */
75
+ subscribeSettings(listener: SettingsListener): () => void;
76
+ /**
77
+ * Set the currently focused event (for cross-panel navigation)
78
+ */
79
+ setFocusedEventId(eventId: string | null): void;
80
+ /**
81
+ * Get focused event id
82
+ */
83
+ getFocusedEventId(): string | null;
84
+ /**
85
+ * Get all events
86
+ */
87
+ getAllEvents(): DevToolsEvent[];
88
+ /**
89
+ * Get events by type
90
+ */
91
+ getEventsByType<T extends DevToolsEvent>(type: T["type"]): T[];
92
+ /**
93
+ * Get recent events
94
+ */
95
+ getRecentEvents(count: number): DevToolsEvent[];
96
+ /**
97
+ * Get events for a specific function
98
+ */
99
+ getEventsByFunction(udfPath: string): DevToolsEvent[];
100
+ /**
101
+ * Get active subscriptions
102
+ */
103
+ getActiveSubscriptions(): ActiveSubscription[];
104
+ /**
105
+ * Get subscription by query ID
106
+ */
107
+ getSubscription(queryId: number): ActiveSubscription | undefined;
108
+ /**
109
+ * Calculate performance metrics
110
+ */
111
+ getPerformanceMetrics(): PerformanceMetrics;
112
+ /**
113
+ * Clear all events
114
+ */
115
+ clear(): void;
116
+ /**
117
+ * Pause event capture
118
+ */
119
+ pause(): void;
120
+ /**
121
+ * Resume event capture
122
+ */
123
+ resume(): void;
124
+ /**
125
+ * Check if paused
126
+ */
127
+ isPausedState(): boolean;
128
+ /**
129
+ * Create a snapshot of current state (for time-travel)
130
+ */
131
+ createSnapshot(): void;
132
+ /**
133
+ * Restore from a snapshot
134
+ */
135
+ restoreSnapshot(timestamp: number): boolean;
136
+ /**
137
+ * Get all snapshots
138
+ */
139
+ getSnapshots(): StoreSnapshot[];
140
+ /**
141
+ * Export session data
142
+ */
143
+ exportSession(): ExportedSession;
144
+ /**
145
+ * Import session data
146
+ */
147
+ importSession(session: ExportedSession): boolean;
148
+ /**
149
+ * Save settings
150
+ */
151
+ saveSettings(settings: Partial<typeof this.settings>): void;
152
+ /**
153
+ * Get settings
154
+ */
155
+ getSettings(): typeof this.settings;
156
+ /**
157
+ * Whether this store supports local persistence
158
+ */
159
+ isPersistenceEnabled(): boolean;
160
+ /**
161
+ * Load settings from localStorage
162
+ */
163
+ private loadSettings;
164
+ /**
165
+ * Push latency settings to the shared window config object
166
+ * that the WebSocket interceptors read on every send/receive.
167
+ */
168
+ private syncLatencyConfig;
169
+ private notifySettingsChanged;
170
+ /**
171
+ * Persist events to localStorage
172
+ */
173
+ private persistEvents;
174
+ /**
175
+ * Load persisted events from localStorage
176
+ */
177
+ private loadPersistedEvents;
178
+ /**
179
+ * Update subscription tracking
180
+ */
181
+ private updateSubscription;
182
+ private sanitizeEvents;
183
+ private isEventLike;
184
+ private normalizeMaxEvents;
185
+ private normalizeLatencySettings;
186
+ private isOperationEvent;
187
+ private isTerminalOperationEvent;
188
+ private isMatchingPendingOperation;
189
+ private reconcilePendingOperation;
190
+ }
191
+ export declare function getGlobalEventStore(): EventStore;
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Safe localStorage wrapper that gracefully handles sandboxed iframes,
3
+ * restrictive CSP, and other environments where localStorage is unavailable.
4
+ */
5
+ export declare function getItem(key: string): string | null;
6
+ export declare function setItem(key: string, value: string): void;
7
+ export declare function removeItem(key: string): void;
@@ -0,0 +1,211 @@
1
+ /**
2
+ * Devtools Event Types
3
+ *
4
+ * These types represent the telemetry data captured from the Convex sync protocol
5
+ */
6
+ export type JSONValue = null | boolean | number | string | JSONValue[] | {
7
+ [key: string]: JSONValue;
8
+ };
9
+ export interface TraceEvent {
10
+ name: string;
11
+ atMs: number;
12
+ attributes?: Record<string, JSONValue>;
13
+ }
14
+ export interface TraceSpan {
15
+ id: string;
16
+ parentId: string | null;
17
+ name: string;
18
+ kind: string;
19
+ startMs: number;
20
+ endMs: number;
21
+ durationMs: number;
22
+ status: "ok" | "error";
23
+ attributes?: Record<string, JSONValue>;
24
+ events?: TraceEvent[];
25
+ }
26
+ export interface UdfTrace {
27
+ version: number;
28
+ requestId?: string;
29
+ rootSpanId: string;
30
+ generatedAtMs: number;
31
+ spans: TraceSpan[];
32
+ summary: {
33
+ spanCount: number;
34
+ eventCount: number;
35
+ totalDurationMs: number;
36
+ };
37
+ }
38
+ export type EventType = "query" | "mutation" | "action" | "subscription" | "log" | "auth";
39
+ export type EventStatus = "pending" | "success" | "error";
40
+ export type SubscriptionStatus = "added" | "updated" | "removed";
41
+ /**
42
+ * Base event interface
43
+ */
44
+ export interface BaseEvent {
45
+ id: string;
46
+ timestamp: number;
47
+ type: EventType;
48
+ }
49
+ /**
50
+ * Query execution event (captured from AddQuery + QueryUpdated/QueryFailed)
51
+ */
52
+ export interface QueryEvent extends BaseEvent {
53
+ type: "query";
54
+ queryId: number;
55
+ udfPath: string;
56
+ args: JSONValue[];
57
+ componentPath?: string;
58
+ status: EventStatus;
59
+ result?: JSONValue;
60
+ error?: string;
61
+ logLines?: string[];
62
+ /**
63
+ * Backend/network round-trip time measured when the WS frame is received.
64
+ */
65
+ duration?: number;
66
+ /**
67
+ * Simulated client-side delivery delay applied by devtools latency settings.
68
+ */
69
+ simulatedDelayMs?: number;
70
+ /**
71
+ * End-to-end perceived latency = duration + simulatedDelayMs.
72
+ */
73
+ endToEndDurationMs?: number;
74
+ /** ID of the mutation that likely triggered this query update */
75
+ triggeredBy?: string;
76
+ trace?: UdfTrace;
77
+ }
78
+ /**
79
+ * Mutation execution event (captured from MutationRequest + MutationResponse)
80
+ */
81
+ export interface MutationEvent extends BaseEvent {
82
+ type: "mutation";
83
+ requestId: number;
84
+ udfPath: string;
85
+ args: JSONValue[];
86
+ componentPath?: string;
87
+ status: EventStatus;
88
+ result?: JSONValue;
89
+ error?: string;
90
+ logLines?: string[];
91
+ /**
92
+ * Backend/network round-trip time measured when the WS frame is received.
93
+ */
94
+ duration?: number;
95
+ /**
96
+ * Simulated client-side delivery delay applied by devtools latency settings.
97
+ */
98
+ simulatedDelayMs?: number;
99
+ /**
100
+ * End-to-end perceived latency = duration + simulatedDelayMs.
101
+ */
102
+ endToEndDurationMs?: number;
103
+ trace?: UdfTrace;
104
+ }
105
+ /**
106
+ * Action execution event (captured from ActionRequest + ActionResponse)
107
+ */
108
+ export interface ActionEvent extends BaseEvent {
109
+ type: "action";
110
+ requestId: number;
111
+ udfPath: string;
112
+ args: JSONValue[];
113
+ componentPath?: string;
114
+ status: EventStatus;
115
+ result?: JSONValue;
116
+ error?: string;
117
+ logLines?: string[];
118
+ /**
119
+ * Backend/network round-trip time measured when the WS frame is received.
120
+ */
121
+ duration?: number;
122
+ /**
123
+ * Simulated client-side delivery delay applied by devtools latency settings.
124
+ */
125
+ simulatedDelayMs?: number;
126
+ /**
127
+ * End-to-end perceived latency = duration + simulatedDelayMs.
128
+ */
129
+ endToEndDurationMs?: number;
130
+ trace?: UdfTrace;
131
+ }
132
+ /**
133
+ * Subscription event (captured from ModifyQuerySet)
134
+ */
135
+ export interface SubscriptionEvent extends BaseEvent {
136
+ type: "subscription";
137
+ queryId: number;
138
+ udfPath: string;
139
+ args: JSONValue[];
140
+ componentPath?: string;
141
+ status: SubscriptionStatus;
142
+ updateCount?: number;
143
+ }
144
+ /**
145
+ * Log event (extracted from logLines in responses)
146
+ */
147
+ export interface LogEvent extends BaseEvent {
148
+ type: "log";
149
+ level: "log" | "info" | "warn" | "error";
150
+ message: string;
151
+ relatedEventId?: string;
152
+ }
153
+ /**
154
+ * Authentication protocol event (Connect / Authenticate / AuthError / Authenticated)
155
+ */
156
+ export interface AuthEvent extends BaseEvent {
157
+ type: "auth";
158
+ direction: "client" | "server";
159
+ messageType: string;
160
+ status: EventStatus;
161
+ tokenType?: string;
162
+ error?: string;
163
+ details?: JSONValue;
164
+ }
165
+ export type DevToolsEvent = QueryEvent | MutationEvent | ActionEvent | SubscriptionEvent | LogEvent | AuthEvent;
166
+ /**
167
+ * Active subscription tracking
168
+ */
169
+ export interface ActiveSubscription {
170
+ queryId: number;
171
+ udfPath: string;
172
+ args: JSONValue[];
173
+ componentPath?: string;
174
+ addedAt: number;
175
+ updateCount: number;
176
+ lastUpdate: number;
177
+ currentValue?: JSONValue;
178
+ previousValue?: JSONValue;
179
+ status: "active" | "error";
180
+ error?: string;
181
+ }
182
+ /**
183
+ * Performance metrics
184
+ */
185
+ export interface PerformanceMetrics {
186
+ totalQueries: number;
187
+ totalMutations: number;
188
+ totalActions: number;
189
+ avgQueryDuration: number;
190
+ avgMutationDuration: number;
191
+ avgActionDuration: number;
192
+ p50QueryDuration: number;
193
+ p50MutationDuration: number;
194
+ p50ActionDuration: number;
195
+ p90QueryDuration: number;
196
+ p90MutationDuration: number;
197
+ p90ActionDuration: number;
198
+ p95QueryDuration: number;
199
+ p95MutationDuration: number;
200
+ p95ActionDuration: number;
201
+ p99QueryDuration: number;
202
+ p99MutationDuration: number;
203
+ p99ActionDuration: number;
204
+ slowestOperations: Array<{
205
+ type: "query" | "mutation" | "action";
206
+ eventId: string;
207
+ udfPath: string;
208
+ duration: number;
209
+ timestamp: number;
210
+ }>;
211
+ }
@@ -0,0 +1,10 @@
1
+ /**
2
+ * DevTools package semantic version (source of truth: package.json).
3
+ */
4
+ export declare const DEVTOOLS_VERSION: string;
5
+ /**
6
+ * Chrome extension public version.
7
+ *
8
+ * Chrome Web Store requires numeric dot-separated version strings.
9
+ */
10
+ export declare const CHROME_EXTENSION_VERSION: string;
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Vite Plugin for injecting Concave DevTools
3
+ */
4
+ import type { Plugin } from "vite";
5
+ export interface DevToolsOptions {
6
+ /**
7
+ * Enable devtools (default: true in dev mode)
8
+ */
9
+ enabled?: boolean;
10
+ /**
11
+ * Position of the devtools overlay
12
+ */
13
+ position?: "bottom-right" | "bottom-left" | "top-right" | "top-left";
14
+ }
15
+ export declare function concaveDevTools(options?: DevToolsOptions): Plugin;
16
+ export default concaveDevTools;