@buoy-gg/console 7.0.28 → 7.0.30
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/lib/commonjs/devtools/ConsoleView.js +2 -2
- package/lib/commonjs/devtools/ConsoleView.js.map +1 -1
- package/lib/module/devtools/ConsoleView.js +2 -2
- package/lib/module/devtools/ConsoleView.js.map +1 -1
- package/lib/typescript/devtools/ConsoleView.d.ts.map +1 -1
- package/package.json +5 -6
- package/src/components/ConsoleIcon.tsx +0 -9
- package/src/components/ConsoleLogList.tsx +0 -135
- package/src/components/ConsoleModal.tsx +0 -48
- package/src/components/ConsoleRoot.tsx +0 -25
- package/src/devtools/ConsoleBody.tsx +0 -228
- package/src/devtools/ConsoleHeader.tsx +0 -149
- package/src/devtools/ConsoleMessageRow.tsx +0 -388
- package/src/devtools/ConsoleValue.tsx +0 -265
- package/src/devtools/ConsoleView.tsx +0 -43
- package/src/devtools/consoleFilter.ts +0 -123
- package/src/devtools/consoleFormat.ts +0 -314
- package/src/devtools/origin.ts +0 -190
- package/src/devtools/theme.ts +0 -148
- package/src/devtools/useConsoleController.ts +0 -104
- package/src/devtools/useConsoleMessages.ts +0 -337
- package/src/hooks/useConsoleLogs.ts +0 -26
- package/src/index.tsx +0 -70
- package/src/preset.tsx +0 -67
- package/src/store/__tests__/consoleSyncAdapter.test.ts +0 -156
- package/src/store/consoleLogStore.ts +0 -721
- package/src/store/sanitize.ts +0 -83
- package/src/sync/consoleSyncAdapter.ts +0 -115
package/src/preset.tsx
DELETED
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Pre-configured Console tool for FloatingDevTools.
|
|
3
|
-
*
|
|
4
|
-
* @example
|
|
5
|
-
* ```tsx
|
|
6
|
-
* import { consoleToolPreset } from '@buoy-gg/console';
|
|
7
|
-
*
|
|
8
|
-
* <FloatingDevTools apps={[consoleToolPreset]} />
|
|
9
|
-
* ```
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
import { ConsoleIcon, CONSOLE_ICON_COLOR } from "./components/ConsoleIcon";
|
|
13
|
-
import { ConsoleModal } from "./components/ConsoleModal";
|
|
14
|
-
import { installConsoleCapture } from "./store/consoleLogStore";
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* Install console + fatal-error capture at IMPORT time, not first UI mount.
|
|
18
|
-
*
|
|
19
|
-
* Deliberately a module side effect (same pattern as @buoy-gg/network's
|
|
20
|
-
* override hydration): a fatal error thrown during the app's FIRST render
|
|
21
|
-
* kills the tree before FloatingDevTools' effects ever run, so mount-time
|
|
22
|
-
* install (`ConsoleRoot`) captured nothing for exactly the crashes that most
|
|
23
|
-
* need capturing. Importing the preset happens at bundle-eval, before any
|
|
24
|
-
* rendering, so the `console.*` patch and both fatal-error hooks (`ErrorUtils`
|
|
25
|
-
* and RN's native exception listener) are live from the first line of app code.
|
|
26
|
-
*
|
|
27
|
-
* Guards:
|
|
28
|
-
* - `ErrorUtils` OR `RN$registerExceptionListener` present = a real React Native
|
|
29
|
-
* runtime. The desktop dashboard and web mirrors import this package too, and
|
|
30
|
-
* must never patch their OWN console (they mirror device entries via
|
|
31
|
-
* `replaceEntries`).
|
|
32
|
-
* - Dev-only. In production the existing mount-time path still applies (the
|
|
33
|
-
* tools only mount there for licensed users), so end-user builds are never
|
|
34
|
-
* patched at import.
|
|
35
|
-
*/
|
|
36
|
-
const rnGlobals = globalThis as {
|
|
37
|
-
ErrorUtils?: { setGlobalHandler?: unknown };
|
|
38
|
-
RN$registerExceptionListener?: unknown;
|
|
39
|
-
};
|
|
40
|
-
const isReactNativeRuntime =
|
|
41
|
-
typeof rnGlobals.ErrorUtils?.setGlobalHandler === "function" ||
|
|
42
|
-
typeof rnGlobals.RN$registerExceptionListener === "function";
|
|
43
|
-
if (isReactNativeRuntime && typeof __DEV__ !== "undefined" && __DEV__) {
|
|
44
|
-
installConsoleCapture();
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
export const consoleToolPreset = {
|
|
48
|
-
id: "console",
|
|
49
|
-
name: "CONSOLE",
|
|
50
|
-
description: "Captured console.* output",
|
|
51
|
-
slot: "both" as const,
|
|
52
|
-
icon: ({ size, color }: { size: number; color?: string }) => (
|
|
53
|
-
<ConsoleIcon size={size} color={color || CONSOLE_ICON_COLOR} />
|
|
54
|
-
),
|
|
55
|
-
component: ConsoleModal,
|
|
56
|
-
};
|
|
57
|
-
|
|
58
|
-
/** Create a custom Console tool configuration. */
|
|
59
|
-
export function createConsoleTool(
|
|
60
|
-
options: { name?: string; description?: string } = {}
|
|
61
|
-
) {
|
|
62
|
-
return {
|
|
63
|
-
...consoleToolPreset,
|
|
64
|
-
name: options.name ?? consoleToolPreset.name,
|
|
65
|
-
description: options.description ?? consoleToolPreset.description,
|
|
66
|
-
};
|
|
67
|
-
}
|
|
@@ -1,156 +0,0 @@
|
|
|
1
|
-
import { afterEach, describe, expect, it } from "vitest";
|
|
2
|
-
import { capString, sanitizeArgs, SNAPSHOT_STRING_LIMIT } from "../sanitize";
|
|
3
|
-
import { clearEntries, replaceEntries } from "../consoleLogStore";
|
|
4
|
-
import { consoleSyncAdapter } from "../../sync/consoleSyncAdapter";
|
|
5
|
-
import type { ConsoleLogEntry } from "../consoleLogStore";
|
|
6
|
-
|
|
7
|
-
// Node has no rAF; the store coalesces listener notifies onto one frame.
|
|
8
|
-
globalThis.requestAnimationFrame = ((cb: (t: number) => void) => {
|
|
9
|
-
cb(0);
|
|
10
|
-
return 0;
|
|
11
|
-
}) as typeof requestAnimationFrame;
|
|
12
|
-
|
|
13
|
-
function entry(
|
|
14
|
-
partial: Partial<ConsoleLogEntry> & Pick<ConsoleLogEntry, "id" | "args" | "message">,
|
|
15
|
-
): ConsoleLogEntry {
|
|
16
|
-
return {
|
|
17
|
-
method: "log",
|
|
18
|
-
level: "info",
|
|
19
|
-
timestamp: Date.now(),
|
|
20
|
-
...partial,
|
|
21
|
-
};
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
afterEach(() => {
|
|
25
|
-
clearEntries();
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
describe("sanitizeArgs", () => {
|
|
29
|
-
it("round-trips Errors, functions, and circular refs as JSON", () => {
|
|
30
|
-
const circular: { self?: unknown } = {};
|
|
31
|
-
circular.self = circular;
|
|
32
|
-
const sanitized = sanitizeArgs([
|
|
33
|
-
new Error("boom"),
|
|
34
|
-
() => {},
|
|
35
|
-
circular,
|
|
36
|
-
1n,
|
|
37
|
-
]);
|
|
38
|
-
expect(() => JSON.stringify(sanitized)).not.toThrow();
|
|
39
|
-
expect(sanitized[0]).toEqual(
|
|
40
|
-
expect.objectContaining({ name: "Error", message: "boom" }),
|
|
41
|
-
);
|
|
42
|
-
expect(sanitized[2]).toEqual(
|
|
43
|
-
expect.objectContaining({ self: "[Circular]" }),
|
|
44
|
-
);
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
it("caps oversized strings so a fat console.log cannot blow the snapshot", () => {
|
|
48
|
-
const huge = "x".repeat(SNAPSHOT_STRING_LIMIT + 50_000);
|
|
49
|
-
const sanitized = sanitizeArgs([huge]);
|
|
50
|
-
expect(typeof sanitized[0]).toBe("string");
|
|
51
|
-
expect((sanitized[0] as string).length).toBeLessThan(huge.length);
|
|
52
|
-
expect((sanitized[0] as string).startsWith("x".repeat(32))).toBe(true);
|
|
53
|
-
expect(capString("short")).toBe("short");
|
|
54
|
-
});
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
describe("consoleSyncAdapter", () => {
|
|
58
|
-
it("snapshots { entries } as JSON and clearEntries does not hang", () => {
|
|
59
|
-
expect(consoleSyncAdapter.version).toBe(1);
|
|
60
|
-
clearEntries();
|
|
61
|
-
const snapshot = consoleSyncAdapter.getSnapshot();
|
|
62
|
-
expect(snapshot).toEqual({ entries: expect.any(Array) });
|
|
63
|
-
expect(() => JSON.stringify(snapshot)).not.toThrow();
|
|
64
|
-
expect(consoleSyncAdapter.actions.clearEntries()).toEqual({ cleared: true });
|
|
65
|
-
expect(consoleSyncAdapter.getSnapshot()).toEqual({ entries: [] });
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
it("does not drop the console snapshot when one log is multi-MB", () => {
|
|
69
|
-
const huge = "x".repeat(3 * 1024 * 1024);
|
|
70
|
-
replaceEntries([
|
|
71
|
-
entry({
|
|
72
|
-
id: 1,
|
|
73
|
-
args: [huge],
|
|
74
|
-
message: huge,
|
|
75
|
-
}),
|
|
76
|
-
]);
|
|
77
|
-
|
|
78
|
-
const snapshot = consoleSyncAdapter.getSnapshot();
|
|
79
|
-
expect(snapshot.entries).toHaveLength(1);
|
|
80
|
-
expect(snapshot.entries[0]?.id).toBe(1);
|
|
81
|
-
expect(snapshot.entries[0]?.method).toBe("log");
|
|
82
|
-
expect(String(snapshot.entries[0]?.args[0]).length).toBeLessThan(huge.length);
|
|
83
|
-
expect(snapshot.entries[0]?.message.length).toBeLessThan(huge.length);
|
|
84
|
-
expect(() => JSON.stringify(snapshot)).not.toThrow();
|
|
85
|
-
expect(JSON.stringify(snapshot).length).toBeLessThan(2 * 1024 * 1024);
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
it("keeps a burst of fat logs under the 2MB emit budget", () => {
|
|
89
|
-
const huge = "x".repeat(20 * 1024);
|
|
90
|
-
replaceEntries(
|
|
91
|
-
Array.from({ length: 80 }, (_, i) =>
|
|
92
|
-
entry({
|
|
93
|
-
id: i + 1,
|
|
94
|
-
args: [huge],
|
|
95
|
-
message: huge,
|
|
96
|
-
}),
|
|
97
|
-
),
|
|
98
|
-
);
|
|
99
|
-
|
|
100
|
-
const snapshot = consoleSyncAdapter.getSnapshot();
|
|
101
|
-
expect(snapshot.entries.length).toBeGreaterThan(0);
|
|
102
|
-
expect(snapshot.entries[snapshot.entries.length - 1]?.id).toBe(80);
|
|
103
|
-
expect(JSON.stringify(snapshot).length).toBeLessThan(2 * 1024 * 1024);
|
|
104
|
-
});
|
|
105
|
-
|
|
106
|
-
// ---------------------------------------------------------------------
|
|
107
|
-
// Cost guard. The snapshot is rebuilt ~5x/sec while a dashboard watches,
|
|
108
|
-
// and `sanitizeArgs` deep-copies every argument to make it JSON-safe — so
|
|
109
|
-
// WHETHER that copy is redone per tick is part of the contract. Measured at
|
|
110
|
-
// 12.6ms/tick on 1000 ordinary object logs before this cache, and note that
|
|
111
|
-
// cost predates the size guards entirely.
|
|
112
|
-
// ---------------------------------------------------------------------
|
|
113
|
-
|
|
114
|
-
it("reuses the wire form of an unchanged entry across snapshots", () => {
|
|
115
|
-
replaceEntries([
|
|
116
|
-
entry({ id: 1, message: "hello", args: ["hello"] }),
|
|
117
|
-
entry({
|
|
118
|
-
id: 2,
|
|
119
|
-
message: "payload",
|
|
120
|
-
args: ["payload", { rows: Array.from({ length: 40 }, (_, i) => ({ i })) }],
|
|
121
|
-
}),
|
|
122
|
-
]);
|
|
123
|
-
|
|
124
|
-
const first = consoleSyncAdapter.getSnapshot().entries;
|
|
125
|
-
const second = consoleSyncAdapter.getSnapshot().entries;
|
|
126
|
-
// Identity, not deep equality: a fresh sanitized copy per tick is exactly
|
|
127
|
-
// the waste this guards, and the desktop mirror also feeds these into
|
|
128
|
-
// effect dependencies.
|
|
129
|
-
expect(second[0]).toBe(first[0]);
|
|
130
|
-
expect(second[1]).toBe(first[1]);
|
|
131
|
-
});
|
|
132
|
-
|
|
133
|
-
it("keeps the newest entries when the budget forces a cut", () => {
|
|
134
|
-
// 1000 logs of a ~4KB object are each individually legal and still total
|
|
135
|
-
// over the 2MB emit budget, which would drop the WHOLE console panel.
|
|
136
|
-
replaceEntries(
|
|
137
|
-
Array.from({ length: 1000 }, (_, i) =>
|
|
138
|
-
entry({
|
|
139
|
-
id: i,
|
|
140
|
-
message: `line ${i}`,
|
|
141
|
-
// ~4KB each: individually far under the per-entry cap, and 1000 of
|
|
142
|
-
// them still exceed the snapshot budget.
|
|
143
|
-
args: [`line ${i}`, { blob: "p".repeat(4 * 1024) }],
|
|
144
|
-
}),
|
|
145
|
-
),
|
|
146
|
-
);
|
|
147
|
-
|
|
148
|
-
const { entries } = consoleSyncAdapter.getSnapshot();
|
|
149
|
-
expect(JSON.stringify({ entries }).length).toBeLessThan(2 * 1024 * 1024);
|
|
150
|
-
// Not a drop, and not everything — the budget genuinely bit.
|
|
151
|
-
expect(entries.length).toBeGreaterThan(0);
|
|
152
|
-
expect(entries.length).toBeLessThan(1000);
|
|
153
|
-
// A console reader cares about the most RECENT lines, so those survive.
|
|
154
|
-
expect(entries[entries.length - 1]?.id).toBe(999);
|
|
155
|
-
});
|
|
156
|
-
});
|