@hoist/sdk (0.1.0)
Installation
@hoist:registry=npm install @hoist/sdk@0.1.0"@hoist/sdk": "0.1.0"About this package
Hoist TypeScript SDK
TypeScript SDK for Hoist feature flags with real-time updates.
Features
- ✅ TypeScript-first with full type safety
- ✅ Real-time streaming updates via WebSocket
- ✅ Local evaluation for server-side SDKs
- ✅ Remote evaluation for client-side SDKs
- ✅ Comprehensive targeting rules and segments
- ✅ Percentage rollouts and A/B testing
- ✅ Hooks for lifecycle events
- ✅ Zero dependencies (streaming requires WebSocket)
- ✅ Works in Node.js and modern browsers
Installation
npm config set @hoist:registry https://git.macco.dev/api/packages/macco/npm/
npm install @hoist/sdk
Quick Start
Server-Side Usage (Node.js)
import { HoistClient } from '@hoist/sdk'
const client = new HoistClient({
apiKey: 'hoist_live_...', // Server API key
streaming: true, // Enable real-time updates
})
// Wait for initial config load
await client.waitForInitialization()
// Evaluate flags
const enabled = await client.booleanValue('new-feature', false, {
targetingKey: 'user-123',
attributes: {
plan: 'pro',
region: 'us-east',
},
})
if (enabled) {
// Feature is enabled
}
// Cleanup when done
client.close()
Client-Side Usage (Browser)
import { HoistClient } from '@hoist/sdk'
const client = new HoistClient({
apiKey: 'client-key', // Client API key (no prefix)
streaming: false, // Client keys use remote evaluation
})
const variant = await client.stringValue('banner-text', 'default', {
targetingKey: 'session-abc',
attributes: {
language: 'en',
},
})
API Reference
HoistClient
Constructor
new HoistClient(config: HoistConfig)
Config Options:
apiKey(required): Your Hoist API keybaseURL(optional): API base URL (default:https://hoist.macco.dev/api)streaming(optional): Enable WebSocket streaming (default:truefor server keys,falsefor client keys)timeout(optional): HTTP request timeout in milliseconds (default:5000)hooks(optional): Array of lifecycle hookslogger(optional): Custom logger implementation
Methods
waitForInitialization(): Promise<void>
Wait for the client to finish initializing (loading config or connecting to stream).
await client.waitForInitialization()
booleanValue(key, defaultValue, context?): Promise<boolean>
Evaluate a boolean flag.
const enabled = await client.booleanValue('feature-enabled', false, {
targetingKey: 'user-123',
})
stringValue(key, defaultValue, context?): Promise<string>
Evaluate a string flag.
const variant = await client.stringValue('button-color', 'blue', {
targetingKey: 'user-123',
})
numberValue(key, defaultValue, context?): Promise<number>
Evaluate a number flag.
const limit = await client.numberValue('rate-limit', 100, {
targetingKey: 'user-123',
})
jsonValue<T>(key, defaultValue, context?): Promise<T>
Evaluate a JSON flag.
interface Config {
theme: string
features: string[]
}
const config = await client.jsonValue<Config>('ui-config', {
theme: 'light',
features: [],
})
evaluate<T>(key, defaultValue, context?): Promise<EvaluationResult<T>>
Evaluate a flag with full details.
const result = await client.evaluate('feature', false, {
targetingKey: 'user-123',
})
console.log({
value: result.value,
reason: result.reason, // Why this value was returned
variantKey: result.variantKey, // Matched variant
ruleID: result.ruleID, // Matched rule
version: result.version, // Flag version
})
close(): void
Close the client and cleanup resources (disconnect WebSocket, etc.).
client.close()
Evaluation Context
The evaluation context provides information for targeting rules:
interface EvaluationContext {
targetingKey?: string // Unique identifier (user ID, session ID, etc.)
attributes?: Record<string, unknown> // Additional attributes for targeting
}
Example:
const context = {
targetingKey: 'user-123',
attributes: {
email: 'user@example.com',
plan: 'pro',
region: 'us-east',
age: 25,
beta: true,
},
}
Hooks
Hooks allow you to observe flag evaluation lifecycle:
const logger = {
before: (key, context) => {
console.log(`Evaluating ${key}`, context)
},
after: (key, result) => {
console.log(`Result for ${key}:`, result.value, result.reason)
},
error: (key, error) => {
console.error(`Error evaluating ${key}:`, error)
},
}
const client = new HoistClient({
apiKey: 'hoist_live_...',
hooks: [logger],
})
Evaluation Reasons
The reason field in EvaluationResult explains why a value was returned:
DEFAULT- No rules matched, using default valueTARGETING_MATCH- A targeting rule matchedSEGMENT_MATCH- A segment rule matchedDISABLED- Flag is disabledROLLOUT- User not in rollout percentageNOT_FOUND- Flag doesn't existERROR- Evaluation error occurredIDENTITY_OVERRIDE- Identity-specific override matched
Advanced Usage
Custom Logger
import type { Logger } from '@hoist/sdk'
const customLogger: Logger = {
debug: (msg, ...args) => console.debug(msg, ...args),
info: (msg, ...args) => console.info(msg, ...args),
warn: (msg, ...args) => console.warn(msg, ...args),
error: (msg, ...args) => console.error(msg, ...args),
}
const client = new HoistClient({
apiKey: 'hoist_live_...',
logger: customLogger,
})
Error Handling
The SDK is designed to fail gracefully. If evaluation fails, it returns the default value:
const result = await client.evaluate('my-flag', false)
if (result.error) {
console.error('Evaluation failed:', result.error)
console.log('Using default value:', result.value)
}
Real-time Updates
Server keys with streaming enabled receive real-time updates when flags change:
const client = new HoistClient({
apiKey: 'hoist_live_...',
streaming: true, // Default for server keys
})
await client.waitForInitialization()
// Flags are automatically updated when changes occur
// No action needed - subsequent evaluations use latest config
TypeScript Support
This SDK is written in TypeScript and provides full type definitions:
import { HoistClient, EvaluationContext, EvaluationResult, Reason } from '@hoist/sdk'
// Type-safe flag evaluation
interface FeatureConfig {
enabled: boolean
options: string[]
}
const config = await client.jsonValue<FeatureConfig>('feature-config', {
enabled: false,
options: [],
})
// config is typed as FeatureConfig
Package Exports
This package supports both CommonJS and ES Modules:
// CommonJS
const { HoistClient } = require('@hoist/sdk')
// ES Modules
import { HoistClient } from '@hoist/sdk'
Development
# Install dependencies
pnpm install
# Run type checking
pnpm typecheck
# Run tests
pnpm test
# Run tests in watch mode
pnpm test:watch
# Build the package
pnpm build
# Build in watch mode
pnpm dev
License
MIT
Dependencies
Development dependencies
| ID | Version |
|---|---|
| @types/node | ^22.10.5 |
| @typescript-eslint/eslint-plugin | ^8.20.0 |
| @typescript-eslint/parser | ^8.20.0 |
| eslint | ^9.18.0 |
| tsup | ^8.3.5 |
| typescript | ^5.7.2 |
| vitest | ^2.1.8 |