@cross-deck/node 1.5.2 → 1.6.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 +31 -0
- package/dist/auto-events/index.d.mts +1 -1
- package/dist/auto-events/index.d.ts +1 -1
- package/dist/contracts.json +557 -0
- package/dist/{crossdeck-server-DhnHvUhh.d.mts → crossdeck-server-C1Ue0rv4.d.mts} +219 -12
- package/dist/{crossdeck-server-DhnHvUhh.d.ts → crossdeck-server-C1Ue0rv4.d.ts} +219 -12
- package/dist/index.cjs +1076 -75
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.mts +150 -21
- package/dist/index.d.ts +150 -21
- package/dist/index.mjs +1061 -71
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
|
@@ -111,6 +111,110 @@ declare class CrossdeckConfigurationError extends CrossdeckError {
|
|
|
111
111
|
*/
|
|
112
112
|
declare function makeCrossdeckError(payload: CrossdeckErrorPayload): CrossdeckError;
|
|
113
113
|
|
|
114
|
+
/**
|
|
115
|
+
* Public, typed accessor for the bank-grade behavioural contracts
|
|
116
|
+
* this SDK ships. The full architecture — schema, distribution,
|
|
117
|
+
* audit loop, pillar taxonomy — lives in `contracts/README.md`
|
|
118
|
+
* at the monorepo root.
|
|
119
|
+
*
|
|
120
|
+
* Why a typed surface (vs. plain JSON access): contract IDs and
|
|
121
|
+
* pillar names are part of Crossdeck's public commitment to
|
|
122
|
+
* customers. Reading them through `CrossdeckContracts` means the
|
|
123
|
+
* compiler catches drift the moment a contract is renamed or
|
|
124
|
+
* retired. Tools that consume contracts at runtime (dashboards,
|
|
125
|
+
* AI assistants, customer integration tests) get the exact same
|
|
126
|
+
* shape every SDK ships, with no parsing layer to drift.
|
|
127
|
+
*
|
|
128
|
+
* --- BINARY STABILITY ---
|
|
129
|
+
* `Contract` is treated as an evolving — but back-compat — wire
|
|
130
|
+
* shape. Fields may be added in any minor release. Existing
|
|
131
|
+
* fields will not be removed or repurposed except in a major
|
|
132
|
+
* version bump, even if all known contracts stop using them.
|
|
133
|
+
* Customers can rely on `id`, `pillar`, `status`, `appliesTo`,
|
|
134
|
+
* `codeRef`, `testRef`, `registeredAt`, `firstRegisteredIn`,
|
|
135
|
+
* and `bundledIn` being present on every contract in every
|
|
136
|
+
* future minor/patch release of this SDK.
|
|
137
|
+
*/
|
|
138
|
+
type ContractPillar = "revenue" | "entitlements" | "analytics" | "webhooks" | "errors" | "lifecycle" | "identity";
|
|
139
|
+
type ContractStatus = "enforced" | "proposed" | "retired";
|
|
140
|
+
type ContractAppliesTo = "web" | "node" | "react-native" | "swift" | "android" | "backend";
|
|
141
|
+
interface ContractTestRef {
|
|
142
|
+
readonly file: string;
|
|
143
|
+
readonly name: string;
|
|
144
|
+
}
|
|
145
|
+
interface Contract {
|
|
146
|
+
readonly id: string;
|
|
147
|
+
readonly pillar: ContractPillar;
|
|
148
|
+
readonly status: ContractStatus;
|
|
149
|
+
readonly claim: string;
|
|
150
|
+
readonly appliesTo: readonly ContractAppliesTo[];
|
|
151
|
+
readonly codeRef: readonly string[];
|
|
152
|
+
readonly testRef: readonly ContractTestRef[];
|
|
153
|
+
readonly registeredAt: string;
|
|
154
|
+
readonly firstRegisteredIn: string;
|
|
155
|
+
readonly bundledIn: string;
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Typed entry point to the bank-grade contracts bundled with this
|
|
159
|
+
* SDK release. Stable, side-effect-free, tree-shakeable.
|
|
160
|
+
*
|
|
161
|
+
* @example Audit at app boot
|
|
162
|
+
* ```ts
|
|
163
|
+
* import { CrossdeckContracts } from "@cross-deck/node";
|
|
164
|
+
*
|
|
165
|
+
* for (const c of CrossdeckContracts.all()) {
|
|
166
|
+
* console.log(`[crossdeck] ${c.id} (${c.pillar})`);
|
|
167
|
+
* }
|
|
168
|
+
* ```
|
|
169
|
+
*/
|
|
170
|
+
declare const CrossdeckContracts: {
|
|
171
|
+
readonly all: () => readonly Contract[];
|
|
172
|
+
readonly allIncludingHistorical: () => readonly Contract[];
|
|
173
|
+
readonly byId: (id: string) => Contract | undefined;
|
|
174
|
+
readonly byPillar: (pillar: ContractPillar) => readonly Contract[];
|
|
175
|
+
readonly withStatus: (status: ContractStatus) => readonly Contract[];
|
|
176
|
+
readonly sdkVersion: "1.6.0";
|
|
177
|
+
readonly bundledIn: "@cross-deck/node@1.6.0";
|
|
178
|
+
/**
|
|
179
|
+
* Resolve a failing test back to the contract it exercises.
|
|
180
|
+
* Used by test-framework hooks to find the contract id of a
|
|
181
|
+
* failed contract test so `reportContractFailure(...)` can stamp
|
|
182
|
+
* the right `contract_id` on the emitted event.
|
|
183
|
+
*/
|
|
184
|
+
readonly findByTestName: (name: string) => Contract | undefined;
|
|
185
|
+
};
|
|
186
|
+
/**
|
|
187
|
+
* Input to {@link CrossdeckServer.reportContractFailure}. Mirrors
|
|
188
|
+
* the per-SDK shape exactly.
|
|
189
|
+
*
|
|
190
|
+
* SCHEMA-LOCK: this interface's field set is exhaustively named. No
|
|
191
|
+
* free-form `extra: Record<string, unknown>` — the schema-lock
|
|
192
|
+
* contract at
|
|
193
|
+
* `contracts/diagnostics/contract-failed-payload-schema-lock.json`
|
|
194
|
+
* forbids unbounded fields. Adding a field requires a PR that
|
|
195
|
+
* amends the contract first, then the public interface.
|
|
196
|
+
*/
|
|
197
|
+
interface ContractFailureInput {
|
|
198
|
+
contractId: string;
|
|
199
|
+
/**
|
|
200
|
+
* Short categorical-ish label — the SDK convention is to keep
|
|
201
|
+
* this under 128 chars and stable across runs (so dashboards can
|
|
202
|
+
* group). Never an end-user-supplied string.
|
|
203
|
+
*/
|
|
204
|
+
failureReason: string;
|
|
205
|
+
runContext: "ci" | "dogfood" | "customer-app";
|
|
206
|
+
runId: string;
|
|
207
|
+
testRef?: {
|
|
208
|
+
file: string;
|
|
209
|
+
name: string;
|
|
210
|
+
};
|
|
211
|
+
/**
|
|
212
|
+
* Optional coarse device class, e.g. "linux-server", "container",
|
|
213
|
+
* "lambda". A categorical bucket, not a host identifier.
|
|
214
|
+
*/
|
|
215
|
+
deviceClass?: string;
|
|
216
|
+
}
|
|
217
|
+
|
|
114
218
|
/**
|
|
115
219
|
* Breadcrumb ring buffer — context attached to every error report.
|
|
116
220
|
*
|
|
@@ -409,6 +513,10 @@ interface PurchaseResult {
|
|
|
409
513
|
crossdeckCustomerId: string;
|
|
410
514
|
env: Environment;
|
|
411
515
|
entitlements: PublicEntitlement[];
|
|
516
|
+
/** True when the response came from the backend's idempotency
|
|
517
|
+
* cache instead of fresh processing. Backend also returns
|
|
518
|
+
* `Idempotent-Replayed: true` as a response header (v1.4.0). */
|
|
519
|
+
idempotent_replay?: boolean;
|
|
412
520
|
}
|
|
413
521
|
/**
|
|
414
522
|
* Response shape from `GET /v1/sdk/heartbeat`. Used by
|
|
@@ -462,6 +570,25 @@ interface CrossdeckServerOptions {
|
|
|
462
570
|
* not the source of truth.
|
|
463
571
|
*/
|
|
464
572
|
appId?: string;
|
|
573
|
+
/**
|
|
574
|
+
* Apply Crossdeck's PII scrubber to every `track()` payload before
|
|
575
|
+
* enqueue. Default `true` (parity with Web / RN / Swift SDKs — Node
|
|
576
|
+
* pre-v1.4.0 was the odd one out and SHIPPED EMAILS UNREDACTED, a
|
|
577
|
+
* privacy contract drift versus the README claim).
|
|
578
|
+
*
|
|
579
|
+
* The scrubber rewrites email-shaped and card-number-shaped
|
|
580
|
+
* substrings to `<email>` / `<card>` sentinels recursively across
|
|
581
|
+
* nested maps + arrays. See `scrubPii` / `scrubPiiFromProperties`.
|
|
582
|
+
*
|
|
583
|
+
* **Blast radius of setting `false`:** every `track()` payload —
|
|
584
|
+
* including event names with embedded emails ("user wes@example.com
|
|
585
|
+
* upgraded"), trait values, group memberships, error context blobs
|
|
586
|
+
* — ships verbatim to Crossdeck and downstream warehouses /
|
|
587
|
+
* analytics exports. Disable only for explicit compliance use
|
|
588
|
+
* cases (regulator-required audit trails where the raw value MUST
|
|
589
|
+
* be preserved) and document the decision at the call site.
|
|
590
|
+
*/
|
|
591
|
+
scrubPii?: boolean;
|
|
465
592
|
/**
|
|
466
593
|
* Error capture configuration. Default: ON with `onUncaughtException` +
|
|
467
594
|
* `onUnhandledRejection` + `wrapFetch` all enabled.
|
|
@@ -672,6 +799,14 @@ interface RequestOptions {
|
|
|
672
799
|
* `timeoutMs`. Pass `0` to disable.
|
|
673
800
|
*/
|
|
674
801
|
timeoutMs?: number;
|
|
802
|
+
/**
|
|
803
|
+
* Override the deterministic Idempotency-Key derivation (v1.4.0).
|
|
804
|
+
* The SDK derives a stable key from the request body so retries
|
|
805
|
+
* collapse on the backend. Override only when an outer
|
|
806
|
+
* orchestrator (job runner, retry harness) needs a different
|
|
807
|
+
* idempotency window — and document why at the call site.
|
|
808
|
+
*/
|
|
809
|
+
idempotencyKey?: string;
|
|
675
810
|
}
|
|
676
811
|
interface IdentityHints {
|
|
677
812
|
customerId?: string;
|
|
@@ -1162,6 +1297,10 @@ declare class CrossdeckServer extends EventEmitter {
|
|
|
1162
1297
|
private readonly baseUrl;
|
|
1163
1298
|
private readonly appId;
|
|
1164
1299
|
private readonly env;
|
|
1300
|
+
/** PII scrubber toggle. Default true — parity with Web/RN/Swift.
|
|
1301
|
+
* Pre-v1.4.0 the Node SDK shipped track() payloads UNREDACTED,
|
|
1302
|
+
* a privacy contract drift versus the README. */
|
|
1303
|
+
private readonly scrubPii;
|
|
1165
1304
|
private readonly secretKeyPrefix;
|
|
1166
1305
|
/**
|
|
1167
1306
|
* Process-stable pseudo-anonymous ID. Used as the default identity
|
|
@@ -1173,6 +1312,8 @@ declare class CrossdeckServer extends EventEmitter {
|
|
|
1173
1312
|
private readonly processAnonymousId;
|
|
1174
1313
|
private readonly runtime;
|
|
1175
1314
|
private readonly runtimeProperties;
|
|
1315
|
+
/** Envelope v1 §4 context object — built once at SDK init, reused on every event. */
|
|
1316
|
+
private readonly eventContext;
|
|
1176
1317
|
private readonly breadcrumbs;
|
|
1177
1318
|
private readonly eventQueue;
|
|
1178
1319
|
private readonly errorTracker;
|
|
@@ -1190,6 +1331,23 @@ declare class CrossdeckServer extends EventEmitter {
|
|
|
1190
1331
|
*/
|
|
1191
1332
|
private readonly entitlementStore;
|
|
1192
1333
|
private readonly debug;
|
|
1334
|
+
/**
|
|
1335
|
+
* Event Envelope v1 §3 — per-session monotonic sequence counter.
|
|
1336
|
+
*
|
|
1337
|
+
* Node has no mobile "session" lifecycle (no app launch / background /
|
|
1338
|
+
* foreground). We model a session as the SDK instance lifetime: the
|
|
1339
|
+
* counter starts at 0 on construction and increments once per
|
|
1340
|
+
* `track()` call. `session.started` is the boot-telemetry event
|
|
1341
|
+
* (the first `track()` called from `emitBootTelemetryEvent()`), so
|
|
1342
|
+
* the seq will be 0 for that event, matching the spec's "reset to 0
|
|
1343
|
+
* at session.started" clause — by construction, it's the zeroth event
|
|
1344
|
+
* of this instance's lifecycle.
|
|
1345
|
+
*
|
|
1346
|
+
* The counter persists for the entire process lifetime of the SDK
|
|
1347
|
+
* instance (spec §3 clause 1: background/foreground does not reset).
|
|
1348
|
+
* A new `CrossdeckServer` construction is the only reset (new session).
|
|
1349
|
+
*/
|
|
1350
|
+
private sessionSeq;
|
|
1193
1351
|
/**
|
|
1194
1352
|
* Alias map — `developerUserId` / `anonymousId` → canonical
|
|
1195
1353
|
* `crossdeckCustomerId`. Populated by `getEntitlements()` so a
|
|
@@ -1207,6 +1365,15 @@ declare class CrossdeckServer extends EventEmitter {
|
|
|
1207
1365
|
private errorContext;
|
|
1208
1366
|
private errorTags;
|
|
1209
1367
|
private errorBeforeSend;
|
|
1368
|
+
/**
|
|
1369
|
+
* Dedup gate for `sdk.shutdown`. Both `shutdown()` (async) and
|
|
1370
|
+
* `shutdownSync()` need to emit so direct callers of EITHER see
|
|
1371
|
+
* the event (the async path's listener guarantees pre-launch
|
|
1372
|
+
* tests, the sync path covers `Symbol.dispose` + tests that call
|
|
1373
|
+
* `shutdownSync()` directly). Without this flag, `shutdown()`'s
|
|
1374
|
+
* tail call into `shutdownSync()` would emit twice.
|
|
1375
|
+
*/
|
|
1376
|
+
private didEmitShutdown;
|
|
1210
1377
|
constructor(options: CrossdeckServerOptions);
|
|
1211
1378
|
/**
|
|
1212
1379
|
* Emit the honest "no cold-start durability" warning when the runtime
|
|
@@ -1331,6 +1498,17 @@ declare class CrossdeckServer extends EventEmitter {
|
|
|
1331
1498
|
* `uncaughtException` has no per-request context; without the
|
|
1332
1499
|
* auto-fill, the event would be rejected at queue enqueue.
|
|
1333
1500
|
*/
|
|
1501
|
+
/**
|
|
1502
|
+
* Emit `crossdeck.contract_failed` to the Crossdeck reliability
|
|
1503
|
+
* endpoint — single-fire, one-way, never visible in the customer's
|
|
1504
|
+
* dashboard. Goes over a dedicated HTTP path with the reliability
|
|
1505
|
+
* publishable key embedded at build time; the customer's track()
|
|
1506
|
+
* pipeline never carries `crossdeck.*` events. This is the
|
|
1507
|
+
* independent-controller flow described in Privacy Policy §6
|
|
1508
|
+
* ("Flow B"). The wire shape is fixed by the schema-lock contract
|
|
1509
|
+
* at `contracts/diagnostics/contract-failed-payload-schema-lock.json`.
|
|
1510
|
+
*/
|
|
1511
|
+
reportContractFailure(input: ContractFailureInput): void;
|
|
1334
1512
|
track(event: ServerEvent): void;
|
|
1335
1513
|
/**
|
|
1336
1514
|
* Immediate POST of one or more events. For bulk imports / replay
|
|
@@ -1541,11 +1719,36 @@ declare class CrossdeckServer extends EventEmitter {
|
|
|
1541
1719
|
getGroups(): Record<string, GroupMembership>;
|
|
1542
1720
|
diagnostics(): Diagnostics;
|
|
1543
1721
|
/**
|
|
1544
|
-
* Tear down handlers and clear in-memory state.
|
|
1545
|
-
*
|
|
1546
|
-
* `flush
|
|
1722
|
+
* Tear down handlers and clear in-memory state.
|
|
1723
|
+
*
|
|
1724
|
+
* **v1.4.0 bank-grade contract:** `shutdown()` AWAITS `flush()`
|
|
1725
|
+
* before dropping the queue, so callers don't silently lose
|
|
1726
|
+
* every queued event on a clean shutdown. The pre-v1.4.0
|
|
1727
|
+
* behaviour (sync `eventQueue.reset()` with no flush) was the
|
|
1728
|
+
* default for both `shutdown()` and `[Symbol.dispose]`; only
|
|
1729
|
+
* `await using` + `[Symbol.asyncDispose]` flushed correctly.
|
|
1730
|
+
*
|
|
1731
|
+
* Production servers should still prefer `await server.flush()`
|
|
1732
|
+
* (visible) followed by `server.shutdown()` so the flush
|
|
1733
|
+
* outcome is observable — `shutdown()`'s internal flush swallows
|
|
1734
|
+
* errors as a best-effort drain.
|
|
1735
|
+
*
|
|
1736
|
+
* Use [[shutdownSync]] only when the runtime cannot await
|
|
1737
|
+
* (e.g. inside `Symbol.dispose` — see below).
|
|
1738
|
+
*/
|
|
1739
|
+
shutdown(reason?: "shutdown" | "dispose" | "asyncDispose"): Promise<void>;
|
|
1740
|
+
/**
|
|
1741
|
+
* Synchronous teardown — drops the in-memory queue WITHOUT
|
|
1742
|
+
* flushing, then clears all in-memory state. Used by
|
|
1743
|
+
* `[Symbol.dispose]` (which has no await) and tests that need
|
|
1744
|
+
* an unconditional sync wipe. Production code should use
|
|
1745
|
+
* [[shutdown]] (async) instead so queued events are flushed.
|
|
1746
|
+
*
|
|
1747
|
+
* A queue with items at sync-shutdown logs a warning recommending
|
|
1748
|
+
* `[Symbol.asyncDispose]` or `await server.shutdown()` — silent
|
|
1749
|
+
* loss is incompatible with the bank-grade contract.
|
|
1547
1750
|
*/
|
|
1548
|
-
|
|
1751
|
+
shutdownSync(reason?: "shutdown" | "dispose" | "asyncDispose"): void;
|
|
1549
1752
|
/**
|
|
1550
1753
|
* Convert a `CapturedError` into a `ServerEvent` and push through
|
|
1551
1754
|
* `track()`. Goes through the same queue / enrichment / breadcrumb
|
|
@@ -1614,17 +1817,21 @@ declare class CrossdeckServer extends EventEmitter {
|
|
|
1614
1817
|
* // ... use server ...
|
|
1615
1818
|
* // at end of block, server[Symbol.dispose]() runs automatically
|
|
1616
1819
|
*
|
|
1617
|
-
*
|
|
1618
|
-
*
|
|
1619
|
-
*
|
|
1620
|
-
*
|
|
1820
|
+
* **`Symbol.dispose` is synchronous so it CANNOT await the queue
|
|
1821
|
+
* flush.** A queue with pending events at sync-dispose time will
|
|
1822
|
+
* be DROPPED — `shutdownSync` warns to the console when this
|
|
1823
|
+
* happens. For the common case of "drain the queue before
|
|
1824
|
+
* exit", switch to `await using` + `[Symbol.asyncDispose]` (or
|
|
1825
|
+
* call `await server.shutdown()` explicitly before the variable
|
|
1826
|
+
* goes out of scope).
|
|
1621
1827
|
*/
|
|
1622
1828
|
[Symbol.dispose](): void;
|
|
1623
1829
|
/**
|
|
1624
1830
|
* Async disposal hook — runs when an `await using` declaration
|
|
1625
|
-
* exits scope. Awaits
|
|
1626
|
-
*
|
|
1627
|
-
*
|
|
1831
|
+
* exits scope. Awaits the bank-grade `shutdown()` which flushes
|
|
1832
|
+
* the queue THEN tears down. Use this variant for any code path
|
|
1833
|
+
* that owns queued events at exit (serverless handlers,
|
|
1834
|
+
* background workers, end-of-request hooks).
|
|
1628
1835
|
*
|
|
1629
1836
|
* await using server = new CrossdeckServer({ ... });
|
|
1630
1837
|
*/
|
|
@@ -1710,4 +1917,4 @@ declare class CrossdeckServer extends EventEmitter {
|
|
|
1710
1917
|
private normalizeIngestEvent;
|
|
1711
1918
|
}
|
|
1712
1919
|
|
|
1713
|
-
export { type
|
|
1920
|
+
export { type PurchaseResult as $, type AliasIdentityInput as A, type Breadcrumb as B, CROSSDECK_API_VERSION as C, DEFAULT_BASE_URL as D, type Diagnostics as E, type EntitlementCacheOptions as F, type EntitlementMutationResult as G, type EntitlementStore as H, type EntitlementsListResponse as I, type EntitlementsListener as J, type Environment as K, type ErrorCaptureConfig as L, type ErrorLevel as M, type EventProperties as N, type ForgetResult as O, type GrantDuration as P, type GrantEntitlementInput as Q, type GroupMembership as R, type HeartbeatResponse as S, type HttpRequestInfo as T, type HttpResponseInfo as U, type HttpRetriesConfig as V, type IdentifyOptions as W, type IdentityHints as X, type IngestOptions as Y, type IngestResponse as Z, type PublicEntitlement as _, type AliasResult as a, type RequestOptions as a0, type RevokeEntitlementInput as a1, type RuntimeHost as a2, type RuntimeInfo as a3, type ServerEvent as a4, type StackFrame as a5, type StoredEntitlements as a6, type SyncPurchaseInput as a7, makeCrossdeckError as a8, type AuditDecision as b, type AuditEntry as c, type BreadcrumbCategory as d, type BreadcrumbLevel as e, type CapturedError as f, type Contract as g, type ContractAppliesTo as h, type ContractFailureInput as i, type ContractPillar as j, type ContractStatus as k, type ContractTestRef as l, CrossdeckAuthenticationError as m, CrossdeckConfigurationError as n, CrossdeckContracts as o, CrossdeckError as p, type CrossdeckErrorPayload as q, type CrossdeckErrorType as r, CrossdeckInternalError as s, CrossdeckNetworkError as t, CrossdeckPermissionError as u, CrossdeckRateLimitError as v, CrossdeckServer as w, type CrossdeckServerOptions as x, CrossdeckValidationError as y, DEFAULT_TIMEOUT_MS as z };
|
|
@@ -111,6 +111,110 @@ declare class CrossdeckConfigurationError extends CrossdeckError {
|
|
|
111
111
|
*/
|
|
112
112
|
declare function makeCrossdeckError(payload: CrossdeckErrorPayload): CrossdeckError;
|
|
113
113
|
|
|
114
|
+
/**
|
|
115
|
+
* Public, typed accessor for the bank-grade behavioural contracts
|
|
116
|
+
* this SDK ships. The full architecture — schema, distribution,
|
|
117
|
+
* audit loop, pillar taxonomy — lives in `contracts/README.md`
|
|
118
|
+
* at the monorepo root.
|
|
119
|
+
*
|
|
120
|
+
* Why a typed surface (vs. plain JSON access): contract IDs and
|
|
121
|
+
* pillar names are part of Crossdeck's public commitment to
|
|
122
|
+
* customers. Reading them through `CrossdeckContracts` means the
|
|
123
|
+
* compiler catches drift the moment a contract is renamed or
|
|
124
|
+
* retired. Tools that consume contracts at runtime (dashboards,
|
|
125
|
+
* AI assistants, customer integration tests) get the exact same
|
|
126
|
+
* shape every SDK ships, with no parsing layer to drift.
|
|
127
|
+
*
|
|
128
|
+
* --- BINARY STABILITY ---
|
|
129
|
+
* `Contract` is treated as an evolving — but back-compat — wire
|
|
130
|
+
* shape. Fields may be added in any minor release. Existing
|
|
131
|
+
* fields will not be removed or repurposed except in a major
|
|
132
|
+
* version bump, even if all known contracts stop using them.
|
|
133
|
+
* Customers can rely on `id`, `pillar`, `status`, `appliesTo`,
|
|
134
|
+
* `codeRef`, `testRef`, `registeredAt`, `firstRegisteredIn`,
|
|
135
|
+
* and `bundledIn` being present on every contract in every
|
|
136
|
+
* future minor/patch release of this SDK.
|
|
137
|
+
*/
|
|
138
|
+
type ContractPillar = "revenue" | "entitlements" | "analytics" | "webhooks" | "errors" | "lifecycle" | "identity";
|
|
139
|
+
type ContractStatus = "enforced" | "proposed" | "retired";
|
|
140
|
+
type ContractAppliesTo = "web" | "node" | "react-native" | "swift" | "android" | "backend";
|
|
141
|
+
interface ContractTestRef {
|
|
142
|
+
readonly file: string;
|
|
143
|
+
readonly name: string;
|
|
144
|
+
}
|
|
145
|
+
interface Contract {
|
|
146
|
+
readonly id: string;
|
|
147
|
+
readonly pillar: ContractPillar;
|
|
148
|
+
readonly status: ContractStatus;
|
|
149
|
+
readonly claim: string;
|
|
150
|
+
readonly appliesTo: readonly ContractAppliesTo[];
|
|
151
|
+
readonly codeRef: readonly string[];
|
|
152
|
+
readonly testRef: readonly ContractTestRef[];
|
|
153
|
+
readonly registeredAt: string;
|
|
154
|
+
readonly firstRegisteredIn: string;
|
|
155
|
+
readonly bundledIn: string;
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Typed entry point to the bank-grade contracts bundled with this
|
|
159
|
+
* SDK release. Stable, side-effect-free, tree-shakeable.
|
|
160
|
+
*
|
|
161
|
+
* @example Audit at app boot
|
|
162
|
+
* ```ts
|
|
163
|
+
* import { CrossdeckContracts } from "@cross-deck/node";
|
|
164
|
+
*
|
|
165
|
+
* for (const c of CrossdeckContracts.all()) {
|
|
166
|
+
* console.log(`[crossdeck] ${c.id} (${c.pillar})`);
|
|
167
|
+
* }
|
|
168
|
+
* ```
|
|
169
|
+
*/
|
|
170
|
+
declare const CrossdeckContracts: {
|
|
171
|
+
readonly all: () => readonly Contract[];
|
|
172
|
+
readonly allIncludingHistorical: () => readonly Contract[];
|
|
173
|
+
readonly byId: (id: string) => Contract | undefined;
|
|
174
|
+
readonly byPillar: (pillar: ContractPillar) => readonly Contract[];
|
|
175
|
+
readonly withStatus: (status: ContractStatus) => readonly Contract[];
|
|
176
|
+
readonly sdkVersion: "1.6.0";
|
|
177
|
+
readonly bundledIn: "@cross-deck/node@1.6.0";
|
|
178
|
+
/**
|
|
179
|
+
* Resolve a failing test back to the contract it exercises.
|
|
180
|
+
* Used by test-framework hooks to find the contract id of a
|
|
181
|
+
* failed contract test so `reportContractFailure(...)` can stamp
|
|
182
|
+
* the right `contract_id` on the emitted event.
|
|
183
|
+
*/
|
|
184
|
+
readonly findByTestName: (name: string) => Contract | undefined;
|
|
185
|
+
};
|
|
186
|
+
/**
|
|
187
|
+
* Input to {@link CrossdeckServer.reportContractFailure}. Mirrors
|
|
188
|
+
* the per-SDK shape exactly.
|
|
189
|
+
*
|
|
190
|
+
* SCHEMA-LOCK: this interface's field set is exhaustively named. No
|
|
191
|
+
* free-form `extra: Record<string, unknown>` — the schema-lock
|
|
192
|
+
* contract at
|
|
193
|
+
* `contracts/diagnostics/contract-failed-payload-schema-lock.json`
|
|
194
|
+
* forbids unbounded fields. Adding a field requires a PR that
|
|
195
|
+
* amends the contract first, then the public interface.
|
|
196
|
+
*/
|
|
197
|
+
interface ContractFailureInput {
|
|
198
|
+
contractId: string;
|
|
199
|
+
/**
|
|
200
|
+
* Short categorical-ish label — the SDK convention is to keep
|
|
201
|
+
* this under 128 chars and stable across runs (so dashboards can
|
|
202
|
+
* group). Never an end-user-supplied string.
|
|
203
|
+
*/
|
|
204
|
+
failureReason: string;
|
|
205
|
+
runContext: "ci" | "dogfood" | "customer-app";
|
|
206
|
+
runId: string;
|
|
207
|
+
testRef?: {
|
|
208
|
+
file: string;
|
|
209
|
+
name: string;
|
|
210
|
+
};
|
|
211
|
+
/**
|
|
212
|
+
* Optional coarse device class, e.g. "linux-server", "container",
|
|
213
|
+
* "lambda". A categorical bucket, not a host identifier.
|
|
214
|
+
*/
|
|
215
|
+
deviceClass?: string;
|
|
216
|
+
}
|
|
217
|
+
|
|
114
218
|
/**
|
|
115
219
|
* Breadcrumb ring buffer — context attached to every error report.
|
|
116
220
|
*
|
|
@@ -409,6 +513,10 @@ interface PurchaseResult {
|
|
|
409
513
|
crossdeckCustomerId: string;
|
|
410
514
|
env: Environment;
|
|
411
515
|
entitlements: PublicEntitlement[];
|
|
516
|
+
/** True when the response came from the backend's idempotency
|
|
517
|
+
* cache instead of fresh processing. Backend also returns
|
|
518
|
+
* `Idempotent-Replayed: true` as a response header (v1.4.0). */
|
|
519
|
+
idempotent_replay?: boolean;
|
|
412
520
|
}
|
|
413
521
|
/**
|
|
414
522
|
* Response shape from `GET /v1/sdk/heartbeat`. Used by
|
|
@@ -462,6 +570,25 @@ interface CrossdeckServerOptions {
|
|
|
462
570
|
* not the source of truth.
|
|
463
571
|
*/
|
|
464
572
|
appId?: string;
|
|
573
|
+
/**
|
|
574
|
+
* Apply Crossdeck's PII scrubber to every `track()` payload before
|
|
575
|
+
* enqueue. Default `true` (parity with Web / RN / Swift SDKs — Node
|
|
576
|
+
* pre-v1.4.0 was the odd one out and SHIPPED EMAILS UNREDACTED, a
|
|
577
|
+
* privacy contract drift versus the README claim).
|
|
578
|
+
*
|
|
579
|
+
* The scrubber rewrites email-shaped and card-number-shaped
|
|
580
|
+
* substrings to `<email>` / `<card>` sentinels recursively across
|
|
581
|
+
* nested maps + arrays. See `scrubPii` / `scrubPiiFromProperties`.
|
|
582
|
+
*
|
|
583
|
+
* **Blast radius of setting `false`:** every `track()` payload —
|
|
584
|
+
* including event names with embedded emails ("user wes@example.com
|
|
585
|
+
* upgraded"), trait values, group memberships, error context blobs
|
|
586
|
+
* — ships verbatim to Crossdeck and downstream warehouses /
|
|
587
|
+
* analytics exports. Disable only for explicit compliance use
|
|
588
|
+
* cases (regulator-required audit trails where the raw value MUST
|
|
589
|
+
* be preserved) and document the decision at the call site.
|
|
590
|
+
*/
|
|
591
|
+
scrubPii?: boolean;
|
|
465
592
|
/**
|
|
466
593
|
* Error capture configuration. Default: ON with `onUncaughtException` +
|
|
467
594
|
* `onUnhandledRejection` + `wrapFetch` all enabled.
|
|
@@ -672,6 +799,14 @@ interface RequestOptions {
|
|
|
672
799
|
* `timeoutMs`. Pass `0` to disable.
|
|
673
800
|
*/
|
|
674
801
|
timeoutMs?: number;
|
|
802
|
+
/**
|
|
803
|
+
* Override the deterministic Idempotency-Key derivation (v1.4.0).
|
|
804
|
+
* The SDK derives a stable key from the request body so retries
|
|
805
|
+
* collapse on the backend. Override only when an outer
|
|
806
|
+
* orchestrator (job runner, retry harness) needs a different
|
|
807
|
+
* idempotency window — and document why at the call site.
|
|
808
|
+
*/
|
|
809
|
+
idempotencyKey?: string;
|
|
675
810
|
}
|
|
676
811
|
interface IdentityHints {
|
|
677
812
|
customerId?: string;
|
|
@@ -1162,6 +1297,10 @@ declare class CrossdeckServer extends EventEmitter {
|
|
|
1162
1297
|
private readonly baseUrl;
|
|
1163
1298
|
private readonly appId;
|
|
1164
1299
|
private readonly env;
|
|
1300
|
+
/** PII scrubber toggle. Default true — parity with Web/RN/Swift.
|
|
1301
|
+
* Pre-v1.4.0 the Node SDK shipped track() payloads UNREDACTED,
|
|
1302
|
+
* a privacy contract drift versus the README. */
|
|
1303
|
+
private readonly scrubPii;
|
|
1165
1304
|
private readonly secretKeyPrefix;
|
|
1166
1305
|
/**
|
|
1167
1306
|
* Process-stable pseudo-anonymous ID. Used as the default identity
|
|
@@ -1173,6 +1312,8 @@ declare class CrossdeckServer extends EventEmitter {
|
|
|
1173
1312
|
private readonly processAnonymousId;
|
|
1174
1313
|
private readonly runtime;
|
|
1175
1314
|
private readonly runtimeProperties;
|
|
1315
|
+
/** Envelope v1 §4 context object — built once at SDK init, reused on every event. */
|
|
1316
|
+
private readonly eventContext;
|
|
1176
1317
|
private readonly breadcrumbs;
|
|
1177
1318
|
private readonly eventQueue;
|
|
1178
1319
|
private readonly errorTracker;
|
|
@@ -1190,6 +1331,23 @@ declare class CrossdeckServer extends EventEmitter {
|
|
|
1190
1331
|
*/
|
|
1191
1332
|
private readonly entitlementStore;
|
|
1192
1333
|
private readonly debug;
|
|
1334
|
+
/**
|
|
1335
|
+
* Event Envelope v1 §3 — per-session monotonic sequence counter.
|
|
1336
|
+
*
|
|
1337
|
+
* Node has no mobile "session" lifecycle (no app launch / background /
|
|
1338
|
+
* foreground). We model a session as the SDK instance lifetime: the
|
|
1339
|
+
* counter starts at 0 on construction and increments once per
|
|
1340
|
+
* `track()` call. `session.started` is the boot-telemetry event
|
|
1341
|
+
* (the first `track()` called from `emitBootTelemetryEvent()`), so
|
|
1342
|
+
* the seq will be 0 for that event, matching the spec's "reset to 0
|
|
1343
|
+
* at session.started" clause — by construction, it's the zeroth event
|
|
1344
|
+
* of this instance's lifecycle.
|
|
1345
|
+
*
|
|
1346
|
+
* The counter persists for the entire process lifetime of the SDK
|
|
1347
|
+
* instance (spec §3 clause 1: background/foreground does not reset).
|
|
1348
|
+
* A new `CrossdeckServer` construction is the only reset (new session).
|
|
1349
|
+
*/
|
|
1350
|
+
private sessionSeq;
|
|
1193
1351
|
/**
|
|
1194
1352
|
* Alias map — `developerUserId` / `anonymousId` → canonical
|
|
1195
1353
|
* `crossdeckCustomerId`. Populated by `getEntitlements()` so a
|
|
@@ -1207,6 +1365,15 @@ declare class CrossdeckServer extends EventEmitter {
|
|
|
1207
1365
|
private errorContext;
|
|
1208
1366
|
private errorTags;
|
|
1209
1367
|
private errorBeforeSend;
|
|
1368
|
+
/**
|
|
1369
|
+
* Dedup gate for `sdk.shutdown`. Both `shutdown()` (async) and
|
|
1370
|
+
* `shutdownSync()` need to emit so direct callers of EITHER see
|
|
1371
|
+
* the event (the async path's listener guarantees pre-launch
|
|
1372
|
+
* tests, the sync path covers `Symbol.dispose` + tests that call
|
|
1373
|
+
* `shutdownSync()` directly). Without this flag, `shutdown()`'s
|
|
1374
|
+
* tail call into `shutdownSync()` would emit twice.
|
|
1375
|
+
*/
|
|
1376
|
+
private didEmitShutdown;
|
|
1210
1377
|
constructor(options: CrossdeckServerOptions);
|
|
1211
1378
|
/**
|
|
1212
1379
|
* Emit the honest "no cold-start durability" warning when the runtime
|
|
@@ -1331,6 +1498,17 @@ declare class CrossdeckServer extends EventEmitter {
|
|
|
1331
1498
|
* `uncaughtException` has no per-request context; without the
|
|
1332
1499
|
* auto-fill, the event would be rejected at queue enqueue.
|
|
1333
1500
|
*/
|
|
1501
|
+
/**
|
|
1502
|
+
* Emit `crossdeck.contract_failed` to the Crossdeck reliability
|
|
1503
|
+
* endpoint — single-fire, one-way, never visible in the customer's
|
|
1504
|
+
* dashboard. Goes over a dedicated HTTP path with the reliability
|
|
1505
|
+
* publishable key embedded at build time; the customer's track()
|
|
1506
|
+
* pipeline never carries `crossdeck.*` events. This is the
|
|
1507
|
+
* independent-controller flow described in Privacy Policy §6
|
|
1508
|
+
* ("Flow B"). The wire shape is fixed by the schema-lock contract
|
|
1509
|
+
* at `contracts/diagnostics/contract-failed-payload-schema-lock.json`.
|
|
1510
|
+
*/
|
|
1511
|
+
reportContractFailure(input: ContractFailureInput): void;
|
|
1334
1512
|
track(event: ServerEvent): void;
|
|
1335
1513
|
/**
|
|
1336
1514
|
* Immediate POST of one or more events. For bulk imports / replay
|
|
@@ -1541,11 +1719,36 @@ declare class CrossdeckServer extends EventEmitter {
|
|
|
1541
1719
|
getGroups(): Record<string, GroupMembership>;
|
|
1542
1720
|
diagnostics(): Diagnostics;
|
|
1543
1721
|
/**
|
|
1544
|
-
* Tear down handlers and clear in-memory state.
|
|
1545
|
-
*
|
|
1546
|
-
* `flush
|
|
1722
|
+
* Tear down handlers and clear in-memory state.
|
|
1723
|
+
*
|
|
1724
|
+
* **v1.4.0 bank-grade contract:** `shutdown()` AWAITS `flush()`
|
|
1725
|
+
* before dropping the queue, so callers don't silently lose
|
|
1726
|
+
* every queued event on a clean shutdown. The pre-v1.4.0
|
|
1727
|
+
* behaviour (sync `eventQueue.reset()` with no flush) was the
|
|
1728
|
+
* default for both `shutdown()` and `[Symbol.dispose]`; only
|
|
1729
|
+
* `await using` + `[Symbol.asyncDispose]` flushed correctly.
|
|
1730
|
+
*
|
|
1731
|
+
* Production servers should still prefer `await server.flush()`
|
|
1732
|
+
* (visible) followed by `server.shutdown()` so the flush
|
|
1733
|
+
* outcome is observable — `shutdown()`'s internal flush swallows
|
|
1734
|
+
* errors as a best-effort drain.
|
|
1735
|
+
*
|
|
1736
|
+
* Use [[shutdownSync]] only when the runtime cannot await
|
|
1737
|
+
* (e.g. inside `Symbol.dispose` — see below).
|
|
1738
|
+
*/
|
|
1739
|
+
shutdown(reason?: "shutdown" | "dispose" | "asyncDispose"): Promise<void>;
|
|
1740
|
+
/**
|
|
1741
|
+
* Synchronous teardown — drops the in-memory queue WITHOUT
|
|
1742
|
+
* flushing, then clears all in-memory state. Used by
|
|
1743
|
+
* `[Symbol.dispose]` (which has no await) and tests that need
|
|
1744
|
+
* an unconditional sync wipe. Production code should use
|
|
1745
|
+
* [[shutdown]] (async) instead so queued events are flushed.
|
|
1746
|
+
*
|
|
1747
|
+
* A queue with items at sync-shutdown logs a warning recommending
|
|
1748
|
+
* `[Symbol.asyncDispose]` or `await server.shutdown()` — silent
|
|
1749
|
+
* loss is incompatible with the bank-grade contract.
|
|
1547
1750
|
*/
|
|
1548
|
-
|
|
1751
|
+
shutdownSync(reason?: "shutdown" | "dispose" | "asyncDispose"): void;
|
|
1549
1752
|
/**
|
|
1550
1753
|
* Convert a `CapturedError` into a `ServerEvent` and push through
|
|
1551
1754
|
* `track()`. Goes through the same queue / enrichment / breadcrumb
|
|
@@ -1614,17 +1817,21 @@ declare class CrossdeckServer extends EventEmitter {
|
|
|
1614
1817
|
* // ... use server ...
|
|
1615
1818
|
* // at end of block, server[Symbol.dispose]() runs automatically
|
|
1616
1819
|
*
|
|
1617
|
-
*
|
|
1618
|
-
*
|
|
1619
|
-
*
|
|
1620
|
-
*
|
|
1820
|
+
* **`Symbol.dispose` is synchronous so it CANNOT await the queue
|
|
1821
|
+
* flush.** A queue with pending events at sync-dispose time will
|
|
1822
|
+
* be DROPPED — `shutdownSync` warns to the console when this
|
|
1823
|
+
* happens. For the common case of "drain the queue before
|
|
1824
|
+
* exit", switch to `await using` + `[Symbol.asyncDispose]` (or
|
|
1825
|
+
* call `await server.shutdown()` explicitly before the variable
|
|
1826
|
+
* goes out of scope).
|
|
1621
1827
|
*/
|
|
1622
1828
|
[Symbol.dispose](): void;
|
|
1623
1829
|
/**
|
|
1624
1830
|
* Async disposal hook — runs when an `await using` declaration
|
|
1625
|
-
* exits scope. Awaits
|
|
1626
|
-
*
|
|
1627
|
-
*
|
|
1831
|
+
* exits scope. Awaits the bank-grade `shutdown()` which flushes
|
|
1832
|
+
* the queue THEN tears down. Use this variant for any code path
|
|
1833
|
+
* that owns queued events at exit (serverless handlers,
|
|
1834
|
+
* background workers, end-of-request hooks).
|
|
1628
1835
|
*
|
|
1629
1836
|
* await using server = new CrossdeckServer({ ... });
|
|
1630
1837
|
*/
|
|
@@ -1710,4 +1917,4 @@ declare class CrossdeckServer extends EventEmitter {
|
|
|
1710
1917
|
private normalizeIngestEvent;
|
|
1711
1918
|
}
|
|
1712
1919
|
|
|
1713
|
-
export { type
|
|
1920
|
+
export { type PurchaseResult as $, type AliasIdentityInput as A, type Breadcrumb as B, CROSSDECK_API_VERSION as C, DEFAULT_BASE_URL as D, type Diagnostics as E, type EntitlementCacheOptions as F, type EntitlementMutationResult as G, type EntitlementStore as H, type EntitlementsListResponse as I, type EntitlementsListener as J, type Environment as K, type ErrorCaptureConfig as L, type ErrorLevel as M, type EventProperties as N, type ForgetResult as O, type GrantDuration as P, type GrantEntitlementInput as Q, type GroupMembership as R, type HeartbeatResponse as S, type HttpRequestInfo as T, type HttpResponseInfo as U, type HttpRetriesConfig as V, type IdentifyOptions as W, type IdentityHints as X, type IngestOptions as Y, type IngestResponse as Z, type PublicEntitlement as _, type AliasResult as a, type RequestOptions as a0, type RevokeEntitlementInput as a1, type RuntimeHost as a2, type RuntimeInfo as a3, type ServerEvent as a4, type StackFrame as a5, type StoredEntitlements as a6, type SyncPurchaseInput as a7, makeCrossdeckError as a8, type AuditDecision as b, type AuditEntry as c, type BreadcrumbCategory as d, type BreadcrumbLevel as e, type CapturedError as f, type Contract as g, type ContractAppliesTo as h, type ContractFailureInput as i, type ContractPillar as j, type ContractStatus as k, type ContractTestRef as l, CrossdeckAuthenticationError as m, CrossdeckConfigurationError as n, CrossdeckContracts as o, CrossdeckError as p, type CrossdeckErrorPayload as q, type CrossdeckErrorType as r, CrossdeckInternalError as s, CrossdeckNetworkError as t, CrossdeckPermissionError as u, CrossdeckRateLimitError as v, CrossdeckServer as w, type CrossdeckServerOptions as x, CrossdeckValidationError as y, DEFAULT_TIMEOUT_MS as z };
|