@bubblesjs/utils
Utility function collection containing common helper methods and type utilities.
Installation
pnpm add @bubblesjs/utils
Quick Usage
import { deepMergeObject, isReadableStream } from '@bubblesjs/utils'
// Deep merge objects
const merged = deepMergeObject(
{ a: 1, b: { c: 2 } },
{ b: { d: 3 }, e: 4 }
)
// Result: { a: 1, b: { c: 2, d: 3 }, e: 4 }
// Check if it's a readable stream
const isStream = isReadableStream(response.body)
API Documentation
Utility Functions
deepMergeObject(target, source)
Deep merge two objects, supporting recursive merging of nested objects.
Parameters:
target: object - Target object
source: object - Source object
Return Value:
object - New merged object
Example:
import { deepMergeObject } from '@bubblesjs/utils'
const target = {
user: {
name: 'Alice',
settings: {
theme: 'light'
}
},
version: '1.0.0'
}
const source = {
user: {
age: 25,
settings: {
language: 'en-US'
}
},
features: ['dark-mode']
}
const result = deepMergeObject(target, source)
// Result:
// {
// user: {
// name: 'Alice',
// age: 25,
// settings: {
// theme: 'light',
// language: 'en-US'
// }
// },
// version: '1.0.0',
// features: ['dark-mode']
// }
Notes:
- Arrays are replaced entirely, not merged at element level
- Special objects like functions, Date, RegExp are assigned directly
- Source object properties override target object properties with the same name
isReadableStream(obj)
Checks if an object is a ReadableStream.
Parameters:
obj: any - Object to check
Return Value:
boolean - Returns true if it's a readable stream, false otherwise
Example:
import { isReadableStream } from '@bubblesjs/utils'
// Check fetch response body
const response = await fetch('/api/data')
if (isReadableStream(response.body)) {
// Handle stream response
const data = await response.json()
} else {
// Handle regular response
const data = response.body
}
// Check custom stream
const customStream = new ReadableStream({
start(controller) {
controller.enqueue('Hello')
controller.close()
}
})
console.log(isReadableStream(customStream)) // true
console.log(isReadableStream({})) // false
console.log(isReadableStream(null)) // false
Use Cases:
- Distinguish different types of response bodies
- Conditionally handle stream data
- Adapter compatibility checks
Use Cases
1. Configuration Object Merging
import { deepMergeObject } from '@bubblesjs/utils'
// Default configuration
const defaultConfig = {
api: {
baseUrl: '/api',
timeout: 5000
},
ui: {
theme: 'light',
language: 'en'
}
}
// User configuration
const userConfig = {
api: {
timeout: 10000
},
ui: {
theme: 'dark'
}
}
// Merge configuration
const finalConfig = deepMergeObject(defaultConfig, userConfig)
// Result preserves all default settings while applying user overrides
2. Response Handling Adaptation
import { isReadableStream } from '@bubblesjs/utils'
const handleResponse = async (response) => {
// Handle different response body types
if (response?.body && isReadableStream(response.body)) {
// Fetch API stream response
return await response.json()
} else {
// Other adapter direct response
return response.data
}
}
3. Component Property Merging
import { deepMergeObject } from '@bubblesjs/utils'
// React component example
const Button = ({ className, style, ...props }) => {
const defaultStyle = {
padding: '8px 16px',
border: 'none',
borderRadius: '4px'
}
const mergedStyle = deepMergeObject(defaultStyle, style || {})
return (
<button
className={`btn ${className || ''}`}
style={mergedStyle}
{...props}
/>
)
}
Type Definitions
// Deep merge function type
function deepMergeObject<T extends object, U extends object>(
target: T,
source: U
): T & U
// Readable stream check function type
function isReadableStream(obj: any): obj is ReadableStream
Best Practices
1. Configuration Management
import { deepMergeObject } from '@bubblesjs/utils'
class ConfigManager {
private defaultConfig: Record<string, any>
constructor(defaultConfig: Record<string, any>) {
this.defaultConfig = defaultConfig
}
merge(userConfig: Record<string, any>) {
return deepMergeObject(this.defaultConfig, userConfig)
}
// Support multi-layer merging
mergeMultiple(...configs: Record<string, any>[]) {
return configs.reduce(
(acc, config) => deepMergeObject(acc, config),
this.defaultConfig
)
}
}
2. Response Adapter
import { isReadableStream } from '@bubblesjs/utils'
class ResponseAdapter {
static async parse(response: any) {
// Unified response parsing logic
if (response?.body && isReadableStream(response.body)) {
try {
return await response.json()
} catch (error) {
return await response.text()
}
}
return response.data || response
}
}
3. Utility Function Combination
import { deepMergeObject, isReadableStream } from '@bubblesjs/utils'
// Create a general data processing tool
const createDataProcessor = (defaultOptions = {}) => {
return {
process: async (data: any, options = {}) => {
const mergedOptions = deepMergeObject(defaultOptions, options)
if (isReadableStream(data)) {
// Handle stream data
const reader = data.getReader()
// ... stream processing logic
}
// Handle regular data
return processWithOptions(data, mergedOptions)
}
}
}
FAQ
Q: Does deepMergeObject modify the original objects?
A: No. deepMergeObject creates a new object and doesn't modify the input objects.
const original = { a: 1, b: { c: 2 } }
const source = { b: { d: 3 } }
const result = deepMergeObject(original, source)
console.log(original) // { a: 1, b: { c: 2 } } - unchanged
console.log(result) // { a: 1, b: { c: 2, d: 3 } } - new object
Q: How to handle array merging?
A: The current implementation replaces arrays entirely. If you need element-level array merging, you can customize the handling:
const customMerge = (target, source) => {
const result = deepMergeObject(target, source)
// Custom array merging logic
if (Array.isArray(target.items) && Array.isArray(source.items)) {
result.items = [...target.items, ...source.items]
}
return result
}
Q: Which environments does isReadableStream support?
A: Supports ReadableStream in modern browsers and Node.js environments. For unsupported environments, it safely returns false.
Summary
@bubblesjs/utils provides practical utility functions, particularly suitable for:
- Configuration Management: Use
deepMergeObject for flexible configuration merging
- Response Handling: Use
isReadableStream to adapt different response types
- Data Processing: Provide reliable tool support in various data manipulation scenarios
These utility functions are thoroughly tested and can be safely used in production environments.