@tapsioss/ripple-node
v2.1.0
Published
<div align="center">
Readme
Ripple | TypeScript | Node.js
A high-performance, scalable, and fault-tolerant event tracking TypeScript SDK for Node.js.
Features
- 🚀 High Performance: Efficient buffer management with O(1) operations
- 📦 Automatic Batching: Configurable batch size, payload size, and flush intervals
- 🔄 Dynamic Rebatching: Optimizes throughput with stop-on-failure ordering guarantees
- 🔄 Retry Logic: Configurable exponential backoff with jitter
- 🔒 Concurrency Safe: Thread-safe flush operations with mutex protection
- 🔌 Pluggable Adapters: Custom HTTP, storage, and logger implementations
- 📘 Type-Safe: Full TypeScript support with predefined CDP events + custom events
- 🎯 Zero Dependencies: No external dependencies
- 🌐 Native Fetch: Uses Node.js 18+ native fetch API
Installation
npm install @tapsioss/ripple-nodeRequirements
- Node.js 18.0.0 or higher (for native fetch API support)
Quick Start
import { RippleClient, NoOpStorageAdapter } from "@tapsioss/ripple-node";
// Define custom events (predefined CDP events always available)
type CustomEvents = {
"api.request": { endpoint: string; method: string; duration: number };
};
type AppMetadata = {
serverId: string;
environment: "prod" | "staging" | "dev";
};
const client = new RippleClient<CustomEvents, AppMetadata>({
apiKey: "your-api-key",
endpoint: "https://api.example.com/events",
storageAdapter: new NoOpStorageAdapter(),
});
await client.init();
client.setMetadata("serverId", "srv-1");
client.setMetadata("environment", "prod");
// Track predefined events via events namespace (schema version auto-managed)
await client.events.orderCompleted({
order: {
orderId: "order-789",
products: [{ productId: "p-1", price: { amount: 49.99, currency: "USD" } }],
revenue: { amount: 49.99, currency: "USD" },
total: { amount: 54.99, currency: "USD" },
},
});
// Track custom events with schema version
await client.track(
"api.request",
{
endpoint: "/api/users",
method: "GET",
duration: 150,
},
"1.0.0",
);
// Identity
await client.identify("user-123", { email: "[email protected]" });
await client.flush();
client.dispose();Configuration
const client = new RippleClient({
// Required
apiKey: "your-api-key",
endpoint: "https://api.example.com/events",
storageAdapter: new NoOpStorageAdapter(),
// Batching (all optional)
batchOptions: {
interval: 10000, // Auto-flush interval in ms (default: 10000)
size: 10, // Max events per batch (default: 10)
maxPayloadSize: 65536, // Max batch payload in bytes (default: 64KB)
},
// Retry (all optional)
retryOptions: {
maxAttempts: 3, // Max retry attempts (default: 3)
minDelay: 1000, // Base delay in ms (default: 1000)
maxDelay: 360000, // Max delay cap in ms (default: 360000)
backoffFactor: 2, // Exponential multiplier (default: 2)
},
// Other options
apiKeyHeader: "X-API-Key", // Header name (default: "X-API-Key")
maxBufferSize: 1000, // Max in-memory events (default: 50)
eventTtl: 86400000, // Drop events older than 24h at flush (default: disabled)
loggerAdapter: new ConsoleLogger(LogLevel.INFO),
eventSampler: event => true, // Keep all events
});API Reference
new RippleClient<TCustomEvents, TMetadata>(config)
Creates a new client. TCustomEvents defines your custom events.
init(): Promise<void>
Initializes the client and restores persisted events.
track(name, payload?, schemaVersion?): Promise<void>
Tracks an event. Accepts custom event names with full type safety.
identify(userId, traits): Promise<void>
Identifies a user. Sends a user_identified event.
clicked(payload): Promise<void>
Tracks a clicked event.
viewed(payload): Promise<void>
Tracks a viewed event.
screen(payload): Promise<void>
Tracks a screened page view event. Requires ScreenPayload.
setMetadata(key, value): void
Sets global metadata attached to all subsequent events.
getMetadata(): Partial<TMetadata>
Returns current metadata.
getAnonymousId(): string
Returns the anonymous ID (auto-generated UUID per instance).
getUserId(): string | null
Returns the authenticated user ID if set via identify().
flush(): Promise<void>
Immediately flushes all queued events.
dispose(): void
Cleans up resources and cancels timers.
Custom HTTP Adapter
import type {
HttpAdapter,
HttpAdapterContext,
HttpResponse,
} from "@tapsioss/ripple-node";
class GrpcHttpAdapter implements HttpAdapter {
async send(context: HttpAdapterContext): Promise<HttpResponse> {
const response = await grpcClient.sendEvents({
events: context.events,
metadata: context.headers,
});
return { status: response.code };
}
}Custom Storage Adapter
import type { StorageAdapter, Event } from "@tapsioss/ripple-node";
class RedisStorageAdapter implements StorageAdapter {
async init(): Promise<void> {
// initialization
}
async save(events: Event[]): Promise<void> {
await redis.set("ripple:events", JSON.stringify(events));
}
async load(): Promise<Event[]> {
const data = await redis.get("ripple:events");
return data ? JSON.parse(data) : [];
}
async clear(): Promise<void> {
await redis.del("ripple:events");
}
async close(): Promise<void> {
// cleanup if needed
}
}Graceful Shutdown
process.on("SIGTERM", async () => {
await client.flush();
client.dispose();
process.exit(0);
});Error Handling
- 2xx: Events cleared from storage
- 4xx: Events dropped (client errors won't self-resolve)
- 5xx / Network errors: Retried with exponential backoff, then requeued with ordering preserved
Migration from v1
See the Migration Guide for detailed instructions on upgrading from v1 to v2.
