This commit is contained in:
Raven Scott
2026-02-19 07:49:56 -05:00
parent b4df4a8a9c
commit ec5d40c36d
11 changed files with 3319 additions and 75 deletions
+546
View File
@@ -0,0 +1,546 @@
# bare-dgram - UDP Datagrams
## Overview
bare-dgram provides UDP (User Datagram Protocol) support for JavaScript. It enables connectionless, unreliable datagram communication for use cases where speed is prioritized over reliability.
### Key Features
- **UDP sockets**: Send/receive datagrams
- **Connectionless**: No persistent connections
- **Multicast**: Support for multicast groups
- **Built on UDX**: High-performance UDP implementation
- **Node.js compatible**: Similar to dgram module
### Use Cases
- **DNS queries**: Domain name resolution
- **Streaming media**: Real-time audio/video
- **Gaming**: Fast game state updates
- **IoT**: Device communication
- **Broadcasting**: Discovery protocols
## Installation
```bash
npm install bare-dgram
```
## Quick Start
### UDP Server
```js
const dgram = require('bare-dgram')
const server = dgram.createSocket('udp4')
server.on('message', (msg, rinfo) => {
console.log(`Received: ${msg} from ${rinfo.address}:${rinfo.port}`)
// Echo back
server.send(msg, rinfo.port, rinfo.address)
})
server.bind(41234, () => {
console.log('UDP server listening')
})
```
### UDP Client
```js
const dgram = require('bare-dgram')
const client = dgram.createSocket('udp4')
const message = Buffer.from('Hello UDP!')
client.send(message, 41234, 'localhost', (err) => {
if (err) console.error(err)
client.close()
})
```
## API Reference
### Socket Creation
#### `dgram.createSocket(type[, callback])`
Create UDP socket.
**Parameters:**
- `type` (string): 'udp4' or 'udp6'
- `callback` (function): Message handler
**Returns:** Socket
#### `dgram.createSocket(options[, callback])`
Create with options.
**Options:**
- `type`: 'udp4' or 'udp6'
- `reuseAddr`: Allow address reuse
### Socket Methods
#### `socket.bind([port][, address][, callback])`
Bind to address.
#### `socket.send(msg[, offset, length], port, address[, callback])`
Send datagram.
**Example:**
```js
socket.send(Buffer.from('Hello'), 0, 5, 8080, 'localhost')
```
#### `socket.close([callback])`
Close socket.
#### `socket.address()`
Get address info.
**Returns:** `{ address, family, port }`
#### `socket.setBroadcast(flag)`
Enable/disable broadcast.
#### `socket.setMulticastTTL(ttl)`
Set multicast TTL.
#### `socket.addMembership(multicastAddress[, multicastInterface])`
Join multicast group.
#### `socket.dropMembership(multicastAddress[, multicastInterface])`
Leave multicast group.
### Events
- `message` - Datagram received `(msg, rinfo)`
- `listening` - Socket bound
- `close` - Socket closed
- `error` - Error occurred
## Complete Examples
### Example 1: DNS Client
```js
const dgram = require('bare-dgram')
// Simple DNS query
function dnsQuery(domain, dnsServer = '8.8.8.8') {
const socket = dgram.createSocket('udp4')
// Build DNS query (simplified)
const query = buildDNSQuery(domain)
return new Promise((resolve, reject) => {
socket.on('message', (response) => {
socket.close()
resolve(parseDNSResponse(response))
})
socket.on('error', reject)
socket.send(query, 53, dnsServer, (err) => {
if (err) reject(err)
})
// Timeout
setTimeout(() => {
socket.close()
reject(new Error('DNS timeout'))
}, 5000)
})
}
function buildDNSQuery(domain) {
// Simplified DNS query construction
const buf = Buffer.alloc(512)
let offset = 0
// Transaction ID
buf.writeUInt16BE(Math.floor(Math.random() * 65535), offset)
offset += 2
// Flags (standard query)
buf.writeUInt16BE(0x0100, offset)
offset += 2
// Questions: 1
buf.writeUInt16BE(1, offset)
offset += 2
// Other counts: 0
buf.writeUInt32BE(0, offset)
offset += 4
buf.writeUInt32BE(0, offset)
offset += 4
// Question: domain
for (const part of domain.split('.')) {
buf.writeUInt8(part.length, offset++)
buf.write(part, offset)
offset += part.length
}
buf.writeUInt8(0, offset++) // End of name
// Type A, Class IN
buf.writeUInt16BE(1, offset)
offset += 2
buf.writeUInt16BE(1, offset)
offset += 2
return buf.slice(0, offset)
}
function parseDNSResponse(buf) {
// Simplified parsing
const answers = []
// ... parsing logic
return answers
}
// Usage
dnsQuery('example.com').then(console.log).catch(console.error)
```
### Example 2: Multicast Discovery
```js
const dgram = require('bare-dgram')
const MULTICAST_ADDR = '239.255.255.250'
const MULTICAST_PORT = 1900
class DiscoveryService {
constructor(serviceId) {
this.serviceId = serviceId
this.peers = new Map()
}
start() {
this.socket = dgram.createSocket('udp4')
this.socket.on('message', (msg, rinfo) => {
try {
const data = JSON.parse(msg)
this.handleMessage(data, rinfo)
} catch (err) {
// Not our protocol
}
})
this.socket.on('listening', () => {
const addr = this.socket.address()
console.log(`Discovery listening on ${addr.address}:${addr.port}`)
// Join multicast group
this.socket.addMembership(MULTICAST_ADDR)
this.socket.setMulticastTTL(128)
// Announce ourselves
this.announce()
// Periodic announce
this.interval = setInterval(() => this.announce(), 5000)
})
this.socket.bind(MULTICAST_PORT)
}
announce() {
const msg = Buffer.from(JSON.stringify({
type: 'announce',
id: this.serviceId,
timestamp: Date.now()
}))
this.socket.send(msg, MULTICAST_PORT, MULTICAST_ADDR)
}
handleMessage(data, rinfo) {
if (data.type === 'announce' && data.id !== this.serviceId) {
console.log(`Found peer: ${data.id} at ${rinfo.address}`)
this.peers.set(data.id, {
address: rinfo.address,
port: rinfo.port,
lastSeen: Date.now()
})
}
}
stop() {
clearInterval(this.interval)
this.socket.close()
}
}
// Usage
const discovery = new DiscoveryService('my-service-' + Date.now())
discovery.start()
setTimeout(() => discovery.stop(), 60000)
```
### Example 3: Game State Server
```js
const dgram = require('bare-dgram')
class GameServer {
constructor(port) {
this.port = port
this.clients = new Map()
this.gameState = {}
}
start() {
this.socket = dgram.createSocket('udp4')
this.socket.on('message', (msg, rinfo) => {
const clientId = `${rinfo.address}:${rinfo.port}`
// Update client activity
this.clients.set(clientId, {
address: rinfo.address,
port: rinfo.port,
lastPing: Date.now()
})
// Handle input
try {
const input = JSON.parse(msg)
this.handleInput(clientId, input)
} catch (err) {
// Invalid input
}
})
this.socket.bind(this.port, () => {
console.log(`Game server on port ${this.port}`)
})
// Broadcast game state
this.broadcastInterval = setInterval(() => {
this.broadcastState()
}, 1000 / 60) // 60 FPS
// Clean up inactive clients
this.cleanupInterval = setInterval(() => {
this.cleanupClients()
}, 5000)
}
handleInput(clientId, input) {
// Update game state based on input
if (!this.gameState[clientId]) {
this.gameState[clientId] = { x: 0, y: 0 }
}
if (input.up) this.gameState[clientId].y -= 5
if (input.down) this.gameState[clientId].y += 5
if (input.left) this.gameState[clientId].x -= 5
if (input.right) this.gameState[clientId].x += 5
}
broadcastState() {
const state = JSON.stringify({
type: 'state',
timestamp: Date.now(),
players: this.gameState
})
const msg = Buffer.from(state)
for (const [clientId, client] of this.clients) {
this.socket.send(msg, client.port, client.address, (err) => {
if (err) console.error(`Failed to send to ${clientId}:`, err.message)
})
}
}
cleanupClients() {
const now = Date.now()
for (const [clientId, client] of this.clients) {
if (now - client.lastPing > 10000) {
console.log(`Removing inactive client: ${clientId}`)
this.clients.delete(clientId)
delete this.gameState[clientId]
}
}
}
stop() {
clearInterval(this.broadcastInterval)
clearInterval(this.cleanupInterval)
this.socket.close()
}
}
// Usage
const server = new GameServer(7777)
server.start()
```
### Example 4: Reliable UDP
```js
const dgram = require('bare-dgram')
// Simple reliability layer on top of UDP
class ReliableUDP {
constructor(port) {
this.port = port
this.sequence = 0
this.acknowledged = new Map()
this.pending = new Map()
this.socket = null
}
start() {
this.socket = dgram.createSocket('udp4')
this.socket.on('message', (msg, rinfo) => {
try {
const packet = JSON.parse(msg)
this.handlePacket(packet, rinfo)
} catch (err) {
console.error('Invalid packet:', err)
}
})
this.socket.bind(this.port)
// Retransmit unacknowledged packets
this.retransmitInterval = setInterval(() => {
this.retransmit()
}, 1000)
}
send(data, port, address) {
this.sequence++
const packet = {
seq: this.sequence,
data: data,
timestamp: Date.now()
}
const key = `${address}:${port}`
if (!this.pending.has(key)) {
this.pending.set(key, [])
}
this.pending.get(key).push(packet)
this.socket.send(Buffer.from(JSON.stringify(packet)), port, address)
}
handlePacket(packet, rinfo) {
const key = `${rinfo.address}:${rinfo.port}`
if (packet.ack) {
// Remove acknowledged packets
const pending = this.pending.get(key) || []
const index = pending.findIndex(p => p.seq === packet.ack)
if (index > -1) {
pending.splice(index, 1)
}
} else {
// Send ACK
const ack = { ack: packet.seq }
this.socket.send(Buffer.from(JSON.stringify(ack)), rinfo.port, rinfo.address)
// Process data (if not duplicate)
console.log(`Received: ${packet.data}`)
}
}
retransmit() {
const now = Date.now()
for (const [key, packets] of this.pending) {
const [address, port] = key.split(':')
for (const packet of packets) {
if (now - packet.timestamp > 1000) {
packet.timestamp = now
this.socket.send(
Buffer.from(JSON.stringify(packet)),
parseInt(port),
address
)
}
}
}
}
stop() {
clearInterval(this.retransmitInterval)
this.socket.close()
}
}
// Usage
const reliable = new ReliableUDP(12345)
reliable.start()
reliable.send('Hello!', 12346, 'localhost')
```
## Best Practices
### Handle Packet Loss
```js
// UDP is unreliable - expect packet loss
socket.on('message', (msg) => {
try {
const data = JSON.parse(msg)
// Process data
} catch (err) {
// Corrupted packet - ignore or log
}
})
```
### Limit Packet Size
```js
// Stay under MTU to avoid fragmentation
const MAX_SIZE = 1400 // Safe for most networks
if (message.length > MAX_SIZE) {
// Split into multiple packets
}
```
### Use Timeouts
```js
const timeout = setTimeout(() => {
socket.close()
reject(new Error('Timeout'))
}, 5000)
socket.on('message', (msg) => {
clearTimeout(timeout)
resolve(msg)
})
```
## License
Apache-2.0
---
**Module Type**: Runtime/Networking | **Ecosystem Role**: UDP | **Dependencies**: udx-native
+223
View File
@@ -0,0 +1,223 @@
# bare-dns - Domain Name Resolution
## Overview
bare-dns provides domain name resolution (DNS) capabilities for JavaScript. It enables looking up IP addresses from hostnames and other DNS operations.
### Key Features
- **DNS lookup**: Resolve hostnames to IPs
- **IPv4/IPv6**: Support for both address families
- **Asynchronous**: Promise and callback APIs
- **Caching**: Built-in DNS caching
### Use Cases
- **Network connections**: Resolve hosts before connecting
- **Service discovery**: Find service endpoints
- **Email validation**: MX record lookups
- **Network diagnostics**: DNS troubleshooting
## Installation
```bash
npm install bare-dns
```
## Quick Start
### Basic Lookup
```js
const dns = require('bare-dns')
// Promise API
const { address, family } = await dns.lookup('github.com')
console.log(`IP: ${address}, Family: IPv${family}`)
// Callback API
dns.lookup('example.com', (err, address, family) => {
if (err) throw err
console.log(`IP: ${address}, Family: IPv${family}`)
})
```
### Options
```js
const dns = require('bare-dns')
// Force IPv4
const result = await dns.lookup('google.com', { family: 4 })
// Force IPv6
const result = await dns.lookup('google.com', { family: 6 })
```
## API Reference
### dns.lookup(hostname[, options])
Resolve hostname to IP address.
**Parameters:**
- `hostname` (string): Domain to lookup
- `options` (object):
- `family` (number): 4 for IPv4, 6 for IPv6, 0 for either (default)
**Returns:** Promise<{ address, family }>
**Callback Signature:**
```js
dns.lookup(hostname, (err, address, family) => {
// address: IP address string
// family: 4 or 6
})
```
## Complete Examples
### Example 1: Connection Helper
```js
const dns = require('bare-dns')
const net = require('bare-net')
async function connectToHost(hostname, port) {
// Resolve hostname first
const { address } = await dns.lookup(hostname)
// Connect to resolved IP
return new Promise((resolve, reject) => {
const socket = net.createConnection(port, address, () => {
resolve(socket)
})
socket.on('error', reject)
})
}
// Usage
const conn = await connectToHost('api.example.com', 8080)
conn.write('Hello')
```
### Example 2: DNS Cache
```js
const dns = require('bare-dns')
class DNSCache {
constructor(ttl = 300000) { // 5 min default
this.cache = new Map()
this.ttl = ttl
}
async lookup(hostname, options = {}) {
const key = `${hostname}:${options.family || 0}`
const cached = this.cache.get(key)
if (cached && Date.now() - cached.time < this.ttl) {
return cached.result
}
const result = await dns.lookup(hostname, options)
this.cache.set(key, { result, time: Date.now() })
return result
}
clear() {
this.cache.clear()
}
}
// Usage
const cache = new DNSCache()
const result1 = await cache.lookup('github.com')
const result2 = await cache.lookup('github.com') // From cache
```
### Example 3: Multi-DNS Resolver
```js
const dns = require('bare-dns')
class MultiResolver {
async resolveAll(hostname) {
const results = {
ipv4: null,
ipv6: null
}
// Try IPv4
try {
results.ipv4 = await dns.lookup(hostname, { family: 4 })
} catch (err) {
console.log(`No IPv4 for ${hostname}`)
}
// Try IPv6
try {
results.ipv6 = await dns.lookup(hostname, { family: 6 })
} catch (err) {
console.log(`No IPv6 for ${hostname}`)
}
return results
}
async resolveFastest(hostname) {
const promises = [
dns.lookup(hostname, { family: 4 }).then(r => ({ ...r, family: 4 })),
dns.lookup(hostname, { family: 6 }).then(r => ({ ...r, family: 6 }))
]
// Return first successful
return Promise.race(promises)
}
}
// Usage
const resolver = new MultiResolver()
const all = await resolver.resolveAll('google.com')
console.log(all)
```
## Best Practices
### Always Handle Failures
```js
try {
const { address } = await dns.lookup('example.com')
} catch (err) {
console.error('DNS lookup failed:', err.message)
// Use fallback or cached value
}
```
### Cache Results
```js
// DNS lookups can be slow - cache results
const cache = new Map()
async function cachedLookup(hostname) {
if (cache.has(hostname)) {
return cache.get(hostname)
}
const result = await dns.lookup(hostname)
cache.set(hostname, result)
return result
}
```
## License
Apache-2.0
---
**Module Type**: Runtime/Networking | **Ecosystem Role**: DNS Resolution | **Dependencies**: None
+459
View File
@@ -0,0 +1,459 @@
# bare-net - TCP and IPC Networking
## Overview
bare-net provides TCP and IPC (Inter-Process Communication) servers and clients for JavaScript. It enables network communication with a Node.js-compatible API.
### Key Features
- **TCP sockets**: Create TCP servers and clients
- **IPC support**: Unix domain sockets and Windows named pipes
- **Stream-based**: Built on bare-stream for composability
- **Node.js compatible**: Familiar net API
### Use Cases
- **Network servers**: HTTP servers, game servers, etc.
- **Client connections**: Connect to remote services
- **IPC**: Communicate between local processes
- **Protocols**: Build custom network protocols
## Installation
```bash
npm install bare-net
```
## Quick Start
### TCP Server
```js
const net = require('bare-net')
const server = net.createServer((socket) => {
console.log('Client connected')
socket.on('data', (data) => {
console.log('Received:', data.toString())
socket.write('Echo: ' + data)
})
socket.on('close', () => {
console.log('Client disconnected')
})
})
server.listen(8080, () => {
console.log('Server listening on port 8080')
})
```
### TCP Client
```js
const net = require('bare-net')
const client = net.createConnection(8080, 'localhost', () => {
console.log('Connected to server')
client.write('Hello, Server!')
})
client.on('data', (data) => {
console.log('Server says:', data.toString())
})
client.on('close', () => {
console.log('Connection closed')
})
```
## API Reference
### Server
#### `net.createServer([options][, connectionListener])`
Create TCP server.
**Parameters:**
- `options` (object): Server options
- `connectionListener` (function): Connection callback
**Example:**
```js
const server = net.createServer((socket) => {
// Handle connection
})
```
#### `server.listen(port[, host][, callback])`
Start listening.
**Example:**
```js
server.listen(8080, '0.0.0.0', () => {
console.log('Server ready')
})
```
#### `server.address()`
Get server address.
**Returns:** `{ port, family, address }`
#### `server.close([callback])`
Stop server.
### Socket
#### `net.createConnection(port[, host][, connectListener])`
Create client connection.
**Example:**
```js
const socket = net.createConnection(8080, 'localhost')
```
#### `net.createConnection(options[, connectListener])`
Create connection with options.
**Options:**
- `port`: Target port
- `host`: Target host
- `localAddress`: Local address to bind
- `localPort`: Local port
#### `socket.write(data[, encoding][, callback])`
Send data.
#### `socket.end([data][, encoding])`
Close connection.
#### `socket.setTimeout(timeout[, callback])`
Set timeout.
#### `socket.setKeepAlive([enable][, initialDelay])`
Enable keepalive.
#### `socket.address()`
Get local address.
#### `socket.remoteAddress`
Get remote address.
#### `socket.remotePort`
Get remote port.
#### Events
- `connect` - Connection established
- `data` - Data received
- `end` - Remote closed
- `close` - Socket closed
- `error` - Error occurred
- `timeout` - Timeout occurred
## Complete Examples
### Example 1: Simple Echo Server
```js
const net = require('bare-net')
const server = net.createServer((socket) => {
const clientInfo = `${socket.remoteAddress}:${socket.remotePort}`
console.log(`Client ${clientInfo} connected`)
socket.on('data', (data) => {
console.log(`Received from ${clientInfo}:`, data.toString())
socket.write(data) // Echo back
})
socket.on('close', () => {
console.log(`Client ${clientInfo} disconnected`)
})
socket.on('error', (err) => {
console.error(`Socket error from ${clientInfo}:`, err.message)
})
})
server.listen(3000, () => {
const addr = server.address()
console.log(`Echo server listening on ${addr.address}:${addr.port}`)
})
// Graceful shutdown
process.on('SIGINT', () => {
console.log('\nShutting down...')
server.close(() => {
process.exit(0)
})
})
```
### Example 2: Connection Pool
```js
const net = require('bare-net')
class ConnectionPool {
constructor(host, port, maxConnections = 10) {
this.host = host
this.port = port
this.maxConnections = maxConnections
this.pool = []
this.waiting = []
}
async getConnection() {
// Return existing available connection
const available = this.pool.find(c => !c.busy)
if (available) {
available.busy = true
return available
}
// Create new if under limit
if (this.pool.length < this.maxConnections) {
const conn = await this._createConnection()
conn.busy = true
this.pool.push(conn)
return conn
}
// Wait for available connection
return new Promise((resolve) => {
this.waiting.push(resolve)
})
}
_createConnection() {
return new Promise((resolve, reject) => {
const socket = net.createConnection(this.port, this.host, () => {
socket.busy = false
resolve(socket)
})
socket.on('error', reject)
socket.on('close', () => {
const idx = this.pool.indexOf(socket)
if (idx > -1) this.pool.splice(idx, 1)
})
})
}
releaseConnection(conn) {
conn.busy = false
// Give to waiting client
if (this.waiting.length > 0) {
const next = this.waiting.shift()
conn.busy = true
next(conn)
}
}
closeAll() {
for (const conn of this.pool) {
conn.end()
}
this.pool = []
}
}
// Usage
const pool = new ConnectionPool('localhost', 3000)
const conn = await pool.getConnection()
conn.write('Request data')
conn.once('data', (response) => {
console.log('Response:', response.toString())
pool.releaseConnection(conn)
})
```
### Example 3: IPC Server
```js
const net = require('bare-net')
const path = require('bare-path')
const socketPath = process.platform === 'win32'
? '\\.\pipe\my-service'
: '/tmp/my-service.sock'
// Clean up old socket
if (process.platform !== 'win32') {
try {
require('bare-fs').unlinkSync(socketPath)
} catch (err) {
// May not exist
}
}
const server = net.createServer((socket) => {
console.log('IPC client connected')
socket.on('data', (data) => {
try {
const request = JSON.parse(data)
console.log('Request:', request)
// Process request
const response = { id: request.id, result: 'processed' }
socket.write(JSON.stringify(response))
} catch (err) {
socket.write(JSON.stringify({ error: err.message }))
}
})
})
server.listen(socketPath, () => {
console.log('IPC server listening on', socketPath)
})
```
### Example 4: Protocol Client
```js
const net = require('bare-net')
const { Transform } = require('bare-stream')
class ProtocolClient {
constructor(host, port) {
this.host = host
this.port = port
this.socket = null
this.requestId = 0
this.pending = new Map()
}
connect() {
return new Promise((resolve, reject) => {
this.socket = net.createConnection(this.port, this.host, () => {
console.log('Connected')
resolve()
})
this.socket.on('data', (data) => {
this._handleResponse(data)
})
this.socket.on('error', reject)
this.socket.on('close', () => {
console.log('Connection closed')
})
})
}
async send(method, params) {
const id = ++this.requestId
const request = JSON.stringify({ id, method, params })
return new Promise((resolve, reject) => {
this.pending.set(id, { resolve, reject })
this.socket.write(request + '\n')
// Timeout
setTimeout(() => {
if (this.pending.has(id)) {
this.pending.delete(id)
reject(new Error('Request timeout'))
}
}, 5000)
})
}
_handleResponse(data) {
try {
const response = JSON.parse(data.toString())
const pending = this.pending.get(response.id)
if (pending) {
this.pending.delete(response.id)
if (response.error) {
pending.reject(new Error(response.error))
} else {
pending.resolve(response.result)
}
}
} catch (err) {
console.error('Failed to parse response:', err)
}
}
close() {
this.socket.end()
}
}
// Usage
const client = new ProtocolClient('localhost', 3000)
await client.connect()
const result = await client.send('getData', { key: 'test' })
console.log('Result:', result)
client.close()
```
## Best Practices
### Handle Errors
```js
socket.on('error', (err) => {
console.error('Socket error:', err.message)
})
server.on('error', (err) => {
console.error('Server error:', err.message)
})
```
### Set Timeouts
```js
socket.setTimeout(30000, () => {
console.log('Socket timeout')
socket.end()
})
```
### Use Keepalive
```js
socket.setKeepAlive(true, 60000)
```
### Clean Up
```js
process.on('SIGINT', () => {
server.close(() => {
process.exit(0)
})
})
```
## License
Apache-2.0
---
**Module Type**: Runtime/Networking | **Ecosystem Role**: TCP/IPC | **Dependencies**: bare-stream
+269
View File
@@ -0,0 +1,269 @@
# bare-pipe - Native I/O Pipes
## Overview
bare-pipe provides native I/O pipe operations for JavaScript. It enables direct access to system pipes for inter-process communication and stream redirection.
### Key Features
- **System pipes**: Access stdin, stdout, stderr
- **Named pipes**: Create and use named pipes
- **Stream interface**: Compatible with bare-stream
- **Cross-platform**: Works on Unix and Windows
### Use Cases
- **CLI tools**: Read/write to standard streams
- **Pipe chaining**: Connect processes
- **Redirection**: Redirect I/O streams
- **Inter-process**: Communication between processes
## Installation
```bash
npm install bare-pipe
```
## Quick Start
### Access Standard Streams
```js
const Pipe = require('bare-pipe')
// Write to stdout
const stdout = new Pipe(1)
stdout.write('Hello stdout!\n')
// Read from stdin
const stdin = new Pipe(0)
stdin.on('data', (data) => {
console.log('Received:', data.toString())
})
```
### File Descriptors
```js
const Pipe = require('bare-pipe')
// File descriptor numbers:
// 0 = stdin
// 1 = stdout
// 2 = stderr
const stderr = new Pipe(2)
stderr.write('Error message\n')
```
## API Reference
### Pipe Class
#### `new Pipe(fd)`
Create pipe from file descriptor.
**Parameters:**
- `fd` (number): File descriptor number
- `0` - Standard input (stdin)
- `1` - Standard output (stdout)
- `2` - Standard error (stderr)
#### `pipe.write(data[, encoding][, callback])`
Write data to pipe.
#### `pipe.end([data][, encoding][, callback])`
End the pipe.
#### `pipe.destroy([error])`
Destroy the pipe.
#### `pipe.on('data', callback)`
Listen for data events.
#### `pipe.on('end', callback)`
Listen for end events.
#### `pipe.on('error', callback)`
Listen for error events.
## Complete Examples
### Example 1: CLI Logger
```js
const Pipe = require('bare-pipe')
class CLILogger {
constructor() {
this.stdout = new Pipe(1)
this.stderr = new Pipe(2)
}
log(message) {
this.stdout.write(`${message}\n`)
}
error(message) {
this.stderr.write(`ERROR: ${message}\n`)
}
info(message) {
this.stdout.write(`[INFO] ${message}\n`)
}
warn(message) {
this.stderr.write(`[WARN] ${message}\n`)
}
close() {
this.stdout.end()
this.stderr.end()
}
}
// Usage
const logger = new CLILogger()
logger.info('Application started')
logger.log('Processing data...')
logger.warn('Low memory')
logger.error('Failed to save file')
logger.close()
```
### Example 2: Interactive CLI
```js
const Pipe = require('bare-pipe')
class InteractiveCLI {
constructor() {
this.stdin = new Pipe(0)
this.stdout = new Pipe(1)
this.buffer = ''
}
start() {
this.stdout.write('> ')
this.stdin.on('data', (data) => {
this.buffer += data.toString()
// Check for complete line
const lines = this.buffer.split('\n')
this.buffer = lines.pop() // Keep incomplete line
for (const line of lines) {
this.handleCommand(line.trim())
}
})
this.stdin.on('end', () => {
this.stdout.write('\nGoodbye!\n')
})
}
handleCommand(cmd) {
switch (cmd) {
case 'help':
this.stdout.write('Commands: help, echo <msg>, quit\n')
break
case 'quit':
this.stdin.destroy()
return
default:
if (cmd.startsWith('echo ')) {
this.stdout.write(cmd.slice(5) + '\n')
} else if (cmd) {
this.stdout.write(`Unknown command: ${cmd}\n`)
}
}
this.stdout.write('> ')
}
}
// Usage
const cli = new InteractiveCLI()
cli.start()
```
### Example 3: Progress Bar
```js
const Pipe = require('bare-pipe')
class ProgressBar {
constructor(total) {
this.total = total
this.current = 0
this.stdout = new Pipe(1)
this.width = 40
}
update(value) {
this.current = Math.min(value, this.total)
this.render()
}
render() {
const percent = this.current / this.total
const filled = Math.floor(this.width * percent)
const empty = this.width - filled
const bar = '█'.repeat(filled) + '░'.repeat(empty)
const percentage = Math.floor(percent * 100)
// Clear line and write progress
this.stdout.write(`\r[${bar}] ${percentage}%`)
if (this.current >= this.total) {
this.stdout.write('\n')
}
}
complete() {
this.update(this.total)
}
}
// Usage
const progress = new ProgressBar(100)
for (let i = 0; i <= 100; i++) {
setTimeout(() => {
progress.update(i)
}, i * 50)
}
```
## Best Practices
### Always End Pipes
```js
const pipe = new Pipe(1)
pipe.write('data')
pipe.end() // Important to signal EOF
```
### Handle Errors
```js
pipe.on('error', (err) => {
console.error('Pipe error:', err)
})
```
## License
Apache-2.0
---
**Module Type**: Runtime/IO | **Ecosystem Role**: System Pipes | **Dependencies**: bare-stream
+354
View File
@@ -0,0 +1,354 @@
# bare-signals - Signal Handling
## Overview
bare-signals provides native signal handling for JavaScript. It enables capturing and handling Unix/Windows signals like SIGINT, SIGTERM, etc.
### Key Features
- **Signal handling**: Catch OS signals
- **Multiple signals**: Support for various signal types
- **Cross-platform**: Unix signals and Windows equivalents
- **Event-based**: Event-driven signal handling
### Use Cases
- **Graceful shutdown**: Clean up on SIGINT/SIGTERM
- **Process management**: Handle process control signals
- **Application lifecycle**: Respond to system events
- **Debugging**: Catch debug signals
## Installation
```bash
npm install bare-signals
```
## Quick Start
### Handle SIGINT
```js
const Signal = require('bare-signals')
const sigint = new Signal('SIGINT')
sigint.on('signal', () => {
console.log('SIGINT received, shutting down gracefully...')
// Clean up resources
process.exit(0)
})
sigint.start()
console.log('Press Ctrl+C to trigger SIGINT')
```
### Handle Multiple Signals
```js
const Signal = require('bare-signals')
const signals = ['SIGINT', 'SIGTERM', 'SIGHUP']
for (const name of signals) {
const signal = new Signal(name)
signal.on('signal', () => {
console.log(`${name} received`)
shutdown()
})
signal.start()
}
function shutdown() {
console.log('Shutting down...')
process.exit(0)
}
```
## API Reference
### Signal Class
#### `new Signal(name)`
Create signal handler.
**Parameters:**
- `name` (string): Signal name
- `'SIGINT'` - Interrupt (Ctrl+C)
- `'SIGTERM'` - Termination
- `'SIGHUP'` - Hang up
- `'SIGUSR1'` - User-defined 1
- `'SIGUSR2'` - User-defined 2
- `'SIGWINCH'` - Window change
#### `signal.start()`
Start listening for signal.
#### `signal.stop()`
Stop listening for signal.
#### `signal.destroy()`
Destroy signal handler.
#### `signal.on('signal', callback)`
Listen for signal events.
## Complete Examples
### Example 1: Graceful Shutdown
```js
const Signal = require('bare-signals')
class Application {
constructor() {
this.running = true
this.resources = []
this.setupSignals()
}
setupSignals() {
// Handle interrupt
const sigint = new Signal('SIGINT')
sigint.on('signal', () => this.shutdown('SIGINT'))
sigint.start()
// Handle termination
const sigterm = new Signal('SIGTERM')
sigterm.on('signal', () => this.shutdown('SIGTERM'))
sigterm.start()
// Handle hangup (config reload)
const sighup = new Signal('SIGHUP')
sighup.on('signal', () => this.reload())
sighup.start()
}
addResource(resource) {
this.resources.push(resource)
}
async shutdown(signal) {
console.log(`\n${signal} received, starting graceful shutdown...`)
this.running = false
// Close all resources
for (const resource of this.resources) {
if (resource.close) {
await resource.close()
}
}
console.log('Cleanup complete, exiting')
process.exit(0)
}
async reload() {
console.log('SIGHUP received, reloading configuration...')
// Reload config
}
run() {
console.log('Application running (PID:', process.pid, ')')
console.log('Press Ctrl+C to exit')
}
}
// Usage
const app = new Application()
app.run()
```
### Example 2: Signal Debounce
```js
const Signal = require('bare-signals')
class SignalDebouncer {
constructor(signalName, handler, delay = 1000) {
this.signal = new Signal(signalName)
this.handler = handler
this.delay = delay
this.timeout = null
this.count = 0
this.signal.on('signal', () => this.onSignal())
}
onSignal() {
this.count++
if (this.timeout) {
clearTimeout(this.timeout)
}
this.timeout = setTimeout(() => {
this.handler(this.count)
this.count = 0
}, this.delay)
}
start() {
this.signal.start()
}
stop() {
this.signal.stop()
if (this.timeout) {
clearTimeout(this.timeout)
}
}
}
// Usage: Count rapid SIGINTs
const debouncer = new SignalDebouncer('SIGINT', (count) => {
if (count === 1) {
console.log('Single interrupt, continuing...')
} else {
console.log(`${count} interrupts, forcing exit`)
process.exit(1)
}
}, 500)
debouncer.start()
```
### Example 3: Process Monitor
```js
const Signal = require('bare-signals')
class ProcessMonitor {
constructor() {
this.signals = []
this.stats = {
startTime: Date.now(),
signalCounts: {}
}
this.setupMonitoring()
}
setupMonitoring() {
const signalNames = [
'SIGINT', 'SIGTERM', 'SIGHUP',
'SIGUSR1', 'SIGUSR2', 'SIGWINCH'
]
for (const name of signalNames) {
try {
const signal = new Signal(name)
signal.on('signal', () => {
this.handleSignal(name)
})
signal.start()
this.signals.push(signal)
this.stats.signalCounts[name] = 0
} catch (err) {
// Signal not supported on this platform
}
}
}
handleSignal(name) {
this.stats.signalCounts[name]++
console.log(`[${new Date().toISOString()}] Signal: ${name}`)
switch (name) {
case 'SIGUSR1':
this.dumpStats()
break
case 'SIGUSR2':
this.toggleDebug()
break
case 'SIGWINCH':
this.handleResize()
break
}
}
dumpStats() {
const uptime = (Date.now() - this.stats.startTime) / 1000
console.log('=== Process Stats ===')
console.log(`Uptime: ${uptime}s`)
console.log('Signal counts:', this.stats.signalCounts)
console.log('=====================')
}
toggleDebug() {
console.log('Toggling debug mode')
// Toggle debug flag
}
handleResize() {
console.log('Terminal resized')
// Adjust terminal UI
}
stop() {
for (const signal of this.signals) {
signal.stop()
signal.destroy()
}
}
}
// Usage
const monitor = new ProcessMonitor()
console.log('Process monitoring started')
console.log('Send SIGUSR1 for stats, SIGUSR2 for debug toggle')
```
## Best Practices
### Always Handle Cleanup
```js
const sigint = new Signal('SIGINT')
sigint.on('signal', () => {
// Always clean up before exit
cleanup()
process.exit(0)
})
sigint.start()
```
### Don't Ignore All Signals
```js
// Bad - process becomes unkillable
const sigterm = new Signal('SIGTERM')
sigterm.on('signal', () => {
console.log('Ignoring SIGTERM')
})
sigterm.start()
// Good - handle then exit
sigterm.on('signal', () => {
cleanup()
process.exit(0)
})
```
### Platform Awareness
```js
// Not all signals available on all platforms
const signals = process.platform === 'win32'
? ['SIGINT', 'SIGTERM', 'SIGBREAK']
: ['SIGINT', 'SIGTERM', 'SIGHUP']
```
## License
Apache-2.0
---
**Module Type**: Runtime/Process | **Ecosystem Role**: Signal Handling | **Dependencies**: None
+403
View File
@@ -0,0 +1,403 @@
# bare-tls - TLS/SSL for JavaScript
## Overview
bare-tls provides Transport Layer Security (TLS) streams for JavaScript in the Bare runtime. It enables encrypted network communication with support for both client and server TLS connections.
### Key Features
- **TLS encryption**: Secure socket connections
- **Client/Server**: Both TLS client and server support
- **Certificate management**: Custom certs and keys
- **Stream-based**: Built on bare-stream
- **Node.js compatible**: Similar to tls module
### Use Cases
- **HTTPS servers**: Secure web servers
- **Secure clients**: Encrypted connections to services
- **Certificate pinning**: Enhanced security
- **Mutual TLS**: Two-way authentication
## Installation
```bash
npm install bare-tls
```
## Quick Start
### TLS Server
```js
const tls = require('bare-tls')
const fs = require('bare-fs')
const options = {
key: fs.readFileSync('server-key.pem'),
cert: fs.readFileSync('server-cert.pem')
}
const server = tls.createServer(options, (socket) => {
console.log('Secure connection established')
socket.write('Hello secure client!')
socket.on('data', (data) => console.log(data.toString()))
})
server.listen(8443, () => {
console.log('TLS server listening')
})
```
### TLS Client
```js
const tls = require('bare-tls')
const options = {
host: 'localhost',
port: 8443,
rejectUnauthorized: false // For self-signed certs
}
const socket = tls.connect(options, () => {
console.log('Secure connection established')
socket.write('Hello secure server!')
})
socket.on('data', (data) => console.log(data.toString()))
```
## API Reference
### Server
#### `tls.createServer(options[, secureConnectionListener])`
Create TLS server.
**Options:**
- `key`: Private key (string | Buffer)
- `cert`: Certificate (string | Buffer)
- `ca`: CA certificate for client auth
- `requestCert`: Request client certificate
- `rejectUnauthorized`: Reject unauthorized clients
#### `server.listen(port[, host][, callback])`
Start listening.
#### `server.close([callback])`
Stop server.
### Client
#### `tls.connect(options[, callback])`
Create TLS connection.
**Options:**
- `host`: Target host
- `port`: Target port
- `key`: Client private key
- `cert`: Client certificate
- `ca`: Trusted CA certificates
- `rejectUnauthorized`: Reject unauthorized server
#### `tls.connect(port[, host][, options][, callback])`
Alternative signature.
### TLSSocket
#### `socket.getCipher()`
Get cipher info.
**Returns:** `{ name, version }`
#### `socket.getPeerCertificate()`
Get peer certificate.
#### `socket.authorized`
Whether peer is authorized.
#### `socket.authorizationError`
Authorization error if not authorized.
#### Events
- `secureConnect` - TLS handshake complete
- `error` - TLS error
## Complete Examples
### Example 1: Mutual TLS Server
```js
const tls = require('bare-tls')
const fs = require('bare-fs')
const server = tls.createServer({
key: fs.readFileSync('server-key.pem'),
cert: fs.readFileSync('server-cert.pem'),
ca: fs.readFileSync('ca-cert.pem'), // Trust this CA
requestCert: true, // Request client cert
rejectUnauthorized: true // Require valid client cert
}, (socket) => {
console.log('Client connected')
console.log('Client authorized:', socket.authorized)
if (socket.authorized) {
const cert = socket.getPeerCertificate()
console.log('Client cert subject:', cert.subject)
}
socket.write('Welcome, authenticated client!')
socket.on('data', (data) => {
console.log('Received:', data.toString())
})
})
server.listen(8443, () => {
console.log('mTLS server listening on port 8443')
})
```
### Example 2: HTTPS Client
```js
const tls = require('bare-tls')
// Connect to HTTPS server
const socket = tls.connect({
host: 'api.example.com',
port: 443,
servername: 'api.example.com' // For SNI
}, () => {
console.log('Connected')
console.log('Cipher:', socket.getCipher())
// Send HTTP request
const request = [
'GET /v1/data HTTP/1.1',
'Host: api.example.com',
'Connection: close',
'',
''
].join('\r\n')
socket.write(request)
})
let response = ''
socket.on('data', (data) => {
response += data.toString()
})
socket.on('end', () => {
console.log('Response:', response)
})
socket.on('error', (err) => {
console.error('TLS error:', err.message)
})
```
### Example 3: Secure Proxy
```js
const tls = require('bare-tls')
const net = require('bare-net')
// Simple TLS proxy: plaintext <-> TLS
class SecureProxy {
constructor(listenPort, targetHost, targetPort, tlsOptions) {
this.listenPort = listenPort
this.targetHost = targetHost
this.targetPort = targetPort
this.tlsOptions = tlsOptions
}
start() {
this.server = net.createServer((clientSocket) => {
console.log('Client connected')
// Connect to secure backend
const serverSocket = tls.connect({
host: this.targetHost,
port: this.targetPort,
...this.tlsOptions
}, () => {
console.log('Connected to secure backend')
// Pipe data between client and server
clientSocket.pipe(serverSocket)
serverSocket.pipe(clientSocket)
})
serverSocket.on('error', (err) => {
console.error('Backend error:', err.message)
clientSocket.end()
})
clientSocket.on('close', () => {
serverSocket.end()
})
})
this.server.listen(this.listenPort, () => {
console.log(`Proxy listening on port ${this.listenPort}`)
})
}
}
// Usage
const proxy = new SecureProxy(
8080, // Listen on plaintext port
'backend', // Backend host
8443, // Backend TLS port
{
rejectUnauthorized: true,
ca: require('bare-fs').readFileSync('ca-cert.pem')
}
)
proxy.start()
```
### Example 4: Certificate Manager
```js
const tls = require('bare-tls')
const fs = require('bare-fs')
const crypto = require('bare-crypto')
class CertificateManager {
constructor(certDir) {
this.certDir = certDir
this.cache = new Map()
}
loadCertificate(domain) {
// Check cache
if (this.cache.has(domain)) {
return this.cache.get(domain)
}
// Load from disk
const keyPath = `${this.certDir}/${domain}-key.pem`
const certPath = `${this.certDir}/${domain}-cert.pem`
try {
const options = {
key: fs.readFileSync(keyPath),
cert: fs.readFileSync(certPath)
}
this.cache.set(domain, options)
return options
} catch (err) {
console.error(`Failed to load cert for ${domain}:`, err.message)
return null
}
}
createServer(domain, handler) {
const options = this.loadCertificate(domain)
if (!options) {
throw new Error(`No certificate for ${domain}`)
}
return tls.createServer(options, handler)
}
verifyCertificate(cert) {
// Check expiration
// Check chain
// Return validation result
return {
valid: true,
expires: new Date(),
fingerprint: this.getFingerprint(cert)
}
}
getFingerprint(cert) {
const hash = crypto.createHash('sha256')
hash.update(cert)
return hash.digest('hex').match(/.{2}/g).join(':')
}
}
// Usage
const manager = new CertificateManager('./certs')
const server = manager.createServer('example.com', (socket) => {
socket.write('Hello from secure server!')
})
server.listen(443)
```
## Best Practices
### Always Verify Certificates
```js
const socket = tls.connect({
host: 'api.example.com',
port: 443,
rejectUnauthorized: true // Don't disable in production!
})
```
### Use Strong Cipher Suites
```js
const server = tls.createServer({
key: privateKey,
cert: certificate,
// Server will use strong defaults
})
```
### Handle Errors
```js
socket.on('error', (err) => {
console.error('TLS error:', err.message)
})
server.on('tlsClientError', (err, socket) => {
console.error('Client TLS error:', err.message)
})
```
### Certificate Pinning
```js
const EXPECTED_FINGERPRINT = 'AA:BB:CC:DD:...'
socket.on('secureConnect', () => {
const cert = socket.getPeerCertificate()
const fingerprint = cert.fingerprint256
if (fingerprint !== EXPECTED_FINGERPRINT) {
console.error('Certificate pin mismatch!')
socket.destroy()
}
})
```
## License
Apache-2.0
---
**Module Type**: Runtime/Security | **Ecosystem Role**: TLS/SSL | **Dependencies**: bare-stream, bare-crypto
+293
View File
@@ -0,0 +1,293 @@
# bare-url - URL Handling
## Overview
bare-url provides WHATWG-compliant URL implementation for JavaScript. It enables parsing, manipulating, and working with URLs in a standardized way.
### Key Features
- **WHATWG URL**: Standard URL API
- **URL parsing**: Parse URLs into components
- **URL manipulation**: Modify URL parts
- **File URLs**: Convert file URLs to paths
### Use Cases
- **URL parsing**: Extract components from URLs
- **URL building**: Construct URLs programmatically
- **Path conversion**: file:// to filesystem paths
- **URL validation**: Validate URL formats
## Installation
```bash
npm install bare-url
```
## Quick Start
### Parse URL
```js
const url = require('bare-url')
const myURL = new url.URL('https://user:[email protected]:8080/path?query=1#hash')
console.log(myURL.protocol) // 'https:'
console.log(myURL.hostname) // 'example.com'
console.log(myURL.port) // '8080'
console.log(myURL.pathname) // '/path'
console.log(myURL.search) // '?query=1'
console.log(myURL.hash) // '#hash'
```
### File URL to Path
```js
const url = require('bare-url')
const filePath = url.fileURLToPath('file:///home/user/file.txt')
console.log(filePath) // '/home/user/file.txt'
```
## API Reference
### URL Class
#### `new URL(input[, base])`
Create URL object.
**Parameters:**
- `input` (string): URL string
- `base` (string | URL): Base URL for relative URLs
**Properties:**
- `href` - Full URL
- `protocol` - Protocol scheme
- `host` - Host (hostname:port)
- `hostname` - Hostname only
- `port` - Port number
- `pathname` - Path
- `search` - Query string
- `searchParams` - URLSearchParams object
- `hash` - Fragment
- `username` - Username
- `password` - Password
### URLSearchParams
#### `new URLSearchParams([init])`
Create query string parser.
**Methods:**
- `append(name, value)` - Add parameter
- `delete(name)` - Remove parameter
- `get(name)` - Get value
- `getAll(name)` - Get all values
- `has(name)` - Check existence
- `set(name, value)` - Set value
- `toString()` - Serialize
**Example:**
```js
const params = new url.URLSearchParams('?a=1&b=2')
params.append('c', '3')
console.log(params.toString()) // 'a=1&b=2&c=3'
```
### Utilities
#### `url.fileURLToPath(url)`
Convert file URL to path.
**Example:**
```js
url.fileURLToPath('file:///C:/Users/file.txt') // 'C:\Users\file.txt' (Windows)
url.fileURLToPath('file:///home/user/file.txt') // '/home/user/file.txt' (Unix)
```
#### `url.pathToFileURL(path)`
Convert path to file URL.
## Complete Examples
### Example 1: URL Builder
```js
const url = require('bare-url')
class URLBuilder {
constructor(baseURL) {
this.url = new url.URL(baseURL)
}
path(segment) {
this.url.pathname = segment
return this
}
query(params) {
for (const [key, value] of Object.entries(params)) {
this.url.searchParams.set(key, value)
}
return this
}
hash(fragment) {
this.url.hash = fragment
return this
}
build() {
return this.url.toString()
}
}
// Usage
const apiURL = new URLBuilder('https://api.example.com')
.path('/v1/users')
.query({ page: 1, limit: 10 })
.build()
console.log(apiURL)
// 'https://api.example.com/v1/users?page=1&limit=10'
```
### Example 2: URL Validator
```js
const url = require('bare-url')
class URLValidator {
static isValid(urlString) {
try {
new url.URL(urlString)
return true
} catch {
return false
}
}
static isAbsolute(urlString) {
return /^[a-z][a-z0-9+.-]*:/i.test(urlString)
}
static isRelative(urlString) {
return !this.isAbsolute(urlString)
}
static getType(urlString) {
try {
const u = new url.URL(urlString)
return u.protocol.slice(0, -1) // Remove trailing colon
} catch {
return null
}
}
static normalize(urlString) {
try {
const u = new url.URL(urlString)
return u.toString()
} catch {
return null
}
}
}
// Usage
console.log(URLValidator.isValid('https://example.com')) // true
console.log(URLValidator.isValid('not a url')) // false
console.log(URLValidator.getType('https://example.com')) // 'https'
```
### Example 3: Request URL Parser
```js
const url = require('bare-url')
class RequestURL {
constructor(requestUrl) {
this.url = new url.URL(requestUrl)
this.params = {}
}
parseRoute(pattern) {
// Pattern: '/users/:id/posts/:postId'
const urlParts = this.url.pathname.split('/').filter(Boolean)
const patternParts = pattern.split('/').filter(Boolean)
if (urlParts.length !== patternParts.length) {
return null
}
for (let i = 0; i < patternParts.length; i++) {
if (patternParts[i].startsWith(':')) {
// Parameter
const paramName = patternParts[i].slice(1)
this.params[paramName] = urlParts[i]
} else if (patternParts[i] !== urlParts[i]) {
// Mismatch
return null
}
}
return this.params
}
getQueryParam(name) {
return this.url.searchParams.get(name)
}
getAllQueryParams() {
const params = {}
for (const [key, value] of this.url.searchParams) {
params[key] = value
}
return params
}
}
// Usage
const req = new RequestURL('https://api.example.com/users/123/posts/456?expand=true')
const params = req.parseRoute('/users/:userId/posts/:postId')
console.log(params) // { userId: '123', postId: '456' }
console.log(req.getQueryParam('expand')) // 'true'
```
## Best Practices
### Always Validate URLs
```js
try {
const u = new url.URL(userInput)
// Use u
} catch {
console.error('Invalid URL')
}
```
### Use URLSearchParams for Query Strings
```js
// Good
const params = new url.URLSearchParams()
params.set('key', value)
// Avoid manual string building
const query = `?key=${value}` // Error-prone
```
## License
Apache-2.0
---
**Module Type**: Runtime/Standard | **Ecosystem Role**: URL Handling | **Dependencies**: None
+422
View File
@@ -0,0 +1,422 @@
# bare-worker - Worker Threads
## Overview
bare-worker provides higher-level worker threads for JavaScript. It enables running JavaScript in parallel threads with message passing.
### Key Features
- **Worker threads**: Parallel execution
- **Message passing**: Communicate with workers
- **Shared memory**: Transfer ArrayBuffers
- **Node.js compatible**: Similar to worker_threads
### Use Cases
- **CPU-intensive tasks**: Offload heavy computation
- **Parallel processing**: Process data in parallel
- **Background tasks**: Run tasks without blocking main thread
- **Isolation**: Run untrusted code safely
## Installation
```bash
npm install bare-worker
```
## Quick Start
### Basic Worker
```js
const Worker = require('bare-worker')
if (Worker.isMainThread) {
// Main thread
const worker = new Worker(__filename)
worker.on('message', (msg) => {
console.log('From worker:', msg)
})
worker.on('exit', (code) => {
console.log('Worker exited with code', code)
})
worker.postMessage('Hello worker')
} else {
// Worker thread
Worker.parentPort.on('message', (msg) => {
console.log('From main:', msg)
Worker.parentPort.postMessage('Hello main')
})
}
```
### Separate Worker File
```js
// main.js
const Worker = require('bare-worker')
const worker = new Worker('./worker.js')
worker.on('message', (result) => {
console.log('Result:', result)
})
worker.postMessage({ n: 40 })
```
```js
// worker.js
const Worker = require('bare-worker')
Worker.parentPort.on('message', ({ n }) => {
const result = fibonacci(n)
Worker.parentPort.postMessage({ n, result })
})
function fibonacci(n) {
if (n < 2) return n
return fibonacci(n - 1) + fibonacci(n - 2)
}
```
## API Reference
### Worker Class
#### `new Worker(filename[, options])`
Create worker thread.
**Parameters:**
- `filename` (string): Worker script path
- `options` (object):
- `workerData`: Data to pass to worker
**Example:**
```js
const worker = new Worker('./task.js', {
workerData: { input: 'data' }
})
```
#### `worker.postMessage(value[, transferList])`
Send message to worker.
**TransferList:** Array of ArrayBuffers to transfer ownership.
#### `worker.terminate()`
Force terminate worker.
#### `worker.on('message', callback)`
Listen for messages from worker.
#### `worker.on('error', callback)`
Listen for errors.
#### `worker.on('exit', callback)`
Listen for exit events.
### Worker Properties
#### `Worker.isMainThread`
Boolean indicating if in main thread.
#### `Worker.parentPort`
Message port in worker thread (to communicate with main).
#### `Worker.workerData`
Data passed from main thread.
## Complete Examples
### Example 1: Worker Pool
```js
const Worker = require('bare-worker')
const os = require('bare-os')
class WorkerPool {
constructor(workerScript, poolSize = os.cpus().length) {
this.workerScript = workerScript
this.poolSize = poolSize
this.workers = []
this.queue = []
this.active = new Map()
}
init() {
for (let i = 0; i < this.poolSize; i++) {
this.addWorker()
}
}
addWorker() {
const worker = new Worker(this.workerScript)
worker.on('message', (result) => {
const { resolve } = this.active.get(worker)
this.active.delete(worker)
resolve(result)
this.processQueue()
})
worker.on('error', (err) => {
console.error('Worker error:', err)
})
this.workers.push(worker)
}
execute(data) {
return new Promise((resolve, reject) => {
this.queue.push({ data, resolve, reject })
this.processQueue()
})
}
processQueue() {
if (this.queue.length === 0) return
// Find available worker
const available = this.workers.find(w => !this.active.has(w))
if (!available) return // All busy
const task = this.queue.shift()
this.active.set(available, task)
available.postMessage(task.data)
}
terminate() {
for (const worker of this.workers) {
worker.terminate()
}
this.workers = []
}
}
// Worker script: cpu-worker.js
if (!require('bare-worker').isMainThread) {
const { parentPort } = require('bare-worker')
parentPort.on('message', (data) => {
// Heavy computation
let result = 0
for (let i = 0; i < data.iterations; i++) {
result += Math.sqrt(i)
}
parentPort.postMessage({ result })
})
}
// Usage
const pool = new WorkerPool('./cpu-worker.js', 4)
pool.init()
// Run multiple tasks
const promises = []
for (let i = 0; i < 10; i++) {
promises.push(pool.execute({ iterations: 1000000 }))
}
Promise.all(promises).then((results) => {
console.log('All tasks complete:', results)
pool.terminate()
})
```
### Example 2: Parallel Map
```js
const Worker = require('bare-worker')
const os = require('bare-os')
async function parallelMap(array, mapper, options = {}) {
const concurrency = options.concurrency || os.cpus().length
// Create workers
const workers = []
for (let i = 0; i < concurrency; i++) {
workers.push(createMapperWorker(mapper))
}
// Process items
const results = new Array(array.length)
const iterator = array.entries()
await Promise.all(workers.map(async (worker) => {
for (const [index, item] of iterator) {
results[index] = await runOnWorker(worker, item, index)
}
}))
// Cleanup
workers.forEach(w => w.terminate())
return results
}
function createMapperWorker(mapperFn) {
const workerScript = `
const { parentPort } = require('bare-worker')
const mapper = ${mapperFn.toString()}
parentPort.on('message', async ({ item, index }) => {
try {
const result = await mapper(item, index)
parentPort.postMessage({ success: true, result, index })
} catch (error) {
parentPort.postMessage({ success: false, error: error.message, index })
}
})
`
return new Worker(workerScript, { eval: true })
}
function runOnWorker(worker, item, index) {
return new Promise((resolve, reject) => {
const handler = (msg) => {
if (msg.index === index) {
worker.off('message', handler)
if (msg.success) {
resolve(msg.result)
} else {
reject(new Error(msg.error))
}
}
}
worker.on('message', handler)
worker.postMessage({ item, index })
})
}
// Usage
const data = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
parallelMap(data, (x) => {
// Heavy computation
let sum = 0
for (let i = 0; i < 1000000; i++) {
sum += x * i
}
return sum
}).then((results) => {
console.log('Results:', results)
})
```
### Example 3: Shared Buffer Processing
```js
const Worker = require('bare-worker')
// Main thread
if (Worker.isMainThread) {
// Create shared buffer
const sharedBuffer = new SharedArrayBuffer(1024)
const view = new Uint8Array(sharedBuffer)
// Fill with data
for (let i = 0; i < view.length; i++) {
view[i] = i % 256
}
// Create worker
const worker = new Worker(__filename)
worker.on('message', (msg) => {
console.log('Processed:', msg)
console.log('First 10 bytes:', view.slice(0, 10))
})
// Transfer shared buffer
worker.postMessage({ sharedBuffer }, [sharedBuffer])
}
else {
// Worker thread
Worker.parentPort.on('message', ({ sharedBuffer }) => {
const view = new Uint8Array(sharedBuffer)
// Process data in place
for (let i = 0; i < view.length; i++) {
view[i] = view[i] * 2 // Double each value
}
Worker.parentPort.postMessage({ done: true, length: view.length })
})
}
```
## Best Practices
### Don't Block Worker
```js
// Bad - blocks worker
while (true) {}
// Good - use async or break up work
async function process() {
while (hasWork) {
await doChunkOfWork()
}
}
```
### Handle Errors
```js
worker.on('error', (err) => {
console.error('Worker error:', err)
})
worker.on('exit', (code) => {
if (code !== 0) {
console.error('Worker stopped with exit code', code)
}
})
```
### Clean Up Workers
```js
// Always terminate workers when done
pool.terminate()
// Or for single worker
worker.terminate()
```
### Use workerData for Initialization
```js
// Main
const worker = new Worker('./worker.js', {
workerData: { config: 'data' }
})
// Worker
const { workerData } = require('bare-worker')
console.log('Config:', workerData.config)
```
## License
Apache-2.0
---
**Module Type**: Runtime/Parallel | **Ecosystem Role**: Worker Threads | **Dependencies**: None
+285
View File
@@ -0,0 +1,285 @@
# bare-zlib - Compression
## Overview
bare-zlib provides stream-based zlib compression and decompression for JavaScript. It enables efficient data compression using gzip, deflate, and other formats.
### Key Features
- **Multiple formats**: gzip, deflate, brotli
- **Stream-based**: Process data incrementally
- **Native bindings**: High-performance compression
- **Node.js compatible**: Similar to zlib module
### Use Cases
- **HTTP compression**: Compress responses
- **File compression**: Archive files
- **Network optimization**: Reduce bandwidth
- **Data storage**: Compressed storage
## Installation
```bash
npm install bare-zlib
```
## Quick Start
### Gzip Compression
```js
const zlib = require('bare-zlib')
// Compress
const input = Buffer.from('Hello, World!')
const compressed = zlib.gzipSync(input)
// Decompress
const decompressed = zlib.gunzipSync(compressed)
console.log(decompressed.toString()) // 'Hello, World!'
```
### Stream Compression
```js
const zlib = require('bare-zlib')
const fs = require('bare-fs')
// Compress file
const input = fs.createReadStream('input.txt')
const gzip = zlib.createGzip()
const output = fs.createWriteStream('input.txt.gz')
input.pipe(gzip).pipe(output)
```
## API Reference
### gzip
#### `zlib.gzip(buffer[, options], callback)`
Compress with gzip.
#### `zlib.gzipSync(buffer[, options])`
Synchronous gzip.
#### `zlib.gunzip(buffer[, options], callback)`
Decompress gzip.
#### `zlib.gunzipSync(buffer[, options])`
Synchronous gunzip.
#### `zlib.createGzip([options])`
Create gzip stream.
### deflate
#### `zlib.deflate(buffer[, options], callback)`
Compress with deflate.
#### `zlib.deflateSync(buffer[, options])`
Synchronous deflate.
#### `zlib.inflate(buffer[, options], callback)`
Decompress deflate.
#### `zlib.inflateSync(buffer[, options])`
Synchronous inflate.
#### `zlib.createDeflate([options])`
Create deflate stream.
### brotli
#### `zlib.brotliCompress(buffer[, options], callback)`
Compress with brotli.
#### `zlib.brotliCompressSync(buffer[, options])`
Synchronous brotli.
#### `zlib.brotliDecompress(buffer[, options], callback)`
Decompress brotli.
#### `zlib.createBrotliCompress([options])`
Create brotli stream.
### Constants
- `zlib.constants.Z_NO_COMPRESSION` (0)
- `zlib.constants.Z_BEST_SPEED` (1)
- `zlib.constants.Z_BEST_COMPRESSION` (9)
- `zlib.constants.Z_DEFAULT_COMPRESSION` (-1)
## Complete Examples
### Example 1: HTTP Compression
```js
const zlib = require('bare-zlib')
const http = require('bare-http1')
const server = http.createServer((req, res) => {
const acceptEncoding = req.headers['accept-encoding'] || ''
let stream = res
// Compress based on client support
if (acceptEncoding.includes('br')) {
res.setHeader('Content-Encoding', 'br')
stream = zlib.createBrotliCompress()
stream.pipe(res)
} else if (acceptEncoding.includes('gzip')) {
res.setHeader('Content-Encoding', 'gzip')
stream = zlib.createGzip()
stream.pipe(res)
}
res.setHeader('Content-Type', 'text/plain')
stream.write('Hello, compressed world!')
stream.end()
})
server.listen(8080)
```
### Example 2: File Archiver
```js
const zlib = require('bare-zlib')
const fs = require('bare-fs')
const path = require('bare-path')
class FileArchiver {
async compressFile(inputPath, outputPath) {
const input = fs.createReadStream(inputPath)
const gzip = zlib.createGzip({ level: 9 })
const output = fs.createWriteStream(outputPath)
return new Promise((resolve, reject) => {
input.pipe(gzip).pipe(output)
output.on('finish', resolve)
output.on('error', reject)
})
}
async decompressFile(inputPath, outputPath) {
const input = fs.createReadStream(inputPath)
const gunzip = zlib.createGunzip()
const output = fs.createWriteStream(outputPath)
return new Promise((resolve, reject) => {
input.pipe(gunzip).pipe(output)
output.on('finish', resolve)
output.on('error', reject)
})
}
async compressString(str) {
return zlib.gzipSync(Buffer.from(str))
}
async decompressString(compressed) {
const result = zlib.gunzipSync(compressed)
return result.toString()
}
}
// Usage
const archiver = new FileArchiver()
await archiver.compressFile('large-file.txt', 'large-file.txt.gz')
await archiver.decompressFile('large-file.txt.gz', 'large-file-restored.txt')
```
### Example 3: Compression Comparison
```js
const zlib = require('bare-zlib')
function compareCompression(data) {
const input = Buffer.from(data)
// Gzip (default)
const gzip = zlib.gzipSync(input)
// Gzip (best compression)
const gzipBest = zlib.gzipSync(input, { level: 9 })
// Deflate
const deflate = zlib.deflateSync(input)
// Brotli
const brotli = zlib.brotliCompressSync(input)
console.log('Original size:', input.length)
console.log('Gzip (default):', gzip.length, `(${((1 - gzip.length/input.length) * 100).toFixed(1)}% reduction)`)
console.log('Gzip (best):', gzipBest.length, `(${((1 - gzipBest.length/input.length) * 100).toFixed(1)}% reduction)`)
console.log('Deflate:', deflate.length, `(${((1 - deflate.length/input.length) * 100).toFixed(1)}% reduction)`)
console.log('Brotli:', brotli.length, `(${((1 - brotli.length/input.length) * 100).toFixed(1)}% reduction)`)
}
// Test with sample data
const sampleData = 'The quick brown fox jumps over the lazy dog. '.repeat(100)
compareCompression(sampleData)
```
## Best Practices
### Choose Right Compression Level
```js
// Speed prioritized
const fast = zlib.createGzip({ level: 1 })
// Balanced (default)
const balanced = zlib.createGzip({ level: 6 })
// Compression prioritized
const best = zlib.createGzip({ level: 9 })
```
### Handle Errors
```js
gzip.on('error', (err) => {
console.error('Compression error:', err)
})
gunzip.on('error', (err) => {
console.error('Decompression error:', err)
})
```
### Use Streams for Large Data
```js
// Good for large files
fs.createReadStream('large.bin')
.pipe(zlib.createGzip())
.pipe(fs.createWriteStream('large.bin.gz'))
// Bad for large files (loads all into memory)
const data = fs.readFileSync('large.bin') // May OOM
const compressed = zlib.gzipSync(data)
```
## License
Apache-2.0
---
**Module Type**: Runtime/Compression | **Ecosystem Role**: Data Compression | **Dependencies**: bare-stream