@sazabi/react-native 0.0.0-stage → 0.1.1

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.
Files changed (38) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +286 -2
  3. package/dist/chunk-h2nxjrr9.js +5 -0
  4. package/dist/chunk-h2nxjrr9.js.map +15 -0
  5. package/dist/client-sdk-core/attributes.d.ts +8 -0
  6. package/dist/client-sdk-core/before-send.d.ts +15 -0
  7. package/dist/client-sdk-core/clock.d.ts +6 -0
  8. package/dist/client-sdk-core/console.d.ts +23 -0
  9. package/dist/client-sdk-core/index.d.ts +22 -0
  10. package/dist/client-sdk-core/intake-url.d.ts +38 -0
  11. package/dist/client-sdk-core/network-capture.d.ts +90 -0
  12. package/dist/client-sdk-core/patch-marker.d.ts +8 -0
  13. package/dist/client-sdk-core/pointer.d.ts +73 -0
  14. package/dist/client-sdk-core/random.d.ts +11 -0
  15. package/dist/client-sdk-core/sdk-health.d.ts +37 -0
  16. package/dist/client-sdk-core/session.d.ts +44 -0
  17. package/dist/client-sdk-core/trace-context.d.ts +33 -0
  18. package/dist/client-sdk-core/transport.d.ts +142 -0
  19. package/dist/client-sdk-core/types.d.ts +82 -0
  20. package/dist/client-sdk-core/url.d.ts +30 -0
  21. package/dist/errors.d.ts +46 -0
  22. package/dist/host.d.ts +30 -0
  23. package/dist/index.d.ts +52 -0
  24. package/dist/index.js +6 -0
  25. package/dist/index.js.map +28 -0
  26. package/dist/interactions.d.ts +40 -0
  27. package/dist/lifecycle.d.ts +16 -0
  28. package/dist/navigation.d.ts +24 -0
  29. package/dist/network.d.ts +12 -0
  30. package/dist/outbox.d.ts +53 -0
  31. package/dist/process-state.d.ts +49 -0
  32. package/dist/register.d.ts +1 -0
  33. package/dist/register.js +4 -0
  34. package/dist/register.js.map +10 -0
  35. package/dist/storage.d.ts +42 -0
  36. package/dist/types.d.ts +117 -0
  37. package/dist/version.d.ts +4 -0
  38. package/package.json +62 -4
@@ -0,0 +1,15 @@
1
+ import type { BeforeSend, ClientEvent } from "./types.js";
2
+ /**
3
+ * The host's `beforeSend` hook, run so that it can never break the SDK: a
4
+ * hook that throws or returns something other than an event drops the event
5
+ * (a hook that redacts must not fail open) and warns once per reason. An
6
+ * event captured synchronously while the hook runs (a `log()` or
7
+ * `addEvent()` call inside it) is dropped rather than sent around the hook
8
+ * or fed back into it, so a hook can never recurse. A request the hook
9
+ * starts completes later and produces an ordinary network event the hook
10
+ * sees like any other; the hook is responsible for excluding it.
11
+ */
12
+ export declare const createBeforeSendGate: (hook: BeforeSend | undefined, warnOnce: (warning: {
13
+ reason: string;
14
+ message: string;
15
+ }) => void) => ((event: ClientEvent) => ClientEvent | null);
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Monotonic milliseconds for durations and ordering. `performance.now()` where
3
+ * the host has it (every browser, React Native on Hermes); `Date.now()` as the
4
+ * fallback so the core never assumes a global.
5
+ */
6
+ export declare const monotonicNow: () => number;
@@ -0,0 +1,23 @@
1
+ import type { ClientEvent, ConsoleCaptureLevel } from "./types.js";
2
+ export interface ConsoleCaptureOptions {
3
+ emit(event: ClientEvent): void;
4
+ levels: ConsoleCaptureLevel[];
5
+ /** Lines the SDK itself prints start with this and are never re-captured. */
6
+ selfPrefix: string;
7
+ }
8
+ export interface ConsoleCaptureHandle {
9
+ teardown(): void;
10
+ }
11
+ /**
12
+ * Print through the console without mirroring the line into the event
13
+ * stream: for a host-facing message the SDK prints on another component's
14
+ * behalf (the dev-mode rejection warning it relays for React Native).
15
+ */
16
+ export declare const withoutConsoleCapture: (print: () => void) => void;
17
+ /**
18
+ * Mirror selected console levels into the event stream as `log` events.
19
+ * The original console method always runs first (devtools behavior is never
20
+ * altered) and capture failures are swallowed. Lines the SDK itself prints
21
+ * (prefixed with `selfPrefix`) are skipped to avoid self-capture.
22
+ */
23
+ export declare const installConsoleCapture: (options: ConsoleCaptureOptions) => ConsoleCaptureHandle;
@@ -0,0 +1,22 @@
1
+ /**
2
+ * @sazabi/client-sdk-core: the platform-neutral half of the Sazabi client
3
+ * SDKs. Private and bundled into @sazabi/browser and @sazabi/react-native at
4
+ * build time; never published, never imported by anything else. Compiles
5
+ * without the DOM lib (see host-globals.d.ts), so a browser global here is a
6
+ * typecheck error.
7
+ */
8
+ export { flattenAttributes, truncateValue } from "./attributes.js";
9
+ export { monotonicNow } from "./clock.js";
10
+ export { type ConsoleCaptureHandle, type ConsoleCaptureOptions, installConsoleCapture, withoutConsoleCapture, } from "./console.js";
11
+ export { intakeBaseFromUrl, publicKeyFromIntakeUrl, type ResolvedIntakeEndpoint, resolveIntakeEndpoint, } from "./intake-url.js";
12
+ export { activateNetworkCapture, consumeWrapperSeen, deactivateNetworkCapture, emitNetworkEvent, ensureXhrPatch, getActiveNetworkCapture, isAborted, isActiveInjectionTarget, isAllowedForInjection, type NetworkCaptureOptions, type NetworkEventDetails, readRequestId, recordWrapperRequest, redactUrl, resetWrapperLedger, severityForStatus, uninstallXhrPatch, urlPath, } from "./network-capture.js";
13
+ export { isWrapped, markWrapped, WRAPPED_MARKER } from "./patch-marker.js";
14
+ export { createPointerTracker, type DerivedTap, type PointerContact, type PointerTarget, type PointerTracker, type PointerTrackerOptions, type PointerType, } from "./pointer.js";
15
+ export { createRandomBytes, newPrefixedId, type RandomBytes, randomHex, toHex, } from "./random.js";
16
+ export { createMemoryStore, createSessionManager, type SessionManager, type SessionManagerOptions, type SessionStore, } from "./session.js";
17
+ export { createTraceContextManager, type HostSpanContext, readHostSpanContext, type RequestTraceContext, type TraceContextManager, } from "./trace-context.js";
18
+ export { createSdkHealthReporter, SDK_DIAGNOSTIC_KEY, type SdkHealthDiagnostic, type SdkHealthReport, type SdkHealthReporter, } from "./sdk-health.js";
19
+ export { createTransport, encodeLogRecord, type FetchLike, type OtlpLogRecord, type Transport, type TransportOptions, } from "./transport.js";
20
+ export { isUnderBase, type ParsedUrl, parseAbsoluteUrl } from "./url.js";
21
+ export { createBeforeSendGate } from "./before-send.js";
22
+ export type { AttributeValue, BeforeSend, ClientEvent, ClientEventType, ConsoleCaptureConfig, ConsoleCaptureLevel, EventAttributes, EventSeverity, NetworkCaptureConfig, PointerCaptureConfig, } from "./types.js";
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Resolving the intake endpoint from a single URL.
3
+ *
4
+ * Every Sazabi log source is configured with one intake URL, and public keys
5
+ * stay a low-level detail customers never handle. The key is already in the
6
+ * URL: the intake edge reconstructs it from the hostname
7
+ * (terraform/main/cloudfront-functions/intake-router.js), so the SDK does the
8
+ * same reconstruction locally and sends the Bearer header itself.
9
+ *
10
+ * Two keyed shapes exist, matching the edge's two grammars:
11
+ * {hex}.{region}.intake.{domain} prefixless (what log sources issue)
12
+ * {adapter}-{hex}.{region}.intake.{domain} keyed-adapter (legacy, still served)
13
+ *
14
+ * A URL in neither shape is not an error: it may be a proxy or a custom
15
+ * endpoint. The SDK then requires an explicit `publicKey`, so a
16
+ * misconfiguration surfaces as a clear warning rather than silent no-auth.
17
+ */
18
+ export interface ResolvedIntakeEndpoint {
19
+ /**
20
+ * Base to POST to: origin plus any proxy path prefix, no trailing slash.
21
+ * The transport appends `/v1/logs`.
22
+ */
23
+ baseUrl: string;
24
+ /** Credential for the Authorization header, derived or explicit. */
25
+ publicKey: string;
26
+ }
27
+ /** Public key embedded in a keyed intake hostname, if it carries one. */
28
+ export declare const publicKeyFromIntakeUrl: (url: string) => string | undefined;
29
+ export declare const intakeBaseFromUrl: (url: string) => string | undefined;
30
+ /**
31
+ * Resolve the endpoint from config. An explicit `publicKey` always wins, so a
32
+ * non-keyed or proxied endpoint stays configurable.
33
+ */
34
+ export declare const resolveIntakeEndpoint: (config: {
35
+ intakeUrl?: string;
36
+ intakeHost?: string;
37
+ publicKey?: string;
38
+ }) => ResolvedIntakeEndpoint | undefined;
@@ -0,0 +1,90 @@
1
+ import type { RequestTraceContext, TraceContextManager } from "./trace-context.js";
2
+ import type { ClientEvent, EventSeverity } from "./types.js";
3
+ /**
4
+ * Everything the network patches need at request time. A binding builds one
5
+ * at init and activates it; the patches themselves install dormant (possibly
6
+ * before init, via a register entrypoint) and consult the active slot per
7
+ * call. Undefined slot = transparent passthrough.
8
+ */
9
+ export interface NetworkCaptureOptions {
10
+ emit(event: ClientEvent): void;
11
+ traceContext: TraceContextManager;
12
+ /** Emit network events. */
13
+ capture: boolean;
14
+ /** Inject `traceparent` into allowlisted requests. */
15
+ propagateTraceContext: boolean;
16
+ /** Injection allowlist; empty defers to `isDefaultInjectionTarget`. */
17
+ allowlist: (string | RegExp)[];
18
+ /** Query parameter names redacted from `url.full` beside the default list. */
19
+ redactQueryParams?: (string | RegExp)[];
20
+ /** Response headers probed for a platform request id, in priority order. */
21
+ requestIdHeaders: string[];
22
+ /** SDK-internal endpoints: never captured, never injected. */
23
+ isInternalUrl(url: string): boolean;
24
+ /**
25
+ * Absolute URL for a request target as the host resolves it, with embedded
26
+ * credentials stripped; undefined when the input cannot be parsed.
27
+ */
28
+ resolveUrl(raw: string): string | undefined;
29
+ /** Injection policy when the allowlist is empty (same-origin on the web). */
30
+ isDefaultInjectionTarget(resolved: string): boolean;
31
+ /** Origin elided from the display path in event bodies, if the host has one. */
32
+ displayOrigin?: string;
33
+ /** Host-reported offline state; a failure while offline reads as an abort. */
34
+ isOffline?(): boolean;
35
+ }
36
+ export declare const activateNetworkCapture: (options: NetworkCaptureOptions) => void;
37
+ export declare const deactivateNetworkCapture: () => void;
38
+ export declare const getActiveNetworkCapture: () => NetworkCaptureOptions | undefined;
39
+ /** Compact display path: pathname when on the display origin, host+path otherwise. */
40
+ export declare const urlPath: (resolved: string, displayOrigin?: string) => string;
41
+ /** Status 0 means transport failure. */
42
+ export declare const severityForStatus: (status: number) => EventSeverity;
43
+ /**
44
+ * A recorded URL: the value of every sensitive parameter after the path
45
+ * replaced with `[redacted]`, in the query and in the fragment. `?` separates
46
+ * inside the fragment as well, so a hash router's `#/reset?token=…` is
47
+ * covered. Credentials in the authority are already gone (`resolveUrl`
48
+ * strips them). Applied to `url.full` of requests, resource rows and
49
+ * navigations, and to the page URL the browser SDK stamps on every event.
50
+ */
51
+ export declare const redactUrl: (url: string, extra?: (string | RegExp)[]) => string;
52
+ export declare const isAllowedForInjection: (options: NetworkCaptureOptions, resolved: string) => boolean;
53
+ /** Injection eligibility for the active config; used by the browser's resource
54
+ * observer to decide whether a bypassed request is a diagnosable gap. */
55
+ export declare const isActiveInjectionTarget: (resolved: string) => boolean;
56
+ export declare const recordWrapperRequest: (url: string) => void;
57
+ /** Consume a wrapper-seen record matching this request, if any. */
58
+ export declare const consumeWrapperSeen: (url: string, startTime: number) => boolean;
59
+ export declare const resetWrapperLedger: () => void;
60
+ export interface NetworkEventDetails {
61
+ kind: "fetch" | "xhr";
62
+ method: string;
63
+ resolvedUrl: string;
64
+ status: number;
65
+ durationMs: number;
66
+ severity?: EventSeverity;
67
+ failureMessage?: string;
68
+ injected?: RequestTraceContext;
69
+ requestId?: string;
70
+ requestIdHeader?: string;
71
+ }
72
+ export declare const emitNetworkEvent: (options: NetworkCaptureOptions, details: NetworkEventDetails) => void;
73
+ export declare const readRequestId: (options: NetworkCaptureOptions, getHeader: (name: string) => string | null | undefined) => {
74
+ requestId?: string;
75
+ requestIdHeader?: string;
76
+ };
77
+ export declare const isAborted: (error: unknown, signalAborted?: boolean, offline?: boolean) => boolean;
78
+ /**
79
+ * Install the dormant XMLHttpRequest patch around whatever implementation is
80
+ * currently live. Idempotent, and re-wraps when a later actor replaced the
81
+ * prototype methods after an earlier install (the displaced wrapper is an
82
+ * inert passthrough, so single-processing holds).
83
+ */
84
+ export declare const ensureXhrPatch: () => void;
85
+ /**
86
+ * Restore the natives if the current implementations are still ours; if
87
+ * someone patched on top of us, restoring would clobber them, and the
88
+ * inactive wrappers pass through.
89
+ */
90
+ export declare const uninstallXhrPatch: () => void;
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Shared across every Sazabi client SDK so two SDKs bundled into one app
3
+ * recognize each other's wrappers and never double-wrap a primitive.
4
+ */
5
+ export declare const WRAPPED_MARKER = "__sazabiBrowserWrapped";
6
+ /** Best-effort idempotency marker on wrapper functions. */
7
+ export declare const markWrapped: (fn: object) => void;
8
+ export declare const isWrapped: (fn: unknown) => boolean;
@@ -0,0 +1,73 @@
1
+ import { type RandomBytes } from "./random.js";
2
+ import type { ClientEvent } from "./types.js";
3
+ /**
4
+ * Pointer episodes: one row per contact from down to up, for mouse, touch,
5
+ * and pen alike. A binding feeds raw contacts in (DOM pointer events in the
6
+ * browser, the responder's touch arrays on mobile) and the tracker emits the
7
+ * episode rows plus the derived `click` and `rage_click` rows. Movement is
8
+ * sampled, never streamed: a position sample every `sampleIntervalMs` is
9
+ * packed into the row, and a contact that outlives `partialIntervalMs` emits
10
+ * a partial row at that cadence so a long scroll is not a blind spot.
11
+ */
12
+ export type PointerType = "mouse" | "touch" | "pen";
13
+ export interface PointerTarget {
14
+ /** Developer-authored name for the widget under the pointer, if any. */
15
+ name?: string;
16
+ /** Stable-ish identifier: a selector on the web, a component path on mobile. */
17
+ id?: string;
18
+ /** Element tag or component kind. */
19
+ tag?: string;
20
+ /** Whether a tap here is on something that should respond. */
21
+ interactive: boolean;
22
+ }
23
+ export interface PointerContact {
24
+ id: number | string;
25
+ type: PointerType;
26
+ primary: boolean;
27
+ x: number;
28
+ y: number;
29
+ pressure?: number;
30
+ /**
31
+ * The button that started a mouse contact (DOM `button`: 0 primary, 1
32
+ * auxiliary, 2 secondary). Absent for touch and pen. Only a primary-button
33
+ * contact can derive a click: a short right or middle press is an episode
34
+ * without a tap, matching the host's `contextmenu` and `auxclick`.
35
+ */
36
+ button?: number;
37
+ target?: PointerTarget;
38
+ }
39
+ export interface DerivedTap {
40
+ x: number;
41
+ y: number;
42
+ target?: PointerTarget;
43
+ /** Absent for a tap no contact produced (keyboard activation, script). */
44
+ gestureId?: string;
45
+ }
46
+ export interface PointerTrackerOptions {
47
+ emit(event: ClientEvent): void;
48
+ randomBytes: RandomBytes;
49
+ /** Spacing of packed position samples. Default 1000. */
50
+ sampleIntervalMs?: number;
51
+ /** Cadence of partial rows for long contacts. Default 10000. */
52
+ partialIntervalMs?: number;
53
+ /** Every derived tap, so a binding can run its own dead-tap detection. */
54
+ onTap?(tap: DerivedTap): void;
55
+ }
56
+ export interface PointerTracker {
57
+ down(contact: PointerContact): void;
58
+ move(id: number | string, x: number, y: number, pressure?: number): void;
59
+ up(id: number | string, x: number, y: number): void;
60
+ cancel(id: number | string): void;
61
+ /**
62
+ * A click that no pointer contact produced: keyboard activation or a
63
+ * script calling `click()`. Emits the `click` row and runs the same rage
64
+ * and dead-tap derivation, with no episode behind it.
65
+ */
66
+ tap(tap: {
67
+ x: number;
68
+ y: number;
69
+ target?: PointerTarget;
70
+ }): void;
71
+ teardown(): void;
72
+ }
73
+ export declare const createPointerTracker: (options: PointerTrackerOptions) => PointerTracker;
@@ -0,0 +1,11 @@
1
+ export type RandomBytes = (length: number) => Uint8Array;
2
+ /**
3
+ * Random bytes for session, trace, and gesture ids. Web Crypto where the host
4
+ * has it; a seeded fallback where it does not (React Native without a
5
+ * `crypto.getRandomValues` polyfill). The ids need uniqueness, not secrecy,
6
+ * so the fallback is acceptable there and is never used where crypto exists.
7
+ */
8
+ export declare const createRandomBytes: () => RandomBytes;
9
+ export declare const toHex: (bytes: Uint8Array) => string;
10
+ export declare const randomHex: (randomBytes: RandomBytes, byteLength: number) => string;
11
+ export declare const newPrefixedId: (randomBytes: RandomBytes, prefix: string) => string;
@@ -0,0 +1,37 @@
1
+ import type { ClientEvent, EventAttributes } from "./types.js";
2
+ /**
3
+ * What an `sdk_health` row reports: the SDK's own delivery trouble, so a
4
+ * misconfigured or unreachable app is visible to Sazabi before the customer
5
+ * reports it. Each value is a bounded enumeration, never free text.
6
+ *
7
+ * - `intake_rejected`: the intake refused a batch for good (a 4xx other
8
+ * than 408 and 429); `app.sdk.http_status` carries the status.
9
+ * - `intake_unreachable`: uploads failed on the network (a dropped
10
+ * connection, a timeout) and the intake later answered; `app.sdk.attempts`
11
+ * is how many backoff windows the outage spanned (one per failed try,
12
+ * with concurrent uploads failing together counted once).
13
+ * - `outbox_discarded`: stored crash reports were dropped, by the caps or
14
+ * because they were not valid reports; `app.sdk.outbox_discarded` counts
15
+ * what is known lost, and `app.sdk.outbox_unsaved` what a saved queue had
16
+ * left out at the caps (possible losses: their live upload may have
17
+ * finished before the app was killed) (mobile only).
18
+ * - `outbox_persist_failed`: the storage adapter refused a write of the
19
+ * crash outbox or the saved queue (mobile only).
20
+ */
21
+ export type SdkHealthDiagnostic = "intake_rejected" | "intake_unreachable" | "outbox_discarded" | "outbox_persist_failed";
22
+ export interface SdkHealthReport {
23
+ diagnostic: SdkHealthDiagnostic;
24
+ /** The counters of the condition (`app.sdk.http_status`, `app.sdk.attempts`, ...). */
25
+ attributes?: EventAttributes;
26
+ }
27
+ export type SdkHealthReporter = (report: SdkHealthReport) => void;
28
+ export declare const SDK_DIAGNOSTIC_KEY = "app.sdk.diagnostic";
29
+ /**
30
+ * Turns health reports into `sdk_health` rows, at most one per diagnostic
31
+ * for the life of the reporter (one SDK session). The bound is what keeps
32
+ * the SDK from storming its own intake: a health row the intake refuses is
33
+ * a rejection like any other and is never reported again, and a flapping
34
+ * network produces one row per launch, not one per outage. Never throws
35
+ * into the host.
36
+ */
37
+ export declare const createSdkHealthReporter: (emit: (event: ClientEvent) => void) => SdkHealthReporter;
@@ -0,0 +1,44 @@
1
+ import { type RandomBytes } from "./random.js";
2
+ /**
3
+ * Synchronous key-value access the session machine reads through on every
4
+ * call. A browser binding backs it with `localStorage` (so sibling tabs
5
+ * converge) and `sessionStorage`; a mobile binding backs it with an
6
+ * in-memory mirror hydrated from its storage adapter and written through.
7
+ */
8
+ export interface SessionStore {
9
+ get(key: string): string | null;
10
+ set(key: string, value: string): void;
11
+ remove(key: string): void;
12
+ }
13
+ /** A store that lives for the process only: the fallback on every host. */
14
+ export declare const createMemoryStore: () => SessionStore;
15
+ export interface SessionManager {
16
+ getSessionId(): string;
17
+ getWindowId(): string;
18
+ getDistinctId(): string | undefined;
19
+ /**
20
+ * Set the client-asserted identity. Switching from one identity directly
21
+ * to a different one rotates the session and window ids so two users on a
22
+ * shared device never thread into one timeline.
23
+ */
24
+ setDistinctId(distinctId: string): void;
25
+ /** Logout: clear identity and rotate both session and window ids. */
26
+ reset(): void;
27
+ /** Record user activity; may rotate the session on idle/max-age expiry. */
28
+ touch(): void;
29
+ }
30
+ export interface SessionManagerOptions {
31
+ /** Visit-scoped state: session id and identity. */
32
+ store: SessionStore;
33
+ /** Window-scoped state: one browser tab, or one app launch. */
34
+ windowStore: SessionStore;
35
+ randomBytes: RandomBytes;
36
+ }
37
+ /**
38
+ * Session identity. `session.id` is the visit (last-writer-wins on rotation
39
+ * races between windows; a split session is cosmetic, not data loss).
40
+ * `session.window_id` is this window. `session.distinct_id` is read through
41
+ * the store so identify/reset in one window is visible to siblings on their
42
+ * next event.
43
+ */
44
+ export declare const createSessionManager: (options: SessionManagerOptions) => SessionManager;
@@ -0,0 +1,33 @@
1
+ import { type RandomBytes } from "./random.js";
2
+ export interface RequestTraceContext {
3
+ /** Headers to inject (`traceparent`, and `tracestate` when present). */
4
+ headers: Record<string, string>;
5
+ traceId: string;
6
+ spanId: string;
7
+ }
8
+ export interface TraceContextManager {
9
+ /** Trace context for one outgoing request, minted at request time. */
10
+ forRequest(): RequestTraceContext;
11
+ }
12
+ /** The subset of an OTel SpanContext this module consumes. */
13
+ export interface HostSpanContext {
14
+ traceId: string;
15
+ spanId: string;
16
+ traceFlags: number;
17
+ traceState?: {
18
+ serialize(): string;
19
+ };
20
+ }
21
+ export declare const readHostSpanContext: () => HostSpanContext | undefined;
22
+ /**
23
+ * W3C trace context for outgoing requests: hand-rolled header formatting
24
+ * (equivalence-tested against OTel's W3CTraceContextPropagator), zero
25
+ * runtime dependencies. If the host app runs its own OTel and has an active
26
+ * span, that span's context (including its flags and tracestate) is reused
27
+ * so we join the customer's trace instead of forking a new one; otherwise a
28
+ * fresh root is minted with the sampled flag defaulting to off (`-00`).
29
+ */
30
+ export declare const createTraceContextManager: (options: {
31
+ sampledFlag: boolean;
32
+ randomBytes: RandomBytes;
33
+ }) => TraceContextManager;
@@ -0,0 +1,142 @@
1
+ import type { SdkHealthReport } from "./sdk-health.js";
2
+ import type { ClientEvent, EventAttributes, EventSeverity } from "./types.js";
3
+ type OtlpAnyValue = {
4
+ stringValue: string;
5
+ } | {
6
+ intValue: string;
7
+ } | {
8
+ doubleValue: number;
9
+ } | {
10
+ boolValue: boolean;
11
+ };
12
+ interface OtlpKeyValue {
13
+ key: string;
14
+ value: OtlpAnyValue;
15
+ }
16
+ export interface OtlpLogRecord {
17
+ timeUnixNano: string;
18
+ severityNumber: number;
19
+ severityText: EventSeverity;
20
+ body: {
21
+ stringValue: string;
22
+ };
23
+ attributes: OtlpKeyValue[];
24
+ /** OTLP/JSON encodes trace/span ids as hex (unlike proto3 JSON's base64). */
25
+ traceId?: string;
26
+ spanId?: string;
27
+ }
28
+ export declare const encodeLogRecord: (event: ClientEvent, baseAttributes: EventAttributes) => OtlpLogRecord;
29
+ /**
30
+ * The slice of `fetch` the transport uses. Declared structurally so the core
31
+ * needs no DOM lib; every host's real `fetch` satisfies it.
32
+ */
33
+ export type FetchLike = (url: string, init: {
34
+ method: "POST";
35
+ headers: Record<string, string>;
36
+ body: string;
37
+ keepalive: boolean;
38
+ /** An `AbortSignal` where the host has `AbortController`; aborts a timed-out upload. */
39
+ signal?: unknown;
40
+ }) => Promise<unknown>;
41
+ /**
42
+ * UTF-8 byte length of a string, without `TextEncoder` (Hermes has none).
43
+ * Request budgets are bytes on the wire; a string's `length` counts UTF-16
44
+ * units and undercounts non-ASCII text up to threefold.
45
+ */
46
+ export declare const utf8Length: (text: string) => number;
47
+ /** Stamped on the first row delivered after events were dropped: how many. */
48
+ export declare const DROPPED_EVENTS_KEY = "app.sdk.dropped_events";
49
+ /**
50
+ * Names the sending SDK on every upload, `<package>/<version>`: the input
51
+ * for the intake gateway's per-SDK request label (a separate change), which
52
+ * can then count a request by SDK even when it rejects it before the key is
53
+ * known. The gateway's CORS allow-list admits it, so a browser preflight
54
+ * passes.
55
+ */
56
+ export declare const SDK_HEADER = "x-sazabi-sdk";
57
+ export interface TransportOptions {
58
+ /** Full OTLP logs endpoint, e.g. `https://<key>.<region>.intake.<domain>/v1/logs`. */
59
+ url: string;
60
+ publicKey: string;
61
+ resourceAttributes: EventAttributes;
62
+ /** OTLP instrumentation scope: the emitting SDK's name and version. */
63
+ scope: {
64
+ name: string;
65
+ version: string;
66
+ };
67
+ /**
68
+ * The fetch to deliver with, captured before the SDK's network patch
69
+ * installs, so exporter traffic can never recurse through our own wrapper
70
+ * or appear in the captured event stream.
71
+ */
72
+ fetchImpl: FetchLike;
73
+ flushIntervalMs: number;
74
+ maxBatchBytes: number;
75
+ maxQueuedEvents: number;
76
+ /** Reports a dropped batch or a full queue; called once per reason. */
77
+ warn?(message: string): void;
78
+ /**
79
+ * The SDK's own delivery health: a batch the intake refused for good
80
+ * (`intake_rejected`, with the status) and uploads that failed on the
81
+ * network before one got through (`intake_unreachable`, with the number
82
+ * of failed tries). The binding turns these into `sdk_health` rows, once
83
+ * per condition per session.
84
+ */
85
+ onHealth?(report: SdkHealthReport): void;
86
+ /**
87
+ * Called when an upload settles with nothing queued, nothing in flight, and
88
+ * every row of the last `snapshotEnvelopes` delivered or refused for good:
89
+ * a saved row evicted from the full queue, or given up by `shutdown()`
90
+ * while the intake was unreachable, keeps it from firing until the next
91
+ * snapshot, so the host's saved copy stays.
92
+ */
93
+ onIdle?(): void;
94
+ }
95
+ export interface Transport {
96
+ enqueue(record: OtlpLogRecord): void;
97
+ /**
98
+ * Deliver everything queued. `keepalive: true` is for browser unload paths;
99
+ * each request body must stay inside the browser's ~64 KiB keepalive
100
+ * budget, which enqueue-side chunking guarantees via `maxBatchBytes`. Hosts
101
+ * without keepalive ignore the flag.
102
+ */
103
+ flush(options?: {
104
+ keepalive?: boolean;
105
+ }): Promise<void>;
106
+ /**
107
+ * Encode everything queued into standalone request bodies without sending,
108
+ * leaving the queue empty. The mobile crash outbox persists these and posts
109
+ * them on the next launch; `recordAttributes` are appended to every record
110
+ * (the outbox stamps its replay id there).
111
+ */
112
+ drainEnvelopes(recordAttributes?: EventAttributes): string[];
113
+ /**
114
+ * Encode everything queued into standalone request bodies and keep it
115
+ * queued. `recordAttributes` are stamped on the queued records themselves,
116
+ * so the live upload and the saved copy carry the same values (the mobile
117
+ * SDK saves its queue this way when the app leaves the foreground). Rows an
118
+ * earlier snapshot saved whose upload is still in flight are included as
119
+ * they were sent: the copy this one replaces held them.
120
+ */
121
+ snapshotEnvelopes(recordAttributes: EventAttributes): string[];
122
+ /**
123
+ * While held, `enqueue` never flushes on its own, neither on the byte
124
+ * budget nor by scheduling the timer: the mobile crash path holds the
125
+ * transport while it stamps the fatal row and everything buffered before
126
+ * it, then drains the whole batch into the outbox. Without this, a fatal
127
+ * row that tips the queue over the budget would be flushed out of the queue
128
+ * into a request the dying process never completes.
129
+ */
130
+ holdFlushes(hold: boolean): void;
131
+ /**
132
+ * Deliver a previously drained envelope. Resolves `true` once the envelope
133
+ * is settled: the intake accepted it (2xx) or refused it for good (a 4xx
134
+ * other than 408 and 429, dropped with a warning). Resolves `false` when it
135
+ * is worth sending again (408, 429, 5xx, a network failure, a timeout), and
136
+ * never rejects, so a persisted envelope is deleted only once settled.
137
+ */
138
+ sendEnvelope(body: string): Promise<boolean>;
139
+ shutdown(): Promise<void>;
140
+ }
141
+ export declare const createTransport: (options: TransportOptions) => Transport;
142
+ export {};
@@ -0,0 +1,82 @@
1
+ export type AttributeValue = string | number | boolean;
2
+ export type EventAttributes = Record<string, AttributeValue | undefined>;
3
+ export type EventSeverity = "DEBUG" | "INFO" | "WARN" | "ERROR";
4
+ /**
5
+ * The shared `app.event_type` vocabulary. Every value means the same thing
6
+ * on every host; a binding that cannot produce one simply never emits it.
7
+ */
8
+ export type ClientEventType = "navigation" | "pointer" | "click" | "rage_click" | "dead_click" | "input" | "network" | "error" | "custom" | "log" | "lifecycle"
9
+ /** The SDK's own delivery health, one row per condition per session (`sdk-health.ts`). */
10
+ | "sdk_health";
11
+ /** An SDK-internal semantic event, before OTLP encoding. */
12
+ export interface ClientEvent {
13
+ type: ClientEventType;
14
+ body: string;
15
+ severity: EventSeverity;
16
+ timeUnixMs: number;
17
+ /** Set on network events when trace context was injected into the request. */
18
+ traceId?: string;
19
+ spanId?: string;
20
+ attributes: EventAttributes;
21
+ }
22
+ /**
23
+ * Runs on every event before it is queued. Return the event (edited or as
24
+ * given) to send it, or `null` to drop it. The hook sees the event's own
25
+ * attributes; the session and page fields are stamped afterwards.
26
+ */
27
+ export type BeforeSend = (event: ClientEvent) => ClientEvent | null;
28
+ export type ConsoleCaptureLevel = "error" | "warn" | "info" | "debug";
29
+ export interface ConsoleCaptureConfig {
30
+ /** Mirror console output into the event stream. Default true. */
31
+ capture?: boolean;
32
+ /** Console levels to mirror. Default ["error", "warn"]. */
33
+ levels?: ConsoleCaptureLevel[];
34
+ }
35
+ export interface PointerCaptureConfig {
36
+ /** Emit one `pointer` episode per contact, plus derived taps. Default true. */
37
+ capture?: boolean;
38
+ /** Spacing of the position samples packed into an episode. Default 1000. */
39
+ sampleIntervalMs?: number;
40
+ /**
41
+ * A contact open longer than this emits a partial row at this cadence so a
42
+ * long scroll or drag is not a blind spot until release. Default 10000.
43
+ */
44
+ partialIntervalMs?: number;
45
+ }
46
+ /** Network options every binding shares; a binding may extend the shape. */
47
+ export interface NetworkCaptureConfig {
48
+ /** Emit network events. Default true. */
49
+ capture?: boolean;
50
+ /** Inject W3C `traceparent` into allowlisted requests. Default true. */
51
+ propagateTraceContext?: boolean;
52
+ /**
53
+ * Response headers probed (in order) for a platform request id to attach
54
+ * as `app.network.request_id`, an exact join key against platform logs.
55
+ */
56
+ requestIdHeaders?: string[];
57
+ /**
58
+ * Requests eligible for `traceparent` injection. A string is a host
59
+ * (`api.example.com`), a domain with every subdomain (`.example.com`), or
60
+ * an origin with an optional path prefix (`https://api.example.com/v2`); a
61
+ * RegExp is tested against the full URL. An empty list falls back to the
62
+ * binding's default policy (same-origin in the browser, every request on
63
+ * mobile).
64
+ */
65
+ allowlist?: (string | RegExp)[];
66
+ /**
67
+ * Query parameter names whose values are replaced with `[redacted]` in
68
+ * every recorded URL: `url.full` of requests and navigations, the screen or
69
+ * page URL stamped on each event (`app.screen.url`), and in the browser the
70
+ * referrer (`app.navigation.from`). Beside the default list (token, access_token, refresh_token,
71
+ * id_token, api_key, apikey, key, secret, password, signature, sig,
72
+ * x-amz-signature, x-amz-credential, x-goog-signature, sentry_key,
73
+ * authorization, auth, session, sid, code). Strings match a name exactly,
74
+ * case-insensitively; RegExps are tested against the name.
75
+ */
76
+ redactQueryParams?: (string | RegExp)[];
77
+ /**
78
+ * Set the sampled flag (`-01`) on SDK-minted trace roots. Default false:
79
+ * the sampled bit steers customers' ParentBased backend samplers.
80
+ */
81
+ sampledFlag?: boolean;
82
+ }