@cp949/console-feed
v3.7.1
Published
A React component that displays console logs from the current page, an iframe or transported across a server
Maintainers
Readme
console-feed
한국어 | English
A React component that captures and displays browser console output in a user interface. Supports React 18 and 19.
This is a fork of samdenty/console-feed v3.6.0 with security vulnerability fixes.
Current prepared release: 3.7.1
Installation
npm install @cp949/console-feedPackage info: https://www.npmjs.com/package/@cp949/console-feed
Import Paths
The package root keeps the existing public API for compatibility:
import { Console, Hook, Decode, Unhook } from '@cp949/console-feed'
import type { Message, HookedConsole, Methods } from '@cp949/console-feed'Non-UI utilities can also be imported from stable subpaths:
import Hook from '@cp949/console-feed/hook'
import Unhook from '@cp949/console-feed/unhook'
import { Decode, Encode } from '@cp949/console-feed/transform'Changes
전체 변경 이력은 CHANGELOG.md를 참조하세요. 아래는 최근 주요 변경의 요약입니다.
3.7.1
- Patch release. Library code (
dist/) is identical to 3.7.0 — fixes thetest:compatmatrix and updates the README. See CHANGELOG.md for details.
3.7.0
@emotion/react,@emotion/styled,react-inspectormoved topeerDependencies— consumers must install these packages explicitly.- TypeScript
strictmode enabled —tsconfig.jsonconsolidated to"strict": true.HookedConsole.feedis now an optional property (feed?); runtime behavior unchanged but consumers compiling under strict may need an optional guard when accessingconsole.feeddirectly. react-inline-centerdependency removed — replaced by inline flex styles at the single call site.
3.6.8 — Build / Distribution
- Dual ESM + CJS build via
tsup(replaces the previoustsc-only CJS build) exportsmap now branches onimport/requirefor the root and every public subpath (./component,./hook,./unhook,./transform)- Default-only subpaths (
./hook,./unhook,./component) are emitted so thatrequire('@cp949/console-feed/hook')andimport Hook from '@cp949/console-feed/hook'both resolve to the function directly — fixes Next/Webpack interop whereHookwas being received as{ default: fn }
Earlier — Security Vulnerability Fixes
- react-inspector 9.0.0 upgrade: removed @babel/runtime vulnerabilities
- Jest → Vitest 4.1.4 migration: resolved 22 dependency chain vulnerabilities
- Prototype pollution defense:
__proto__,constructor,prototypekey filtering - DOM sanitization: DOMPurify applied without server-side DOM dependencies
- Serialization depth limits added
Current Stack
- TypeScript 5.9.3
- React 18, 19 support
- Node 20+ baseline
- Vitest 4.1.4
Features
- Styled console entries (color substitution, clickable links)
- Renders DOM nodes, tables, various console methods
- Serialization (functions, circular structures, DOM references)
- Filtering and search
- Error stack trace display
- Performance measurement (
console.time/timeEndsupport) - Dark/light theme support
Basic Usage
Function component (Hooks):
import React, { useState, useEffect } from 'react'
import { Console, Hook, Decode, Unhook } from '@cp949/console-feed'
import type { Message } from '@cp949/console-feed'
const LogsContainer = () => {
const [logs, setLogs] = useState<Message[]>([])
useEffect(() => {
const hookedConsole = Hook(
window.console,
(encoded, message) => {
// When encode is true: encoded is a serialized message, so Decode is required
// When encode is false: encoded is a parsed message, but using Decode is safe
const decoded = Decode(encoded)
const logMessage: Message = {
...decoded,
id: `log-${Date.now()}-${Math.random()}`,
data: decoded.data || [],
}
setLogs((prevLogs) => [...prevLogs, logMessage])
},
true, // encode: true - serialization for network transfer
100, // limit: 100 - serialization depth limit
)
// Cleanup: Unhook when component unmounts
return () => {
Unhook(hookedConsole)
}
}, [])
return <Console logs={logs} variant="dark" />
}API
Console Component
A React component that displays log messages.
Props:
logs: Array of log messages (Message[]) - Requiredvariant?: Theme ("dark"|"light", default:"dark")styles?: Custom style objectfilter?: Method filter array (Methods[]) - Display only specified methodssearchKeywords?: Search keyword string - Regex search in log contentlogFilter?: Log filtering function - Custom filtering logic when used withsearchKeywordslogGrouping?: Enable log grouping (default:true) - Group identical logs and display withamountlinkifyOptions?: linkifyjs options object - URL link handling optionscomponents?: Component override object - Replace with custom components
Hook Function
Wraps window.console or a console-like object to capture logs.
Hook(
console: Console,
callback: (encoded: Message, message: Payload) => void,
encode?: boolean, // Default: true
limit?: number // Default: 100 (serialization depth limit)
): HookedConsoleParameters:
console: Console object to hookcallback: Callback function called when a log is capturedencoded: Log message (first argument)- When
encodeistrue: Serialized message (suitable for network transfer/storage) - When
encodeisfalse: Parsed message (not serialized, for use in the same memory space)
- When
message: Parsed message (second argument, always non-serialized parsed result)
encode: Whether to serialize logs (default:true)true: Serialize parsed messages for network transfer or localStorage storagefalse: Use parsed messages without serialization (more efficient when used in the same memory space)
limit: Serialization depth limit (default:100, only applies whenencodeistrue)
Return Value:
HookedConsole: Hooked Console object (can be passed to Unhook)
Unhook Function
Restores a console wrapped by Hook to its original state. Must be called when the component unmounts.
Unhook(hookedConsole: HookedConsole): booleanParameters:
hookedConsole: Console object wrapped by Hook
Return Value:
boolean: Success status (true: success,false: failure)
Encode / Decode Functions
Used when transmitting logs across network boundaries.
Encode<T>(data: any, limit?: number): T
Decode(data: any): MessageEncode:
- Converts log objects into JSON-serializable format
- Safely converts functions, circular references, DOM nodes, etc.
limit: Serialization depth limit (default: 100)- Return Value: Flat object structure that can be serialized to JSON
- Complex types (functions, DOM nodes, etc.) from the original object are converted with special type markers
- Can be safely serialized with
JSON.stringify() - Suitable for network transfer, localStorage storage, etc.
Decode:
- Deserializes serialized logs into
Messageobjects - Returns a complete log object including the
methodproperty - Important:
- When
encodeistrue: The first argument (encoded) of the callback is a serialized message, soDecodemust be called - When
encodeisfalse: The first argument (encoded) of the callback is a parsed message, but callingDecodeis recommended for safe usage
- When
Usage Example:
import { Encode, Decode } from '@cp949/console-feed'
// 1. Create log message
const logMessage = {
method: 'log' as const,
data: [
'Hello World',
{ name: 'console-feed', version: '3.7.1' },
[1, 2, 3],
],
timestamp: new Date().toISOString(),
}
// 2. Encode: Serialize for network transfer
const encoded = Encode(logMessage, 100)
// encoded is an object that can be serialized with JSON.stringify()
// Example: { method: 'log', data: [...], timestamp: '...' }
// 3. Simulate network transfer (convert to JSON)
const jsonString = JSON.stringify(encoded)
// Can be stored in localStorage or sent over network
// 4. Parse JSON on receiving side
const received = JSON.parse(jsonString)
// 5. Decode: Deserialize to restore original form
const decoded = Decode(received)
// decoded is a complete log object of type Message
// { method: 'log', data: [...], timestamp: '...' }
// 6. Pass to Console component
<Console logs={[decoded]} variant="dark" />Real-world Usage Scenario:
// Send log to server
const sendLogToServer = (log: Message) => {
const encoded = Encode(log, 100)
fetch('/api/logs', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(encoded), // Can be safely serialized
})
}
// Display logs received from server
const receiveLogFromServer = async () => {
const response = await fetch('/api/logs')
const encodedLogs = await response.json()
const decodedLogs = encodedLogs.map((encoded: any) => Decode(encoded))
setLogs(decodedLogs)
}Capturing Logs from iframe
You can display console methods like console.log(), console.error() executed inside an iframe in the main window's console-feed.
Simple Method:
After the iframe loads, hook the iframe's contentWindow.console.
import React, { useState, useEffect, useRef } from 'react'
import { Console, Hook, Decode, Unhook } from '@cp949/console-feed'
import type { Message, HookedConsole } from '@cp949/console-feed'
const IframeLogsContainer = () => {
const [logs, setLogs] = useState<Message[]>([])
const iframeRef = useRef<HTMLIFrameElement | null>(null)
useEffect(() => {
const iframe = iframeRef.current
if (!iframe) return
let hookedConsole: HookedConsole | null = null
// Wait for iframe to load
const handleLoad = () => {
if (!iframe.contentWindow) {
console.error('iframe contentWindow is not available')
return
}
// Hook iframe's console
const iframeConsole = iframe.contentWindow.console
hookedConsole = Hook(
iframeConsole,
(encoded, message) => {
// encode is true, so use Decode to decode the log (required!)
const decoded = Decode(encoded)
const logMessage: Message = {
...decoded,
id: `log-${Date.now()}-${Math.random()}`,
data: decoded.data || [],
}
setLogs((prevLogs) => [...prevLogs, logMessage])
},
true, // encode: true
100, // limit: 100
)
}
iframe.addEventListener('load', handleLoad)
// Cleanup: Clean up when component unmounts
return () => {
if (hookedConsole && iframe.contentWindow) {
Unhook(hookedConsole)
}
iframe.removeEventListener('load', handleLoad)
}
}, [])
return (
<div>
<iframe ref={iframeRef} src="./your-iframe.html" />
<Console logs={logs} variant="dark" />
</div>
)
}Notes:
- Console must be hooked after the iframe loads (
loadevent) - The iframe and main window must be same-origin to access
contentWindow.console - Must call
Unhookwhen the component unmounts to clean up
Development
Project Structure
console-feed/
├── apps/
│ └── demo/ # Demo app (Vite + React)
├── packages/
│ └── console-feed/ # Library core
├── turbo.json # Turborepo configuration
├── pnpm-workspace.yaml # pnpm workspace setup
└── package.json # Root workspaceDevelopment Requirements
- Node 20+
- pnpm 9+
Development Commands
# Install dependencies
pnpm install
# Run development server for entire workspace
pnpm dev
# Build entire workspace
pnpm build
# Test entire workspace
pnpm test
# Run specific package only
pnpm --filter @cp949/console-feed build # Build library
pnpm --filter demo dev # Run demo app only
pnpm --filter @cp949/console-feed test # Test libraryReact 18/19 Compatibility Testing
# Test both React 18 and 19
pnpm test:compat
# Test React 18 only
pnpm test:react18
# Test React 19 only
pnpm test:react19Script: packages/console-feed/scripts/test-react-compat.sh
Release
Only the library package (@cp949/console-feed) is published to npm.
# 1. Verify all tests and builds
pnpm test
pnpm build
# 2. Navigate to packages/console-feed directory
cd packages/console-feed
# 3. Update version
pnpm version patch # or minor, major
# 4. Inspect the tarball before publishing
pnpm publish:dry-run
# 5. Commit and push changes
git add .
git commit -m "Release vX.X.X"
git push
# 6. Publish to npm
pnpm publish:npmSecurity
pnpm audit: 0 vulnerabilities- Tests: 29/29 passing
Resolved vulnerabilities:
- @babel/runtime (Moderate, 2 issues): removed via react-inspector 9.0.0 upgrade
- Jest dependencies (22 issues): eliminated via Vitest 4.1.4 migration
Security mechanisms:
- Prototype pollution defense
- DOM sanitization (DOMPurify)
- Serialization depth limits
- Input filtering
To verify locally:
pnpm --filter @cp949/console-feed test
pnpm --filter @cp949/console-feed test:dist
pnpm auditLicense
MIT
