
Product
Microsoft Teams Notifications Are Now Available in Socket
Socket can now send alerts and supply chain attack notifications to Microsoft Teams, with filters that route the right updates to each channel.
@bfra.me/es
Advanced tools
High-quality reusable types and utilities for ES development (JavaScript and TypeScript). Zero runtime dependencies for core utilities, tree-shakeable via subpath exports.
pipe, compose, curry, memoize with full TypeScript inferenceretry, timeout, debounce, throttle, concurrency control# pnpm (recommended)
pnpm add @bfra.me/es
# npm
npm install @bfra.me/es
# yarn
yarn add @bfra.me/es
# For file watcher functionality
pnpm add chokidar
import {err, isOk, ok, pipe, retry} from '@bfra.me/es'
// Type-safe error handling with Result
function divide(a: number, b: number) {
return b === 0 ? err(new Error('Division by zero')) : ok(a / b)
}
const result = divide(10, 2)
if (isOk(result)) {
console.log(result.data) // 5
}
// Functional composition
const transform = pipe(
(x: number) => x + 1,
(x: number) => x * 2
)
transform(5) // 12
// Async with retry
const data = await retry(() => fetch('/api/data'), {maxAttempts: 3})
All utilities are organized into tree-shakeable subpath exports. Import only what you need:
| Export | Description |
|---|---|
@bfra.me/es/result | Result type for error handling |
@bfra.me/es/functional | Functional programming utilities |
@bfra.me/es/async | Async utilities (retry, timeout, debounce) |
@bfra.me/es/module | ES module interoperability |
@bfra.me/es/types | Branded types and type guards |
@bfra.me/es/validation | Path validation and sanitization |
@bfra.me/es/error | Structured error utilities |
@bfra.me/es/env | Environment detection |
@bfra.me/es/watcher | File watcher abstraction |
@bfra.me/es/result)A discriminated union type for error handling without exceptions. Inspired by Rust's Result type.
import type {Err, Ok, Result} from '@bfra.me/es/result'
import {err, flatMap, isErr, isOk, map, ok, unwrap, unwrapOr} from '@bfra.me/es/result'
// Create success result
const success = ok(42) // Ok<number>
// Create error result
const failure = err(new Error('failed')) // Err<Error>
const result = divide(10, 2)
if (isOk(result)) {
// TypeScript knows result.data is available
console.log(result.data)
}
if (isErr(result)) {
// TypeScript knows result.error is available
console.error(result.error)
}
// map: Transform success values
const doubled = map(ok(5), x => x * 2) // Ok(10)
const mapped = map(err('fail'), x => x * 2) // Err('fail') - unchanged
// flatMap: Chain operations that return Results
const parsed = flatMap(ok('42'), str => {
const num = Number.parseInt(str, 10)
return Number.isNaN(num) ? err('Invalid number') : ok(num)
})
// mapErr: Transform error values
const withContext = mapErr(err('not found'), e => new Error(`Resource ${e}`))
// unwrap: Get value or throw (use sparingly)
const value = unwrap(ok(42)) // 42
const willThrow = unwrap(err('fail')) // throws Error
// unwrapOr: Get value or default (preferred)
const withDefault = unwrapOr(err('fail'), 0) // 0
import {fromPromise, fromThrowable} from '@bfra.me/es/result'
// Wrap synchronous throwing functions
const parsed = fromThrowable(() => JSON.parse(input))
// Wrap promises
const fetched = await fromPromise(fetch('/api/data'))
@bfra.me/es/functional)import {compose, constant, curry, flip, identity, memoize, noop, partial, pipe, tap} from '@bfra.me/es/functional'
Composes functions left-to-right. Each function receives the result of the previous.
const addOne = (x: number) => x + 1
const double = (x: number) => x * 2
const toString = (x: number) => `Value: ${x}`
const transform = pipe(addOne, double, toString)
transform(5) // "Value: 12" (5 + 1 = 6, 6 * 2 = 12)
Composes functions right-to-left (mathematical composition order).
const addOneThenDouble = compose(double, addOne)
addOneThenDouble(5) // 12 (addOne first, then double)
Transforms a function to accept arguments one at a time.
const add = (a: number, b: number, c: number) => a + b + c
const curriedAdd = curry(add)
curriedAdd(1)(2)(3) // 6
curriedAdd(1, 2)(3) // 6
curriedAdd(1)(2, 3) // 6
curriedAdd(1, 2, 3) // 6
Caches function results based on arguments. Supports multiple cache strategies.
import {memoize} from '@bfra.me/es/functional'
// Simple memoization
const expensive = memoize((n: number) => {
// Complex calculation...
return n * 2
})
// With LRU cache (evicts least recently used)
const withLRU = memoize(fn, {
strategy: 'lru',
maxSize: 100
})
// With TTL (time-to-live expiration)
const withTTL = memoize(fn, {
strategy: 'ttl',
ttl: 60000 // 1 minute
})
// Cache statistics
console.log(expensive.getStats()) // { hits: 10, misses: 5, size: 5 }
expensive.clear() // Clear all cached values
// identity: Returns its argument unchanged
const x = identity(42) // 42
// tap: Execute side effect, return original value
const logged = pipe(
addOne,
tap(x => console.log('After addOne:', x)),
double
)
// partial: Pre-fill some arguments
const greet = (greeting: string, name: string) => `${greeting}, ${name}!`
const sayHello = partial(greet, 'Hello')
sayHello('World') // "Hello, World!"
// flip: Swap first two arguments
const divide = (a: number, b: number) => a / b
const flipped = flip(divide)
flipped(2, 10) // 5 (same as divide(10, 2))
// constant: Create function that always returns same value
const alwaysTrue = constant(true)
alwaysTrue() // true
// noop: Do nothing function
element.addEventListener('click', noop)
@bfra.me/es/async)import {debounce, pAll, pLimit, retry, sleep, throttle, timeout} from '@bfra.me/es/async'
Retries a function with exponential backoff.
const result = await retry(
() => fetch('/api/data'),
{
maxAttempts: 3, // Default: 3
initialDelay: 100, // Default: 100ms
maxDelay: 10000, // Default: 10000ms
backoffFactor: 2, // Default: 2
shouldRetry: (error, attempt) => {
// Custom retry logic
return error.message !== 'Not Found'
}
}
)
if (isOk(result)) {
console.log(result.data)
} else {
console.error('All retries failed:', result.error)
}
Wraps a promise with a timeout.
const result = await timeout(fetch('/api/slow'), 5000)
if (isErr(result) && result.error instanceof TimeoutError) {
console.log('Request timed out')
}
Creates a debounced function that delays invocation.
const saveInput = debounce((value: string) => {
localStorage.setItem('draft', value)
}, 300)
input.addEventListener('input', e => saveInput(e.target.value))
// Cancel pending invocation
saveInput.cancel()
Limits function invocation frequency.
const handleScroll = throttle(() => {
updateScrollPosition()
}, 100)
window.addEventListener('scroll', handleScroll)
// pLimit: Create a concurrency limiter
const limit = pLimit(5) // Max 5 concurrent operations
const results = await Promise.all(
urls.map(url => limit(() => fetch(url)))
)
// pAll: Run promises with concurrency control
const allResults = await pAll(
urls.map(url => () => fetch(url)),
{concurrency: 5}
)
@bfra.me/es/module)import {dynamicImport, interopDefault, isESModule, isPackageInScope, resolveModule} from '@bfra.me/es/module'
Unwraps default exports from both ES and CommonJS modules.
// Handles both ESM and CJS exports
const lodash = await interopDefault(import('lodash'))
// Works with dynamic imports
const config = await interopDefault(import('./config.js'))
Safely resolves and imports a module with Result return type.
const result = await resolveModule<typeof import('lodash')>('lodash')
if (isOk(result)) {
const _ = result.data
}
Checks if a package is available from a specific directory context.
if (isPackageInScope('typescript', {scopeUrl: import.meta.url})) {
// TypeScript is available
}
@bfra.me/es/types)import type {Brand, NonEmptyString, Opaque, PositiveInteger} from '@bfra.me/es/types'
import {assertType, brand, hasProperty, isArray, isNonNullable, isNumber, isObject, isString, unbrand} from '@bfra.me/es/types'
Create nominal types in TypeScript's structural type system.
type UserId = Brand<string, 'UserId'>
type OrderId = Brand<string, 'OrderId'>
function getUser(id: UserId): User { /* ... */ }
function getOrder(id: OrderId): Order { /* ... */ }
const userId = brand<string, 'UserId'>('user-123')
const orderId = brand<string, 'OrderId'>('order-456')
getUser(userId) // ✅ OK
getUser(orderId) // ❌ Type error - can't use OrderId as UserId
function processValue(value: unknown) {
if (isString(value)) {
// value is string
return value.toUpperCase()
}
if (isNumber(value)) {
// value is number (excludes NaN)
return value * 2
}
if (isObject(value)) {
// value is Record<string, unknown> (excludes null and arrays)
return Object.keys(value)
}
if (isArray(value)) {
// value is unknown[]
return value.length
}
}
// Check for property existence
if (hasProperty(obj, 'name')) {
// obj is Record<'name', unknown>
console.log(obj.name)
}
// Filter nullish values
const values = [1, null, 2, undefined, 3].filter(isNonNullable) // [1, 2, 3]
// Assert and narrow type
assertType(value, isString) // throws if not string
// value is now string
// Use with custom guards
const isUser = (v: unknown): v is User =>
isObject(v) && hasProperty(v, 'name') && hasProperty(v, 'email')
assertType(data, isUser)
// data is now User
@bfra.me/es/validation)import {createValidator, isWithinBoundary, sanitizeInput, validatePath} from '@bfra.me/es/validation'
Validates paths against traversal attacks.
const result = validatePath('../etc/passwd')
if (isErr(result)) {
console.log(result.error.code) // 'PATH_TRAVERSAL'
}
const valid = validatePath('src/index.ts')
if (isOk(valid)) {
// valid.data is ValidPath branded type
}
// Check if path is within boundary
if (isWithinBoundary('/app/uploads/file.txt', '/app/uploads')) {
// Safe to access
}
const userInput = '<script>alert("xss")</script>'
const safe = sanitizeInput(userInput)
// "<script>alert("xss")</script>"
// With options
const cleaned = sanitizeInput(input, {
escapeHtml: true, // Default: true
removeNullBytes: true, // Default: true
trim: true // Default: true
})
@bfra.me/es/error)import {BaseError, createError, formatError, NotFoundError, PermissionError, TimeoutError, ValidationError, withErrorContext} from '@bfra.me/es/error'
// Create custom errors with code and context
const error = createError('User not found', {
code: 'USER_NOT_FOUND',
cause: originalError,
context: {userId: 123}
})
// Use specialized error types
throw new ValidationError('Invalid email format', {
field: 'email',
value: 'not-an-email'
})
throw new NotFoundError('Resource not found', {
resourceType: 'User',
resourceId: '123'
})
try {
riskyOperation()
} catch (error) {
// Format any error to string with cause chain
console.log(formatError(error))
}
@bfra.me/es/env)import {getEnvironment, isBrowser, isDeno, isInCI, isInEditorEnv, isInGitLifecycle, isNode} from '@bfra.me/es/env'
if (isInCI()) {
// Running in CI (GitHub Actions, Jenkins, etc.)
}
if (isInEditorEnv()) {
// Running in VS Code, JetBrains, Vim, or Neovim
}
if (isInGitLifecycle()) {
// Running during git hook (commit, push, etc.)
}
// Runtime detection
if (isNode()) { /* Node.js */ }
if (isBrowser()) { /* Browser */ }
if (isDeno()) { /* Deno */ }
// Get comprehensive environment info
const env = getEnvironment()
// { runtime: 'node', isCI: false, isEditor: true, ... }
@bfra.me/es/watcher)Note: Requires
chokidaras a peer dependency.
import {createChangeDetector, createDebouncer, createFileHasher, createFileWatcher} from '@bfra.me/es/watcher'
const watcher = createFileWatcher(['src/**/*.ts', 'test/**/*.ts'], {
debounceMs: 100,
ignored: ['**/node_modules/**'],
usePolling: false
})
watcher.on('change', event => {
console.log('Changes detected:', event.changes)
// event.changes: Array<{path: string, type: 'add' | 'change' | 'unlink', timestamp: number}>
})
await watcher.start()
// Later: clean up
await watcher.close()
const hasher = createFileHasher('sha256')
const detector = createChangeDetector()
// Hash file content
const hash = await hasher.hashFile('/path/to/file.ts')
// Detect changes
const {changed, added, removed} = await detector.detectChanges({
'src/index.ts': 'abc123...', // Previous hashes
})
This package requires TypeScript 5.0+ and works best with strict mode:
{
"compilerOptions": {
"strict": true,
"exactOptionalPropertyTypes": true,
"moduleResolution": "bundler"
}
}
@bfra.me/eslint-config - ESLint configuration using these utilities@bfra.me/tsconfig - TypeScript configurationMIT © Marcus R. Brown
FAQs
High-quality reusable types and utilities for ES development
We found that @bfra.me/es demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 2 open source maintainers collaborating on the project.

Product
Socket can now send alerts and supply chain attack notifications to Microsoft Teams, with filters that route the right updates to each channel.

Security News
pnpm 12 rewrites the package manager in Rust, cutting install times by up to 90% while preserving pnpm 11 workflows and lockfiles.

Security News
Socket CTO Ahmad Nassri joins AppSec leaders at Black Hat to discuss active malware, package manager risks, and software supply chain defense.