@dynamic-labs/sdk-react-core 4.86.0 → 4.87.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 CHANGED
@@ -1,4 +1,16 @@
1
1
 
2
+ ## [4.87.0](https://github.com/dynamic-labs/dynamic-auth/compare/v4.86.0...v4.87.0) (2026-06-02)
3
+
4
+
5
+ ### Features
6
+
7
+ * **sdk-react-core:** instrument logout reason for Datadog observability DYNT-870 ([#11429](https://github.com/dynamic-labs/dynamic-auth/issues/11429)) ([8f9b3f3](https://github.com/dynamic-labs/dynamic-auth/commit/8f9b3f364dc711414ae59642c36f5f0a4cc4576a))
8
+
9
+
10
+ ### Bug Fixes
11
+
12
+ * **sdk-react-core:** only flag auto-wallet abandoned on a sustained exit, not transient webview hide DYNT-870 ([#11428](https://github.com/dynamic-labs/dynamic-auth/issues/11428)) ([5984cdd](https://github.com/dynamic-labs/dynamic-auth/commit/5984cddb4ac04b226f6dd6ebe44340186902b012))
13
+
2
14
  ## [4.86.0](https://github.com/dynamic-labs/dynamic-auth/compare/v4.85.0...v4.86.0) (2026-06-01)
3
15
 
4
16
 
package/package.cjs CHANGED
@@ -3,11 +3,11 @@
3
3
 
4
4
  Object.defineProperty(exports, '__esModule', { value: true });
5
5
 
6
- var version = "4.86.0";
6
+ var version = "4.87.0";
7
7
  var dependencies = {
8
8
  "@dynamic-labs/sdk-api-core": "0.0.985",
9
9
  "@dynamic-labs-sdk/client": "1.1.0",
10
- "@dynamic-labs-wallet/browser-wallet-client": "1.0.7",
10
+ "@dynamic-labs-wallet/browser-wallet-client": "1.0.13",
11
11
  "@dynamic-labs-wallet/forward-mpc-client": "0.10.1",
12
12
  "@hcaptcha/react-hcaptcha": "1.4.4",
13
13
  "@thumbmarkjs/thumbmarkjs": "0.16.0",
package/package.js CHANGED
@@ -1,9 +1,9 @@
1
1
  'use client'
2
- var version = "4.86.0";
2
+ var version = "4.87.0";
3
3
  var dependencies = {
4
4
  "@dynamic-labs/sdk-api-core": "0.0.985",
5
5
  "@dynamic-labs-sdk/client": "1.1.0",
6
- "@dynamic-labs-wallet/browser-wallet-client": "1.0.7",
6
+ "@dynamic-labs-wallet/browser-wallet-client": "1.0.13",
7
7
  "@dynamic-labs-wallet/forward-mpc-client": "0.10.1",
8
8
  "@hcaptcha/react-hcaptcha": "1.4.4",
9
9
  "@thumbmarkjs/thumbmarkjs": "0.16.0",
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "@dynamic-labs/sdk-react-core",
3
- "version": "4.86.0",
3
+ "version": "4.87.0",
4
4
  "dependencies": {
5
5
  "@dynamic-labs/sdk-api-core": "0.0.985",
6
6
  "@dynamic-labs-sdk/client": "1.1.0",
7
- "@dynamic-labs-wallet/browser-wallet-client": "1.0.7",
7
+ "@dynamic-labs-wallet/browser-wallet-client": "1.0.13",
8
8
  "@dynamic-labs-wallet/forward-mpc-client": "0.10.1",
9
9
  "@hcaptcha/react-hcaptcha": "1.4.4",
10
10
  "@thumbmarkjs/thumbmarkjs": "0.16.0",
@@ -17,17 +17,17 @@
17
17
  "yup": "0.32.11",
18
18
  "react-international-phone": "4.5.0",
19
19
  "bs58": "5.0.0",
20
- "@dynamic-labs/assert-package-version": "4.86.0",
21
- "@dynamic-labs/iconic": "4.86.0",
22
- "@dynamic-labs/locale": "4.86.0",
23
- "@dynamic-labs/logger": "4.86.0",
24
- "@dynamic-labs/multi-wallet": "4.86.0",
25
- "@dynamic-labs/rpc-providers": "4.86.0",
26
- "@dynamic-labs/store": "4.86.0",
27
- "@dynamic-labs/types": "4.86.0",
28
- "@dynamic-labs/utils": "4.86.0",
29
- "@dynamic-labs/wallet-book": "4.86.0",
30
- "@dynamic-labs/wallet-connector-core": "4.86.0",
20
+ "@dynamic-labs/assert-package-version": "4.87.0",
21
+ "@dynamic-labs/iconic": "4.87.0",
22
+ "@dynamic-labs/locale": "4.87.0",
23
+ "@dynamic-labs/logger": "4.87.0",
24
+ "@dynamic-labs/multi-wallet": "4.87.0",
25
+ "@dynamic-labs/rpc-providers": "4.87.0",
26
+ "@dynamic-labs/store": "4.87.0",
27
+ "@dynamic-labs/types": "4.87.0",
28
+ "@dynamic-labs/utils": "4.87.0",
29
+ "@dynamic-labs/wallet-book": "4.87.0",
30
+ "@dynamic-labs/wallet-connector-core": "4.87.0",
31
31
  "eventemitter3": "5.0.1"
32
32
  },
33
33
  "devDependencies": {
@@ -132,6 +132,20 @@ const useHandleLogout = (params) => {
132
132
  reason,
133
133
  });
134
134
  utils.tracing.logEvent('logout', `useHandleLogout called (reason=${reason})`);
135
+ // Surface the logout reason to Datadog. Every internal logout path funnels
136
+ // through here, but the reason was only sent to tracing/verbose — never the
137
+ // HTTP log transport — so logout causes were invisible in DD (e.g. an
138
+ // auto-logout firing mid-ceremony that revokes the session and abandons
139
+ // wallet creation). @environmentId is attached by the transport from its
140
+ // post URL; pair with @key:sdk_logout to see the reason breakdown per env.
141
+ logger.logger.instrument('SDK logout', {
142
+ key: 'sdk_logout',
143
+ reason: reason !== null && reason !== void 0 ? reason : 'unspecified',
144
+ time: 0,
145
+ });
146
+ // The trace buffer is otherwise never sent — flush the logout + session-key
147
+ // context so a logout that aborts wallet creation is diagnosable in DD.
148
+ utils.tracing.flush((packed) => logger.logger.instrument(packed), ['logout', 'session-key']);
135
149
  p.setSelectedWalletConnectorKey(null);
136
150
  p.clearAllWalletConnectSessions();
137
151
  p.setShowDynamicUserProfile(false);
@@ -128,6 +128,20 @@ const useHandleLogout = (params) => {
128
128
  reason,
129
129
  });
130
130
  tracing.logEvent('logout', `useHandleLogout called (reason=${reason})`);
131
+ // Surface the logout reason to Datadog. Every internal logout path funnels
132
+ // through here, but the reason was only sent to tracing/verbose — never the
133
+ // HTTP log transport — so logout causes were invisible in DD (e.g. an
134
+ // auto-logout firing mid-ceremony that revokes the session and abandons
135
+ // wallet creation). @environmentId is attached by the transport from its
136
+ // post URL; pair with @key:sdk_logout to see the reason breakdown per env.
137
+ logger.instrument('SDK logout', {
138
+ key: 'sdk_logout',
139
+ reason: reason !== null && reason !== void 0 ? reason : 'unspecified',
140
+ time: 0,
141
+ });
142
+ // The trace buffer is otherwise never sent — flush the logout + session-key
143
+ // context so a logout that aborts wallet creation is diagnosable in DD.
144
+ tracing.flush((packed) => logger.instrument(packed), ['logout', 'session-key']);
131
145
  p.setSelectedWalletConnectorKey(null);
132
146
  p.clearAllWalletConnectSessions();
133
147
  p.setShowDynamicUserProfile(false);
@@ -23,6 +23,15 @@ require('../../../shared/consts/index.cjs');
23
23
  * fire here means the flow genuinely stalled rather than just being slow.
24
24
  */
25
25
  const AUTO_WALLET_CREATION_TIMEOUT_MS = 30000;
26
+ /**
27
+ * A page must stay hidden this long before we count the ceremony as abandoned.
28
+ * Android webviews and bfcache navigations fire `pagehide`/`visibilitychange`
29
+ * transiently — frequently ~1s into the flow — and webviews pause JS timers
30
+ * while backgrounded. Requiring a *sustained* hide (and ignoring reversible
31
+ * bfcache `pagehide`) stops a user who never actually left from being logged as
32
+ * abandoned (DYNT-870 follow-up).
33
+ */
34
+ const ABANDON_CONFIRM_MS = 5000;
26
35
  /**
27
36
  * Wraps the auto-wallet-creation ceremony so that "silent" failures carry a
28
37
  * reason. Without this, a ceremony that hangs (promise never settles) or a user
@@ -31,30 +40,67 @@ const AUTO_WALLET_CREATION_TIMEOUT_MS = 30000;
31
40
  *
32
41
  * Emits:
33
42
  * - `auto_wallet_creation_timeout` if `run` has not settled within `timeoutMs`.
34
- * - `auto_wallet_creation_abandoned` if the page is hidden while `run` is still
35
- * in flight (relies on the logger's keepalive transport to survive unload).
43
+ * - `auto_wallet_creation_abandoned` if the user genuinely leaves while `run` is
44
+ * still in flight either a terminal unload (`pagehide`, non-bfcache) or a
45
+ * hide that stays hidden for `ABANDON_CONFIRM_MS`. Transient hides (tab
46
+ * switch, OS pause, the ~1s webview flicker) are ignored. Relies on the
47
+ * logger's keepalive transport to survive unload.
36
48
  *
37
49
  * The watchdog is observational: it never aborts `run`, and its return value /
38
50
  * rejection is passed through unchanged.
39
51
  */
40
52
  const instrumentWalletCreation = (_a) => _tslib.__awaiter(void 0, [_a], void 0, function* ({ run, logData, timeoutMs = AUTO_WALLET_CREATION_TIMEOUT_MS, }) {
41
- var _b, _c;
53
+ var _b, _c, _d, _e;
42
54
  const startTime = Date.now();
55
+ const doc = globalThis.document;
43
56
  const timeoutId = setTimeout(() => {
44
57
  logger.logger.instrument('Auto wallet creation timed out', Object.assign(Object.assign({}, logData), { key: 'auto_wallet_creation_timeout', reason: 'timeout', time: Date.now() - startTime }));
45
58
  }, timeoutMs);
46
- const handlePageHide = () => {
47
- logger.logger.instrument('Auto wallet creation abandoned', Object.assign(Object.assign({}, logData), { key: 'auto_wallet_creation_abandoned', reason: 'page_hidden', time: Date.now() - startTime }));
59
+ let abandonReported = false;
60
+ let hiddenTimer = null;
61
+ const reportAbandoned = (reason) => {
62
+ if (abandonReported) {
63
+ return;
64
+ }
65
+ abandonReported = true;
66
+ logger.logger.instrument('Auto wallet creation abandoned', Object.assign(Object.assign({}, logData), { key: 'auto_wallet_creation_abandoned', reason, time: Date.now() - startTime }));
67
+ };
68
+ // A terminal unload (not a reversible bfcache freeze) is a genuine exit:
69
+ // report immediately and let the logger's keepalive transport flush it.
70
+ const handlePageHide = (event) => {
71
+ if (event.persisted) {
72
+ return;
73
+ }
74
+ reportAbandoned('page_hidden');
75
+ };
76
+ // Transient backgrounding (tab switch, OS pause, the ~1s webview flicker)
77
+ // fires `hidden` then `visible` again; only a hide that *stays* hidden counts.
78
+ // If the webview pauses JS while hidden, the timer simply never fires — which
79
+ // is the desired outcome (the user did not actually abandon).
80
+ const handleVisibilityChange = () => {
81
+ if ((doc === null || doc === void 0 ? void 0 : doc.visibilityState) === 'hidden') {
82
+ hiddenTimer = setTimeout(() => reportAbandoned('backgrounded'), ABANDON_CONFIRM_MS);
83
+ }
84
+ else if (hiddenTimer) {
85
+ clearTimeout(hiddenTimer);
86
+ hiddenTimer = null;
87
+ }
48
88
  };
49
89
  (_b = globalThis.addEventListener) === null || _b === void 0 ? void 0 : _b.call(globalThis, 'pagehide', handlePageHide);
90
+ (_c = doc === null || doc === void 0 ? void 0 : doc.addEventListener) === null || _c === void 0 ? void 0 : _c.call(doc, 'visibilitychange', handleVisibilityChange);
50
91
  try {
51
92
  return yield run();
52
93
  }
53
94
  finally {
54
95
  clearTimeout(timeoutId);
55
- (_c = globalThis.removeEventListener) === null || _c === void 0 ? void 0 : _c.call(globalThis, 'pagehide', handlePageHide);
96
+ if (hiddenTimer) {
97
+ clearTimeout(hiddenTimer);
98
+ }
99
+ (_d = globalThis.removeEventListener) === null || _d === void 0 ? void 0 : _d.call(globalThis, 'pagehide', handlePageHide);
100
+ (_e = doc === null || doc === void 0 ? void 0 : doc.removeEventListener) === null || _e === void 0 ? void 0 : _e.call(doc, 'visibilitychange', handleVisibilityChange);
56
101
  }
57
102
  });
58
103
 
104
+ exports.ABANDON_CONFIRM_MS = ABANDON_CONFIRM_MS;
59
105
  exports.AUTO_WALLET_CREATION_TIMEOUT_MS = AUTO_WALLET_CREATION_TIMEOUT_MS;
60
106
  exports.instrumentWalletCreation = instrumentWalletCreation;
@@ -4,6 +4,15 @@
4
4
  * fire here means the flow genuinely stalled rather than just being slow.
5
5
  */
6
6
  export declare const AUTO_WALLET_CREATION_TIMEOUT_MS = 30000;
7
+ /**
8
+ * A page must stay hidden this long before we count the ceremony as abandoned.
9
+ * Android webviews and bfcache navigations fire `pagehide`/`visibilitychange`
10
+ * transiently — frequently ~1s into the flow — and webviews pause JS timers
11
+ * while backgrounded. Requiring a *sustained* hide (and ignoring reversible
12
+ * bfcache `pagehide`) stops a user who never actually left from being logged as
13
+ * abandoned (DYNT-870 follow-up).
14
+ */
15
+ export declare const ABANDON_CONFIRM_MS = 5000;
7
16
  type InstrumentWalletCreationArgs<T> = {
8
17
  /** The wallet-creation work to run and observe (the MPC ceremony). */
9
18
  run: () => Promise<T>;
@@ -19,8 +28,11 @@ type InstrumentWalletCreationArgs<T> = {
19
28
  *
20
29
  * Emits:
21
30
  * - `auto_wallet_creation_timeout` if `run` has not settled within `timeoutMs`.
22
- * - `auto_wallet_creation_abandoned` if the page is hidden while `run` is still
23
- * in flight (relies on the logger's keepalive transport to survive unload).
31
+ * - `auto_wallet_creation_abandoned` if the user genuinely leaves while `run` is
32
+ * still in flight either a terminal unload (`pagehide`, non-bfcache) or a
33
+ * hide that stays hidden for `ABANDON_CONFIRM_MS`. Transient hides (tab
34
+ * switch, OS pause, the ~1s webview flicker) are ignored. Relies on the
35
+ * logger's keepalive transport to survive unload.
24
36
  *
25
37
  * The watchdog is observational: it never aborts `run`, and its return value /
26
38
  * rejection is passed through unchanged.
@@ -19,6 +19,15 @@ import '../../../shared/consts/index.js';
19
19
  * fire here means the flow genuinely stalled rather than just being slow.
20
20
  */
21
21
  const AUTO_WALLET_CREATION_TIMEOUT_MS = 30000;
22
+ /**
23
+ * A page must stay hidden this long before we count the ceremony as abandoned.
24
+ * Android webviews and bfcache navigations fire `pagehide`/`visibilitychange`
25
+ * transiently — frequently ~1s into the flow — and webviews pause JS timers
26
+ * while backgrounded. Requiring a *sustained* hide (and ignoring reversible
27
+ * bfcache `pagehide`) stops a user who never actually left from being logged as
28
+ * abandoned (DYNT-870 follow-up).
29
+ */
30
+ const ABANDON_CONFIRM_MS = 5000;
22
31
  /**
23
32
  * Wraps the auto-wallet-creation ceremony so that "silent" failures carry a
24
33
  * reason. Without this, a ceremony that hangs (promise never settles) or a user
@@ -27,29 +36,65 @@ const AUTO_WALLET_CREATION_TIMEOUT_MS = 30000;
27
36
  *
28
37
  * Emits:
29
38
  * - `auto_wallet_creation_timeout` if `run` has not settled within `timeoutMs`.
30
- * - `auto_wallet_creation_abandoned` if the page is hidden while `run` is still
31
- * in flight (relies on the logger's keepalive transport to survive unload).
39
+ * - `auto_wallet_creation_abandoned` if the user genuinely leaves while `run` is
40
+ * still in flight either a terminal unload (`pagehide`, non-bfcache) or a
41
+ * hide that stays hidden for `ABANDON_CONFIRM_MS`. Transient hides (tab
42
+ * switch, OS pause, the ~1s webview flicker) are ignored. Relies on the
43
+ * logger's keepalive transport to survive unload.
32
44
  *
33
45
  * The watchdog is observational: it never aborts `run`, and its return value /
34
46
  * rejection is passed through unchanged.
35
47
  */
36
48
  const instrumentWalletCreation = (_a) => __awaiter(void 0, [_a], void 0, function* ({ run, logData, timeoutMs = AUTO_WALLET_CREATION_TIMEOUT_MS, }) {
37
- var _b, _c;
49
+ var _b, _c, _d, _e;
38
50
  const startTime = Date.now();
51
+ const doc = globalThis.document;
39
52
  const timeoutId = setTimeout(() => {
40
53
  logger.instrument('Auto wallet creation timed out', Object.assign(Object.assign({}, logData), { key: 'auto_wallet_creation_timeout', reason: 'timeout', time: Date.now() - startTime }));
41
54
  }, timeoutMs);
42
- const handlePageHide = () => {
43
- logger.instrument('Auto wallet creation abandoned', Object.assign(Object.assign({}, logData), { key: 'auto_wallet_creation_abandoned', reason: 'page_hidden', time: Date.now() - startTime }));
55
+ let abandonReported = false;
56
+ let hiddenTimer = null;
57
+ const reportAbandoned = (reason) => {
58
+ if (abandonReported) {
59
+ return;
60
+ }
61
+ abandonReported = true;
62
+ logger.instrument('Auto wallet creation abandoned', Object.assign(Object.assign({}, logData), { key: 'auto_wallet_creation_abandoned', reason, time: Date.now() - startTime }));
63
+ };
64
+ // A terminal unload (not a reversible bfcache freeze) is a genuine exit:
65
+ // report immediately and let the logger's keepalive transport flush it.
66
+ const handlePageHide = (event) => {
67
+ if (event.persisted) {
68
+ return;
69
+ }
70
+ reportAbandoned('page_hidden');
71
+ };
72
+ // Transient backgrounding (tab switch, OS pause, the ~1s webview flicker)
73
+ // fires `hidden` then `visible` again; only a hide that *stays* hidden counts.
74
+ // If the webview pauses JS while hidden, the timer simply never fires — which
75
+ // is the desired outcome (the user did not actually abandon).
76
+ const handleVisibilityChange = () => {
77
+ if ((doc === null || doc === void 0 ? void 0 : doc.visibilityState) === 'hidden') {
78
+ hiddenTimer = setTimeout(() => reportAbandoned('backgrounded'), ABANDON_CONFIRM_MS);
79
+ }
80
+ else if (hiddenTimer) {
81
+ clearTimeout(hiddenTimer);
82
+ hiddenTimer = null;
83
+ }
44
84
  };
45
85
  (_b = globalThis.addEventListener) === null || _b === void 0 ? void 0 : _b.call(globalThis, 'pagehide', handlePageHide);
86
+ (_c = doc === null || doc === void 0 ? void 0 : doc.addEventListener) === null || _c === void 0 ? void 0 : _c.call(doc, 'visibilitychange', handleVisibilityChange);
46
87
  try {
47
88
  return yield run();
48
89
  }
49
90
  finally {
50
91
  clearTimeout(timeoutId);
51
- (_c = globalThis.removeEventListener) === null || _c === void 0 ? void 0 : _c.call(globalThis, 'pagehide', handlePageHide);
92
+ if (hiddenTimer) {
93
+ clearTimeout(hiddenTimer);
94
+ }
95
+ (_d = globalThis.removeEventListener) === null || _d === void 0 ? void 0 : _d.call(globalThis, 'pagehide', handlePageHide);
96
+ (_e = doc === null || doc === void 0 ? void 0 : doc.removeEventListener) === null || _e === void 0 ? void 0 : _e.call(doc, 'visibilitychange', handleVisibilityChange);
52
97
  }
53
98
  });
54
99
 
55
- export { AUTO_WALLET_CREATION_TIMEOUT_MS, instrumentWalletCreation };
100
+ export { ABANDON_CONFIRM_MS, AUTO_WALLET_CREATION_TIMEOUT_MS, instrumentWalletCreation };