@cross-deck/node 0.1.0 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -4,6 +4,122 @@ All notable changes to `@cross-deck/node` will be documented here. The
4
4
  format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/)
5
5
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
6
 
7
+ ## [1.0.0] — 2026-05-13
8
+
9
+ Full three-USP server SDK release. Version-aligned with `@cross-deck/web@1.0.0`. Bank-grade quality bar — Stripe + Apple + Google VP-level QA review across two passes. 6,796 LOC of source / 6,230 LOC of tests / 398 unit tests + 19 e2e todos passing / Gate 3 fixture verifying the snippet against the built bundle. Web-SDK parity at the capability level: every Web SDK guarantee that has a server-side analogue ships here.
10
+
11
+ ### Added — USP 1 (errors)
12
+
13
+ - `server.captureError(err, options?)` — manual try/catch capture.
14
+ - `server.captureMessage(msg, level?)` — non-error signals (Sentry pattern).
15
+ - `server.setTag(key, value)` / `setTags(tags)` / `setContext(name, data)` / `addBreadcrumb(crumb)` / `setErrorBeforeSend(hook)`.
16
+ - Auto-wired `process.on('uncaughtException')` + `process.on('unhandledRejection')` + `globalThis.fetch` wrap (5xx + network failures).
17
+ - Stack-frame parsing (V8 + Firefox/Safari) with Node `in_app` heuristics for `node_modules/`, `node:`, `internal/`, `@cross-deck/node`.
18
+ - Breadcrumb ring buffer (default 50 entries) attached to every error report.
19
+ - djb2-fingerprinted grouping + per-fingerprint rate limit (default 5/min) + per-session cap (default 100). Fingerprint Map bounded at 4,096 with dead-entry prune + FIFO eviction.
20
+
21
+ ### Added — USP 2 (analytics)
22
+
23
+ - Durable event queue: exponential backoff with full jitter, `Retry-After` honoured, **`Idempotency-Key` reused on retry of the same batch** (Stripe pattern).
24
+ - `flush-on-exit` — `process.on('beforeExit')` + SIGTERM + SIGINT drain bounded by `flushOnExitTimeoutMs`. Critical for Lambda / Cloud Functions where the runtime freezes between invocations.
25
+ - `server.register(properties)` / `server.unregister(key)` / `server.group(type, id, traits?)` — Mixpanel-style super-properties + group analytics.
26
+ - `@cross-deck/node/auto-events` subpath:
27
+ - `crossdeckExpress(server, opts?)` + `crossdeckExpressErrorHandler(server, opts?)` (Express 4 + 5) — emits `request.handled` with route + method + statusCode + durationMs + userAgent + responseBytes. Captures uncaught route errors with request context.
28
+ - `wrapLambdaHandler(server, handler, opts?)` — emits `function.invoked` / `function.completed` / `function.failed` with cold-start detection, awaits `flush()` before return. Extracts `statusCode` + `responseBytes` for HTTP-style returns.
29
+ - `wrapFunction(server, handler, opts?)` — generic Firebase v1/v2 / Cloud Run wrap, shape-preserving.
30
+
31
+ ### Added — USP 3 (entitlements)
32
+
33
+ - Per-customer TTL cache (default 60s) with **LRU eviction bounded at `maxCustomers` (default 10,000)** for long-running multi-tenant servers.
34
+ - `server.isEntitled(hint, key)` — synchronous lookup after first warm. Accepts canonical `customerId` OR `IdentityHints` ({customerId, userId, anonymousId}).
35
+ - `server.listEntitlements(hint)` — full snapshot.
36
+ - `server.onEntitlementsChange(listener)` — subscribe to cache mutations.
37
+ - `userId` / `anonymousId` → `crossdeckCustomerId` alias map (bounded at 10,000 with FIFO eviction).
38
+ - `verifyWebhookSignature(payload, header, secret, options?)` — HMAC-SHA256 + constant-time compare + 5-min replay window + multi-secret rotation.
39
+ - `signWebhookPayload(payload, secret, timestampSec)` — pure helper for fixture authors.
40
+
41
+ ### Added — cross-cutting
42
+
43
+ - `runtime-info` detection for 13 platforms: AWS Lambda, Azure Functions, Google App Engine, Firebase Functions v1/v2, Cloud Run, Vercel, Netlify, Heroku, Render, Railway, Fly.io, generic Kubernetes, plain Node fallback. Auto-attached as `runtime.*` on every event + error.
44
+ - `server.heartbeat()` — boot validation: `GET /sdk/heartbeat` returns project + app metadata, throws on auth failure.
45
+ - `server.flush(): Promise<void>` — explicit drain.
46
+ - `server.diagnostics()` — stable shape with `runtime` + `events` + `errors` + `entitlements` blocks.
47
+ - `server.shutdown()` — teardown for tests + custom lifecycles. Clears super-properties, groups, cache, aliases, breadcrumbs, error state.
48
+ - `scrubPii(value)` + `scrubPiiFromProperties(obj)` — opt-in PII regex utilities (email + card-number shapes).
49
+ - `ConsoleDebugLogger` + `NullDebugLogger` — NorthStar §16 debug signal vocabulary.
50
+ - `CrossdeckErrorCode` literal union derived from `CROSSDECK_ERROR_CODES` + `isCrossdeckErrorCode()` type guard for type-safe code comparisons.
51
+ - `HeartbeatResponse` + `Diagnostics` + 30+ exported types.
52
+ - `/auto-events` subpath in `package.json` exports.
53
+
54
+ ### Changed
55
+
56
+ - **Breaking**: `track(event)` is now synchronous (returns `void`), enqueues for batched delivery, and auto-fills `anonymousId` with a process-stable `anon_node_…` when no identity hint is supplied. The old `await track(...)` shape is replaced by enqueue-and-flush.
57
+ - `ingest(events[])` retains immediate-POST behaviour for bulk-import callers (no auto-fill, returns `IngestResponse`).
58
+ - Secret key prefix in `diagnostics()` is now masked as `cd_sk_(test|live)_****<last4>` (Stripe pattern).
59
+
60
+ ### Added — QA review v2 (bank-grade SDK extras)
61
+
62
+ - **Error subclass hierarchy** (Stripe pattern):
63
+ `CrossdeckAuthenticationError`, `CrossdeckPermissionError`,
64
+ `CrossdeckValidationError`, `CrossdeckRateLimitError`,
65
+ `CrossdeckNetworkError`, `CrossdeckInternalError`,
66
+ `CrossdeckConfigurationError`. All extend `CrossdeckError`. Pick the
67
+ right subclass via `makeCrossdeckError(payload)`. Constructed
68
+ automatically by `crossdeckErrorFromResponse()`.
69
+ - `CrossdeckError.toJSON()` — structured-logger compatible
70
+ serialisation. Includes `type`, `code`, `requestId`, `status`,
71
+ `retryAfterMs`, `stack`. Critical for production observability with
72
+ Pino / Winston / DataDog.
73
+ - `Crossdeck-Api-Version` header on every request, pinned to
74
+ `CROSSDECK_API_VERSION` constant. Forward-compat with backend
75
+ evolution (Stripe `Stripe-Version` pattern).
76
+ - `User-Agent` header: `@cross-deck/node/<sdk> node/<node-version> <platform>`.
77
+ HTTP best practice. Override the runtime token via
78
+ `runtimeToken: "bun/1.0"` in options.
79
+ - **Idempotent retry on GET methods** — default 3 attempts with
80
+ exponential backoff + full jitter, retrying on 408 + 5xx (except
81
+ 501) and on network failures. Honours server `Retry-After`. POST
82
+ retries stay queue-driven (with batch-level `Idempotency-Key` reuse).
83
+ Configurable via `httpRetries: { maxAttempts, retryableStatuses }`.
84
+ - `testMode: true` option — every HTTP call short-circuits to a
85
+ synthetic success response, no network goes out. Path-aware (returns
86
+ the right shape per endpoint). For caller test suites that don't
87
+ want to mock `globalThis.fetch`.
88
+ - `onRequest` / `onResponse` hooks on `CrossdeckServerOptions`. Fire
89
+ on every request (including retries), carrying method, URL, status,
90
+ durationMs, attempt number. Synchronous, errors swallowed — telemetry
91
+ must never break the request pipeline.
92
+ - **AbortSignal pass-through** on every async method. Final
93
+ `RequestOptions` argument with `{ signal, timeoutMs }`. Caller-aborted
94
+ requests throw `CrossdeckNetworkError({ code: "request_aborted" })`.
95
+ Composes with the per-request timeout — whichever fires first wins.
96
+ - **CrossdeckServer extends EventEmitter** — typed `on` / `once` /
97
+ `off` / `emit` overloads via `CrossdeckServerEvents`. Events:
98
+ `queue.flush_succeeded`, `queue.flush_failed`, `queue.dropped`,
99
+ `queue.buffer_changed`, `error.captured`, `entitlements.warmed`,
100
+ `sdk.shutdown`.
101
+ - **`Symbol.dispose` + `Symbol.asyncDispose`** — TC39 explicit
102
+ resource management. `using server = new CrossdeckServer(...)`
103
+ shuts down on scope exit; `await using` flushes first.
104
+ - `server.isReady(): boolean` — synchronous readiness check.
105
+ `false` on sustained retry storm (≥ 5 consecutive failures) or
106
+ buffer pressure (≥ 80% of HARD_BUFFER_CAP).
107
+ - `server.awaitReady(timeoutMs?, pollIntervalMs?): Promise<boolean>` —
108
+ backpressure-aware wait for ready state.
109
+ - `server.getHealth()` — k8s-friendly snapshot: `ready`, `healthy`,
110
+ `bufferedEvents`, `inFlight`, `consecutiveFailures`, `lastFlushAt`,
111
+ `lastError`, `errorHandlersInstalled`.
112
+ - `server.bulkGrantEntitlement(grants[])` + `bulkRevokeEntitlement(revokes[])` —
113
+ bounded-concurrency fan-out (default 5). Returns settled array;
114
+ partial failures preserved as `{ ok: false, error }` entries.
115
+
116
+ ### Notes
117
+
118
+ - Bundle size: `dist/index.cjs` ~98 KB, `dist/auto-events/index.cjs` ~11 KB.
119
+ - Zero runtime dependencies (`fetch` + `node:crypto` + `node:events` only).
120
+ - 398 unit tests + 19 e2e todos passing. Source-to-test ratio ~100%.
121
+ - Deferred to later releases: Fastify adapter (v0.3.0), Cloudflare Workers / Vercel Edge / Bun / Deno (v0.4+), OpenTelemetry / Pino / Winston log-capture integration (roadmap), HTTP keep-alive agent, request compression, SDK-level sampling.
122
+
7
123
  ## [0.1.0] — 2026-05-12
8
124
 
9
125
  Initial server SDK release.