@fixback/expo 0.9.0 → 0.9.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.
@@ -0,0 +1,165 @@
1
+ "use strict";
2
+ /**
3
+ * Mobile replay's arming (ADR-0044): whether the client records at all, the frame
4
+ * recorder it starts and stops on every boot, and turning a frozen window into the
5
+ * report's video.
6
+ *
7
+ * One piece of the headless client (`./client`), created over the state every piece
8
+ * shares (`./client-state`). The recorder and the window the composer froze live
9
+ * there, because the composer (`present`, `submit`) and boot (`./client-boot`) both
10
+ * reach them.
11
+ */
12
+ Object.defineProperty(exports, "__esModule", { value: true });
13
+ exports.createReplayArming = createReplayArming;
14
+ const sdk_core_1 = require("@fixback/sdk-core");
15
+ const replay_1 = require("./replay");
16
+ const replay_encode_1 = require("./replay-encode");
17
+ /** Mobile replay's arming over the client's shared state. */
18
+ function createReplayArming(s) {
19
+ const { options, adapters, warn, now, readEnvironment } = s;
20
+ /**
21
+ * Whether this app asked for mobile replay. Unlike every other stream, replay is
22
+ * **off until asked for** (ADR-0044): it records the device screen unmasked, so
23
+ * shipping an SDK update must never start it. Either spelling opts in —
24
+ * `capture: { replay: true }`, or simply passing a `replay` tuning object — and an
25
+ * explicit `capture: { replay: false }` always wins.
26
+ */
27
+ function replayRequested() {
28
+ if (typeof options.capture?.replay === "boolean")
29
+ return options.capture.replay;
30
+ return options.replay !== undefined;
31
+ }
32
+ /**
33
+ * Whether the server turned mobile replay off: the Project's replay toggle (the master kill
34
+ * switch, #138), or — on a server that sends it — `mobileReplay`, that toggle under the
35
+ * platform's `mobile_replay` feature flag for the workspace (#647). An older server sends only
36
+ * `replay`, which is then read as before.
37
+ */
38
+ function servedOff(served) {
39
+ if (typeof served?.replay === "boolean" && !served.replay)
40
+ return "project";
41
+ if (typeof served?.mobileReplay === "boolean" && !served.mobileReplay)
42
+ return "platform";
43
+ return null;
44
+ }
45
+ /**
46
+ * Whether replay records: this app asked for it, the server has not turned it off, and the
47
+ * platform can actually capture and encode frames.
48
+ */
49
+ function replayEnabled(served) {
50
+ if (!replayRequested())
51
+ return false;
52
+ if (servedOff(served) !== null)
53
+ return false;
54
+ return Boolean(adapters.captureReplayFrame && adapters.encodeReplaySegment);
55
+ }
56
+ /**
57
+ * Start (or stop) the frame recorder for the Project's served config. Re-armed on
58
+ * every boot, so a Project that turns replay off stops recording on the next boot
59
+ * rather than at the next launch — and the frames already buffered are deleted with
60
+ * the recorder, so nothing captured under the old answer can ship.
61
+ */
62
+ function armReplay(served) {
63
+ disarmReplay();
64
+ if (!replayEnabled(served)) {
65
+ const off = servedOff(served);
66
+ if (replayRequested() && off === "project") {
67
+ warn(`${sdk_core_1.WARN_PREFIX} replay is enabled in init but turned off for this Project — ` +
68
+ "no replay will be recorded until it is enabled in the dashboard.");
69
+ }
70
+ else if (replayRequested() && off === "platform") {
71
+ warn(`${sdk_core_1.WARN_PREFIX} replay is enabled in init but mobile replay isn't available for this ` +
72
+ "workspace yet — no replay will be recorded.");
73
+ }
74
+ else if (replayRequested() && !adapters.encodeReplaySegment) {
75
+ warn(`${sdk_core_1.WARN_PREFIX} replay is enabled in init but no native encoder is linked — ` +
76
+ "mobile replay needs a development build (`npx expo prebuild`), not Expo Go.");
77
+ }
78
+ return;
79
+ }
80
+ s.replayRecorder = (0, replay_1.startMobileReplayRecorder)(options.replay ?? {}, {
81
+ captureFrame: (quality) => adapters.captureReplayFrame(quality),
82
+ releaseFrame: (uri) => releaseReplayFile(uri),
83
+ isActive: adapters.isAppActive,
84
+ now,
85
+ });
86
+ }
87
+ /** Stop the recorder and drop every frame it owns (best-effort). */
88
+ function disarmReplay() {
89
+ try {
90
+ s.replayRecorder?.stop();
91
+ }
92
+ catch {
93
+ /* teardown is best-effort */
94
+ }
95
+ s.replayRecorder = null;
96
+ s.pendingReplayWindow = null;
97
+ }
98
+ /** Delete a replay frame or encoded window file (best-effort, repeat-safe). */
99
+ function releaseReplayFile(uri) {
100
+ try {
101
+ adapters.releaseReplayFrame?.(uri);
102
+ }
103
+ catch {
104
+ /* release is best-effort */
105
+ }
106
+ }
107
+ /** Give a frozen window back to the recorder, so its frames can be reclaimed. */
108
+ function releaseReplayWindow(window) {
109
+ if (!window)
110
+ return;
111
+ try {
112
+ s.replayRecorder?.release(window);
113
+ }
114
+ catch {
115
+ /* release is best-effort */
116
+ }
117
+ }
118
+ /**
119
+ * Freeze the window as it stands **now** and encode it — the auto-capture path's
120
+ * whole replay step (ADR-0044): an uncaught error has no composer to open, so the
121
+ * throw itself is the moment the window is taken. The window is given back as soon
122
+ * as the encode is done, since nothing holds a draft for an auto report.
123
+ */
124
+ async function freezeAndEncodeReplay() {
125
+ const window = s.replayRecorder?.snapshot() ?? null;
126
+ if (!window)
127
+ return null;
128
+ try {
129
+ return await encodeReplay(window);
130
+ }
131
+ finally {
132
+ releaseReplayWindow(window);
133
+ }
134
+ }
135
+ /**
136
+ * Encode a frozen window for transport (ADR-0044). `null` whenever the window
137
+ * cannot become a video — no recorder, nothing watchable buffered, or an encoder
138
+ * that declined — so the report ships without replay rather than not at all.
139
+ */
140
+ async function encodeReplay(window) {
141
+ if (!window || !adapters.encodeReplaySegment)
142
+ return null;
143
+ return (0, replay_encode_1.encodeReplayWindow)(window, {
144
+ encodeSegment: adapters.encodeReplaySegment,
145
+ windowSize: () => {
146
+ const env = readEnvironment();
147
+ return {
148
+ width: env.windowWidth ?? 0,
149
+ height: env.windowHeight ?? 0,
150
+ };
151
+ },
152
+ maxEdge: (0, replay_1.resolvedMaxEdge)(options.replay),
153
+ now,
154
+ });
155
+ }
156
+ return {
157
+ armReplay,
158
+ disarmReplay,
159
+ releaseReplayFile,
160
+ releaseReplayWindow,
161
+ freezeAndEncodeReplay,
162
+ encodeReplay,
163
+ };
164
+ }
165
+ //# sourceMappingURL=client-replay.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client-replay.js","sourceRoot":"","sources":["../src/client-replay.ts"],"names":[],"mappings":";AAAA;;;;;;;;;GASG;;AA6BH,gDAsJC;AAjLD,gDAAoE;AAGpE,qCAIkB;AAClB,mDAA4E;AAkB5E,6DAA6D;AAC7D,SAAgB,kBAAkB,CAAC,CAAc;IAC/C,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,GAAG,EAAE,eAAe,EAAE,GAAG,CAAC,CAAC;IAE5D;;;;;;OAMG;IACH,SAAS,eAAe;QACtB,IAAI,OAAO,OAAO,CAAC,OAAO,EAAE,MAAM,KAAK,SAAS;YAAE,OAAO,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC;QAChF,OAAO,OAAO,CAAC,MAAM,KAAK,SAAS,CAAC;IACtC,CAAC;IAED;;;;;OAKG;IACH,SAAS,SAAS,CAAC,MAAiC;QAClD,IAAI,OAAO,MAAM,EAAE,MAAM,KAAK,SAAS,IAAI,CAAC,MAAM,CAAC,MAAM;YAAE,OAAO,SAAS,CAAC;QAC5E,IAAI,OAAO,MAAM,EAAE,YAAY,KAAK,SAAS,IAAI,CAAC,MAAM,CAAC,YAAY;YAAE,OAAO,UAAU,CAAC;QACzF,OAAO,IAAI,CAAC;IACd,CAAC;IAED;;;OAGG;IACH,SAAS,aAAa,CAAC,MAAiC;QACtD,IAAI,CAAC,eAAe,EAAE;YAAE,OAAO,KAAK,CAAC;QACrC,IAAI,SAAS,CAAC,MAAM,CAAC,KAAK,IAAI;YAAE,OAAO,KAAK,CAAC;QAC7C,OAAO,OAAO,CAAC,QAAQ,CAAC,kBAAkB,IAAI,QAAQ,CAAC,mBAAmB,CAAC,CAAC;IAC9E,CAAC;IAED;;;;;OAKG;IACH,SAAS,SAAS,CAAC,MAAiC;QAClD,YAAY,EAAE,CAAC;QACf,IAAI,CAAC,aAAa,CAAC,MAAM,CAAC,EAAE,CAAC;YAC3B,MAAM,GAAG,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC;YAC9B,IAAI,eAAe,EAAE,IAAI,GAAG,KAAK,SAAS,EAAE,CAAC;gBAC3C,IAAI,CACF,GAAG,sBAAW,+DAA+D;oBAC3E,kEAAkE,CACrE,CAAC;YACJ,CAAC;iBAAM,IAAI,eAAe,EAAE,IAAI,GAAG,KAAK,UAAU,EAAE,CAAC;gBACnD,IAAI,CACF,GAAG,sBAAW,wEAAwE;oBACpF,6CAA6C,CAChD,CAAC;YACJ,CAAC;iBAAM,IAAI,eAAe,EAAE,IAAI,CAAC,QAAQ,CAAC,mBAAmB,EAAE,CAAC;gBAC9D,IAAI,CACF,GAAG,sBAAW,+DAA+D;oBAC3E,6EAA6E,CAChF,CAAC;YACJ,CAAC;YACD,OAAO;QACT,CAAC;QACD,CAAC,CAAC,cAAc,GAAG,IAAA,kCAAyB,EAAC,OAAO,CAAC,MAAM,IAAI,EAAE,EAAE;YACjE,YAAY,EAAE,CAAC,OAAO,EAAE,EAAE,CAAC,QAAQ,CAAC,kBAAmB,CAAC,OAAO,CAAC;YAChE,YAAY,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,iBAAiB,CAAC,GAAG,CAAC;YAC7C,QAAQ,EAAE,QAAQ,CAAC,WAAW;YAC9B,GAAG;SACJ,CAAC,CAAC;IACL,CAAC;IAED,oEAAoE;IACpE,SAAS,YAAY;QACnB,IAAI,CAAC;YACH,CAAC,CAAC,cAAc,EAAE,IAAI,EAAE,CAAC;QAC3B,CAAC;QAAC,MAAM,CAAC;YACP,6BAA6B;QAC/B,CAAC;QACD,CAAC,CAAC,cAAc,GAAG,IAAI,CAAC;QACxB,CAAC,CAAC,mBAAmB,GAAG,IAAI,CAAC;IAC/B,CAAC;IAED,+EAA+E;IAC/E,SAAS,iBAAiB,CAAC,GAAW;QACpC,IAAI,CAAC;YACH,QAAQ,CAAC,kBAAkB,EAAE,CAAC,GAAG,CAAC,CAAC;QACrC,CAAC;QAAC,MAAM,CAAC;YACP,4BAA4B;QAC9B,CAAC;IACH,CAAC;IAED,iFAAiF;IACjF,SAAS,mBAAmB,CAAC,MAAiC;QAC5D,IAAI,CAAC,MAAM;YAAE,OAAO;QACpB,IAAI,CAAC;YACH,CAAC,CAAC,cAAc,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC;QACpC,CAAC;QAAC,MAAM,CAAC;YACP,4BAA4B;QAC9B,CAAC;IACH,CAAC;IAED;;;;;OAKG;IACH,KAAK,UAAU,qBAAqB;QAClC,MAAM,MAAM,GAAG,CAAC,CAAC,cAAc,EAAE,QAAQ,EAAE,IAAI,IAAI,CAAC;QACpD,IAAI,CAAC,MAAM;YAAE,OAAO,IAAI,CAAC;QACzB,IAAI,CAAC;YACH,OAAO,MAAM,YAAY,CAAC,MAAM,CAAC,CAAC;QACpC,CAAC;gBAAS,CAAC;YACT,mBAAmB,CAAC,MAAM,CAAC,CAAC;QAC9B,CAAC;IACH,CAAC;IAED;;;;OAIG;IACH,KAAK,UAAU,YAAY,CACzB,MAAiC;QAEjC,IAAI,CAAC,MAAM,IAAI,CAAC,QAAQ,CAAC,mBAAmB;YAAE,OAAO,IAAI,CAAC;QAC1D,OAAO,IAAA,kCAAkB,EAAC,MAAM,EAAE;YAChC,aAAa,EAAE,QAAQ,CAAC,mBAAmB;YAC3C,UAAU,EAAE,GAAG,EAAE;gBACf,MAAM,GAAG,GAAG,eAAe,EAAE,CAAC;gBAC9B,OAAO;oBACL,KAAK,EAAE,GAAG,CAAC,WAAW,IAAI,CAAC;oBAC3B,MAAM,EAAE,GAAG,CAAC,YAAY,IAAI,CAAC;iBAC9B,CAAC;YACJ,CAAC;YACD,OAAO,EAAE,IAAA,wBAAe,EAAC,OAAO,CAAC,MAAM,CAAC;YACxC,GAAG;SACJ,CAAC,CAAC;IACL,CAAC;IAED,OAAO;QACL,SAAS;QACT,YAAY;QACZ,iBAAiB;QACjB,mBAAmB;QACnB,qBAAqB;QACrB,YAAY;KACb,CAAC;AACJ,CAAC"}
@@ -0,0 +1,131 @@
1
+ /**
2
+ * The state the headless client's pieces share (#661): one explicit object, where
3
+ * `createFixbackClient` used to be a single closure whose parts reached into each
4
+ * other's variables.
5
+ *
6
+ * It holds what the client resolves from its options once, at creation — the trimmed
7
+ * key, the canonical origin, the API base, the validated Release, the Correlation id's
8
+ * targets, the trace buffer, the listener sets — and every value that changes over the
9
+ * client's life: the status and the boot answer, the lifecycle generation, the Connect
10
+ * state, the armed capture, and maintenance mode's notice. Every piece is created over
11
+ * the same object — boot and capture arming (`./client-boot`), mobile replay
12
+ * (`./client-replay`), Connect (`./client-connect`), and the composer-facing surface
13
+ * (`./client`) — so what one piece writes is what the next one reads.
14
+ */
15
+ import { type BootAnswer, type BreadcrumbBuffer, type FetchLike, type IdentityInputs, type MaintenanceNotice, type MaintenanceRecheck, type ReporterIdentity, type ReporterTier, type Teardown } from "@fixback/sdk-core";
16
+ import type { AutoCaptureHandle } from "./error-capture";
17
+ import type { FormDataFactory } from "./http";
18
+ import type { ComposerContext, FixbackAdapters, FixbackOptions, FixbackStatus } from "./options";
19
+ import type { EnvironmentInputs } from "./report";
20
+ import type { MobileReplayRecorder, MobileReplayWindow } from "./replay";
21
+ /** Everything a client knows and holds — see the module doc. */
22
+ export interface ClientState {
23
+ readonly options: FixbackOptions;
24
+ readonly adapters: FixbackAdapters;
25
+ /** The publishable key, trimmed; `""` when there is none, which keeps the client dormant. */
26
+ readonly key: string;
27
+ /** The canonical `origin` the app reports as, or `""` when it declared none (ADR-0041). */
28
+ readonly origin: string;
29
+ /** The Fixback API base: trimmed, with no trailing slash. */
30
+ readonly apiUrl: string;
31
+ readonly now: () => number;
32
+ readonly readEnvironment: () => EnvironmentInputs;
33
+ /** The development-only warning sink (silent unless the adapters provide one). */
34
+ readonly warn: (message: string) => void;
35
+ /** The host app's Release (#117, ADR-0024), validated; `undefined` when unset or invalid. */
36
+ readonly release: string | undefined;
37
+ /** The Correlation id's targets (#540): the `propagateTo` entries that are bare origins. */
38
+ readonly propagateTo: readonly string[];
39
+ /** The trace buffer. It outlives every re-arm, so no lead-up is lost to one. */
40
+ readonly buffer: BreadcrumbBuffer;
41
+ readonly statusListeners: Set<(status: FixbackStatus) => void>;
42
+ readonly presentListeners: Set<(context: ComposerContext) => void>;
43
+ readonly identityListeners: Set<(identity: ReporterIdentity) => void>;
44
+ readonly maintenanceListeners: Set<(notice: MaintenanceNotice | null) => void>;
45
+ status: FixbackStatus;
46
+ boot: BootAnswer | null;
47
+ /** Monotonic id of the most recently *started* boot — see `bootAndArm` (`./client-boot`). */
48
+ bootSequence: number;
49
+ currentScreen: string | null;
50
+ teardownTrace: Teardown | null;
51
+ autoCapture: AutoCaptureHandle | null;
52
+ /**
53
+ * The mobile replay recorder, and the window frozen when the composer opened
54
+ * (ADR-0044). Both are null unless the Project enabled replay *and* this app asked
55
+ * for it — recording an unmasked screen is never something the SDK starts on its own.
56
+ */
57
+ replayRecorder: MobileReplayRecorder | null;
58
+ pendingReplayWindow: MobileReplayWindow | null;
59
+ identity: IdentityInputs;
60
+ reporterSessionToken: string | null;
61
+ account: {
62
+ name: string;
63
+ email: string;
64
+ } | null;
65
+ currentTier: ReporterTier | null;
66
+ connectUrl: string | undefined;
67
+ /** Removes the inbound deep-link listener; re-armed by each `start()`. */
68
+ teardownLinking: Teardown | null;
69
+ /**
70
+ * The last inbound Connect code redeemed, so a replayed launch URL is not exchanged
71
+ * twice. A code is single-use: the second attempt would fail, and a failed exchange
72
+ * is indistinguishable from a Connect that never worked.
73
+ */
74
+ lastInboundCode: string | null;
75
+ /**
76
+ * Callers waiting for the next inbound Connect to land — the browser door's
77
+ * `signIn()` (ADR-0052), which hands control to the connect page and gets it back as
78
+ * a deep link the inbound listener redeems. Resolved with `true` by a successful
79
+ * redemption, `false` when the wait gives up or the client is destroyed, so an
80
+ * awaited `signIn()` reports the identity the round trip actually produced instead
81
+ * of the anonymous one it started from.
82
+ */
83
+ inboundWaiters: Array<(landed: boolean) => void>;
84
+ /**
85
+ * The lifecycle generation. Each `start()` claims a new generation and every
86
+ * await inside it re-checks that it is still current; `destroy()` bumps the
87
+ * generation so an in-flight start abandons itself. A destroyed client can be
88
+ * started again — React 18+ StrictMode (and Fast Refresh) replay the
89
+ * provider's effect as mount → cleanup → re-mount on the same client
90
+ * instance, so destroy must not be terminal.
91
+ */
92
+ generation: number;
93
+ /** Whether a start() has claimed the current generation. */
94
+ running: boolean;
95
+ /** Maintenance mode's notice — boot's, or a held report's — else `null`. */
96
+ maintenance: MaintenanceNotice | null;
97
+ /** The next boot while the notice shows. */
98
+ readonly maintenanceRecheck: MaintenanceRecheck;
99
+ /**
100
+ * The last dormancy line printed, so a re-boot that changes nothing (a `signOut`
101
+ * back to the same refusal, an app resumed) does not repeat itself. Reset whenever
102
+ * the client becomes able to submit, so a *later* relapse is reported again.
103
+ */
104
+ lastDormancyWarning: string | null;
105
+ }
106
+ /**
107
+ * Resolve a client's options once, saying what it cannot use as it goes, and set up its
108
+ * state at rest: `idle`, nothing armed, no boot answer yet.
109
+ */
110
+ export declare function createClientState(options: FixbackOptions, adapters: FixbackAdapters): ClientState;
111
+ /** Move the client to `next`, telling every status listener once. */
112
+ export declare function setStatus(s: ClientState, next: FixbackStatus): void;
113
+ /**
114
+ * The `url` a report carries right now (spec 0004 §D). With an `origin` configured
115
+ * it reads `<origin>/<screen>`; without one — the default since ADR-0041 — the
116
+ * screen stands alone, so the Evidence still says *where* the reporter was rather
117
+ * than losing it to a setting nobody has to fill in. Ingest takes `url` as a free
118
+ * string and the dashboard renders it as text, so a bare screen name is honest
119
+ * here where a synthesised origin would be fiction.
120
+ */
121
+ export declare function currentUrl(s: ClientState): string | undefined;
122
+ /** The transport deps every call shares: the injected seams plus this app's origin. */
123
+ export declare function submitDeps(s: ClientState): {
124
+ fetch?: FetchLike;
125
+ formData?: FormDataFactory;
126
+ origin: string;
127
+ };
128
+ /** The current Reporter identity for the composer's identity row and `identity()`. */
129
+ export declare function reporterIdentity(s: ClientState): ReporterIdentity;
130
+ /** Tell identity listeners the identity may have changed (boot / sign in / sign out). */
131
+ export declare function notifyIdentity(s: ClientState): void;
@@ -0,0 +1,158 @@
1
+ "use strict";
2
+ /**
3
+ * The state the headless client's pieces share (#661): one explicit object, where
4
+ * `createFixbackClient` used to be a single closure whose parts reached into each
5
+ * other's variables.
6
+ *
7
+ * It holds what the client resolves from its options once, at creation — the trimmed
8
+ * key, the canonical origin, the API base, the validated Release, the Correlation id's
9
+ * targets, the trace buffer, the listener sets — and every value that changes over the
10
+ * client's life: the status and the boot answer, the lifecycle generation, the Connect
11
+ * state, the armed capture, and maintenance mode's notice. Every piece is created over
12
+ * the same object — boot and capture arming (`./client-boot`), mobile replay
13
+ * (`./client-replay`), Connect (`./client-connect`), and the composer-facing surface
14
+ * (`./client`) — so what one piece writes is what the next one reads.
15
+ */
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ exports.createClientState = createClientState;
18
+ exports.setStatus = setStatus;
19
+ exports.currentUrl = currentUrl;
20
+ exports.submitDeps = submitDeps;
21
+ exports.reporterIdentity = reporterIdentity;
22
+ exports.notifyIdentity = notifyIdentity;
23
+ const sdk_core_1 = require("@fixback/sdk-core");
24
+ const origin_1 = require("./origin");
25
+ const propagation_1 = require("./propagation");
26
+ /** Normalise a base URL: trim and drop trailing slashes. */
27
+ function normalizeBase(value) {
28
+ return value.trim().replace(/\/+$/, "");
29
+ }
30
+ /** Turn a screen name into the path segment of the report `url`. */
31
+ function screenPath(name) {
32
+ return name.trim().replace(/^\/+/, "");
33
+ }
34
+ /**
35
+ * Resolve a client's options once, saying what it cannot use as it goes, and set up its
36
+ * state at rest: `idle`, nothing armed, no boot answer yet.
37
+ */
38
+ function createClientState(options, adapters) {
39
+ const key = typeof options.key === "string" ? options.key.trim() : "";
40
+ const origin = typeof options.origin === "string" ? (0, origin_1.canonicalizeOrigin)(options.origin) : "";
41
+ const apiUrl = normalizeBase(options.apiUrl || sdk_core_1.DEFAULT_API_URL) || sdk_core_1.DEFAULT_API_URL;
42
+ const now = adapters.now ?? Date.now;
43
+ const readEnvironment = adapters.environment ?? (() => ({}));
44
+ const warn = adapters.warn ?? (() => { });
45
+ // The host app's Release (#117, ADR-0024), validated once here: an invalid value
46
+ // is dropped with a warning rather than shipped for the server to discard — and
47
+ // never a reason to refuse to start (capture must not break on config). Unlike the
48
+ // browser SDK, nothing falls back to a bundler-injected Release: with no option,
49
+ // none is stamped.
50
+ const { release, warning: releaseWarning } = (0, sdk_core_1.readReleaseOption)(options.release);
51
+ if (releaseWarning)
52
+ warn(`${sdk_core_1.WARN_PREFIX} ${releaseWarning}`);
53
+ // `environment` is no longer an option — the key selects the Environment (ADR
54
+ // 2026-09-24) — but a plain-JS caller gets no type error for passing it, and would
55
+ // otherwise believe it was honoured. Said once, here, never per report.
56
+ const removedOption = (0, sdk_core_1.removedEnvironmentOptionWarning)(options);
57
+ if (removedOption)
58
+ warn(`${sdk_core_1.WARN_PREFIX} ${removedOption}`);
59
+ // The Correlation id's targets (#540) — app topology known in code, so an init option
60
+ // rather than a served setting, and allowlist-only on mobile. An entry that is not a
61
+ // bare origin is never used, and said so once, the way an invalid release is — in the
62
+ // words every capture SDK shares.
63
+ const propagation = (0, propagation_1.normalizePropagateTo)(options.propagateTo);
64
+ const ignoredPropagateTo = (0, sdk_core_1.ignoredPropagateToWarning)(propagation.rejected);
65
+ if (ignoredPropagateTo)
66
+ warn(`${sdk_core_1.WARN_PREFIX} ${ignoredPropagateTo}`);
67
+ const buffer = (0, sdk_core_1.createBreadcrumbBuffer)({
68
+ beforeBreadcrumb: options.beforeBreadcrumb,
69
+ now,
70
+ });
71
+ return {
72
+ options,
73
+ adapters,
74
+ key,
75
+ origin,
76
+ apiUrl,
77
+ now,
78
+ readEnvironment,
79
+ warn,
80
+ release,
81
+ propagateTo: propagation.origins,
82
+ buffer,
83
+ statusListeners: new Set(),
84
+ presentListeners: new Set(),
85
+ identityListeners: new Set(),
86
+ maintenanceListeners: new Set(),
87
+ status: "idle",
88
+ boot: null,
89
+ bootSequence: 0,
90
+ currentScreen: null,
91
+ teardownTrace: null,
92
+ autoCapture: null,
93
+ replayRecorder: null,
94
+ pendingReplayWindow: null,
95
+ identity: {},
96
+ reporterSessionToken: null,
97
+ account: null,
98
+ currentTier: null,
99
+ connectUrl: undefined,
100
+ teardownLinking: null,
101
+ lastInboundCode: null,
102
+ inboundWaiters: [],
103
+ generation: 0,
104
+ running: false,
105
+ maintenance: null,
106
+ maintenanceRecheck: (0, sdk_core_1.createMaintenanceRecheck)(),
107
+ lastDormancyWarning: null,
108
+ };
109
+ }
110
+ /** Move the client to `next`, telling every status listener once. */
111
+ function setStatus(s, next) {
112
+ if (s.status === next)
113
+ return;
114
+ s.status = next;
115
+ for (const listener of s.statusListeners) {
116
+ try {
117
+ listener(next);
118
+ }
119
+ catch {
120
+ /* a listener must never break the client */
121
+ }
122
+ }
123
+ }
124
+ /**
125
+ * The `url` a report carries right now (spec 0004 §D). With an `origin` configured
126
+ * it reads `<origin>/<screen>`; without one — the default since ADR-0041 — the
127
+ * screen stands alone, so the Evidence still says *where* the reporter was rather
128
+ * than losing it to a setting nobody has to fill in. Ingest takes `url` as a free
129
+ * string and the dashboard renders it as text, so a bare screen name is honest
130
+ * here where a synthesised origin would be fiction.
131
+ */
132
+ function currentUrl(s) {
133
+ const { origin, currentScreen } = s;
134
+ if (!origin)
135
+ return currentScreen ? screenPath(currentScreen) : undefined;
136
+ return currentScreen ? `${origin}/${screenPath(currentScreen)}` : origin;
137
+ }
138
+ /** The transport deps every call shares: the injected seams plus this app's origin. */
139
+ function submitDeps(s) {
140
+ return { fetch: s.adapters.fetch, formData: s.adapters.formData, origin: s.origin };
141
+ }
142
+ /** The current Reporter identity for the composer's identity row and `identity()`. */
143
+ function reporterIdentity(s) {
144
+ return (0, sdk_core_1.reporterIdentityOf)(s.account, s.currentTier);
145
+ }
146
+ /** Tell identity listeners the identity may have changed (boot / sign in / sign out). */
147
+ function notifyIdentity(s) {
148
+ const current = reporterIdentity(s);
149
+ for (const listener of s.identityListeners) {
150
+ try {
151
+ listener(current);
152
+ }
153
+ catch {
154
+ /* a listener must never break the client */
155
+ }
156
+ }
157
+ }
158
+ //# sourceMappingURL=client-state.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client-state.js","sourceRoot":"","sources":["../src/client-state.ts"],"names":[],"mappings":"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"}