@cross-deck/web 0.7.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 +197 -0
- package/dist/crossdeck.umd.min.js +3 -0
- package/dist/crossdeck.umd.min.js.map +1 -0
- package/dist/error-codes.json +91 -0
- package/dist/index.cjs +1810 -30
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.mts +469 -4
- package/dist/index.d.ts +469 -4
- package/dist/index.mjs +1807 -29
- package/dist/index.mjs.map +1 -1
- package/dist/react.cjs +1710 -28
- package/dist/react.cjs.map +1 -1
- package/dist/react.mjs +1710 -28
- package/dist/react.mjs.map +1 -1
- package/dist/vue.cjs +3350 -0
- package/dist/vue.cjs.map +1 -0
- package/dist/vue.d.mts +37 -0
- package/dist/vue.d.ts +37 -0
- package/dist/vue.mjs +3324 -0
- package/dist/vue.mjs.map +1 -0
- package/package.json +25 -6
package/dist/index.d.ts
CHANGED
|
@@ -139,6 +139,23 @@ interface CrossdeckOptions {
|
|
|
139
139
|
* `Crossdeck.setDebugMode(true)` after init.
|
|
140
140
|
*/
|
|
141
141
|
debug?: boolean;
|
|
142
|
+
/**
|
|
143
|
+
* Respect the browser's Do Not Track signal at init (v0.10.0+).
|
|
144
|
+
* Default `false`. When `true` AND the user has `navigator.doNotTrack === "1"`,
|
|
145
|
+
* the SDK boots with analytics / marketing / errors all denied —
|
|
146
|
+
* locked off even if the developer later calls `Crossdeck.consent({...})`.
|
|
147
|
+
* Industry has effectively deprecated DNT, but opt-in support is the
|
|
148
|
+
* polite default for privacy-first apps.
|
|
149
|
+
*/
|
|
150
|
+
respectDnt?: boolean;
|
|
151
|
+
/**
|
|
152
|
+
* Scrub PII-shaped strings (email addresses, card numbers) from
|
|
153
|
+
* URL paths, event properties, and acquisition referrer before they
|
|
154
|
+
* leave the SDK. Default `true` — Stripe-grade. Disable only if your
|
|
155
|
+
* pipeline does its own PII redaction downstream and you need the
|
|
156
|
+
* raw strings.
|
|
157
|
+
*/
|
|
158
|
+
scrubPii?: boolean;
|
|
142
159
|
}
|
|
143
160
|
/** Auto-tracking flags. See CrossdeckOptions.autoTrack. */
|
|
144
161
|
interface AutoTrackOptions {
|
|
@@ -157,6 +174,25 @@ interface AutoTrackOptions {
|
|
|
157
174
|
* or data-cd-prop-* for custom property tagging.
|
|
158
175
|
*/
|
|
159
176
|
clicks: boolean;
|
|
177
|
+
/**
|
|
178
|
+
* Web Vitals capture (v0.9.0+) — emits `webvitals.lcp`, `webvitals.inp`,
|
|
179
|
+
* `webvitals.cls`, `webvitals.fcp`, `webvitals.ttfb` events using the
|
|
180
|
+
* browser's `PerformanceObserver`. Defaults to true in browsers,
|
|
181
|
+
* no-op everywhere else. Disable if you have a separate RUM provider
|
|
182
|
+
* (DataDog, Sentry Performance) and don't want duplicates.
|
|
183
|
+
*/
|
|
184
|
+
webVitals: boolean;
|
|
185
|
+
/**
|
|
186
|
+
* Error capture (v1.0.0+) — installs window.onerror +
|
|
187
|
+
* window.onunhandledrejection listeners, wraps fetch + XHR to catch
|
|
188
|
+
* 5xx + network failures, ships each captured error as a Crossdeck
|
|
189
|
+
* event (kind: error.unhandled / error.unhandledrejection /
|
|
190
|
+
* error.handled / error.http / error.message). Errors gate on
|
|
191
|
+
* `consent.errors`. Rate-limited per-fingerprint so a runaway loop
|
|
192
|
+
* can't flood the queue; browser-extension noise filtered by
|
|
193
|
+
* default. Default true in browsers, no-op everywhere else.
|
|
194
|
+
*/
|
|
195
|
+
errors: boolean;
|
|
160
196
|
}
|
|
161
197
|
/** Minimal interface for any pluggable key-value persistence. */
|
|
162
198
|
interface KeyValueStorage {
|
|
@@ -164,10 +200,44 @@ interface KeyValueStorage {
|
|
|
164
200
|
setItem(key: string, value: string): void;
|
|
165
201
|
removeItem(key: string): void;
|
|
166
202
|
}
|
|
167
|
-
/**
|
|
203
|
+
/**
|
|
204
|
+
* Identity hint + profile traits passed to identify().
|
|
205
|
+
*
|
|
206
|
+
* `traits` is a free-form bag of profile data (name, plan, signupDate,
|
|
207
|
+
* teamRole, etc.) that gets persisted on the Crossdeck customer record
|
|
208
|
+
* and attached to every subsequent event of the identified user as
|
|
209
|
+
* `$user.<key>` properties for dashboard filtering.
|
|
210
|
+
*
|
|
211
|
+
* Like event properties, traits are validated at the SDK boundary —
|
|
212
|
+
* functions/symbols/undefined dropped, Date / BigInt / Error coerced,
|
|
213
|
+
* strings > 1024 chars truncated. Caller's object is never mutated.
|
|
214
|
+
*/
|
|
168
215
|
interface IdentifyOptions {
|
|
169
216
|
/** Optional email to attach to the customer record. */
|
|
170
217
|
email?: string;
|
|
218
|
+
/**
|
|
219
|
+
* Optional profile traits. Examples:
|
|
220
|
+
* `{ name: "Wes", plan: "pro", signedUpAt: "2026-05-11" }`
|
|
221
|
+
*
|
|
222
|
+
* Treated like event properties — values are sanitised at the SDK
|
|
223
|
+
* boundary so a `{ avatar: <File>, callback: () => {} }` payload
|
|
224
|
+
* doesn't crash the alias request. Server-side, traits land on
|
|
225
|
+
* `customers/{cdcust}.traits` (additively — existing fields are
|
|
226
|
+
* preserved unless the new identify call overrides them).
|
|
227
|
+
*/
|
|
228
|
+
traits?: Record<string, unknown>;
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* Group context — Mixpanel-style. Identifies a customer's membership
|
|
232
|
+
* in an organisational entity (org, account, team, workspace) so B2B
|
|
233
|
+
* dashboards can answer "how is account X using my product".
|
|
234
|
+
*
|
|
235
|
+
* Attached to every event as `$groups.<type>` until cleared via
|
|
236
|
+
* `Crossdeck.group(type, null)`. Multiple types can coexist (e.g.
|
|
237
|
+
* `org` + `team`) — the SDK keeps a map keyed by type.
|
|
238
|
+
*/
|
|
239
|
+
interface GroupTraits {
|
|
240
|
+
[key: string]: unknown;
|
|
171
241
|
}
|
|
172
242
|
/** Properties payload for track(). Arbitrary key/value, JSON-serialisable, ≤ 8 KB. */
|
|
173
243
|
type EventProperties = Record<string, unknown>;
|
|
@@ -184,9 +254,34 @@ interface Diagnostics {
|
|
|
184
254
|
developerUserId: string | null;
|
|
185
255
|
sdkVersion: string | null;
|
|
186
256
|
baseUrl: string | null;
|
|
257
|
+
/**
|
|
258
|
+
* Last `serverTime` value the SDK saw on a /sdk/heartbeat response,
|
|
259
|
+
* along with the local clock value AT that moment. Lets dashboards
|
|
260
|
+
* (and the developer, in debug mode) detect a wrong-system-clock
|
|
261
|
+
* problem before it corrupts a day of analytics. Null until the
|
|
262
|
+
* first heartbeat completes.
|
|
263
|
+
*/
|
|
264
|
+
clock: {
|
|
265
|
+
/** Server's view of "now" from the last heartbeat (epoch ms). */
|
|
266
|
+
lastServerTime: number | null;
|
|
267
|
+
/** Client's `Date.now()` taken at the same moment as `lastServerTime`. */
|
|
268
|
+
lastClientTime: number | null;
|
|
269
|
+
/**
|
|
270
|
+
* `lastClientTime - lastServerTime` — positive means the client
|
|
271
|
+
* clock is AHEAD of the server. Outside ±5 minutes is suspicious
|
|
272
|
+
* and worth surfacing to the developer.
|
|
273
|
+
*/
|
|
274
|
+
skewMs: number | null;
|
|
275
|
+
};
|
|
187
276
|
entitlements: {
|
|
188
277
|
count: number;
|
|
189
278
|
lastUpdated: number;
|
|
279
|
+
/**
|
|
280
|
+
* Cumulative count of listener invocations that threw. Swallowed
|
|
281
|
+
* inside the cache (a buggy consumer must not crash the SDK) but
|
|
282
|
+
* surfaced here so developers can spot broken subscribers.
|
|
283
|
+
*/
|
|
284
|
+
listenerErrors: number;
|
|
190
285
|
};
|
|
191
286
|
events: {
|
|
192
287
|
buffered: number;
|
|
@@ -194,6 +289,13 @@ interface Diagnostics {
|
|
|
194
289
|
inFlight: number;
|
|
195
290
|
lastFlushAt: number;
|
|
196
291
|
lastError: string | null;
|
|
292
|
+
/** Consecutive flush failures since the last success. */
|
|
293
|
+
consecutiveFailures: number;
|
|
294
|
+
/**
|
|
295
|
+
* When the next retry is scheduled (epoch ms), or null if the queue
|
|
296
|
+
* is idle / healthy.
|
|
297
|
+
*/
|
|
298
|
+
nextRetryAt: number | null;
|
|
197
299
|
};
|
|
198
300
|
}
|
|
199
301
|
|
|
@@ -239,6 +341,187 @@ interface Diagnostics {
|
|
|
239
341
|
|
|
240
342
|
type EntitlementsListener = (entitlements: PublicEntitlement[]) => void;
|
|
241
343
|
|
|
344
|
+
/**
|
|
345
|
+
* Consent gating — GDPR / CCPA-grade kill switches.
|
|
346
|
+
*
|
|
347
|
+
* Three independent dimensions, each defaulting to "granted" but
|
|
348
|
+
* runtime-overridable:
|
|
349
|
+
*
|
|
350
|
+
* analytics — track(), identify(), heartbeat(), session/page auto-
|
|
351
|
+
* emissions. Off → events drop silently, no network
|
|
352
|
+
* calls fire.
|
|
353
|
+
* marketing — paid-traffic click IDs (gclid/fbclid/etc) and
|
|
354
|
+
* acquisition referrer URL. Off → these get scrubbed
|
|
355
|
+
* before they ever land in the event bag.
|
|
356
|
+
* errors — error / breadcrumb / Web Vitals capture. Off → no
|
|
357
|
+
* webvitals.* events emitted, no error reporting (when
|
|
358
|
+
* Phase 3 errors land).
|
|
359
|
+
*
|
|
360
|
+
* Why this granularity: real consent banners offer "Analytics",
|
|
361
|
+
* "Marketing", "Functional" as separate boxes. The SDK has to match.
|
|
362
|
+
*
|
|
363
|
+
* Default state: every dimension is granted. The developer must
|
|
364
|
+
* explicitly call `Crossdeck.consent({ analytics: false })` before
|
|
365
|
+
* the first event to opt OUT — same convention as Google Tag Manager
|
|
366
|
+
* Consent Mode. To start in deny mode, call `init(...)` then
|
|
367
|
+
* immediately `consent({ analytics: false, marketing: false, errors:
|
|
368
|
+
* false })` before any user activity.
|
|
369
|
+
*
|
|
370
|
+
* DNT (Do Not Track) browser header is checked once at init and
|
|
371
|
+
* applied as an automatic deny across all dimensions when
|
|
372
|
+
* `respectDnt: true` is set in CrossdeckOptions (default false because
|
|
373
|
+
* the industry has effectively deprecated DNT — but opt-in support
|
|
374
|
+
* is the polite default for privacy-first apps).
|
|
375
|
+
*/
|
|
376
|
+
interface ConsentState {
|
|
377
|
+
analytics: boolean;
|
|
378
|
+
marketing: boolean;
|
|
379
|
+
errors: boolean;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
/**
|
|
383
|
+
* Breadcrumb ring buffer — context attached to every error report.
|
|
384
|
+
*
|
|
385
|
+
* Sentry / Datadog / Bugsnag all ship the same idea: keep a rolling
|
|
386
|
+
* record of the last N "things the user did" (page views, clicks,
|
|
387
|
+
* custom events, network calls, console logs). When an error fires,
|
|
388
|
+
* attach the buffer so the engineer reading the error can see exactly
|
|
389
|
+
* how the user got into the broken state. The single most powerful
|
|
390
|
+
* debugging signal in error monitoring — without breadcrumbs, errors
|
|
391
|
+
* are stack traces with no story.
|
|
392
|
+
*
|
|
393
|
+
* Implementation: a circular buffer with a fixed cap. Old entries are
|
|
394
|
+
* evicted as new ones arrive. The default cap (50) is enough to cover
|
|
395
|
+
* ~5 minutes of typical user activity without ballooning the error
|
|
396
|
+
* payload — Sentry uses 100 by default but the SDK is more aggressive
|
|
397
|
+
* about size since we ship breadcrumbs over the wire with every error,
|
|
398
|
+
* not as a separate batch.
|
|
399
|
+
*
|
|
400
|
+
* Privacy: breadcrumbs auto-emit from the same auto-tracking sources
|
|
401
|
+
* as analytics events (page.viewed, element.clicked). Those already
|
|
402
|
+
* skip password fields, form inputs, and cd-noTrack subtrees. Custom
|
|
403
|
+
* crumbs added via Crossdeck.addBreadcrumb() pass through the same
|
|
404
|
+
* property sanitiser as track() events.
|
|
405
|
+
*/
|
|
406
|
+
type BreadcrumbCategory = "navigation" | "ui.click" | "ui.input" | "http" | "console" | "custom" | "info";
|
|
407
|
+
type BreadcrumbLevel = "debug" | "info" | "warning" | "error";
|
|
408
|
+
interface Breadcrumb {
|
|
409
|
+
/** epoch ms */
|
|
410
|
+
timestamp: number;
|
|
411
|
+
category: BreadcrumbCategory;
|
|
412
|
+
level?: BreadcrumbLevel;
|
|
413
|
+
/** Short human-readable description. */
|
|
414
|
+
message?: string;
|
|
415
|
+
/** Arbitrary key/value context for the crumb. */
|
|
416
|
+
data?: Record<string, unknown>;
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
/**
|
|
420
|
+
* Stack-trace parser — normalises Chrome / Firefox / Safari / Edge
|
|
421
|
+
* stack strings into a common frame shape.
|
|
422
|
+
*
|
|
423
|
+
* Why hand-rolled, not stack-trace-js or error-stack-parser libraries:
|
|
424
|
+
* those weigh 5–15 KB after minification and we'd be pulling in their
|
|
425
|
+
* full feature matrix just for the parser. The patterns below cover
|
|
426
|
+
* the four shapes any modern browser emits, totalling ~80 lines.
|
|
427
|
+
*
|
|
428
|
+
* The output frame shape mirrors what Sentry's `mechanism: { type:
|
|
429
|
+
* 'generic' }` events ship, so future source-map symbolication on the
|
|
430
|
+
* Crossdeck backend has a stable input to work against.
|
|
431
|
+
*
|
|
432
|
+
* Defensive: never throws. An unparseable line becomes a `raw` frame
|
|
433
|
+
* with just the literal text. Engineers reading errors still get the
|
|
434
|
+
* raw stack as fallback.
|
|
435
|
+
*/
|
|
436
|
+
interface StackFrame {
|
|
437
|
+
/** Function name, or "?" if anonymous / unparseable. */
|
|
438
|
+
function: string;
|
|
439
|
+
/** Source file URL the frame ran in. Empty when unknown. */
|
|
440
|
+
filename: string;
|
|
441
|
+
/** 1-indexed line number, or 0 when unknown. */
|
|
442
|
+
lineno: number;
|
|
443
|
+
/** 1-indexed column number, or 0 when unknown. */
|
|
444
|
+
colno: number;
|
|
445
|
+
/**
|
|
446
|
+
* True when the frame is in the app's own code (best-effort:
|
|
447
|
+
* detected by URL not starting with chrome-extension://, etc.).
|
|
448
|
+
* Helps the dashboard's "your code vs library code" view.
|
|
449
|
+
*/
|
|
450
|
+
in_app: boolean;
|
|
451
|
+
/** Raw line from the stack string for debugging when parse fails. */
|
|
452
|
+
raw: string;
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
/**
|
|
456
|
+
* Error capture — the third Crossdeck USP.
|
|
457
|
+
*
|
|
458
|
+
* Catches every error source the browser can hand us and ships them as
|
|
459
|
+
* Crossdeck events. The pipeline reuses the analytics queue:
|
|
460
|
+
* - Same durable persistence (errors survive crashes / hard closes)
|
|
461
|
+
* - Same exponential backoff (a flapping server doesn't flood
|
|
462
|
+
* errors past the rate limit)
|
|
463
|
+
* - Same Idempotency-Key (duplicate batches dedup server-side)
|
|
464
|
+
* - Same consent gate (consent.errors)
|
|
465
|
+
* - Same PII scrub on properties before they leave
|
|
466
|
+
*
|
|
467
|
+
* Error sources captured (each toggleable):
|
|
468
|
+
* 1. window.onerror — uncaught synchronous errors
|
|
469
|
+
* 2. window.onunhandledrejection — unhandled promise rejections
|
|
470
|
+
* 3. fetch() wrap — HTTP errors the app code didn't catch
|
|
471
|
+
* 4. XMLHttpRequest wrap — same, for legacy XHR consumers
|
|
472
|
+
* 5. Crossdeck.captureError(err) — manual API for try/catch blocks
|
|
473
|
+
* 6. Crossdeck.captureMessage(msg) — non-error events you want to
|
|
474
|
+
* surface as issues (e.g. "we hit the soft-deprecated path")
|
|
475
|
+
*
|
|
476
|
+
* Defensive design rules:
|
|
477
|
+
* - The error handler must NEVER throw — if our own code crashes
|
|
478
|
+
* while reporting an error, we'd take down the host app's error
|
|
479
|
+
* handler too. Every callback is wrapped in try/swallow.
|
|
480
|
+
* - Recursion guard: a `_reporting` flag prevents the SDK from
|
|
481
|
+
* reporting its own errors recursively forever.
|
|
482
|
+
* - Rate limited per-fingerprint: max N reports per second to defend
|
|
483
|
+
* against runaway loops (e.g. an error in setInterval).
|
|
484
|
+
* - Browser-extension noise is filtered by default — those errors
|
|
485
|
+
* aren't the developer's fault and would otherwise drown the
|
|
486
|
+
* signal.
|
|
487
|
+
*/
|
|
488
|
+
|
|
489
|
+
type ErrorLevel = "error" | "warning" | "info";
|
|
490
|
+
interface CapturedError {
|
|
491
|
+
/** When the error fired (epoch ms). */
|
|
492
|
+
timestamp: number;
|
|
493
|
+
/** error.unhandled, error.unhandledrejection, error.handled, error.message, error.http */
|
|
494
|
+
kind: "error.unhandled" | "error.unhandledrejection" | "error.handled" | "error.message" | "error.http";
|
|
495
|
+
level: ErrorLevel;
|
|
496
|
+
message: string;
|
|
497
|
+
/** The error class name when we have it (TypeError, ReferenceError, etc.) */
|
|
498
|
+
errorType: string | null;
|
|
499
|
+
/** Parsed stack frames, empty when unavailable. */
|
|
500
|
+
frames: StackFrame[];
|
|
501
|
+
/** Raw stack string for fallback display. */
|
|
502
|
+
rawStack: string | null;
|
|
503
|
+
/** Origin URL when available (window.onerror's `source` arg). */
|
|
504
|
+
filename: string | null;
|
|
505
|
+
lineno: number | null;
|
|
506
|
+
colno: number | null;
|
|
507
|
+
/** djb2 hash of message + top frames — group identical errors. */
|
|
508
|
+
fingerprint: string;
|
|
509
|
+
/** Snapshot of the breadcrumb buffer at the moment the error fired. */
|
|
510
|
+
breadcrumbs: Breadcrumb[];
|
|
511
|
+
/** Free-form context attached via Crossdeck.setContext(). */
|
|
512
|
+
context: Record<string, unknown>;
|
|
513
|
+
/** Free-form tags attached via Crossdeck.setTag(). */
|
|
514
|
+
tags: Record<string, string>;
|
|
515
|
+
/** "TypeError: x is not a function" → "TypeError" + "x is not a function". */
|
|
516
|
+
/** Whether the error happened during a fetch / XHR. */
|
|
517
|
+
http?: {
|
|
518
|
+
url: string;
|
|
519
|
+
method: string;
|
|
520
|
+
status: number;
|
|
521
|
+
statusText?: string;
|
|
522
|
+
};
|
|
523
|
+
}
|
|
524
|
+
|
|
242
525
|
/**
|
|
243
526
|
* Public API surface for @cross-deck/web.
|
|
244
527
|
*
|
|
@@ -294,8 +577,145 @@ declare class CrossdeckClient {
|
|
|
294
577
|
/**
|
|
295
578
|
* Link the anonymous device to a developer-supplied user ID. Cache
|
|
296
579
|
* the resolved Crossdeck customer for follow-up calls.
|
|
580
|
+
*
|
|
581
|
+
* v0.9.0+ accepts an optional `traits` bag — profile data (name,
|
|
582
|
+
* plan, signupDate, role) persisted on the Crossdeck customer record
|
|
583
|
+
* and queryable from dashboards. Traits are sanitised through the
|
|
584
|
+
* same validator that gates `track()` properties, so a `{ avatar:
|
|
585
|
+
* <File>, onSave: () => {} }` payload can't corrupt the alias call.
|
|
586
|
+
*
|
|
587
|
+
* Crossdeck.identify("user_847", {
|
|
588
|
+
* email: "wes@pinet.co.za",
|
|
589
|
+
* traits: { name: "Wes", plan: "pro", signedUpAt: "2026-05-11" },
|
|
590
|
+
* });
|
|
591
|
+
*/
|
|
592
|
+
identify(userId: string, options?: IdentifyOptions): Promise<AliasResult>;
|
|
593
|
+
/**
|
|
594
|
+
* Register super-properties — Mixpanel pattern. Once set, every
|
|
595
|
+
* subsequent event of THIS SDK instance carries these keys on its
|
|
596
|
+
* properties bag automatically.
|
|
597
|
+
*
|
|
598
|
+
* Crossdeck.register({ plan: "pro", releaseChannel: "beta" });
|
|
599
|
+
* Crossdeck.track("paywall_shown"); // includes plan + releaseChannel
|
|
600
|
+
*
|
|
601
|
+
* Values that are `null` are deleted (the explicit "stop tracking
|
|
602
|
+
* this key" idiom). Returns the resulting bag.
|
|
603
|
+
*
|
|
604
|
+
* Sanitised through `validateEventProperties` so a `{ avatar: File }`
|
|
605
|
+
* payload can't poison the queue at flush time.
|
|
606
|
+
*/
|
|
607
|
+
register(properties: Record<string, unknown>): Record<string, unknown>;
|
|
608
|
+
/** Remove a single super-property key. Idempotent. */
|
|
609
|
+
unregister(key: string): void;
|
|
610
|
+
/** Snapshot of the current super-property bag. */
|
|
611
|
+
getSuperProperties(): Record<string, unknown>;
|
|
612
|
+
/**
|
|
613
|
+
* Associate the current user with a group (org, team, account, etc.).
|
|
614
|
+
* Mixpanel / Segment "Group Analytics" pattern.
|
|
615
|
+
*
|
|
616
|
+
* Crossdeck.group("org", "acme_inc");
|
|
617
|
+
* Crossdeck.group("team", "design", { headcount: 12 });
|
|
618
|
+
*
|
|
619
|
+
* Once set, every subsequent event carries `$groups.<type>: id` on
|
|
620
|
+
* its properties bag, enabling B2B dashboards ("how is Acme using
|
|
621
|
+
* the product"). Pass `id: null` to clear a group membership.
|
|
622
|
+
*/
|
|
623
|
+
group(type: string, id: string | null, traits?: GroupTraits): void;
|
|
624
|
+
/** Snapshot of the current groups map keyed by type. */
|
|
625
|
+
getGroups(): Record<string, {
|
|
626
|
+
id: string;
|
|
627
|
+
traits?: Record<string, unknown>;
|
|
628
|
+
}>;
|
|
629
|
+
/**
|
|
630
|
+
* Update consent state. Three independent dimensions:
|
|
631
|
+
*
|
|
632
|
+
* analytics — track() + identify() + auto-emissions
|
|
633
|
+
* marketing — paid-traffic click IDs + referrer URL on events
|
|
634
|
+
* errors — Web Vitals + (future) error reporting
|
|
635
|
+
*
|
|
636
|
+
* Each defaults to `true` (granted). Pass partial state — only the
|
|
637
|
+
* keys you provide are changed.
|
|
638
|
+
*
|
|
639
|
+
* Crossdeck.consent({ analytics: false });
|
|
640
|
+
* Crossdeck.consent({ marketing: true, errors: true });
|
|
641
|
+
*
|
|
642
|
+
* DNT-derived denies cannot be flipped back on; if the browser said
|
|
643
|
+
* "don't track" we don't track even if the developer code disagrees.
|
|
644
|
+
*/
|
|
645
|
+
consent(state: Partial<ConsentState>): ConsentState;
|
|
646
|
+
/** Snapshot of the current consent state. */
|
|
647
|
+
consentStatus(): ConsentState;
|
|
648
|
+
/**
|
|
649
|
+
* Manually capture an error from a try/catch block.
|
|
650
|
+
*
|
|
651
|
+
* try { …risky… } catch (err) {
|
|
652
|
+
* Crossdeck.captureError(err, { context: { plan: "pro" } });
|
|
653
|
+
* }
|
|
654
|
+
*
|
|
655
|
+
* The error is shipped through the same event queue as analytics
|
|
656
|
+
* (durable, retried, rate-limited per fingerprint). Sends are gated
|
|
657
|
+
* by `consent.errors`. Returns silently — never throws, even if the
|
|
658
|
+
* SDK isn't initialised yet.
|
|
297
659
|
*/
|
|
298
|
-
|
|
660
|
+
captureError(error: unknown, options?: {
|
|
661
|
+
context?: Record<string, unknown>;
|
|
662
|
+
tags?: Record<string, string>;
|
|
663
|
+
level?: ErrorLevel;
|
|
664
|
+
}): void;
|
|
665
|
+
/**
|
|
666
|
+
* Capture a non-error event you want to surface as an issue
|
|
667
|
+
* ("deprecated path hit", "we entered the slow code path"). Sentry
|
|
668
|
+
* captureMessage pattern. Returns silently if not initialised.
|
|
669
|
+
*/
|
|
670
|
+
captureMessage(message: string, level?: ErrorLevel): void;
|
|
671
|
+
/**
|
|
672
|
+
* Attach a tag to every subsequent error report. Tags are key/value
|
|
673
|
+
* strings (Sentry pattern): `setTag("flow", "checkout")` → every
|
|
674
|
+
* error from this point on carries `tags.flow === "checkout"`.
|
|
675
|
+
*/
|
|
676
|
+
setTag(key: string, value: string): void;
|
|
677
|
+
/** Bulk-set tags. Merges with existing tags. */
|
|
678
|
+
setTags(tags: Record<string, string>): void;
|
|
679
|
+
/**
|
|
680
|
+
* Attach a structured context blob to every subsequent error report.
|
|
681
|
+
* Unlike tags (flat key/value), context is a named bag of arbitrary
|
|
682
|
+
* data: `setContext("cart", { items: 3, total: 42.99 })`.
|
|
683
|
+
*/
|
|
684
|
+
setContext(name: string, data: Record<string, unknown>): void;
|
|
685
|
+
/**
|
|
686
|
+
* Add a custom breadcrumb to the rolling buffer. Useful for marking
|
|
687
|
+
* domain-meaningful moments ("user opened paywall") that aren't
|
|
688
|
+
* already auto-captured. The buffer caps at 50 entries; old ones
|
|
689
|
+
* evict.
|
|
690
|
+
*/
|
|
691
|
+
addBreadcrumb(crumb: Breadcrumb): void;
|
|
692
|
+
/**
|
|
693
|
+
* Install a pre-send hook for errors. Return null to drop, or a
|
|
694
|
+
* modified CapturedError to scrub / rewrite. Sentry's beforeSend
|
|
695
|
+
* pattern — the only way to redact app-specific PII (auth tokens
|
|
696
|
+
* in URLs, etc.) before the report leaves the browser.
|
|
697
|
+
*/
|
|
698
|
+
setErrorBeforeSend(hook: ((err: CapturedError) => CapturedError | null) | null): void;
|
|
699
|
+
/**
|
|
700
|
+
* Internal: turn a CapturedError into a Crossdeck event and enqueue
|
|
701
|
+
* it. Goes through the same queue / persistence / consent / scrub
|
|
702
|
+
* pipeline as analytics events.
|
|
703
|
+
*/
|
|
704
|
+
private reportError;
|
|
705
|
+
/**
|
|
706
|
+
* GDPR/CCPA "right to be forgotten" — calls the backend's
|
|
707
|
+
* /v1/identity/forget endpoint to schedule a server-side deletion of
|
|
708
|
+
* the customer's events and profile, then wipes all local state
|
|
709
|
+
* (identity, entitlements, queue, super-props, persistent stores).
|
|
710
|
+
*
|
|
711
|
+
* Idempotent. Safe to call when no identity has been established
|
|
712
|
+
* (it just wipes the empty local state).
|
|
713
|
+
*
|
|
714
|
+
* After forget() resolves, the SDK is in the same shape as if the
|
|
715
|
+
* developer had called `Crossdeck.reset()` — a fresh anonymousId is
|
|
716
|
+
* minted and the next session is a brand new identity-graph entry.
|
|
717
|
+
*/
|
|
718
|
+
forget(): Promise<void>;
|
|
299
719
|
/**
|
|
300
720
|
* Read the current customer's active entitlements from the server.
|
|
301
721
|
* Updates the local cache so subsequent isEntitled() calls answer
|
|
@@ -458,12 +878,21 @@ interface CrossdeckErrorPayload {
|
|
|
458
878
|
requestId?: string;
|
|
459
879
|
/** HTTP status code if the error came from an API response. */
|
|
460
880
|
status?: number;
|
|
881
|
+
/**
|
|
882
|
+
* Server-suggested wait (in milliseconds) before retrying. Populated
|
|
883
|
+
* from the `Retry-After` response header on 429 / 503. The header
|
|
884
|
+
* spec allows either delta-seconds or an HTTP-date; the parser below
|
|
885
|
+
* normalises both to milliseconds. Consumers MUST honour this — the
|
|
886
|
+
* server is telling you the safe rate.
|
|
887
|
+
*/
|
|
888
|
+
retryAfterMs?: number;
|
|
461
889
|
}
|
|
462
890
|
declare class CrossdeckError extends Error {
|
|
463
891
|
readonly type: CrossdeckErrorType;
|
|
464
892
|
readonly code: string;
|
|
465
893
|
readonly requestId?: string;
|
|
466
894
|
readonly status?: number;
|
|
895
|
+
readonly retryAfterMs?: number;
|
|
467
896
|
constructor(payload: CrossdeckErrorPayload);
|
|
468
897
|
}
|
|
469
898
|
|
|
@@ -526,9 +955,45 @@ declare class MemoryStorage implements KeyValueStorage {
|
|
|
526
955
|
* fetch shim, no transitive deps.
|
|
527
956
|
*/
|
|
528
957
|
declare const SDK_NAME = "@cross-deck/web";
|
|
529
|
-
declare const SDK_VERSION = "0.
|
|
958
|
+
declare const SDK_VERSION = "1.0.0";
|
|
530
959
|
declare const DEFAULT_BASE_URL = "https://api.cross-deck.com/v1";
|
|
531
960
|
|
|
961
|
+
/**
|
|
962
|
+
* Machine-readable index of every error code the SDK can throw, with
|
|
963
|
+
* a short description and a hint on what action to take. Published
|
|
964
|
+
* verbatim as `crossdeck-error-codes.json` in the npm tarball so AI
|
|
965
|
+
* integration assistants, error-aggregator dashboards (Sentry,
|
|
966
|
+
* DataDog), and the Crossdeck dashboard can render human-friendly
|
|
967
|
+
* messages without parsing freeform `message` strings.
|
|
968
|
+
*
|
|
969
|
+
* Stripe publishes the same surface at stripe.com/docs/error-codes;
|
|
970
|
+
* developers love it because every code has a canonical "what does
|
|
971
|
+
* this mean / what should I do" answer.
|
|
972
|
+
*
|
|
973
|
+
* Adding a new error code:
|
|
974
|
+
* 1. Add the code string to the union in `errors.ts` (where used).
|
|
975
|
+
* 2. Add an entry here.
|
|
976
|
+
* 3. The next `npm run build` regenerates the JSON sidecar.
|
|
977
|
+
*
|
|
978
|
+
* Keep entries terse — the consumer surfaces this in tooltips and
|
|
979
|
+
* automated tickets, not in long-form docs.
|
|
980
|
+
*/
|
|
981
|
+
interface ErrorCodeEntry {
|
|
982
|
+
/** The string thrown as CrossdeckError.code. */
|
|
983
|
+
code: string;
|
|
984
|
+
/** CrossdeckError.type — broad category. */
|
|
985
|
+
type: "authentication_error" | "permission_error" | "invalid_request_error" | "rate_limit_error" | "internal_error" | "network_error" | "configuration_error";
|
|
986
|
+
/** One-sentence description. Surfaced verbatim in dashboards. */
|
|
987
|
+
description: string;
|
|
988
|
+
/** What the developer should do. Imperative phrasing. */
|
|
989
|
+
resolution: string;
|
|
990
|
+
/** True for codes the SDK can auto-recover from (no developer action). */
|
|
991
|
+
retryable: boolean;
|
|
992
|
+
}
|
|
993
|
+
declare const CROSSDECK_ERROR_CODES: readonly ErrorCodeEntry[];
|
|
994
|
+
/** Lookup helper — returns the entry matching a CrossdeckError.code, or undefined. */
|
|
995
|
+
declare function getErrorCode(code: string): ErrorCodeEntry | undefined;
|
|
996
|
+
|
|
532
997
|
/**
|
|
533
998
|
* Device + environment enrichment.
|
|
534
999
|
*
|
|
@@ -563,4 +1028,4 @@ interface DeviceInfo {
|
|
|
563
1028
|
appVersion?: string;
|
|
564
1029
|
}
|
|
565
1030
|
|
|
566
|
-
export { type AliasResult, type AuditRail, type AutoTrackOptions, Crossdeck, CrossdeckClient, CrossdeckError, type CrossdeckErrorPayload, type CrossdeckErrorType, type CrossdeckOptions, DEFAULT_BASE_URL, type DeviceInfo, type Diagnostics, type EntitlementsListResponse, type Environment, type EventProperties, type HeartbeatResponse, type IdentifyOptions, type KeyValueStorage, MemoryStorage, type Platform, type PublicEntitlement, type PurchaseResult, SDK_NAME, SDK_VERSION };
|
|
1031
|
+
export { type AliasResult, type AuditRail, type AutoTrackOptions, type Breadcrumb, type BreadcrumbCategory, type BreadcrumbLevel, CROSSDECK_ERROR_CODES, type CapturedError, type ConsentState, Crossdeck, CrossdeckClient, CrossdeckError, type CrossdeckErrorPayload, type CrossdeckErrorType, type CrossdeckOptions, DEFAULT_BASE_URL, type DeviceInfo, type Diagnostics, type EntitlementsListResponse, type Environment, type ErrorCodeEntry, type ErrorLevel, type EventProperties, type GroupTraits, type HeartbeatResponse, type IdentifyOptions, type KeyValueStorage, MemoryStorage, type Platform, type PublicEntitlement, type PurchaseResult, SDK_NAME, SDK_VERSION, type StackFrame, getErrorCode };
|