@dynamic-labs/react-native-extension 4.83.1 → 4.84.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.
Files changed (32) hide show
  1. package/android/EmbeddedWebViewController.kt +26 -0
  2. package/android/EmbeddedWebViewModule.kt +8 -1
  3. package/android/dynamic/embeddedwebview/EmbeddedWebViewController.kt +26 -0
  4. package/android/dynamic/embeddedwebview/EmbeddedWebViewModule.kt +8 -1
  5. package/android/embeddedwebview/EmbeddedWebViewController.kt +26 -0
  6. package/android/embeddedwebview/EmbeddedWebViewModule.kt +8 -1
  7. package/android/java/xyz/dynamic/embeddedwebview/EmbeddedWebViewController.kt +26 -0
  8. package/android/java/xyz/dynamic/embeddedwebview/EmbeddedWebViewModule.kt +8 -1
  9. package/android/main/java/xyz/dynamic/embeddedwebview/EmbeddedWebViewController.kt +26 -0
  10. package/android/main/java/xyz/dynamic/embeddedwebview/EmbeddedWebViewModule.kt +8 -1
  11. package/android/src/main/java/xyz/dynamic/embeddedwebview/EmbeddedWebViewController.kt +26 -0
  12. package/android/src/main/java/xyz/dynamic/embeddedwebview/EmbeddedWebViewModule.kt +8 -1
  13. package/android/xyz/dynamic/embeddedwebview/EmbeddedWebViewController.kt +26 -0
  14. package/android/xyz/dynamic/embeddedwebview/EmbeddedWebViewModule.kt +8 -1
  15. package/index.cjs +601 -57
  16. package/index.js +602 -58
  17. package/ios/EmbeddedWebViewController.swift +22 -0
  18. package/ios/EmbeddedWebViewModule.swift +8 -1
  19. package/package.json +6 -6
  20. package/src/components/WebView/EmbeddedWebView/EmbeddedWebView.d.ts +20 -1
  21. package/src/components/WebView/EmbeddedWebView/embeddedWebViewPhaseTimers/embeddedWebViewPhaseTimers.d.ts +66 -0
  22. package/src/components/WebView/EmbeddedWebView/embeddedWebViewPhaseTimers/index.d.ts +1 -0
  23. package/src/components/WebView/successLog/emitSuccessLog.d.ts +35 -0
  24. package/src/components/WebView/successLog/index.d.ts +3 -0
  25. package/src/components/WebView/successLog/successLogCooldown.d.ts +39 -0
  26. package/src/components/WebView/useWebViewPhaseTimers/index.d.ts +1 -0
  27. package/src/components/WebView/useWebViewPhaseTimers/useWebViewPhaseTimers.d.ts +46 -0
  28. package/src/components/WebView/useWebViewSuccessLog/index.d.ts +1 -0
  29. package/src/components/WebView/useWebViewSuccessLog/useWebViewSuccessLog.d.ts +28 -0
  30. package/src/components/WebView/webViewPhaseTimerCore/index.d.ts +2 -0
  31. package/src/components/WebView/webViewPhaseTimerCore/webViewPhaseTimerCore.d.ts +123 -0
  32. package/src/nativeModules/EmbeddedWebView.d.ts +27 -2
@@ -380,12 +380,34 @@ extension EmbeddedWebViewController: WKNavigationDelegate {
380
380
  emitLoadError(error: error as NSError, isProvisional: false, url: webView.url?.absoluteString)
381
381
  }
382
382
 
383
+ public func webView(
384
+ _ webView: WKWebView,
385
+ didStartProvisionalNavigation _: WKNavigation!
386
+ ) {
387
+ eventEmitter?("onLoadStart", [
388
+ "url": webView.url?.absoluteString ?? "",
389
+ ])
390
+ }
391
+
383
392
  public func webView(_ webView: WKWebView, didCommit _: WKNavigation!) {
384
393
  // WKContentView is created lazily once content starts being committed —
385
394
  // suppress its input accessory bar here so focusing inputs in the page
386
395
  // doesn't show the system "< > Done" toolbar. Matches the
387
396
  // `hideKeyboardAccessoryView` setting used by `react-native-webview`.
388
397
  hideInputAccessoryView(on: webView)
398
+
399
+ // didCommit fires when the first byte of the response is rendered —
400
+ // analogous to RN's `onLoad` callback (page content has actually
401
+ // started arriving, not just the navigation request).
402
+ eventEmitter?("onLoad", [
403
+ "url": webView.url?.absoluteString ?? "",
404
+ ])
405
+ }
406
+
407
+ public func webView(_ webView: WKWebView, didFinish _: WKNavigation!) {
408
+ eventEmitter?("onLoadEnd", [
409
+ "url": webView.url?.absoluteString ?? "",
410
+ ])
389
411
  }
390
412
 
391
413
  public func webViewWebContentProcessDidTerminate(_ webView: WKWebView) {
@@ -6,7 +6,14 @@ public class EmbeddedWebViewModule: Module {
6
6
  public func definition() -> ModuleDefinition {
7
7
  Name("EmbeddedWebView")
8
8
 
9
- Events("onMessage", "onShouldStartLoad", "onLoadError")
9
+ Events(
10
+ "onMessage",
11
+ "onShouldStartLoad",
12
+ "onLoadError",
13
+ "onLoadStart",
14
+ "onLoad",
15
+ "onLoadEnd"
16
+ )
10
17
 
11
18
  OnCreate {
12
19
  self.emitterToken = EmbeddedWebViewController.shared.setEmitter { [weak self] name, payload in
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dynamic-labs/react-native-extension",
3
- "version": "4.83.1",
3
+ "version": "4.84.0",
4
4
  "main": "./index.cjs",
5
5
  "module": "./index.js",
6
6
  "types": "./src/index.d.ts",
@@ -18,11 +18,11 @@
18
18
  "@turnkey/react-native-passkey-stamper": "1.2.7",
19
19
  "@react-native-documents/picker": "^11.0.0",
20
20
  "react-native-fs": ">=2.20.0",
21
- "@dynamic-labs/assert-package-version": "4.83.1",
22
- "@dynamic-labs/client": "4.83.1",
23
- "@dynamic-labs/logger": "4.83.1",
24
- "@dynamic-labs/message-transport": "4.83.1",
25
- "@dynamic-labs/webview-messages": "4.83.1"
21
+ "@dynamic-labs/assert-package-version": "4.84.0",
22
+ "@dynamic-labs/client": "4.84.0",
23
+ "@dynamic-labs/logger": "4.84.0",
24
+ "@dynamic-labs/message-transport": "4.84.0",
25
+ "@dynamic-labs/webview-messages": "4.84.0"
26
26
  },
27
27
  "peerDependencies": {
28
28
  "react": ">=18.0.0 <20.0.0",
@@ -3,5 +3,24 @@ export type SetupEmbeddedWebViewArgs = {
3
3
  webviewUrl: string;
4
4
  core: Core;
5
5
  webviewDebuggingEnabled?: boolean;
6
+ /**
7
+ * Max time (ms) between the native webview reporting it has started a
8
+ * navigation (`onLoadStart`) and reporting that the response committed
9
+ * (`onLoad`). If this elapses, the SDK raises
10
+ * {@link WebViewFailedToLoadError} with `phase: 'html_load'` so the page
11
+ * can no longer hang silently waiting for bytes that never arrive.
12
+ *
13
+ * Defaults to 20s, matching the RN `<WebView>` path.
14
+ */
15
+ loadingTimeoutMs?: number;
16
+ /**
17
+ * Max time (ms) between the native webview reporting it has finished
18
+ * loading (`onLoadEnd`) and the SDK signalling it has finished booting
19
+ * (`sdkHasLoadedEventName`). If this elapses, the SDK raises
20
+ * {@link WebViewFailedToLoadError} with `phase: 'sdk_bootstrap'`.
21
+ *
22
+ * Defaults to 20s, matching the RN `<WebView>` path.
23
+ */
24
+ recoveryTimeoutMs?: number;
6
25
  };
7
- export declare const setupEmbeddedWebView: ({ webviewUrl, core, webviewDebuggingEnabled, }: SetupEmbeddedWebViewArgs) => (() => void);
26
+ export declare const setupEmbeddedWebView: ({ webviewUrl, core, webviewDebuggingEnabled, loadingTimeoutMs, recoveryTimeoutMs, }: SetupEmbeddedWebViewArgs) => (() => void);
@@ -0,0 +1,66 @@
1
+ import { Core } from '@dynamic-labs/client';
2
+ import { WebViewFailedToLoadErrorMeta, WebViewFailedToLoadErrorPhase } from '../../../../errors/WebViewFailedToLoadError';
3
+ import { WebViewLoadSuccessMeta, WebViewPhaseEvent } from '../../webViewPhaseTimerCore';
4
+ /**
5
+ * Path-agnostic success-log meta. Re-exported here for callsite
6
+ * ergonomics so `setupEmbeddedWebView` doesn't need to reach into
7
+ * `webViewPhaseTimerCore` directly.
8
+ */
9
+ export type EmbeddedWebViewLoadSuccessMeta = WebViewLoadSuccessMeta;
10
+ /**
11
+ * Re-exported for callsite ergonomics: lets `setupEmbeddedWebView` import
12
+ * a single `EmbeddedWebViewPhaseEvent` type rather than reach into
13
+ * `webViewPhaseTimerCore` directly.
14
+ */
15
+ export type EmbeddedWebViewPhaseEvent = WebViewPhaseEvent;
16
+ type EmbeddedWebViewPhaseTimersProps = {
17
+ core: Core;
18
+ loadingTimeoutMs: number;
19
+ recoveryTimeoutMs: number;
20
+ webViewUrl: URL;
21
+ };
22
+ export type EmbeddedWebViewPhaseTimers = {
23
+ /**
24
+ * Detach from the message transport. Call this on teardown so the
25
+ * embedded webview controller can be re-created without leaking
26
+ * subscriptions.
27
+ */
28
+ dispose: () => void;
29
+ /**
30
+ * Build the structured meta to attach to
31
+ * {@link WebViewFailedToLoadError}. The `phase` argument reflects the
32
+ * source of the failure; the duration fields come from the recorded
33
+ * lifecycle events and observed message-transport activity.
34
+ */
35
+ getMeta: (args: {
36
+ phase: WebViewFailedToLoadErrorPhase;
37
+ }) => WebViewFailedToLoadErrorMeta;
38
+ /**
39
+ * Build the structured meta to attach to the `webview.load_succeeded`
40
+ * instrumentation log. Same timing fields as {@link getMeta} but without
41
+ * the failure-specific `phase` discriminator.
42
+ */
43
+ getSuccessMeta: () => EmbeddedWebViewLoadSuccessMeta;
44
+ /**
45
+ * Record a per-attempt lifecycle event. See {@link WebViewPhaseEvent}
46
+ * for the semantics of each event.
47
+ */
48
+ recordEvent: (event: EmbeddedWebViewPhaseEvent) => void;
49
+ };
50
+ /**
51
+ * Non-React equivalent of `useWebViewPhaseTimers` for the embedded native
52
+ * webview path. The embedded path is a singleton wired by
53
+ * `setupEmbeddedWebView` (not a React component), so we expose the same
54
+ * phase-timing surface as a plain factory.
55
+ *
56
+ * Both this factory and the hook delegate the actual state machine to
57
+ * {@link createWebViewPhaseTimerCore}; the only path-specific bits are:
58
+ *
59
+ * - `hadClearState` is always `false` (embedded path has no clear-state
60
+ * recovery model — that's exclusive to the React `<WebView>` path's
61
+ * loading-timeout recovery flow)
62
+ * - `retryCount` is always `0` (embedded path has no retry counter URL
63
+ * query param either)
64
+ */
65
+ export declare const createEmbeddedWebViewPhaseTimers: ({ core, loadingTimeoutMs, recoveryTimeoutMs, webViewUrl, }: EmbeddedWebViewPhaseTimersProps) => EmbeddedWebViewPhaseTimers;
66
+ export {};
@@ -0,0 +1 @@
1
+ export { createEmbeddedWebViewPhaseTimers, type EmbeddedWebViewPhaseEvent, type EmbeddedWebViewPhaseTimers, } from './embeddedWebViewPhaseTimers';
@@ -0,0 +1,35 @@
1
+ import { Core } from '@dynamic-labs/client';
2
+ import { SuccessLogCooldown } from './successLogCooldown';
3
+ export declare const SUCCESS_LOG_KEY = "webview.load_succeeded";
4
+ type SuccessLogMeta = Record<string, unknown>;
5
+ type EmitSuccessLogArgs<Meta extends SuccessLogMeta> = {
6
+ /**
7
+ * Device-local cooldown gate. If `shouldEmit` returns false the log
8
+ * is suppressed; on emit, the cooldown is recorded immediately so a
9
+ * crash between emit and `recordEmitted` doesn't replay the log.
10
+ */
11
+ cooldown: SuccessLogCooldown;
12
+ core: Core;
13
+ /**
14
+ * Late-bound meta builder. We call this only after the cooldown
15
+ * passes so paths that maintain expensive snapshots don't pay the
16
+ * cost when they're going to be suppressed.
17
+ */
18
+ getMeta: () => Meta;
19
+ /**
20
+ * Short identifier describing the path. Appears in the log message
21
+ * (`<name> loaded successfully`) so the two streams are
22
+ * distinguishable in Datadog without a per-path `key` field.
23
+ */
24
+ name: string;
25
+ };
26
+ /**
27
+ * Shared emission pattern for the `webview.load_succeeded`
28
+ * instrumentation log. Both the RN `<WebView>` and the embedded
29
+ * WebView fire the same event with the same payload shape after
30
+ * checking the same cooldown gate — this helper centralises that
31
+ * sequence so the two call sites stay aligned and any future field we
32
+ * add to the success log lands in both places automatically.
33
+ */
34
+ export declare const emitSuccessLog: <Meta extends SuccessLogMeta>({ cooldown, core, getMeta, name, }: EmitSuccessLogArgs<Meta>) => Promise<void>;
35
+ export {};
@@ -0,0 +1,3 @@
1
+ export { emitSuccessLog, SUCCESS_LOG_KEY } from './emitSuccessLog';
2
+ export { createSuccessLogCooldown, SUCCESS_LOG_COOLDOWN_MS, } from './successLogCooldown';
3
+ export type { SuccessLogCooldown } from './successLogCooldown';
@@ -0,0 +1,39 @@
1
+ export declare const SUCCESS_LOG_COOLDOWN_MS: number;
2
+ export type SuccessLogCooldown = {
3
+ recordEmitted: (now?: number) => Promise<void>;
4
+ shouldEmit: (now?: number) => Promise<boolean>;
5
+ };
6
+ type CreateSuccessLogCooldownArgs = {
7
+ /**
8
+ * Short identifier used only in warning log messages so storage
9
+ * failures are distinguishable in Datadog (e.g. `'webview'` vs
10
+ * `'embedded webview'`).
11
+ */
12
+ name: string;
13
+ /**
14
+ * Dedicated `expo-secure-store` key under which the timestamp of the
15
+ * last successful emit is persisted. Each call site MUST use a
16
+ * distinct key so the two log streams cooldown independently.
17
+ */
18
+ storageKey: string;
19
+ };
20
+ /**
21
+ * Create a device-local cooldown gate for a `webview.load_succeeded`
22
+ * instrumentation log.
23
+ *
24
+ * Both `<WebView>` (RN bridge) and `<EmbeddedWebView>` (native bridge)
25
+ * emit the same success log; each instantiates its own cooldown with a
26
+ * dedicated storage key so emitting one log doesn't starve the other
27
+ * for an app that mounts both.
28
+ *
29
+ * - `shouldEmit` returns `true` when the cooldown has elapsed (or no
30
+ * timestamp has ever been stored) and `false` while inside the
31
+ * window. If secure-store throws on read we default to emitting —
32
+ * losing a diagnostic log is worse than the rare duplicate.
33
+ * - `recordEmitted` persists the current timestamp so subsequent calls
34
+ * within the window are suppressed. Write failures are swallowed
35
+ * (logged as a warning) so they don't break the host wrapper —
36
+ * worst case the next emit also goes through.
37
+ */
38
+ export declare const createSuccessLogCooldown: ({ name, storageKey, }: CreateSuccessLogCooldownArgs) => SuccessLogCooldown;
39
+ export {};
@@ -0,0 +1 @@
1
+ export { useWebViewPhaseTimers, type WebViewPhaseTimers, } from './useWebViewPhaseTimers';
@@ -0,0 +1,46 @@
1
+ import { Core } from '@dynamic-labs/client';
2
+ import { WebViewFailedToLoadErrorMeta, WebViewFailedToLoadErrorPhase } from '../../../errors/WebViewFailedToLoadError';
3
+ import { WebViewLoadSuccessMeta, WebViewPhaseEvent } from '../webViewPhaseTimerCore';
4
+ export type { WebViewLoadSuccessMeta };
5
+ type UseWebViewPhaseTimersProps = {
6
+ core: Core;
7
+ loadingTimeout: number;
8
+ recoveryTimeout: number;
9
+ webViewUrl: URL;
10
+ };
11
+ export type WebViewPhaseTimers = {
12
+ /**
13
+ * Build the structured meta to attach to {@link WebViewFailedToLoadError}.
14
+ * Pass `phase` explicitly from the callsite (the phase reflects the
15
+ * source of the failure, not just the current timing state).
16
+ */
17
+ getMeta: (args: {
18
+ phase: WebViewFailedToLoadErrorPhase;
19
+ }) => WebViewFailedToLoadErrorMeta;
20
+ /**
21
+ * Build the structured meta to attach to the `webview.load_succeeded`
22
+ * instrumentation log. Same timing fields as {@link getMeta} but without
23
+ * the failure-specific `phase` discriminator.
24
+ */
25
+ getSuccessMeta: () => WebViewLoadSuccessMeta;
26
+ /**
27
+ * Record a per-attempt lifecycle event. See {@link WebViewPhaseEvent}
28
+ * for the semantics of each event.
29
+ */
30
+ recordEvent: (event: WebViewPhaseEvent) => void;
31
+ };
32
+ /**
33
+ * Tracks how long each step of the WebView load took, so when we raise
34
+ * `WebViewFailedToLoadError` we can attach the timings to the error log.
35
+ *
36
+ * The timings cover the host-side phases of the load — the webview-side
37
+ * timings (`webview.time_to_load_manifest`, `webview.time_to_sdk_ready`)
38
+ * still come from the webview itself once it boots, but those don't help
39
+ * when the webview never boots in the first place.
40
+ *
41
+ * The actual state machine lives in {@link createWebViewPhaseTimerCore},
42
+ * which is also reused by the embedded native WebView path. This hook
43
+ * only layers the React-specific bits (ref-based core lifetime tied to
44
+ * `useEffect`, URL-derived `hadClearState` / `retryCount`).
45
+ */
46
+ export declare const useWebViewPhaseTimers: ({ core, loadingTimeout, recoveryTimeout, webViewUrl, }: UseWebViewPhaseTimersProps) => WebViewPhaseTimers;
@@ -0,0 +1 @@
1
+ export { useWebViewSuccessLog } from './useWebViewSuccessLog';
@@ -0,0 +1,28 @@
1
+ import { Core } from '@dynamic-labs/client';
2
+ import { WebViewLoadSuccessMeta } from '../useWebViewPhaseTimers/useWebViewPhaseTimers';
3
+ type UseWebViewSuccessLogProps = {
4
+ core: Core;
5
+ getSuccessMeta: () => WebViewLoadSuccessMeta;
6
+ };
7
+ /**
8
+ * Emit a `webview.load_succeeded` instrumentation log the first time
9
+ * the SDK signals it's ready, gated by a device-local 1h cooldown so a
10
+ * customer with many WebView mounts (or many app launches) doesn't spam
11
+ * the logs pipeline.
12
+ *
13
+ * The error path is unaffected: failures keep going through
14
+ * `logger.error(WebViewFailedToLoadError, meta)` with no cooldown.
15
+ *
16
+ * The hook intentionally fires only once per mount (`hasEmittedRef`):
17
+ * after a successful boot we don't expect another `sdkHasLoaded` for
18
+ * the same WebView lifetime, and emitting twice for the same load would
19
+ * be misleading.
20
+ *
21
+ * Cooldown gating + log emission are delegated to the shared
22
+ * `successLog` helpers so this hook and the embedded native WebView
23
+ * path stay in lockstep on payload shape and storage semantics; the
24
+ * dedicated storage key here keeps the RN-path cooldown independent
25
+ * from the embedded path's.
26
+ */
27
+ export declare const useWebViewSuccessLog: ({ core, getSuccessMeta, }: UseWebViewSuccessLogProps) => void;
28
+ export {};
@@ -0,0 +1,2 @@
1
+ export { buildSuccessMeta, computePhaseDurations, createWebViewPhaseTimerCore, } from './webViewPhaseTimerCore';
2
+ export type { CumulativeCounters, PerAttemptTimings, PhaseDurations, WebViewLoadSuccessMeta, WebViewPhaseEvent, WebViewPhaseTimerCore, } from './webViewPhaseTimerCore';
@@ -0,0 +1,123 @@
1
+ import { Core } from '@dynamic-labs/client';
2
+ /**
3
+ * Per-attempt lifecycle event recorded by the phase-timer core.
4
+ *
5
+ * - `load_start` — native bridge fired `onLoadStart` (resets per-attempt
6
+ * state so timings reflect the current attempt)
7
+ * - `load` — native bridge fired `onLoad` (HTML byte stream received)
8
+ * - `load_end` — native bridge fired `onLoadEnd` (page finished
9
+ * rendering)
10
+ * - `native_error` — native bridge raised an `onLoadError`; increments a
11
+ * counter that persists across reloads
12
+ * - `os_kill` — host detected an OS-level WebView kill (e.g. backgrounded
13
+ * for too long); also a cumulative counter
14
+ */
15
+ export type WebViewPhaseEvent = 'load' | 'load_end' | 'load_start' | 'native_error' | 'os_kill';
16
+ export type PerAttemptTimings = {
17
+ htmlLoadStartedAt: number | null;
18
+ htmlLoadedAt: number | null;
19
+ manifestReceivedAt: number | null;
20
+ onLoadEndAt: number | null;
21
+ sdkReadyAt: number | null;
22
+ };
23
+ export type CumulativeCounters = {
24
+ nativeErrorCount: number;
25
+ osKillCount: number;
26
+ };
27
+ export type PhaseDurations = {
28
+ htmlLoadMs: number | null;
29
+ manifestReceivedMs: number | null;
30
+ onLoadToOnLoadEndMs: number | null;
31
+ sdkReadyMs: number | null;
32
+ };
33
+ /**
34
+ * Derive cross-phase durations from raw timestamps. The four durations
35
+ * are what end up in the failure / success log meta:
36
+ *
37
+ * - `htmlLoadMs` — `onLoadStart` to `onLoad`; native HTML fetch
38
+ * - `onLoadToOnLoadEndMs` — `onLoad` to `onLoadEnd`; native render/parse
39
+ * - `manifestReceivedMs` — `onLoadEnd` to first `manifest` request from
40
+ * the webview JS; "JS bundle is alive" signal
41
+ * - `sdkReadyMs` — `onLoadEnd` to first `sdkHasLoaded` event from the
42
+ * webview JS; "SDK fully bootstrapped" signal
43
+ */
44
+ export declare const computePhaseDurations: (timings: PerAttemptTimings) => PhaseDurations;
45
+ export type WebViewPhaseTimerCore = {
46
+ /** Detach the message-transport subscription. */
47
+ dispose: () => void;
48
+ getCounters: () => CumulativeCounters;
49
+ getTimings: () => PerAttemptTimings;
50
+ recordEvent: (event: WebViewPhaseEvent) => void;
51
+ };
52
+ /**
53
+ * Path-agnostic shape of the `webview.load_succeeded` instrumentation
54
+ * log meta. Both the React `<WebView>` and the embedded native WebView
55
+ * paths emit this exact surface so the two log streams can be queried
56
+ * together in Datadog without per-path field translation.
57
+ *
58
+ * The failure meta (`WebViewFailedToLoadErrorMeta`) is this shape plus
59
+ * a `phase` discriminator — see `buildSuccessMeta` callers.
60
+ */
61
+ export type WebViewLoadSuccessMeta = {
62
+ hadClearState: boolean;
63
+ htmlLoadMs: number | null;
64
+ loadingTimeoutMs: number;
65
+ manifestReceivedMs: number | null;
66
+ nativeErrorCount: number;
67
+ onLoadToOnLoadEndMs: number | null;
68
+ osKillCount: number;
69
+ recoveryTimeoutMs: number;
70
+ retryCount: number;
71
+ sdkReadyMs: number | null;
72
+ webviewUrl: string;
73
+ };
74
+ type BuildSuccessMetaArgs = {
75
+ /**
76
+ * Snapshot source for raw timings + cumulative counters. Typically a
77
+ * live {@link WebViewPhaseTimerCore} instance, but the type only
78
+ * requires `getTimings` / `getCounters` so callers can fall back to
79
+ * empty values if the core hasn't been created yet.
80
+ */
81
+ core: Pick<WebViewPhaseTimerCore, 'getCounters' | 'getTimings'>;
82
+ /**
83
+ * Whether the current attempt was kicked off by the React
84
+ * `<WebView>`'s clear-state recovery flow. Embedded path is always
85
+ * `false`.
86
+ */
87
+ hadClearState: boolean;
88
+ loadingTimeoutMs: number;
89
+ recoveryTimeoutMs: number;
90
+ /**
91
+ * Number of times the React `<WebView>` re-mounted via the `?retry=`
92
+ * URL query param. Embedded path is always `0`.
93
+ */
94
+ retryCount: number;
95
+ webviewUrl: string;
96
+ };
97
+ /**
98
+ * Build the success-shape meta from a {@link WebViewPhaseTimerCore}
99
+ * snapshot plus the path-specific fields. Both call sites call this
100
+ * with their own context (RN reads `hadClearState` / `retryCount` from
101
+ * the URL; embedded passes constants) so the success-log payload stays
102
+ * identical across the two paths.
103
+ */
104
+ export declare const buildSuccessMeta: ({ core, hadClearState, loadingTimeoutMs, recoveryTimeoutMs, retryCount, webviewUrl, }: BuildSuccessMetaArgs) => WebViewLoadSuccessMeta;
105
+ /**
106
+ * Shared state machine driving WebView load-phase instrumentation.
107
+ *
108
+ * Both the React `<WebView>` (RN bridge) and the native embedded WebView
109
+ * (native bridge) feed lifecycle events into this core and produce
110
+ * matching meta from the same timings + counters. Each path layers its
111
+ * own path-specific fields (`hadClearState`, `retryCount`, etc.) on top
112
+ * of the raw state this core exposes via `getTimings()` / `getCounters()`.
113
+ *
114
+ * The core owns the `core.messageTransport` subscription that captures
115
+ * the two webview-originated signals bracketing SDK bootstrap
116
+ * (`manifest` once the JS bundle is alive; `sdkHasLoadedEventName`
117
+ * once the SDK is fully ready). Native bridge lifecycle events come in
118
+ * via `recordEvent` instead.
119
+ */
120
+ export declare const createWebViewPhaseTimerCore: ({ core, }: {
121
+ core: Core;
122
+ }) => WebViewPhaseTimerCore;
123
+ export {};
@@ -16,7 +16,32 @@ export type EmbeddedWebViewOnLoadErrorEvent = {
16
16
  description: string;
17
17
  isProvisional: boolean;
18
18
  };
19
- export type EmbeddedWebViewEventName = 'onMessage' | 'onShouldStartLoad' | 'onLoadError';
19
+ /**
20
+ * Fired when the native webview starts a provisional navigation — the
21
+ * request has been issued but no response bytes have arrived yet.
22
+ * Maps to `WKNavigationDelegate.didStartProvisionalNavigation` on iOS and
23
+ * `WebViewClient.onPageStarted` on Android.
24
+ */
25
+ export type EmbeddedWebViewOnLoadStartEvent = {
26
+ url: string;
27
+ };
28
+ /**
29
+ * Fired when the response has committed and the native webview has begun
30
+ * rendering. Maps to `WKNavigationDelegate.didCommit` on iOS and
31
+ * `WebViewClient.onPageCommitVisible` on Android.
32
+ */
33
+ export type EmbeddedWebViewOnLoadEvent = {
34
+ url: string;
35
+ };
36
+ /**
37
+ * Fired when the native webview finishes loading the page. Maps to
38
+ * `WKNavigationDelegate.didFinish` on iOS and `WebViewClient.onPageFinished`
39
+ * on Android.
40
+ */
41
+ export type EmbeddedWebViewOnLoadEndEvent = {
42
+ url: string;
43
+ };
44
+ export type EmbeddedWebViewEventName = 'onMessage' | 'onShouldStartLoad' | 'onLoadError' | 'onLoadStart' | 'onLoad' | 'onLoadEnd';
20
45
  export type EmbeddedWebViewNativeModule = {
21
46
  setUrl: (url: string) => Promise<void>;
22
47
  setVisible: (visible: boolean) => Promise<void>;
@@ -24,6 +49,6 @@ export type EmbeddedWebViewNativeModule = {
24
49
  destroy: () => Promise<void>;
25
50
  postMessage: (message: string) => Promise<void>;
26
51
  respondToShouldStartLoad: (id: string, allow: boolean) => Promise<void>;
27
- addListener: <T extends EmbeddedWebViewOnMessageEvent | EmbeddedWebViewOnShouldStartLoadEvent | EmbeddedWebViewOnLoadErrorEvent>(eventName: EmbeddedWebViewEventName, listener: (event: T) => void) => EmbeddedWebViewSubscription;
52
+ addListener: <T extends EmbeddedWebViewOnMessageEvent | EmbeddedWebViewOnShouldStartLoadEvent | EmbeddedWebViewOnLoadErrorEvent | EmbeddedWebViewOnLoadStartEvent | EmbeddedWebViewOnLoadEvent | EmbeddedWebViewOnLoadEndEvent>(eventName: EmbeddedWebViewEventName, listener: (event: T) => void) => EmbeddedWebViewSubscription;
28
53
  };
29
54
  export declare const getEmbeddedWebView: () => EmbeddedWebViewNativeModule;