feat(dashboard): add performance and operations slices
This commit is contained in:
1 parent
20fa83f245
commit
5917b63755
13 files changed
+1059
No files matched your search
@@ -0,0 +1,106 @@
|
||||
export type ConsoleLevel = 'debug' | 'info' | 'warn' | 'error';
|
||||
export type ConsoleLevelFilter = 'all' | ConsoleLevel;
|
||||
|
||||
export interface ConsoleEntry {
|
||||
id: string;
|
||||
occurredAt: string;
|
||||
level: ConsoleLevel;
|
||||
message: string;
|
||||
data?: unknown;
|
||||
}
|
||||
|
||||
export interface LiveConsoleState {
|
||||
capacity: number;
|
||||
entries: readonly ConsoleEntry[];
|
||||
pendingEntries: readonly ConsoleEntry[];
|
||||
isPaused: boolean;
|
||||
isAutoScrollEnabled: boolean;
|
||||
level: ConsoleLevelFilter;
|
||||
search: string;
|
||||
}
|
||||
|
||||
export type LiveConsoleAction =
|
||||
| { type: 'append'; entry: ConsoleEntry }
|
||||
| { type: 'set-paused'; paused: boolean }
|
||||
| { type: 'set-auto-scroll'; enabled: boolean }
|
||||
| { type: 'set-level'; level: ConsoleLevelFilter }
|
||||
| { type: 'set-search'; search: string };
|
||||
|
||||
function boundedAppend(
|
||||
entries: readonly ConsoleEntry[],
|
||||
entry: ConsoleEntry,
|
||||
capacity: number,
|
||||
): readonly ConsoleEntry[] {
|
||||
if (entries.some(({ id }) => id === entry.id)) return entries;
|
||||
return [...entries, entry].slice(-capacity);
|
||||
}
|
||||
|
||||
export function createLiveConsoleState(capacity = 500): LiveConsoleState {
|
||||
if (!Number.isInteger(capacity) || capacity <= 0) {
|
||||
throw new RangeError('capacity must be a positive integer');
|
||||
}
|
||||
return {
|
||||
capacity,
|
||||
entries: [],
|
||||
pendingEntries: [],
|
||||
isPaused: false,
|
||||
isAutoScrollEnabled: true,
|
||||
level: 'all',
|
||||
search: '',
|
||||
};
|
||||
}
|
||||
|
||||
export function reduceLiveConsoleState(
|
||||
state: LiveConsoleState,
|
||||
action: LiveConsoleAction,
|
||||
): LiveConsoleState {
|
||||
switch (action.type) {
|
||||
case 'append': {
|
||||
if (
|
||||
state.entries.some(({ id }) => id === action.entry.id) ||
|
||||
state.pendingEntries.some(({ id }) => id === action.entry.id)
|
||||
) return state;
|
||||
return state.isPaused
|
||||
? { ...state, pendingEntries: boundedAppend(state.pendingEntries, action.entry, state.capacity) }
|
||||
: { ...state, entries: boundedAppend(state.entries, action.entry, state.capacity) };
|
||||
}
|
||||
case 'set-paused':
|
||||
if (action.paused === state.isPaused) return state;
|
||||
return action.paused
|
||||
? { ...state, isPaused: true }
|
||||
: {
|
||||
...state,
|
||||
isPaused: false,
|
||||
entries: [...state.entries, ...state.pendingEntries].slice(-state.capacity),
|
||||
pendingEntries: [],
|
||||
};
|
||||
case 'set-auto-scroll':
|
||||
return { ...state, isAutoScrollEnabled: action.enabled };
|
||||
case 'set-level':
|
||||
return { ...state, level: action.level };
|
||||
case 'set-search':
|
||||
return { ...state, search: action.search };
|
||||
}
|
||||
}
|
||||
|
||||
function searchableData(data: unknown): string {
|
||||
if (data === undefined) return '';
|
||||
try {
|
||||
return JSON.stringify(data);
|
||||
} catch {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
export function selectVisibleConsoleEntries(
|
||||
state: LiveConsoleState,
|
||||
): readonly ConsoleEntry[] {
|
||||
const search = state.search.trim().toLocaleLowerCase();
|
||||
return state.entries.filter((entry) => {
|
||||
if (state.level !== 'all' && entry.level !== state.level) return false;
|
||||
if (search.length === 0) return true;
|
||||
return `${entry.message} ${searchableData(entry.data)}`
|
||||
.toLocaleLowerCase()
|
||||
.includes(search);
|
||||
});
|
||||
}
|
||||
Reference in new issue
Block a user