billdogeng-node
v1.0.0-beta.1
Published
Official BilldogEng server SDK for Node.js — Analytics, Feature Flags (remote + local eval), Surveys, Messaging, and LLM observability.
Maintainers
Readme
billdogeng-node
Official BilldogEng server SDK for Node.js / TypeScript — the engagement suite for server-side use: Analytics, Feature Flags (remote + local evaluation), Surveys (data API), Messaging dispatch, and LLM observability.
This is the canonical reference implementation that the other
billdogeng-{python,go,php,ruby,java,rust,dotnet,elixir} SDKs mirror.
Install
npm install billdogeng-nodeRequires Node.js ≥ 18 (uses the built-in global fetch).
Quickstart
import { BilldogEng } from 'billdogeng-node';
const bd = new BilldogEng('bd_test_xxx', {
// host: 'https://api.billdog.io/v1', // default
flushAt: 20, // batch size that triggers a flush
flushInterval: 10_000, // ms background flush cadence
localEvaluation: true, // server-only: evaluate flags locally
});
// ─ Analytics ─ (batched + flushed automatically)
bd.capture('user-123', 'order_completed', { revenue: 49.99 }, { company: 'acme' });
bd.identify('user-123', { email: '[email protected]', plan: 'pro' });
bd.groupIdentify('company', 'acme', { seats: 50 });
bd.alias('user-123', 'anon-abc');
// ─ Feature flags ─
const on = await bd.isFeatureEnabled('new_checkout', 'user-123');
const variant = await bd.getFeatureFlag('paywall_test', 'user-123'); // boolean | variant key | null
const payload = await bd.getFeatureFlagPayload('paywall_test', 'user-123'); // variant config
const all = await bd.getAllFlags('user-123');
// ─ Surveys (data API) ─
const surveys = await bd.surveys.list('user-123');
const config = await bd.surveys.fetch(surveys[0].id, 'user-123');
const { respondent_id } = await bd.surveys.start(config.id, { customerId: 'user-123', idempotencyKey: 'k1' });
await bd.surveys.submit(config.id, [{ question_id: 'q1', answer_number: 9 }], { respondentId: respondent_id });
// ─ Messaging dispatch ─ (Bearer JWT auth, not the API key)
await bd.messaging.dispatch({
projectId: 'project-uuid',
channel: 'push',
content: { title: 'Hi', body: 'There' },
targeting: { type: 'all' },
scheduling: { deliveryType: 'immediate' },
accessToken: '<supabase-session-jwt>',
});
// ─ LLM observability ─
await bd.captureTrace({
traceId: 't-1', spanId: 's-1', model: 'gpt-4o',
inputText: 'prompt', outputText: 'completion',
promptTokens: 100, completionTokens: 50, durationMs: 820, costUsd: 0.003,
});
// Flush remaining events + stop the background timer before exit.
await bd.shutdown();Configuration
| Option | Default | Description |
| ----------------- | ------------------------------ | ----------- |
| host | https://api.billdog.io/v1 | API base URL |
| flushAt | 20 | Batch size that triggers a flush |
| flushInterval | 10000 | Background flush cadence (ms) |
| maxQueueSize | 1000 | Drop oldest events past this many queued |
| gzip | true | Gzip large request bodies |
| localEvaluation | false | Evaluate feature flags locally (server-only) |
| requestTimeout | 10000 | Per-request timeout (ms) |
| maxRetries | 3 | Retry attempts for 5xx / 429 / network errors |
| groupTypeIndex | — | Stable group-type → $group_0..4 index map |
| enableLogging | false | Verbose diagnostics to console |
Authentication uses the x-api-key: <apiKey> header (bd_test_* sandbox /
bd_live_* live) on every request, except messaging dispatch, which
authenticates with a Supabase session Bearer JWT + project membership.
Analytics batching & delivery
- Events accumulate in an in-memory queue and ship as a single batched POST
to
/ingest-events. - A flush happens on
flushAt, everyflushInterval, or onflush()/shutdown(). - Failed flushes re-queue their batch (never lose events on a transient failure); the transport also retries with exponential backoff (1s, 2s, 4s).
- Bodies are gzip-compressed when large enough to benefit.
Feature flags — local evaluation
With localEvaluation: true, the SDK fetches flag definitions once
(POST /feature-flag-definitions), caches them for 5 minutes, and evaluates
deterministically on-process:
- missing/inactive →
false - ALL
targeting_rulesmust matchpersonProperties, elsefalse bucket = murmurhash3("{key}.{distinctId}") % 100; ON iffbucket < rollout_percentage- multivariate: walk
variantsby cumulative rollout within the ON bucket → variant key
The murmurhash3 (32-bit, seed 0) implementation is byte-identical across web / iOS / Android / all server SDKs, so a user buckets the same everywhere.
Build & test
npm install
npm run build # tsc → dist/
npm test # vitestLicense
MIT
