fauxbase-devtools 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.cjs ADDED
@@ -0,0 +1,447 @@
1
+ 'use strict';
2
+
3
+ var react = require('react');
4
+ var jsxRuntime = require('react/jsx-runtime');
5
+
6
+ // src/devtools.tsx
7
+
8
+ // src/request-logger.ts
9
+ var idCounter = 0;
10
+ function createRequestLogger(maxEntries = 100) {
11
+ const entries = [];
12
+ const listeners = /* @__PURE__ */ new Set();
13
+ function addEntry(entry) {
14
+ entries.unshift(entry);
15
+ if (entries.length > maxEntries) {
16
+ entries.pop();
17
+ }
18
+ listeners.forEach((fn) => fn());
19
+ }
20
+ function getEntries() {
21
+ return entries;
22
+ }
23
+ function clear() {
24
+ entries.length = 0;
25
+ listeners.forEach((fn) => fn());
26
+ }
27
+ function subscribe(fn) {
28
+ listeners.add(fn);
29
+ return () => listeners.delete(fn);
30
+ }
31
+ function wrapService(service, serviceName) {
32
+ return new Proxy(service, {
33
+ get(target, prop, receiver) {
34
+ const value = Reflect.get(target, prop, receiver);
35
+ if (typeof value !== "function") return value;
36
+ if (typeof prop === "string" && prop.startsWith("_")) return value;
37
+ return async function(...args) {
38
+ const entry = {
39
+ id: `log_${++idCounter}`,
40
+ timestamp: Date.now(),
41
+ service: serviceName,
42
+ method: prop,
43
+ args,
44
+ duration: 0
45
+ };
46
+ const start = performance.now();
47
+ try {
48
+ const result = await value.apply(target, args);
49
+ entry.result = result;
50
+ entry.duration = Math.round(performance.now() - start);
51
+ addEntry(entry);
52
+ return result;
53
+ } catch (err) {
54
+ entry.error = err.message ?? String(err);
55
+ entry.duration = Math.round(performance.now() - start);
56
+ addEntry(entry);
57
+ throw err;
58
+ }
59
+ };
60
+ }
61
+ });
62
+ }
63
+ return { addEntry, getEntries, clear, subscribe, wrapService };
64
+ }
65
+
66
+ // src/styles.ts
67
+ var STYLES = {
68
+ container: {
69
+ position: "fixed",
70
+ bottom: "16px",
71
+ right: "16px",
72
+ zIndex: 99999,
73
+ fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, monospace',
74
+ fontSize: "13px",
75
+ color: "#e4e4e7"
76
+ },
77
+ containerLeft: {
78
+ right: "auto",
79
+ left: "16px"
80
+ },
81
+ toggleButton: {
82
+ width: "40px",
83
+ height: "40px",
84
+ borderRadius: "8px",
85
+ border: "1px solid #3f3f46",
86
+ background: "#18181b",
87
+ color: "#a1a1aa",
88
+ cursor: "pointer",
89
+ display: "flex",
90
+ alignItems: "center",
91
+ justifyContent: "center",
92
+ fontSize: "18px",
93
+ boxShadow: "0 4px 12px rgba(0,0,0,0.4)"
94
+ },
95
+ panel: {
96
+ width: "480px",
97
+ maxHeight: "400px",
98
+ background: "#18181b",
99
+ border: "1px solid #3f3f46",
100
+ borderRadius: "8px",
101
+ boxShadow: "0 8px 24px rgba(0,0,0,0.5)",
102
+ overflow: "hidden",
103
+ display: "flex",
104
+ flexDirection: "column"
105
+ },
106
+ tabBar: {
107
+ display: "flex",
108
+ borderBottom: "1px solid #3f3f46",
109
+ background: "#09090b"
110
+ },
111
+ tab: {
112
+ padding: "8px 16px",
113
+ border: "none",
114
+ background: "transparent",
115
+ color: "#71717a",
116
+ cursor: "pointer",
117
+ fontSize: "12px",
118
+ fontWeight: 500,
119
+ borderBottom: "2px solid transparent"
120
+ },
121
+ tabActive: {
122
+ color: "#e4e4e7",
123
+ borderBottomColor: "#3b82f6"
124
+ },
125
+ panelContent: {
126
+ padding: "12px",
127
+ overflowY: "auto",
128
+ maxHeight: "340px",
129
+ flex: 1
130
+ },
131
+ table: {
132
+ width: "100%",
133
+ borderCollapse: "collapse",
134
+ fontSize: "12px"
135
+ },
136
+ th: {
137
+ textAlign: "left",
138
+ padding: "6px 8px",
139
+ borderBottom: "1px solid #27272a",
140
+ color: "#a1a1aa",
141
+ fontWeight: 600
142
+ },
143
+ td: {
144
+ padding: "6px 8px",
145
+ borderBottom: "1px solid #27272a",
146
+ maxWidth: "160px",
147
+ overflow: "hidden",
148
+ textOverflow: "ellipsis",
149
+ whiteSpace: "nowrap"
150
+ },
151
+ button: {
152
+ padding: "6px 12px",
153
+ borderRadius: "4px",
154
+ border: "1px solid #3f3f46",
155
+ background: "#27272a",
156
+ color: "#e4e4e7",
157
+ cursor: "pointer",
158
+ fontSize: "12px"
159
+ },
160
+ buttonDanger: {
161
+ background: "#7f1d1d",
162
+ borderColor: "#991b1b"
163
+ },
164
+ badge: {
165
+ display: "inline-block",
166
+ padding: "2px 6px",
167
+ borderRadius: "4px",
168
+ fontSize: "11px",
169
+ fontWeight: 600
170
+ },
171
+ badgeSuccess: {
172
+ background: "#14532d",
173
+ color: "#4ade80"
174
+ },
175
+ badgeError: {
176
+ background: "#7f1d1d",
177
+ color: "#f87171"
178
+ },
179
+ emptyState: {
180
+ color: "#71717a",
181
+ textAlign: "center",
182
+ padding: "24px",
183
+ fontSize: "12px"
184
+ },
185
+ label: {
186
+ color: "#a1a1aa",
187
+ fontSize: "11px",
188
+ marginBottom: "4px"
189
+ },
190
+ value: {
191
+ color: "#e4e4e7",
192
+ fontSize: "13px",
193
+ marginBottom: "12px",
194
+ wordBreak: "break-all"
195
+ }
196
+ };
197
+ function DataInspector({ client }) {
198
+ const [selected, setSelected] = react.useState(null);
199
+ const [records, setRecords] = react.useState([]);
200
+ const serviceNames = Object.keys(client).filter(
201
+ (k) => k !== "auth" && client[k] && typeof client[k].list === "function"
202
+ );
203
+ react.useEffect(() => {
204
+ if (!selected && serviceNames.length > 0) {
205
+ setSelected(serviceNames[0]);
206
+ }
207
+ }, [serviceNames.length]);
208
+ react.useEffect(() => {
209
+ if (!selected) return;
210
+ const service = client[selected];
211
+ if (!service) return;
212
+ service.list({ size: 50 }).then((res) => setRecords(res.items ?? [])).catch(() => setRecords([]));
213
+ }, [selected]);
214
+ if (serviceNames.length === 0) {
215
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { style: STYLES.emptyState, children: "No services registered" });
216
+ }
217
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
218
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: { display: "flex", gap: "4px", marginBottom: "8px", flexWrap: "wrap" }, children: serviceNames.map((name) => /* @__PURE__ */ jsxRuntime.jsx(
219
+ "button",
220
+ {
221
+ onClick: () => setSelected(name),
222
+ style: {
223
+ ...STYLES.button,
224
+ ...selected === name ? { background: "#3b82f6", borderColor: "#3b82f6" } : {}
225
+ },
226
+ children: name
227
+ },
228
+ name
229
+ )) }),
230
+ records.length === 0 ? /* @__PURE__ */ jsxRuntime.jsxs("div", { style: STYLES.emptyState, children: [
231
+ "No records in ",
232
+ selected
233
+ ] }) : /* @__PURE__ */ jsxRuntime.jsxs("table", { style: STYLES.table, children: [
234
+ /* @__PURE__ */ jsxRuntime.jsx("thead", { children: /* @__PURE__ */ jsxRuntime.jsxs("tr", { children: [
235
+ /* @__PURE__ */ jsxRuntime.jsx("th", { style: STYLES.th, children: "id" }),
236
+ Object.keys(records[0]).filter((k) => k !== "id" && !k.endsWith("At") && !k.endsWith("Id") && !k.endsWith("Name") && k !== "version" && k !== "password").slice(0, 3).map((k) => /* @__PURE__ */ jsxRuntime.jsx("th", { style: STYLES.th, children: k }, k))
237
+ ] }) }),
238
+ /* @__PURE__ */ jsxRuntime.jsx("tbody", { children: records.map((r) => /* @__PURE__ */ jsxRuntime.jsxs("tr", { children: [
239
+ /* @__PURE__ */ jsxRuntime.jsxs("td", { style: STYLES.td, children: [
240
+ r.id?.substring(0, 12),
241
+ "..."
242
+ ] }),
243
+ Object.keys(records[0]).filter((k) => k !== "id" && !k.endsWith("At") && !k.endsWith("Id") && !k.endsWith("Name") && k !== "version" && k !== "password").slice(0, 3).map((k) => /* @__PURE__ */ jsxRuntime.jsx("td", { style: STYLES.td, children: String(r[k] ?? "") }, k))
244
+ ] }, r.id)) })
245
+ ] })
246
+ ] });
247
+ }
248
+ function AuthInspector({ client }) {
249
+ const auth = client.auth;
250
+ if (!auth) {
251
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { style: STYLES.emptyState, children: "Auth not configured" });
252
+ }
253
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
254
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: STYLES.label, children: "Status" }),
255
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: { ...STYLES.value, display: "flex", alignItems: "center", gap: "8px" }, children: /* @__PURE__ */ jsxRuntime.jsx(
256
+ "span",
257
+ {
258
+ style: {
259
+ ...STYLES.badge,
260
+ ...auth.isLoggedIn ? STYLES.badgeSuccess : STYLES.badgeError
261
+ },
262
+ children: auth.isLoggedIn ? "Logged In" : "Logged Out"
263
+ }
264
+ ) }),
265
+ auth.isLoggedIn && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
266
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: STYLES.label, children: "User" }),
267
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: STYLES.value, children: auth.currentUser?.email ?? auth.currentUser?.id ?? "Unknown" }),
268
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: STYLES.label, children: "Token" }),
269
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { ...STYLES.value, fontSize: "11px", fontFamily: "monospace" }, children: [
270
+ auth.token?.substring(0, 40),
271
+ "..."
272
+ ] }),
273
+ /* @__PURE__ */ jsxRuntime.jsx(
274
+ "button",
275
+ {
276
+ style: { ...STYLES.button, ...STYLES.buttonDanger },
277
+ onClick: () => {
278
+ auth.logout();
279
+ window.dispatchEvent(new Event("fauxbase:auth-change"));
280
+ },
281
+ children: "Logout"
282
+ }
283
+ )
284
+ ] })
285
+ ] });
286
+ }
287
+ function RequestLog({ entries, onClear }) {
288
+ if (entries.length === 0) {
289
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { style: STYLES.emptyState, children: "No requests logged yet" });
290
+ }
291
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
292
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", justifyContent: "space-between", marginBottom: "8px" }, children: [
293
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { style: STYLES.label, children: [
294
+ entries.length,
295
+ " requests"
296
+ ] }),
297
+ /* @__PURE__ */ jsxRuntime.jsx("button", { style: STYLES.button, onClick: onClear, children: "Clear" })
298
+ ] }),
299
+ /* @__PURE__ */ jsxRuntime.jsxs("table", { style: STYLES.table, children: [
300
+ /* @__PURE__ */ jsxRuntime.jsx("thead", { children: /* @__PURE__ */ jsxRuntime.jsxs("tr", { children: [
301
+ /* @__PURE__ */ jsxRuntime.jsx("th", { style: STYLES.th, children: "Service" }),
302
+ /* @__PURE__ */ jsxRuntime.jsx("th", { style: STYLES.th, children: "Method" }),
303
+ /* @__PURE__ */ jsxRuntime.jsx("th", { style: STYLES.th, children: "Status" }),
304
+ /* @__PURE__ */ jsxRuntime.jsx("th", { style: STYLES.th, children: "Time" })
305
+ ] }) }),
306
+ /* @__PURE__ */ jsxRuntime.jsx("tbody", { children: entries.map((entry) => /* @__PURE__ */ jsxRuntime.jsxs("tr", { children: [
307
+ /* @__PURE__ */ jsxRuntime.jsx("td", { style: STYLES.td, children: entry.service }),
308
+ /* @__PURE__ */ jsxRuntime.jsx("td", { style: STYLES.td, children: entry.method }),
309
+ /* @__PURE__ */ jsxRuntime.jsx("td", { style: STYLES.td, children: /* @__PURE__ */ jsxRuntime.jsx(
310
+ "span",
311
+ {
312
+ style: {
313
+ ...STYLES.badge,
314
+ ...entry.error ? STYLES.badgeError : STYLES.badgeSuccess
315
+ },
316
+ children: entry.error ? "ERR" : "OK"
317
+ }
318
+ ) }),
319
+ /* @__PURE__ */ jsxRuntime.jsxs("td", { style: STYLES.td, children: [
320
+ entry.duration,
321
+ "ms"
322
+ ] })
323
+ ] }, entry.id)) })
324
+ ] })
325
+ ] });
326
+ }
327
+ function SeedManager({ client }) {
328
+ const [resetting, setResetting] = react.useState(false);
329
+ const serviceNames = Object.keys(client).filter(
330
+ (k) => k !== "auth" && client[k] && typeof client[k].list === "function"
331
+ );
332
+ const handleReset = async (name) => {
333
+ setResetting(true);
334
+ try {
335
+ const service = client[name];
336
+ if (service?.driver?.clear) {
337
+ service.driver.clear(name);
338
+ }
339
+ } finally {
340
+ setResetting(false);
341
+ }
342
+ };
343
+ if (serviceNames.length === 0) {
344
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { style: STYLES.emptyState, children: "No services registered" });
345
+ }
346
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
347
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: { ...STYLES.label, marginBottom: "12px" }, children: "Reset seed data for individual resources (LocalDriver only)" }),
348
+ serviceNames.map((name) => /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: "8px" }, children: [
349
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: name }),
350
+ /* @__PURE__ */ jsxRuntime.jsx(
351
+ "button",
352
+ {
353
+ style: { ...STYLES.button, ...STYLES.buttonDanger },
354
+ onClick: () => handleReset(name),
355
+ disabled: resetting,
356
+ children: "Reset"
357
+ }
358
+ )
359
+ ] }, name))
360
+ ] });
361
+ }
362
+ var TABS = [
363
+ { key: "data", label: "Data" },
364
+ { key: "auth", label: "Auth" },
365
+ { key: "requests", label: "Requests" },
366
+ { key: "seeds", label: "Seeds" }
367
+ ];
368
+ function FauxbaseDevtools({ client, config = {} }) {
369
+ const [open, setOpen] = react.useState(config.defaultOpen ?? false);
370
+ const [activeTab, setActiveTab] = react.useState("data");
371
+ const [logger] = react.useState(() => createRequestLogger(config.maxLogEntries ?? 100));
372
+ const [, setTick] = react.useState(0);
373
+ react.useEffect(() => {
374
+ return logger.subscribe(() => setTick((t) => t + 1));
375
+ }, [logger]);
376
+ const [wrappedClient] = react.useState(() => {
377
+ const wrapped = { ...client };
378
+ for (const [name, service] of Object.entries(client)) {
379
+ if (name !== "auth" && service && typeof service.list === "function") {
380
+ wrapped[name] = logger.wrapService(service, name);
381
+ }
382
+ }
383
+ return wrapped;
384
+ });
385
+ const position = config.position ?? "bottom-right";
386
+ const isLeft = position === "bottom-left";
387
+ const containerStyle = {
388
+ ...STYLES.container,
389
+ ...isLeft ? STYLES.containerLeft : {}
390
+ };
391
+ if (!open) {
392
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { style: containerStyle, "data-testid": "fauxbase-devtools", children: /* @__PURE__ */ jsxRuntime.jsx(
393
+ "button",
394
+ {
395
+ style: STYLES.toggleButton,
396
+ onClick: () => setOpen(true),
397
+ "aria-label": "Open Fauxbase DevTools",
398
+ "data-testid": "devtools-toggle",
399
+ children: "{ }"
400
+ }
401
+ ) });
402
+ }
403
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { style: containerStyle, "data-testid": "fauxbase-devtools", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { style: STYLES.panel, children: [
404
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: STYLES.tabBar, children: [
405
+ TABS.map((tab) => /* @__PURE__ */ jsxRuntime.jsx(
406
+ "button",
407
+ {
408
+ style: {
409
+ ...STYLES.tab,
410
+ ...activeTab === tab.key ? STYLES.tabActive : {}
411
+ },
412
+ onClick: () => setActiveTab(tab.key),
413
+ "data-testid": `tab-${tab.key}`,
414
+ children: tab.label
415
+ },
416
+ tab.key
417
+ )),
418
+ /* @__PURE__ */ jsxRuntime.jsx(
419
+ "button",
420
+ {
421
+ style: { ...STYLES.tab, marginLeft: "auto" },
422
+ onClick: () => setOpen(false),
423
+ "aria-label": "Close DevTools",
424
+ "data-testid": "devtools-close",
425
+ children: "x"
426
+ }
427
+ )
428
+ ] }),
429
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: STYLES.panelContent, children: [
430
+ activeTab === "data" && /* @__PURE__ */ jsxRuntime.jsx(DataInspector, { client: wrappedClient }),
431
+ activeTab === "auth" && /* @__PURE__ */ jsxRuntime.jsx(AuthInspector, { client }),
432
+ activeTab === "requests" && /* @__PURE__ */ jsxRuntime.jsx(
433
+ RequestLog,
434
+ {
435
+ entries: logger.getEntries(),
436
+ onClear: () => logger.clear()
437
+ }
438
+ ),
439
+ activeTab === "seeds" && /* @__PURE__ */ jsxRuntime.jsx(SeedManager, { client })
440
+ ] })
441
+ ] }) });
442
+ }
443
+
444
+ exports.FauxbaseDevtools = FauxbaseDevtools;
445
+ exports.createRequestLogger = createRequestLogger;
446
+ //# sourceMappingURL=index.cjs.map
447
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/request-logger.ts","../src/styles.ts","../src/panels/data-inspector.tsx","../src/panels/auth-inspector.tsx","../src/panels/request-log.tsx","../src/panels/seed-manager.tsx","../src/devtools.tsx"],"names":["useState","useEffect","jsx","jsxs","Fragment"],"mappings":"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","file":"index.cjs","sourcesContent":["import type { RequestLogEntry } from './types';\n\nlet idCounter = 0;\n\nexport function createRequestLogger(maxEntries = 100) {\n const entries: RequestLogEntry[] = [];\n const listeners = new Set<() => void>();\n\n function addEntry(entry: RequestLogEntry): void {\n entries.unshift(entry);\n if (entries.length > maxEntries) {\n entries.pop();\n }\n listeners.forEach(fn => fn());\n }\n\n function getEntries(): RequestLogEntry[] {\n return entries;\n }\n\n function clear(): void {\n entries.length = 0;\n listeners.forEach(fn => fn());\n }\n\n function subscribe(fn: () => void): () => void {\n listeners.add(fn);\n return () => listeners.delete(fn);\n }\n\n /**\n * Wraps a service instance with a Proxy that logs all method calls.\n * Zero changes to the Service class.\n */\n function wrapService<T extends object>(service: T, serviceName: string): T {\n return new Proxy(service, {\n get(target, prop, receiver) {\n const value = Reflect.get(target, prop, receiver);\n if (typeof value !== 'function') return value;\n\n // Skip internal methods\n if (typeof prop === 'string' && prop.startsWith('_')) return value;\n\n return async function (this: any, ...args: any[]) {\n const entry: RequestLogEntry = {\n id: `log_${++idCounter}`,\n timestamp: Date.now(),\n service: serviceName,\n method: prop as string,\n args,\n duration: 0,\n };\n\n const start = performance.now();\n try {\n const result = await value.apply(target, args);\n entry.result = result;\n entry.duration = Math.round(performance.now() - start);\n addEntry(entry);\n return result;\n } catch (err: any) {\n entry.error = err.message ?? String(err);\n entry.duration = Math.round(performance.now() - start);\n addEntry(entry);\n throw err;\n }\n };\n },\n });\n }\n\n return { addEntry, getEntries, clear, subscribe, wrapService };\n}\n\nexport type RequestLogger = ReturnType<typeof createRequestLogger>;\n","export const STYLES = {\n container: {\n position: 'fixed' as const,\n bottom: '16px',\n right: '16px',\n zIndex: 99999,\n fontFamily: '-apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, monospace',\n fontSize: '13px',\n color: '#e4e4e7',\n },\n containerLeft: {\n right: 'auto',\n left: '16px',\n },\n toggleButton: {\n width: '40px',\n height: '40px',\n borderRadius: '8px',\n border: '1px solid #3f3f46',\n background: '#18181b',\n color: '#a1a1aa',\n cursor: 'pointer',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n fontSize: '18px',\n boxShadow: '0 4px 12px rgba(0,0,0,0.4)',\n },\n panel: {\n width: '480px',\n maxHeight: '400px',\n background: '#18181b',\n border: '1px solid #3f3f46',\n borderRadius: '8px',\n boxShadow: '0 8px 24px rgba(0,0,0,0.5)',\n overflow: 'hidden',\n display: 'flex',\n flexDirection: 'column' as const,\n },\n tabBar: {\n display: 'flex',\n borderBottom: '1px solid #3f3f46',\n background: '#09090b',\n },\n tab: {\n padding: '8px 16px',\n border: 'none',\n background: 'transparent',\n color: '#71717a',\n cursor: 'pointer',\n fontSize: '12px',\n fontWeight: 500,\n borderBottom: '2px solid transparent',\n },\n tabActive: {\n color: '#e4e4e7',\n borderBottomColor: '#3b82f6',\n },\n panelContent: {\n padding: '12px',\n overflowY: 'auto' as const,\n maxHeight: '340px',\n flex: 1,\n },\n table: {\n width: '100%',\n borderCollapse: 'collapse' as const,\n fontSize: '12px',\n },\n th: {\n textAlign: 'left' as const,\n padding: '6px 8px',\n borderBottom: '1px solid #27272a',\n color: '#a1a1aa',\n fontWeight: 600,\n },\n td: {\n padding: '6px 8px',\n borderBottom: '1px solid #27272a',\n maxWidth: '160px',\n overflow: 'hidden',\n textOverflow: 'ellipsis',\n whiteSpace: 'nowrap' as const,\n },\n button: {\n padding: '6px 12px',\n borderRadius: '4px',\n border: '1px solid #3f3f46',\n background: '#27272a',\n color: '#e4e4e7',\n cursor: 'pointer',\n fontSize: '12px',\n },\n buttonDanger: {\n background: '#7f1d1d',\n borderColor: '#991b1b',\n },\n badge: {\n display: 'inline-block',\n padding: '2px 6px',\n borderRadius: '4px',\n fontSize: '11px',\n fontWeight: 600,\n },\n badgeSuccess: {\n background: '#14532d',\n color: '#4ade80',\n },\n badgeError: {\n background: '#7f1d1d',\n color: '#f87171',\n },\n emptyState: {\n color: '#71717a',\n textAlign: 'center' as const,\n padding: '24px',\n fontSize: '12px',\n },\n label: {\n color: '#a1a1aa',\n fontSize: '11px',\n marginBottom: '4px',\n },\n value: {\n color: '#e4e4e7',\n fontSize: '13px',\n marginBottom: '12px',\n wordBreak: 'break-all' as const,\n },\n};\n","import React, { useEffect, useState } from 'react';\nimport { STYLES } from '../styles';\n\ninterface DataInspectorProps {\n client: any;\n}\n\nexport function DataInspector({ client }: DataInspectorProps) {\n const [selected, setSelected] = useState<string | null>(null);\n const [records, setRecords] = useState<any[]>([]);\n\n const serviceNames = Object.keys(client).filter(\n k => k !== 'auth' && client[k] && typeof client[k].list === 'function',\n );\n\n useEffect(() => {\n if (!selected && serviceNames.length > 0) {\n setSelected(serviceNames[0]);\n }\n }, [serviceNames.length]);\n\n useEffect(() => {\n if (!selected) return;\n const service = client[selected];\n if (!service) return;\n service.list({ size: 50 }).then((res: any) => setRecords(res.items ?? [])).catch(() => setRecords([]));\n }, [selected]);\n\n if (serviceNames.length === 0) {\n return <div style={STYLES.emptyState}>No services registered</div>;\n }\n\n return (\n <div>\n <div style={{ display: 'flex', gap: '4px', marginBottom: '8px', flexWrap: 'wrap' }}>\n {serviceNames.map(name => (\n <button\n key={name}\n onClick={() => setSelected(name)}\n style={{\n ...STYLES.button,\n ...(selected === name ? { background: '#3b82f6', borderColor: '#3b82f6' } : {}),\n }}\n >\n {name}\n </button>\n ))}\n </div>\n\n {records.length === 0 ? (\n <div style={STYLES.emptyState}>No records in {selected}</div>\n ) : (\n <table style={STYLES.table}>\n <thead>\n <tr>\n <th style={STYLES.th}>id</th>\n {Object.keys(records[0])\n .filter(k => k !== 'id' && !k.endsWith('At') && !k.endsWith('Id') && !k.endsWith('Name') && k !== 'version' && k !== 'password')\n .slice(0, 3)\n .map(k => (\n <th key={k} style={STYLES.th}>{k}</th>\n ))}\n </tr>\n </thead>\n <tbody>\n {records.map((r: any) => (\n <tr key={r.id}>\n <td style={STYLES.td}>{r.id?.substring(0, 12)}...</td>\n {Object.keys(records[0])\n .filter(k => k !== 'id' && !k.endsWith('At') && !k.endsWith('Id') && !k.endsWith('Name') && k !== 'version' && k !== 'password')\n .slice(0, 3)\n .map(k => (\n <td key={k} style={STYLES.td}>{String(r[k] ?? '')}</td>\n ))}\n </tr>\n ))}\n </tbody>\n </table>\n )}\n </div>\n );\n}\n","import React from 'react';\nimport { STYLES } from '../styles';\n\ninterface AuthInspectorProps {\n client: any;\n}\n\nexport function AuthInspector({ client }: AuthInspectorProps) {\n const auth = client.auth;\n\n if (!auth) {\n return <div style={STYLES.emptyState}>Auth not configured</div>;\n }\n\n return (\n <div>\n <div style={STYLES.label}>Status</div>\n <div style={{ ...STYLES.value, display: 'flex', alignItems: 'center', gap: '8px' }}>\n <span\n style={{\n ...STYLES.badge,\n ...(auth.isLoggedIn ? STYLES.badgeSuccess : STYLES.badgeError),\n }}\n >\n {auth.isLoggedIn ? 'Logged In' : 'Logged Out'}\n </span>\n </div>\n\n {auth.isLoggedIn && (\n <>\n <div style={STYLES.label}>User</div>\n <div style={STYLES.value}>\n {auth.currentUser?.email ?? auth.currentUser?.id ?? 'Unknown'}\n </div>\n\n <div style={STYLES.label}>Token</div>\n <div style={{ ...STYLES.value, fontSize: '11px', fontFamily: 'monospace' }}>\n {auth.token?.substring(0, 40)}...\n </div>\n\n <button\n style={{ ...STYLES.button, ...STYLES.buttonDanger }}\n onClick={() => {\n auth.logout();\n // Force re-render\n window.dispatchEvent(new Event('fauxbase:auth-change'));\n }}\n >\n Logout\n </button>\n </>\n )}\n </div>\n );\n}\n","import React from 'react';\nimport type { RequestLogEntry } from '../types';\nimport { STYLES } from '../styles';\n\ninterface RequestLogProps {\n entries: RequestLogEntry[];\n onClear: () => void;\n}\n\nexport function RequestLog({ entries, onClear }: RequestLogProps) {\n if (entries.length === 0) {\n return <div style={STYLES.emptyState}>No requests logged yet</div>;\n }\n\n return (\n <div>\n <div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: '8px' }}>\n <span style={STYLES.label}>{entries.length} requests</span>\n <button style={STYLES.button} onClick={onClear}>Clear</button>\n </div>\n <table style={STYLES.table}>\n <thead>\n <tr>\n <th style={STYLES.th}>Service</th>\n <th style={STYLES.th}>Method</th>\n <th style={STYLES.th}>Status</th>\n <th style={STYLES.th}>Time</th>\n </tr>\n </thead>\n <tbody>\n {entries.map(entry => (\n <tr key={entry.id}>\n <td style={STYLES.td}>{entry.service}</td>\n <td style={STYLES.td}>{entry.method}</td>\n <td style={STYLES.td}>\n <span\n style={{\n ...STYLES.badge,\n ...(entry.error ? STYLES.badgeError : STYLES.badgeSuccess),\n }}\n >\n {entry.error ? 'ERR' : 'OK'}\n </span>\n </td>\n <td style={STYLES.td}>{entry.duration}ms</td>\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n );\n}\n","import React, { useState } from 'react';\nimport { STYLES } from '../styles';\n\ninterface SeedManagerProps {\n client: any;\n}\n\nexport function SeedManager({ client }: SeedManagerProps) {\n const [resetting, setResetting] = useState(false);\n\n const serviceNames = Object.keys(client).filter(\n k => k !== 'auth' && client[k] && typeof client[k].list === 'function',\n );\n\n const handleReset = async (name: string) => {\n setResetting(true);\n try {\n // Check if the driver has a clear method (LocalDriver only)\n const service = client[name];\n if (service?.driver?.clear) {\n service.driver.clear(name);\n }\n } finally {\n setResetting(false);\n }\n };\n\n if (serviceNames.length === 0) {\n return <div style={STYLES.emptyState}>No services registered</div>;\n }\n\n return (\n <div>\n <div style={{ ...STYLES.label, marginBottom: '12px' }}>\n Reset seed data for individual resources (LocalDriver only)\n </div>\n {serviceNames.map(name => (\n <div key={name} style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '8px' }}>\n <span>{name}</span>\n <button\n style={{ ...STYLES.button, ...STYLES.buttonDanger }}\n onClick={() => handleReset(name)}\n disabled={resetting}\n >\n Reset\n </button>\n </div>\n ))}\n </div>\n );\n}\n","import React, { useState, useEffect, useCallback } from 'react';\nimport type { PanelTab, DevtoolsConfig } from './types';\nimport { createRequestLogger, type RequestLogger } from './request-logger';\nimport { DataInspector } from './panels/data-inspector';\nimport { AuthInspector } from './panels/auth-inspector';\nimport { RequestLog } from './panels/request-log';\nimport { SeedManager } from './panels/seed-manager';\nimport { STYLES } from './styles';\n\ninterface FauxbaseDevtoolsProps {\n client: any;\n config?: DevtoolsConfig;\n}\n\nconst TABS: { key: PanelTab; label: string }[] = [\n { key: 'data', label: 'Data' },\n { key: 'auth', label: 'Auth' },\n { key: 'requests', label: 'Requests' },\n { key: 'seeds', label: 'Seeds' },\n];\n\nexport function FauxbaseDevtools({ client, config = {} }: FauxbaseDevtoolsProps) {\n const [open, setOpen] = useState(config.defaultOpen ?? false);\n const [activeTab, setActiveTab] = useState<PanelTab>('data');\n const [logger] = useState<RequestLogger>(() => createRequestLogger(config.maxLogEntries ?? 100));\n const [, setTick] = useState(0);\n\n // Subscribe to logger updates for re-render\n useEffect(() => {\n return logger.subscribe(() => setTick(t => t + 1));\n }, [logger]);\n\n // Wrap services with proxy logger\n const [wrappedClient] = useState(() => {\n const wrapped = { ...client };\n for (const [name, service] of Object.entries(client)) {\n if (name !== 'auth' && service && typeof (service as any).list === 'function') {\n wrapped[name] = logger.wrapService(service as object, name);\n }\n }\n return wrapped;\n });\n\n const position = config.position ?? 'bottom-right';\n const isLeft = position === 'bottom-left';\n\n const containerStyle = {\n ...STYLES.container,\n ...(isLeft ? STYLES.containerLeft : {}),\n };\n\n if (!open) {\n return (\n <div style={containerStyle} data-testid=\"fauxbase-devtools\">\n <button\n style={STYLES.toggleButton}\n onClick={() => setOpen(true)}\n aria-label=\"Open Fauxbase DevTools\"\n data-testid=\"devtools-toggle\"\n >\n {'{ }'}\n </button>\n </div>\n );\n }\n\n return (\n <div style={containerStyle} data-testid=\"fauxbase-devtools\">\n <div style={STYLES.panel}>\n <div style={STYLES.tabBar}>\n {TABS.map(tab => (\n <button\n key={tab.key}\n style={{\n ...STYLES.tab,\n ...(activeTab === tab.key ? STYLES.tabActive : {}),\n }}\n onClick={() => setActiveTab(tab.key)}\n data-testid={`tab-${tab.key}`}\n >\n {tab.label}\n </button>\n ))}\n <button\n style={{ ...STYLES.tab, marginLeft: 'auto' }}\n onClick={() => setOpen(false)}\n aria-label=\"Close DevTools\"\n data-testid=\"devtools-close\"\n >\n x\n </button>\n </div>\n <div style={STYLES.panelContent}>\n {activeTab === 'data' && <DataInspector client={wrappedClient} />}\n {activeTab === 'auth' && <AuthInspector client={client} />}\n {activeTab === 'requests' && (\n <RequestLog\n entries={logger.getEntries()}\n onClear={() => logger.clear()}\n />\n )}\n {activeTab === 'seeds' && <SeedManager client={client} />}\n </div>\n </div>\n </div>\n );\n}\n"]}
@@ -0,0 +1,35 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+
3
+ interface RequestLogEntry {
4
+ id: string;
5
+ timestamp: number;
6
+ service: string;
7
+ method: string;
8
+ args: any[];
9
+ result?: any;
10
+ error?: string;
11
+ duration: number;
12
+ }
13
+ type PanelTab = 'data' | 'auth' | 'requests' | 'seeds';
14
+ interface DevtoolsConfig {
15
+ position?: 'bottom-right' | 'bottom-left';
16
+ defaultOpen?: boolean;
17
+ maxLogEntries?: number;
18
+ }
19
+
20
+ interface FauxbaseDevtoolsProps {
21
+ client: any;
22
+ config?: DevtoolsConfig;
23
+ }
24
+ declare function FauxbaseDevtools({ client, config }: FauxbaseDevtoolsProps): react_jsx_runtime.JSX.Element;
25
+
26
+ declare function createRequestLogger(maxEntries?: number): {
27
+ addEntry: (entry: RequestLogEntry) => void;
28
+ getEntries: () => RequestLogEntry[];
29
+ clear: () => void;
30
+ subscribe: (fn: () => void) => () => void;
31
+ wrapService: <T extends object>(service: T, serviceName: string) => T;
32
+ };
33
+ type RequestLogger = ReturnType<typeof createRequestLogger>;
34
+
35
+ export { type DevtoolsConfig, FauxbaseDevtools, type PanelTab, type RequestLogEntry, type RequestLogger, createRequestLogger };
@@ -0,0 +1,35 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+
3
+ interface RequestLogEntry {
4
+ id: string;
5
+ timestamp: number;
6
+ service: string;
7
+ method: string;
8
+ args: any[];
9
+ result?: any;
10
+ error?: string;
11
+ duration: number;
12
+ }
13
+ type PanelTab = 'data' | 'auth' | 'requests' | 'seeds';
14
+ interface DevtoolsConfig {
15
+ position?: 'bottom-right' | 'bottom-left';
16
+ defaultOpen?: boolean;
17
+ maxLogEntries?: number;
18
+ }
19
+
20
+ interface FauxbaseDevtoolsProps {
21
+ client: any;
22
+ config?: DevtoolsConfig;
23
+ }
24
+ declare function FauxbaseDevtools({ client, config }: FauxbaseDevtoolsProps): react_jsx_runtime.JSX.Element;
25
+
26
+ declare function createRequestLogger(maxEntries?: number): {
27
+ addEntry: (entry: RequestLogEntry) => void;
28
+ getEntries: () => RequestLogEntry[];
29
+ clear: () => void;
30
+ subscribe: (fn: () => void) => () => void;
31
+ wrapService: <T extends object>(service: T, serviceName: string) => T;
32
+ };
33
+ type RequestLogger = ReturnType<typeof createRequestLogger>;
34
+
35
+ export { type DevtoolsConfig, FauxbaseDevtools, type PanelTab, type RequestLogEntry, type RequestLogger, createRequestLogger };
package/dist/index.js ADDED
@@ -0,0 +1,444 @@
1
+ import { useState, useEffect } from 'react';
2
+ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
3
+
4
+ // src/devtools.tsx
5
+
6
+ // src/request-logger.ts
7
+ var idCounter = 0;
8
+ function createRequestLogger(maxEntries = 100) {
9
+ const entries = [];
10
+ const listeners = /* @__PURE__ */ new Set();
11
+ function addEntry(entry) {
12
+ entries.unshift(entry);
13
+ if (entries.length > maxEntries) {
14
+ entries.pop();
15
+ }
16
+ listeners.forEach((fn) => fn());
17
+ }
18
+ function getEntries() {
19
+ return entries;
20
+ }
21
+ function clear() {
22
+ entries.length = 0;
23
+ listeners.forEach((fn) => fn());
24
+ }
25
+ function subscribe(fn) {
26
+ listeners.add(fn);
27
+ return () => listeners.delete(fn);
28
+ }
29
+ function wrapService(service, serviceName) {
30
+ return new Proxy(service, {
31
+ get(target, prop, receiver) {
32
+ const value = Reflect.get(target, prop, receiver);
33
+ if (typeof value !== "function") return value;
34
+ if (typeof prop === "string" && prop.startsWith("_")) return value;
35
+ return async function(...args) {
36
+ const entry = {
37
+ id: `log_${++idCounter}`,
38
+ timestamp: Date.now(),
39
+ service: serviceName,
40
+ method: prop,
41
+ args,
42
+ duration: 0
43
+ };
44
+ const start = performance.now();
45
+ try {
46
+ const result = await value.apply(target, args);
47
+ entry.result = result;
48
+ entry.duration = Math.round(performance.now() - start);
49
+ addEntry(entry);
50
+ return result;
51
+ } catch (err) {
52
+ entry.error = err.message ?? String(err);
53
+ entry.duration = Math.round(performance.now() - start);
54
+ addEntry(entry);
55
+ throw err;
56
+ }
57
+ };
58
+ }
59
+ });
60
+ }
61
+ return { addEntry, getEntries, clear, subscribe, wrapService };
62
+ }
63
+
64
+ // src/styles.ts
65
+ var STYLES = {
66
+ container: {
67
+ position: "fixed",
68
+ bottom: "16px",
69
+ right: "16px",
70
+ zIndex: 99999,
71
+ fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, monospace',
72
+ fontSize: "13px",
73
+ color: "#e4e4e7"
74
+ },
75
+ containerLeft: {
76
+ right: "auto",
77
+ left: "16px"
78
+ },
79
+ toggleButton: {
80
+ width: "40px",
81
+ height: "40px",
82
+ borderRadius: "8px",
83
+ border: "1px solid #3f3f46",
84
+ background: "#18181b",
85
+ color: "#a1a1aa",
86
+ cursor: "pointer",
87
+ display: "flex",
88
+ alignItems: "center",
89
+ justifyContent: "center",
90
+ fontSize: "18px",
91
+ boxShadow: "0 4px 12px rgba(0,0,0,0.4)"
92
+ },
93
+ panel: {
94
+ width: "480px",
95
+ maxHeight: "400px",
96
+ background: "#18181b",
97
+ border: "1px solid #3f3f46",
98
+ borderRadius: "8px",
99
+ boxShadow: "0 8px 24px rgba(0,0,0,0.5)",
100
+ overflow: "hidden",
101
+ display: "flex",
102
+ flexDirection: "column"
103
+ },
104
+ tabBar: {
105
+ display: "flex",
106
+ borderBottom: "1px solid #3f3f46",
107
+ background: "#09090b"
108
+ },
109
+ tab: {
110
+ padding: "8px 16px",
111
+ border: "none",
112
+ background: "transparent",
113
+ color: "#71717a",
114
+ cursor: "pointer",
115
+ fontSize: "12px",
116
+ fontWeight: 500,
117
+ borderBottom: "2px solid transparent"
118
+ },
119
+ tabActive: {
120
+ color: "#e4e4e7",
121
+ borderBottomColor: "#3b82f6"
122
+ },
123
+ panelContent: {
124
+ padding: "12px",
125
+ overflowY: "auto",
126
+ maxHeight: "340px",
127
+ flex: 1
128
+ },
129
+ table: {
130
+ width: "100%",
131
+ borderCollapse: "collapse",
132
+ fontSize: "12px"
133
+ },
134
+ th: {
135
+ textAlign: "left",
136
+ padding: "6px 8px",
137
+ borderBottom: "1px solid #27272a",
138
+ color: "#a1a1aa",
139
+ fontWeight: 600
140
+ },
141
+ td: {
142
+ padding: "6px 8px",
143
+ borderBottom: "1px solid #27272a",
144
+ maxWidth: "160px",
145
+ overflow: "hidden",
146
+ textOverflow: "ellipsis",
147
+ whiteSpace: "nowrap"
148
+ },
149
+ button: {
150
+ padding: "6px 12px",
151
+ borderRadius: "4px",
152
+ border: "1px solid #3f3f46",
153
+ background: "#27272a",
154
+ color: "#e4e4e7",
155
+ cursor: "pointer",
156
+ fontSize: "12px"
157
+ },
158
+ buttonDanger: {
159
+ background: "#7f1d1d",
160
+ borderColor: "#991b1b"
161
+ },
162
+ badge: {
163
+ display: "inline-block",
164
+ padding: "2px 6px",
165
+ borderRadius: "4px",
166
+ fontSize: "11px",
167
+ fontWeight: 600
168
+ },
169
+ badgeSuccess: {
170
+ background: "#14532d",
171
+ color: "#4ade80"
172
+ },
173
+ badgeError: {
174
+ background: "#7f1d1d",
175
+ color: "#f87171"
176
+ },
177
+ emptyState: {
178
+ color: "#71717a",
179
+ textAlign: "center",
180
+ padding: "24px",
181
+ fontSize: "12px"
182
+ },
183
+ label: {
184
+ color: "#a1a1aa",
185
+ fontSize: "11px",
186
+ marginBottom: "4px"
187
+ },
188
+ value: {
189
+ color: "#e4e4e7",
190
+ fontSize: "13px",
191
+ marginBottom: "12px",
192
+ wordBreak: "break-all"
193
+ }
194
+ };
195
+ function DataInspector({ client }) {
196
+ const [selected, setSelected] = useState(null);
197
+ const [records, setRecords] = useState([]);
198
+ const serviceNames = Object.keys(client).filter(
199
+ (k) => k !== "auth" && client[k] && typeof client[k].list === "function"
200
+ );
201
+ useEffect(() => {
202
+ if (!selected && serviceNames.length > 0) {
203
+ setSelected(serviceNames[0]);
204
+ }
205
+ }, [serviceNames.length]);
206
+ useEffect(() => {
207
+ if (!selected) return;
208
+ const service = client[selected];
209
+ if (!service) return;
210
+ service.list({ size: 50 }).then((res) => setRecords(res.items ?? [])).catch(() => setRecords([]));
211
+ }, [selected]);
212
+ if (serviceNames.length === 0) {
213
+ return /* @__PURE__ */ jsx("div", { style: STYLES.emptyState, children: "No services registered" });
214
+ }
215
+ return /* @__PURE__ */ jsxs("div", { children: [
216
+ /* @__PURE__ */ jsx("div", { style: { display: "flex", gap: "4px", marginBottom: "8px", flexWrap: "wrap" }, children: serviceNames.map((name) => /* @__PURE__ */ jsx(
217
+ "button",
218
+ {
219
+ onClick: () => setSelected(name),
220
+ style: {
221
+ ...STYLES.button,
222
+ ...selected === name ? { background: "#3b82f6", borderColor: "#3b82f6" } : {}
223
+ },
224
+ children: name
225
+ },
226
+ name
227
+ )) }),
228
+ records.length === 0 ? /* @__PURE__ */ jsxs("div", { style: STYLES.emptyState, children: [
229
+ "No records in ",
230
+ selected
231
+ ] }) : /* @__PURE__ */ jsxs("table", { style: STYLES.table, children: [
232
+ /* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsxs("tr", { children: [
233
+ /* @__PURE__ */ jsx("th", { style: STYLES.th, children: "id" }),
234
+ Object.keys(records[0]).filter((k) => k !== "id" && !k.endsWith("At") && !k.endsWith("Id") && !k.endsWith("Name") && k !== "version" && k !== "password").slice(0, 3).map((k) => /* @__PURE__ */ jsx("th", { style: STYLES.th, children: k }, k))
235
+ ] }) }),
236
+ /* @__PURE__ */ jsx("tbody", { children: records.map((r) => /* @__PURE__ */ jsxs("tr", { children: [
237
+ /* @__PURE__ */ jsxs("td", { style: STYLES.td, children: [
238
+ r.id?.substring(0, 12),
239
+ "..."
240
+ ] }),
241
+ Object.keys(records[0]).filter((k) => k !== "id" && !k.endsWith("At") && !k.endsWith("Id") && !k.endsWith("Name") && k !== "version" && k !== "password").slice(0, 3).map((k) => /* @__PURE__ */ jsx("td", { style: STYLES.td, children: String(r[k] ?? "") }, k))
242
+ ] }, r.id)) })
243
+ ] })
244
+ ] });
245
+ }
246
+ function AuthInspector({ client }) {
247
+ const auth = client.auth;
248
+ if (!auth) {
249
+ return /* @__PURE__ */ jsx("div", { style: STYLES.emptyState, children: "Auth not configured" });
250
+ }
251
+ return /* @__PURE__ */ jsxs("div", { children: [
252
+ /* @__PURE__ */ jsx("div", { style: STYLES.label, children: "Status" }),
253
+ /* @__PURE__ */ jsx("div", { style: { ...STYLES.value, display: "flex", alignItems: "center", gap: "8px" }, children: /* @__PURE__ */ jsx(
254
+ "span",
255
+ {
256
+ style: {
257
+ ...STYLES.badge,
258
+ ...auth.isLoggedIn ? STYLES.badgeSuccess : STYLES.badgeError
259
+ },
260
+ children: auth.isLoggedIn ? "Logged In" : "Logged Out"
261
+ }
262
+ ) }),
263
+ auth.isLoggedIn && /* @__PURE__ */ jsxs(Fragment, { children: [
264
+ /* @__PURE__ */ jsx("div", { style: STYLES.label, children: "User" }),
265
+ /* @__PURE__ */ jsx("div", { style: STYLES.value, children: auth.currentUser?.email ?? auth.currentUser?.id ?? "Unknown" }),
266
+ /* @__PURE__ */ jsx("div", { style: STYLES.label, children: "Token" }),
267
+ /* @__PURE__ */ jsxs("div", { style: { ...STYLES.value, fontSize: "11px", fontFamily: "monospace" }, children: [
268
+ auth.token?.substring(0, 40),
269
+ "..."
270
+ ] }),
271
+ /* @__PURE__ */ jsx(
272
+ "button",
273
+ {
274
+ style: { ...STYLES.button, ...STYLES.buttonDanger },
275
+ onClick: () => {
276
+ auth.logout();
277
+ window.dispatchEvent(new Event("fauxbase:auth-change"));
278
+ },
279
+ children: "Logout"
280
+ }
281
+ )
282
+ ] })
283
+ ] });
284
+ }
285
+ function RequestLog({ entries, onClear }) {
286
+ if (entries.length === 0) {
287
+ return /* @__PURE__ */ jsx("div", { style: STYLES.emptyState, children: "No requests logged yet" });
288
+ }
289
+ return /* @__PURE__ */ jsxs("div", { children: [
290
+ /* @__PURE__ */ jsxs("div", { style: { display: "flex", justifyContent: "space-between", marginBottom: "8px" }, children: [
291
+ /* @__PURE__ */ jsxs("span", { style: STYLES.label, children: [
292
+ entries.length,
293
+ " requests"
294
+ ] }),
295
+ /* @__PURE__ */ jsx("button", { style: STYLES.button, onClick: onClear, children: "Clear" })
296
+ ] }),
297
+ /* @__PURE__ */ jsxs("table", { style: STYLES.table, children: [
298
+ /* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsxs("tr", { children: [
299
+ /* @__PURE__ */ jsx("th", { style: STYLES.th, children: "Service" }),
300
+ /* @__PURE__ */ jsx("th", { style: STYLES.th, children: "Method" }),
301
+ /* @__PURE__ */ jsx("th", { style: STYLES.th, children: "Status" }),
302
+ /* @__PURE__ */ jsx("th", { style: STYLES.th, children: "Time" })
303
+ ] }) }),
304
+ /* @__PURE__ */ jsx("tbody", { children: entries.map((entry) => /* @__PURE__ */ jsxs("tr", { children: [
305
+ /* @__PURE__ */ jsx("td", { style: STYLES.td, children: entry.service }),
306
+ /* @__PURE__ */ jsx("td", { style: STYLES.td, children: entry.method }),
307
+ /* @__PURE__ */ jsx("td", { style: STYLES.td, children: /* @__PURE__ */ jsx(
308
+ "span",
309
+ {
310
+ style: {
311
+ ...STYLES.badge,
312
+ ...entry.error ? STYLES.badgeError : STYLES.badgeSuccess
313
+ },
314
+ children: entry.error ? "ERR" : "OK"
315
+ }
316
+ ) }),
317
+ /* @__PURE__ */ jsxs("td", { style: STYLES.td, children: [
318
+ entry.duration,
319
+ "ms"
320
+ ] })
321
+ ] }, entry.id)) })
322
+ ] })
323
+ ] });
324
+ }
325
+ function SeedManager({ client }) {
326
+ const [resetting, setResetting] = useState(false);
327
+ const serviceNames = Object.keys(client).filter(
328
+ (k) => k !== "auth" && client[k] && typeof client[k].list === "function"
329
+ );
330
+ const handleReset = async (name) => {
331
+ setResetting(true);
332
+ try {
333
+ const service = client[name];
334
+ if (service?.driver?.clear) {
335
+ service.driver.clear(name);
336
+ }
337
+ } finally {
338
+ setResetting(false);
339
+ }
340
+ };
341
+ if (serviceNames.length === 0) {
342
+ return /* @__PURE__ */ jsx("div", { style: STYLES.emptyState, children: "No services registered" });
343
+ }
344
+ return /* @__PURE__ */ jsxs("div", { children: [
345
+ /* @__PURE__ */ jsx("div", { style: { ...STYLES.label, marginBottom: "12px" }, children: "Reset seed data for individual resources (LocalDriver only)" }),
346
+ serviceNames.map((name) => /* @__PURE__ */ jsxs("div", { style: { display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: "8px" }, children: [
347
+ /* @__PURE__ */ jsx("span", { children: name }),
348
+ /* @__PURE__ */ jsx(
349
+ "button",
350
+ {
351
+ style: { ...STYLES.button, ...STYLES.buttonDanger },
352
+ onClick: () => handleReset(name),
353
+ disabled: resetting,
354
+ children: "Reset"
355
+ }
356
+ )
357
+ ] }, name))
358
+ ] });
359
+ }
360
+ var TABS = [
361
+ { key: "data", label: "Data" },
362
+ { key: "auth", label: "Auth" },
363
+ { key: "requests", label: "Requests" },
364
+ { key: "seeds", label: "Seeds" }
365
+ ];
366
+ function FauxbaseDevtools({ client, config = {} }) {
367
+ const [open, setOpen] = useState(config.defaultOpen ?? false);
368
+ const [activeTab, setActiveTab] = useState("data");
369
+ const [logger] = useState(() => createRequestLogger(config.maxLogEntries ?? 100));
370
+ const [, setTick] = useState(0);
371
+ useEffect(() => {
372
+ return logger.subscribe(() => setTick((t) => t + 1));
373
+ }, [logger]);
374
+ const [wrappedClient] = useState(() => {
375
+ const wrapped = { ...client };
376
+ for (const [name, service] of Object.entries(client)) {
377
+ if (name !== "auth" && service && typeof service.list === "function") {
378
+ wrapped[name] = logger.wrapService(service, name);
379
+ }
380
+ }
381
+ return wrapped;
382
+ });
383
+ const position = config.position ?? "bottom-right";
384
+ const isLeft = position === "bottom-left";
385
+ const containerStyle = {
386
+ ...STYLES.container,
387
+ ...isLeft ? STYLES.containerLeft : {}
388
+ };
389
+ if (!open) {
390
+ return /* @__PURE__ */ jsx("div", { style: containerStyle, "data-testid": "fauxbase-devtools", children: /* @__PURE__ */ jsx(
391
+ "button",
392
+ {
393
+ style: STYLES.toggleButton,
394
+ onClick: () => setOpen(true),
395
+ "aria-label": "Open Fauxbase DevTools",
396
+ "data-testid": "devtools-toggle",
397
+ children: "{ }"
398
+ }
399
+ ) });
400
+ }
401
+ return /* @__PURE__ */ jsx("div", { style: containerStyle, "data-testid": "fauxbase-devtools", children: /* @__PURE__ */ jsxs("div", { style: STYLES.panel, children: [
402
+ /* @__PURE__ */ jsxs("div", { style: STYLES.tabBar, children: [
403
+ TABS.map((tab) => /* @__PURE__ */ jsx(
404
+ "button",
405
+ {
406
+ style: {
407
+ ...STYLES.tab,
408
+ ...activeTab === tab.key ? STYLES.tabActive : {}
409
+ },
410
+ onClick: () => setActiveTab(tab.key),
411
+ "data-testid": `tab-${tab.key}`,
412
+ children: tab.label
413
+ },
414
+ tab.key
415
+ )),
416
+ /* @__PURE__ */ jsx(
417
+ "button",
418
+ {
419
+ style: { ...STYLES.tab, marginLeft: "auto" },
420
+ onClick: () => setOpen(false),
421
+ "aria-label": "Close DevTools",
422
+ "data-testid": "devtools-close",
423
+ children: "x"
424
+ }
425
+ )
426
+ ] }),
427
+ /* @__PURE__ */ jsxs("div", { style: STYLES.panelContent, children: [
428
+ activeTab === "data" && /* @__PURE__ */ jsx(DataInspector, { client: wrappedClient }),
429
+ activeTab === "auth" && /* @__PURE__ */ jsx(AuthInspector, { client }),
430
+ activeTab === "requests" && /* @__PURE__ */ jsx(
431
+ RequestLog,
432
+ {
433
+ entries: logger.getEntries(),
434
+ onClear: () => logger.clear()
435
+ }
436
+ ),
437
+ activeTab === "seeds" && /* @__PURE__ */ jsx(SeedManager, { client })
438
+ ] })
439
+ ] }) });
440
+ }
441
+
442
+ export { FauxbaseDevtools, createRequestLogger };
443
+ //# sourceMappingURL=index.js.map
444
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/request-logger.ts","../src/styles.ts","../src/panels/data-inspector.tsx","../src/panels/auth-inspector.tsx","../src/panels/request-log.tsx","../src/panels/seed-manager.tsx","../src/devtools.tsx"],"names":["jsx","jsxs","useState","useEffect"],"mappings":"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","file":"index.js","sourcesContent":["import type { RequestLogEntry } from './types';\n\nlet idCounter = 0;\n\nexport function createRequestLogger(maxEntries = 100) {\n const entries: RequestLogEntry[] = [];\n const listeners = new Set<() => void>();\n\n function addEntry(entry: RequestLogEntry): void {\n entries.unshift(entry);\n if (entries.length > maxEntries) {\n entries.pop();\n }\n listeners.forEach(fn => fn());\n }\n\n function getEntries(): RequestLogEntry[] {\n return entries;\n }\n\n function clear(): void {\n entries.length = 0;\n listeners.forEach(fn => fn());\n }\n\n function subscribe(fn: () => void): () => void {\n listeners.add(fn);\n return () => listeners.delete(fn);\n }\n\n /**\n * Wraps a service instance with a Proxy that logs all method calls.\n * Zero changes to the Service class.\n */\n function wrapService<T extends object>(service: T, serviceName: string): T {\n return new Proxy(service, {\n get(target, prop, receiver) {\n const value = Reflect.get(target, prop, receiver);\n if (typeof value !== 'function') return value;\n\n // Skip internal methods\n if (typeof prop === 'string' && prop.startsWith('_')) return value;\n\n return async function (this: any, ...args: any[]) {\n const entry: RequestLogEntry = {\n id: `log_${++idCounter}`,\n timestamp: Date.now(),\n service: serviceName,\n method: prop as string,\n args,\n duration: 0,\n };\n\n const start = performance.now();\n try {\n const result = await value.apply(target, args);\n entry.result = result;\n entry.duration = Math.round(performance.now() - start);\n addEntry(entry);\n return result;\n } catch (err: any) {\n entry.error = err.message ?? String(err);\n entry.duration = Math.round(performance.now() - start);\n addEntry(entry);\n throw err;\n }\n };\n },\n });\n }\n\n return { addEntry, getEntries, clear, subscribe, wrapService };\n}\n\nexport type RequestLogger = ReturnType<typeof createRequestLogger>;\n","export const STYLES = {\n container: {\n position: 'fixed' as const,\n bottom: '16px',\n right: '16px',\n zIndex: 99999,\n fontFamily: '-apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, monospace',\n fontSize: '13px',\n color: '#e4e4e7',\n },\n containerLeft: {\n right: 'auto',\n left: '16px',\n },\n toggleButton: {\n width: '40px',\n height: '40px',\n borderRadius: '8px',\n border: '1px solid #3f3f46',\n background: '#18181b',\n color: '#a1a1aa',\n cursor: 'pointer',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n fontSize: '18px',\n boxShadow: '0 4px 12px rgba(0,0,0,0.4)',\n },\n panel: {\n width: '480px',\n maxHeight: '400px',\n background: '#18181b',\n border: '1px solid #3f3f46',\n borderRadius: '8px',\n boxShadow: '0 8px 24px rgba(0,0,0,0.5)',\n overflow: 'hidden',\n display: 'flex',\n flexDirection: 'column' as const,\n },\n tabBar: {\n display: 'flex',\n borderBottom: '1px solid #3f3f46',\n background: '#09090b',\n },\n tab: {\n padding: '8px 16px',\n border: 'none',\n background: 'transparent',\n color: '#71717a',\n cursor: 'pointer',\n fontSize: '12px',\n fontWeight: 500,\n borderBottom: '2px solid transparent',\n },\n tabActive: {\n color: '#e4e4e7',\n borderBottomColor: '#3b82f6',\n },\n panelContent: {\n padding: '12px',\n overflowY: 'auto' as const,\n maxHeight: '340px',\n flex: 1,\n },\n table: {\n width: '100%',\n borderCollapse: 'collapse' as const,\n fontSize: '12px',\n },\n th: {\n textAlign: 'left' as const,\n padding: '6px 8px',\n borderBottom: '1px solid #27272a',\n color: '#a1a1aa',\n fontWeight: 600,\n },\n td: {\n padding: '6px 8px',\n borderBottom: '1px solid #27272a',\n maxWidth: '160px',\n overflow: 'hidden',\n textOverflow: 'ellipsis',\n whiteSpace: 'nowrap' as const,\n },\n button: {\n padding: '6px 12px',\n borderRadius: '4px',\n border: '1px solid #3f3f46',\n background: '#27272a',\n color: '#e4e4e7',\n cursor: 'pointer',\n fontSize: '12px',\n },\n buttonDanger: {\n background: '#7f1d1d',\n borderColor: '#991b1b',\n },\n badge: {\n display: 'inline-block',\n padding: '2px 6px',\n borderRadius: '4px',\n fontSize: '11px',\n fontWeight: 600,\n },\n badgeSuccess: {\n background: '#14532d',\n color: '#4ade80',\n },\n badgeError: {\n background: '#7f1d1d',\n color: '#f87171',\n },\n emptyState: {\n color: '#71717a',\n textAlign: 'center' as const,\n padding: '24px',\n fontSize: '12px',\n },\n label: {\n color: '#a1a1aa',\n fontSize: '11px',\n marginBottom: '4px',\n },\n value: {\n color: '#e4e4e7',\n fontSize: '13px',\n marginBottom: '12px',\n wordBreak: 'break-all' as const,\n },\n};\n","import React, { useEffect, useState } from 'react';\nimport { STYLES } from '../styles';\n\ninterface DataInspectorProps {\n client: any;\n}\n\nexport function DataInspector({ client }: DataInspectorProps) {\n const [selected, setSelected] = useState<string | null>(null);\n const [records, setRecords] = useState<any[]>([]);\n\n const serviceNames = Object.keys(client).filter(\n k => k !== 'auth' && client[k] && typeof client[k].list === 'function',\n );\n\n useEffect(() => {\n if (!selected && serviceNames.length > 0) {\n setSelected(serviceNames[0]);\n }\n }, [serviceNames.length]);\n\n useEffect(() => {\n if (!selected) return;\n const service = client[selected];\n if (!service) return;\n service.list({ size: 50 }).then((res: any) => setRecords(res.items ?? [])).catch(() => setRecords([]));\n }, [selected]);\n\n if (serviceNames.length === 0) {\n return <div style={STYLES.emptyState}>No services registered</div>;\n }\n\n return (\n <div>\n <div style={{ display: 'flex', gap: '4px', marginBottom: '8px', flexWrap: 'wrap' }}>\n {serviceNames.map(name => (\n <button\n key={name}\n onClick={() => setSelected(name)}\n style={{\n ...STYLES.button,\n ...(selected === name ? { background: '#3b82f6', borderColor: '#3b82f6' } : {}),\n }}\n >\n {name}\n </button>\n ))}\n </div>\n\n {records.length === 0 ? (\n <div style={STYLES.emptyState}>No records in {selected}</div>\n ) : (\n <table style={STYLES.table}>\n <thead>\n <tr>\n <th style={STYLES.th}>id</th>\n {Object.keys(records[0])\n .filter(k => k !== 'id' && !k.endsWith('At') && !k.endsWith('Id') && !k.endsWith('Name') && k !== 'version' && k !== 'password')\n .slice(0, 3)\n .map(k => (\n <th key={k} style={STYLES.th}>{k}</th>\n ))}\n </tr>\n </thead>\n <tbody>\n {records.map((r: any) => (\n <tr key={r.id}>\n <td style={STYLES.td}>{r.id?.substring(0, 12)}...</td>\n {Object.keys(records[0])\n .filter(k => k !== 'id' && !k.endsWith('At') && !k.endsWith('Id') && !k.endsWith('Name') && k !== 'version' && k !== 'password')\n .slice(0, 3)\n .map(k => (\n <td key={k} style={STYLES.td}>{String(r[k] ?? '')}</td>\n ))}\n </tr>\n ))}\n </tbody>\n </table>\n )}\n </div>\n );\n}\n","import React from 'react';\nimport { STYLES } from '../styles';\n\ninterface AuthInspectorProps {\n client: any;\n}\n\nexport function AuthInspector({ client }: AuthInspectorProps) {\n const auth = client.auth;\n\n if (!auth) {\n return <div style={STYLES.emptyState}>Auth not configured</div>;\n }\n\n return (\n <div>\n <div style={STYLES.label}>Status</div>\n <div style={{ ...STYLES.value, display: 'flex', alignItems: 'center', gap: '8px' }}>\n <span\n style={{\n ...STYLES.badge,\n ...(auth.isLoggedIn ? STYLES.badgeSuccess : STYLES.badgeError),\n }}\n >\n {auth.isLoggedIn ? 'Logged In' : 'Logged Out'}\n </span>\n </div>\n\n {auth.isLoggedIn && (\n <>\n <div style={STYLES.label}>User</div>\n <div style={STYLES.value}>\n {auth.currentUser?.email ?? auth.currentUser?.id ?? 'Unknown'}\n </div>\n\n <div style={STYLES.label}>Token</div>\n <div style={{ ...STYLES.value, fontSize: '11px', fontFamily: 'monospace' }}>\n {auth.token?.substring(0, 40)}...\n </div>\n\n <button\n style={{ ...STYLES.button, ...STYLES.buttonDanger }}\n onClick={() => {\n auth.logout();\n // Force re-render\n window.dispatchEvent(new Event('fauxbase:auth-change'));\n }}\n >\n Logout\n </button>\n </>\n )}\n </div>\n );\n}\n","import React from 'react';\nimport type { RequestLogEntry } from '../types';\nimport { STYLES } from '../styles';\n\ninterface RequestLogProps {\n entries: RequestLogEntry[];\n onClear: () => void;\n}\n\nexport function RequestLog({ entries, onClear }: RequestLogProps) {\n if (entries.length === 0) {\n return <div style={STYLES.emptyState}>No requests logged yet</div>;\n }\n\n return (\n <div>\n <div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: '8px' }}>\n <span style={STYLES.label}>{entries.length} requests</span>\n <button style={STYLES.button} onClick={onClear}>Clear</button>\n </div>\n <table style={STYLES.table}>\n <thead>\n <tr>\n <th style={STYLES.th}>Service</th>\n <th style={STYLES.th}>Method</th>\n <th style={STYLES.th}>Status</th>\n <th style={STYLES.th}>Time</th>\n </tr>\n </thead>\n <tbody>\n {entries.map(entry => (\n <tr key={entry.id}>\n <td style={STYLES.td}>{entry.service}</td>\n <td style={STYLES.td}>{entry.method}</td>\n <td style={STYLES.td}>\n <span\n style={{\n ...STYLES.badge,\n ...(entry.error ? STYLES.badgeError : STYLES.badgeSuccess),\n }}\n >\n {entry.error ? 'ERR' : 'OK'}\n </span>\n </td>\n <td style={STYLES.td}>{entry.duration}ms</td>\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n );\n}\n","import React, { useState } from 'react';\nimport { STYLES } from '../styles';\n\ninterface SeedManagerProps {\n client: any;\n}\n\nexport function SeedManager({ client }: SeedManagerProps) {\n const [resetting, setResetting] = useState(false);\n\n const serviceNames = Object.keys(client).filter(\n k => k !== 'auth' && client[k] && typeof client[k].list === 'function',\n );\n\n const handleReset = async (name: string) => {\n setResetting(true);\n try {\n // Check if the driver has a clear method (LocalDriver only)\n const service = client[name];\n if (service?.driver?.clear) {\n service.driver.clear(name);\n }\n } finally {\n setResetting(false);\n }\n };\n\n if (serviceNames.length === 0) {\n return <div style={STYLES.emptyState}>No services registered</div>;\n }\n\n return (\n <div>\n <div style={{ ...STYLES.label, marginBottom: '12px' }}>\n Reset seed data for individual resources (LocalDriver only)\n </div>\n {serviceNames.map(name => (\n <div key={name} style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '8px' }}>\n <span>{name}</span>\n <button\n style={{ ...STYLES.button, ...STYLES.buttonDanger }}\n onClick={() => handleReset(name)}\n disabled={resetting}\n >\n Reset\n </button>\n </div>\n ))}\n </div>\n );\n}\n","import React, { useState, useEffect, useCallback } from 'react';\nimport type { PanelTab, DevtoolsConfig } from './types';\nimport { createRequestLogger, type RequestLogger } from './request-logger';\nimport { DataInspector } from './panels/data-inspector';\nimport { AuthInspector } from './panels/auth-inspector';\nimport { RequestLog } from './panels/request-log';\nimport { SeedManager } from './panels/seed-manager';\nimport { STYLES } from './styles';\n\ninterface FauxbaseDevtoolsProps {\n client: any;\n config?: DevtoolsConfig;\n}\n\nconst TABS: { key: PanelTab; label: string }[] = [\n { key: 'data', label: 'Data' },\n { key: 'auth', label: 'Auth' },\n { key: 'requests', label: 'Requests' },\n { key: 'seeds', label: 'Seeds' },\n];\n\nexport function FauxbaseDevtools({ client, config = {} }: FauxbaseDevtoolsProps) {\n const [open, setOpen] = useState(config.defaultOpen ?? false);\n const [activeTab, setActiveTab] = useState<PanelTab>('data');\n const [logger] = useState<RequestLogger>(() => createRequestLogger(config.maxLogEntries ?? 100));\n const [, setTick] = useState(0);\n\n // Subscribe to logger updates for re-render\n useEffect(() => {\n return logger.subscribe(() => setTick(t => t + 1));\n }, [logger]);\n\n // Wrap services with proxy logger\n const [wrappedClient] = useState(() => {\n const wrapped = { ...client };\n for (const [name, service] of Object.entries(client)) {\n if (name !== 'auth' && service && typeof (service as any).list === 'function') {\n wrapped[name] = logger.wrapService(service as object, name);\n }\n }\n return wrapped;\n });\n\n const position = config.position ?? 'bottom-right';\n const isLeft = position === 'bottom-left';\n\n const containerStyle = {\n ...STYLES.container,\n ...(isLeft ? STYLES.containerLeft : {}),\n };\n\n if (!open) {\n return (\n <div style={containerStyle} data-testid=\"fauxbase-devtools\">\n <button\n style={STYLES.toggleButton}\n onClick={() => setOpen(true)}\n aria-label=\"Open Fauxbase DevTools\"\n data-testid=\"devtools-toggle\"\n >\n {'{ }'}\n </button>\n </div>\n );\n }\n\n return (\n <div style={containerStyle} data-testid=\"fauxbase-devtools\">\n <div style={STYLES.panel}>\n <div style={STYLES.tabBar}>\n {TABS.map(tab => (\n <button\n key={tab.key}\n style={{\n ...STYLES.tab,\n ...(activeTab === tab.key ? STYLES.tabActive : {}),\n }}\n onClick={() => setActiveTab(tab.key)}\n data-testid={`tab-${tab.key}`}\n >\n {tab.label}\n </button>\n ))}\n <button\n style={{ ...STYLES.tab, marginLeft: 'auto' }}\n onClick={() => setOpen(false)}\n aria-label=\"Close DevTools\"\n data-testid=\"devtools-close\"\n >\n x\n </button>\n </div>\n <div style={STYLES.panelContent}>\n {activeTab === 'data' && <DataInspector client={wrappedClient} />}\n {activeTab === 'auth' && <AuthInspector client={client} />}\n {activeTab === 'requests' && (\n <RequestLog\n entries={logger.getEntries()}\n onClear={() => logger.clear()}\n />\n )}\n {activeTab === 'seeds' && <SeedManager client={client} />}\n </div>\n </div>\n </div>\n );\n}\n"]}
package/package.json ADDED
@@ -0,0 +1,40 @@
1
+ {
2
+ "name": "fauxbase-devtools",
3
+ "version": "0.4.0",
4
+ "type": "module",
5
+ "main": "./dist/index.cjs",
6
+ "module": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.js",
12
+ "require": "./dist/index.cjs"
13
+ }
14
+ },
15
+ "files": ["dist"],
16
+ "scripts": {
17
+ "build": "tsup",
18
+ "test": "vitest run",
19
+ "test:watch": "vitest",
20
+ "test:coverage": "vitest run --coverage",
21
+ "clean": "rm -rf dist"
22
+ },
23
+ "peerDependencies": {
24
+ "react": ">=18",
25
+ "fauxbase": ">=0.4.0"
26
+ },
27
+ "devDependencies": {
28
+ "react": "^18.3.0",
29
+ "react-dom": "^18.3.0",
30
+ "@types/react": "^18.3.0",
31
+ "@types/react-dom": "^18.3.0",
32
+ "@testing-library/react": "^16.0.0",
33
+ "fauxbase": "workspace:*",
34
+ "jsdom": "^25.0.0",
35
+ "tsup": "^8.0.0",
36
+ "vitest": "^3.0.0",
37
+ "@vitest/coverage-v8": "^3.0.0",
38
+ "typescript": "^5.7.0"
39
+ }
40
+ }