@meshconnect/uwc-core 1.0.3 → 1.1.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 (121) hide show
  1. package/README.md +247 -76
  2. package/dist/events.d.ts +241 -5
  3. package/dist/events.d.ts.map +1 -1
  4. package/dist/events.js +77 -1
  5. package/dist/events.js.map +1 -1
  6. package/dist/index.d.ts +2 -0
  7. package/dist/index.d.ts.map +1 -1
  8. package/dist/index.js +2 -0
  9. package/dist/index.js.map +1 -1
  10. package/dist/logger/create-logger.d.ts +32 -0
  11. package/dist/logger/create-logger.d.ts.map +1 -0
  12. package/dist/logger/create-logger.js +79 -0
  13. package/dist/logger/create-logger.js.map +1 -0
  14. package/dist/managers/event-manager.d.ts.map +1 -1
  15. package/dist/managers/event-manager.js +5 -1
  16. package/dist/managers/event-manager.js.map +1 -1
  17. package/dist/observability/connect-observer.d.ts +33 -0
  18. package/dist/observability/connect-observer.d.ts.map +1 -0
  19. package/dist/observability/connect-observer.js +115 -0
  20. package/dist/observability/connect-observer.js.map +1 -0
  21. package/dist/observability/focus-tracker.d.ts +42 -0
  22. package/dist/observability/focus-tracker.d.ts.map +1 -0
  23. package/dist/observability/focus-tracker.js +57 -0
  24. package/dist/observability/focus-tracker.js.map +1 -0
  25. package/dist/observability/instrument-handoff.d.ts +54 -0
  26. package/dist/observability/instrument-handoff.d.ts.map +1 -0
  27. package/dist/observability/instrument-handoff.js +173 -0
  28. package/dist/observability/instrument-handoff.js.map +1 -0
  29. package/dist/observability/lifecycle-bridge.d.ts +69 -0
  30. package/dist/observability/lifecycle-bridge.d.ts.map +1 -0
  31. package/dist/observability/lifecycle-bridge.js +155 -0
  32. package/dist/observability/lifecycle-bridge.js.map +1 -0
  33. package/dist/observability/scrub.d.ts +31 -0
  34. package/dist/observability/scrub.d.ts.map +1 -0
  35. package/dist/observability/scrub.js +167 -0
  36. package/dist/observability/scrub.js.map +1 -0
  37. package/dist/observability/telemetry.d.ts +157 -0
  38. package/dist/observability/telemetry.d.ts.map +1 -0
  39. package/dist/observability/telemetry.js +373 -0
  40. package/dist/observability/telemetry.js.map +1 -0
  41. package/dist/services/connection-service.d.ts +23 -1
  42. package/dist/services/connection-service.d.ts.map +1 -1
  43. package/dist/services/connection-service.js +128 -19
  44. package/dist/services/connection-service.js.map +1 -1
  45. package/dist/services/network-switch-service.d.ts +2 -1
  46. package/dist/services/network-switch-service.d.ts.map +1 -1
  47. package/dist/services/network-switch-service.js +45 -16
  48. package/dist/services/network-switch-service.js.map +1 -1
  49. package/dist/services/signature-service.d.ts +14 -7
  50. package/dist/services/signature-service.d.ts.map +1 -1
  51. package/dist/services/signature-service.js +56 -58
  52. package/dist/services/signature-service.js.map +1 -1
  53. package/dist/services/transaction-service.d.ts +19 -5
  54. package/dist/services/transaction-service.d.ts.map +1 -1
  55. package/dist/services/transaction-service.js +55 -38
  56. package/dist/services/transaction-service.js.map +1 -1
  57. package/dist/universal-wallet-connector.d.ts +56 -1
  58. package/dist/universal-wallet-connector.d.ts.map +1 -1
  59. package/dist/universal-wallet-connector.js +207 -110
  60. package/dist/universal-wallet-connector.js.map +1 -1
  61. package/dist/utils/id.d.ts +2 -0
  62. package/dist/utils/id.d.ts.map +1 -0
  63. package/dist/utils/id.js +41 -0
  64. package/dist/utils/id.js.map +1 -0
  65. package/dist/utils/resolve-session-provider.d.ts +10 -0
  66. package/dist/utils/resolve-session-provider.d.ts.map +1 -0
  67. package/dist/utils/resolve-session-provider.js +21 -0
  68. package/dist/utils/resolve-session-provider.js.map +1 -0
  69. package/dist/utils/to-wallet-error.d.ts +35 -0
  70. package/dist/utils/to-wallet-error.d.ts.map +1 -0
  71. package/dist/utils/to-wallet-error.js +130 -0
  72. package/dist/utils/to-wallet-error.js.map +1 -0
  73. package/dist/utils/validate-wallet-session.d.ts +15 -0
  74. package/dist/utils/validate-wallet-session.d.ts.map +1 -0
  75. package/dist/utils/validate-wallet-session.js +29 -0
  76. package/dist/utils/validate-wallet-session.js.map +1 -0
  77. package/dist/version.d.ts +2 -0
  78. package/dist/version.d.ts.map +1 -0
  79. package/dist/version.js +3 -0
  80. package/dist/version.js.map +1 -0
  81. package/package.json +6 -6
  82. package/src/events.ts +326 -14
  83. package/src/index.ts +10 -0
  84. package/src/logger/create-logger.test.ts +126 -0
  85. package/src/logger/create-logger.ts +112 -0
  86. package/src/managers/event-manager.test.ts +55 -0
  87. package/src/managers/event-manager.ts +5 -1
  88. package/src/observability/connect-observer.test.ts +214 -0
  89. package/src/observability/connect-observer.ts +128 -0
  90. package/src/observability/focus-tracker.test.ts +191 -0
  91. package/src/observability/focus-tracker.ts +96 -0
  92. package/src/observability/instrument-handoff.test.ts +397 -0
  93. package/src/observability/instrument-handoff.ts +231 -0
  94. package/src/observability/lifecycle-bridge.test.ts +398 -0
  95. package/src/observability/lifecycle-bridge.ts +216 -0
  96. package/src/observability/scrub.test.ts +183 -0
  97. package/src/observability/scrub.ts +174 -0
  98. package/src/observability/telemetry.test.ts +549 -0
  99. package/src/observability/telemetry.ts +546 -0
  100. package/src/services/connection-service.test.ts +261 -1
  101. package/src/services/connection-service.ts +167 -38
  102. package/src/services/network-switch-service.test.ts +51 -1
  103. package/src/services/network-switch-service.ts +56 -24
  104. package/src/services/signature-service.test.ts +181 -76
  105. package/src/services/signature-service.ts +75 -85
  106. package/src/services/transaction-service.test.ts +270 -69
  107. package/src/services/transaction-service.ts +74 -51
  108. package/src/universal-wallet-connector.instrumentation-guard.test.ts +199 -0
  109. package/src/universal-wallet-connector.observability.test.ts +610 -0
  110. package/src/universal-wallet-connector.test.ts +27 -146
  111. package/src/universal-wallet-connector.ts +279 -142
  112. package/src/utils/id.test.ts +15 -0
  113. package/src/utils/id.ts +48 -0
  114. package/src/utils/resolve-session-provider.test.ts +54 -0
  115. package/src/utils/resolve-session-provider.ts +25 -0
  116. package/src/utils/to-wallet-error.test.ts +181 -0
  117. package/src/utils/to-wallet-error.ts +152 -0
  118. package/src/utils/validate-wallet-session.test.ts +77 -0
  119. package/src/utils/validate-wallet-session.ts +40 -0
  120. package/src/version.test.ts +11 -0
  121. package/src/version.ts +2 -0
@@ -0,0 +1,155 @@
1
+ /**
2
+ * Hard ceiling on lifecycle emissions per attachment. Every other telemetry
3
+ * sink this layer added is bounded (relay / bridge / observer-error dedup sets
4
+ * cap at 100); a provider looping `chainChanged`/`accountsChanged` must not be
5
+ * the one unbounded path to the observer. A legitimate session stays far below
6
+ * this; a storming provider goes silent instead of driving egress cost.
7
+ */
8
+ export const MAX_LIFECYCLE_EVENTS_PER_ATTACHMENT = 100;
9
+ /**
10
+ * Bridge a connector's PII-safe `WalletLifecycleEvent` stream onto the event bus
11
+ * as `walletAccountChanged` / `walletChainChanged` / `walletDisconnected` /
12
+ * `walletSessionExpired`.
13
+ *
14
+ * Two correctness concerns are handled here:
15
+ * - **Echo suppression.** When UWC drives a `switchNetwork`, the wallet emits its
16
+ * own `chainChanged` for the same chain — that's already captured by UWC's
17
+ * `networkSwitched` / the switch handoff, so re-emitting it as a wallet-
18
+ * initiated `walletChainChanged` would double-count. We watch the bus for
19
+ * `networkSwitched` and drop a matching `chainChanged` inside `echoWindowMs`.
20
+ * - **Availability is signal.** If the connector can't observe lifecycle, we emit
21
+ * `lifecycleTelemetryUnavailable` ONCE so a dashboard can tell "silent" from
22
+ * "unwired".
23
+ *
24
+ * @returns a teardown that removes the connector subscription AND the bus watcher.
25
+ */
26
+ export function attachLifecycle(opts) {
27
+ const { eventManager, subscribe, getRouting } = opts;
28
+ const runtime = opts.runtime ?? 'direct';
29
+ const now = opts.now ?? Date.now;
30
+ const echoWindowMs = opts.echoWindowMs ?? 3000;
31
+ const emitUnavailable = (reason) => eventManager.emit('lifecycleTelemetryUnavailable', {
32
+ reason,
33
+ connectionMode: opts.connectionMode,
34
+ namespace: opts.namespace,
35
+ walletId: opts.walletId,
36
+ runtime
37
+ });
38
+ if (!subscribe) {
39
+ // In the production iframe the injected subscription is deliberately not
40
+ // attempted (the provider is a Comlink proxy) — the BRIDGE, not the connector,
41
+ // is why lifecycle is unobservable. On the direct page, a connector with no
42
+ // `onLifecycleEvent` simply has no lifecycle support for this namespace yet.
43
+ emitUnavailable(runtime === 'iframe'
44
+ ? 'bridge_subscription_unavailable'
45
+ : 'unsupported_namespace');
46
+ return () => { };
47
+ }
48
+ // Record UWC-initiated switches so the wallet's chainChanged echo can be
49
+ // dropped. Armed from BOTH signals: `awaitingWallet` (operation switchNetwork)
50
+ // arms at the handoff START, because some providers emit their chainChanged
51
+ // BEFORE the switch request resolves — i.e. before `networkSwitched` exists —
52
+ // and `networkSwitched` re-arms at completion for the late-echo case. The
53
+ // re-arm means one extra suppression window can follow an early echo; the
54
+ // only event it could swallow is a sub-window user re-switch to the chain
55
+ // that just became active — accepted over double-counting every early echo.
56
+ let lastSwitch = opts.initialSwitch;
57
+ const offAwaiting = eventManager.on('awaitingWallet', data => {
58
+ if (data.operation === 'switchNetwork' && data.chainId !== undefined) {
59
+ lastSwitch = { chainId: data.chainId, at: now() };
60
+ }
61
+ });
62
+ const offSwitched = eventManager.on('networkSwitched', data => {
63
+ lastSwitch = { chainId: data.network.id, at: now() };
64
+ });
65
+ const detachBusWatchers = () => {
66
+ offAwaiting();
67
+ offSwitched();
68
+ };
69
+ // Defense-in-depth: a connector whose subscribe THROWS (e.g. a provider whose
70
+ // `.on` rejects an un-proxied handler across the iframe bridge) must degrade to
71
+ // "unavailable", never break the connect path that called us.
72
+ let unsub;
73
+ // Emission budget for THIS attachment — suppressed echoes don't spend it.
74
+ let emitted = 0;
75
+ let capNotified = false;
76
+ try {
77
+ unsub = subscribe(e => {
78
+ // The whole body is guarded: wallet SDKs dispatch their listeners
79
+ // unguarded (e.g. TonConnect's bare `forEach`), so a throw here — from
80
+ // `getRouting()` or any future edit — would abort delivery to the
81
+ // wallet's OWN later subscribers, including one resolving a pending
82
+ // connect. Telemetry must never do that.
83
+ try {
84
+ if (emitted >= MAX_LIFECYCLE_EVENTS_PER_ATTACHMENT) {
85
+ // Mark the trip ONCE — a silent cap would be indistinguishable from a
86
+ // provider that simply went quiet.
87
+ if (!capNotified) {
88
+ capNotified = true;
89
+ emitUnavailable('emission_cap');
90
+ }
91
+ return;
92
+ }
93
+ const routing = getRouting();
94
+ switch (e.type) {
95
+ case 'accountChanged':
96
+ emitted++;
97
+ eventManager.emit('walletAccountChanged', routing);
98
+ return;
99
+ case 'chainChanged': {
100
+ const isEcho = !!lastSwitch &&
101
+ e.chainId !== undefined &&
102
+ lastSwitch.chainId === e.chainId &&
103
+ now() - lastSwitch.at <= echoWindowMs;
104
+ if (isEcho) {
105
+ // Consume the echo: a UWC switch produces exactly one wallet echo, so
106
+ // a LATER chainChanged to the same chain inside the window is a genuine
107
+ // user-initiated switch-back and must not be suppressed.
108
+ lastSwitch = undefined;
109
+ return;
110
+ }
111
+ emitted++;
112
+ eventManager.emit('walletChainChanged', {
113
+ ...routing,
114
+ ...(e.chainId !== undefined ? { chainId: e.chainId } : {})
115
+ });
116
+ return;
117
+ }
118
+ case 'disconnect':
119
+ emitted++;
120
+ eventManager.emit('walletDisconnected', routing);
121
+ return;
122
+ case 'sessionExpired':
123
+ emitted++;
124
+ eventManager.emit('walletSessionExpired', routing);
125
+ return;
126
+ }
127
+ }
128
+ catch {
129
+ // Swallowed: better to drop one lifecycle record than to break the
130
+ // wallet's event dispatch.
131
+ }
132
+ });
133
+ }
134
+ catch {
135
+ unsub = undefined;
136
+ }
137
+ // The connector accepted the subscribe call but this session has no observable
138
+ // surface (returned undefined) or threw — treat as "unavailable" and don't
139
+ // leave the bus watchers attached.
140
+ if (!unsub) {
141
+ detachBusWatchers();
142
+ // `onLifecycleEvent` ran but exposed no live subscription surface for this
143
+ // session (returned undefined or threw). Over the bridge, that absence is the
144
+ // bridge's doing, not the provider's.
145
+ emitUnavailable(runtime === 'iframe'
146
+ ? 'bridge_subscription_unavailable'
147
+ : 'provider_subscription_unavailable');
148
+ return () => { };
149
+ }
150
+ return () => {
151
+ detachBusWatchers();
152
+ unsub();
153
+ };
154
+ }
155
+ //# sourceMappingURL=lifecycle-bridge.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lifecycle-bridge.js","sourceRoot":"","sources":["../../src/observability/lifecycle-bridge.ts"],"names":[],"mappings":"AAgBA;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,mCAAmC,GAAG,GAAG,CAAA;AAoCtD;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,UAAU,eAAe,CAAC,IAA4B;IAC1D,MAAM,EAAE,YAAY,EAAE,SAAS,EAAE,UAAU,EAAE,GAAG,IAAI,CAAA;IACpD,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,IAAI,QAAQ,CAAA;IACxC,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG,CAAA;IAChC,MAAM,YAAY,GAAG,IAAI,CAAC,YAAY,IAAI,IAAI,CAAA;IAE9C,MAAM,eAAe,GAAG,CAAC,MAAkC,EAAE,EAAE,CAC7D,YAAY,CAAC,IAAI,CAAC,+BAA+B,EAAE;QACjD,MAAM;QACN,cAAc,EAAE,IAAI,CAAC,cAAc;QACnC,SAAS,EAAE,IAAI,CAAC,SAAS;QACzB,QAAQ,EAAE,IAAI,CAAC,QAAQ;QACvB,OAAO;KACR,CAAC,CAAA;IAEJ,IAAI,CAAC,SAAS,EAAE,CAAC;QACf,yEAAyE;QACzE,+EAA+E;QAC/E,4EAA4E;QAC5E,6EAA6E;QAC7E,eAAe,CACb,OAAO,KAAK,QAAQ;YAClB,CAAC,CAAC,iCAAiC;YACnC,CAAC,CAAC,uBAAuB,CAC5B,CAAA;QACD,OAAO,GAAG,EAAE,GAAE,CAAC,CAAA;IACjB,CAAC;IAED,yEAAyE;IACzE,+EAA+E;IAC/E,4EAA4E;IAC5E,8EAA8E;IAC9E,0EAA0E;IAC1E,0EAA0E;IAC1E,0EAA0E;IAC1E,4EAA4E;IAC5E,IAAI,UAAU,GACZ,IAAI,CAAC,aAAa,CAAA;IACpB,MAAM,WAAW,GAAG,YAAY,CAAC,EAAE,CAAC,gBAAgB,EAAE,IAAI,CAAC,EAAE;QAC3D,IAAI,IAAI,CAAC,SAAS,KAAK,eAAe,IAAI,IAAI,CAAC,OAAO,KAAK,SAAS,EAAE,CAAC;YACrE,UAAU,GAAG,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,EAAE,EAAE,GAAG,EAAE,EAAE,CAAA;QACnD,CAAC;IACH,CAAC,CAAC,CAAA;IACF,MAAM,WAAW,GAAG,YAAY,CAAC,EAAE,CAAC,iBAAiB,EAAE,IAAI,CAAC,EAAE;QAC5D,UAAU,GAAG,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,CAAC,EAAE,EAAE,EAAE,EAAE,GAAG,EAAE,EAAE,CAAA;IACtD,CAAC,CAAC,CAAA;IACF,MAAM,iBAAiB,GAAG,GAAG,EAAE;QAC7B,WAAW,EAAE,CAAA;QACb,WAAW,EAAE,CAAA;IACf,CAAC,CAAA;IAED,8EAA8E;IAC9E,gFAAgF;IAChF,8DAA8D;IAC9D,IAAI,KAA+B,CAAA;IACnC,0EAA0E;IAC1E,IAAI,OAAO,GAAG,CAAC,CAAA;IACf,IAAI,WAAW,GAAG,KAAK,CAAA;IACvB,IAAI,CAAC;QACH,KAAK,GAAG,SAAS,CAAC,CAAC,CAAC,EAAE;YACpB,kEAAkE;YAClE,uEAAuE;YACvE,kEAAkE;YAClE,oEAAoE;YACpE,yCAAyC;YACzC,IAAI,CAAC;gBACH,IAAI,OAAO,IAAI,mCAAmC,EAAE,CAAC;oBACnD,sEAAsE;oBACtE,mCAAmC;oBACnC,IAAI,CAAC,WAAW,EAAE,CAAC;wBACjB,WAAW,GAAG,IAAI,CAAA;wBAClB,eAAe,CAAC,cAAc,CAAC,CAAA;oBACjC,CAAC;oBACD,OAAM;gBACR,CAAC;gBACD,MAAM,OAAO,GAAG,UAAU,EAAE,CAAA;gBAC5B,QAAQ,CAAC,CAAC,IAAI,EAAE,CAAC;oBACf,KAAK,gBAAgB;wBACnB,OAAO,EAAE,CAAA;wBACT,YAAY,CAAC,IAAI,CAAC,sBAAsB,EAAE,OAAO,CAAC,CAAA;wBAClD,OAAM;oBACR,KAAK,cAAc,CAAC,CAAC,CAAC;wBACpB,MAAM,MAAM,GACV,CAAC,CAAC,UAAU;4BACZ,CAAC,CAAC,OAAO,KAAK,SAAS;4BACvB,UAAU,CAAC,OAAO,KAAK,CAAC,CAAC,OAAO;4BAChC,GAAG,EAAE,GAAG,UAAU,CAAC,EAAE,IAAI,YAAY,CAAA;wBACvC,IAAI,MAAM,EAAE,CAAC;4BACX,sEAAsE;4BACtE,wEAAwE;4BACxE,yDAAyD;4BACzD,UAAU,GAAG,SAAS,CAAA;4BACtB,OAAM;wBACR,CAAC;wBACD,OAAO,EAAE,CAAA;wBACT,YAAY,CAAC,IAAI,CAAC,oBAAoB,EAAE;4BACtC,GAAG,OAAO;4BACV,GAAG,CAAC,CAAC,CAAC,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;yBAC3D,CAAC,CAAA;wBACF,OAAM;oBACR,CAAC;oBACD,KAAK,YAAY;wBACf,OAAO,EAAE,CAAA;wBACT,YAAY,CAAC,IAAI,CAAC,oBAAoB,EAAE,OAAO,CAAC,CAAA;wBAChD,OAAM;oBACR,KAAK,gBAAgB;wBACnB,OAAO,EAAE,CAAA;wBACT,YAAY,CAAC,IAAI,CAAC,sBAAsB,EAAE,OAAO,CAAC,CAAA;wBAClD,OAAM;gBACV,CAAC;YACH,CAAC;YAAC,MAAM,CAAC;gBACP,mEAAmE;gBACnE,2BAA2B;YAC7B,CAAC;QACH,CAAC,CAAC,CAAA;IACJ,CAAC;IAAC,MAAM,CAAC;QACP,KAAK,GAAG,SAAS,CAAA;IACnB,CAAC;IAED,+EAA+E;IAC/E,2EAA2E;IAC3E,mCAAmC;IACnC,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,iBAAiB,EAAE,CAAA;QACnB,2EAA2E;QAC3E,8EAA8E;QAC9E,sCAAsC;QACtC,eAAe,CACb,OAAO,KAAK,QAAQ;YAClB,CAAC,CAAC,iCAAiC;YACnC,CAAC,CAAC,mCAAmC,CACxC,CAAA;QACD,OAAO,GAAG,EAAE,GAAE,CAAC,CAAA;IACjB,CAAC;IAED,OAAO,GAAG,EAAE;QACV,iBAAiB,EAAE,CAAA;QACnB,KAAK,EAAE,CAAA;IACT,CAAC,CAAA;AACH,CAAC"}
@@ -0,0 +1,31 @@
1
+ /**
2
+ * PII / secret scrubber for data leaving UWC through the observer seam.
3
+ *
4
+ * Security-first: the normalized `UWCTelemetryEvent` excludes signing payloads
5
+ * and addresses by construction — its STRUCTURED fields have no slot for them
6
+ * (§ telemetry.ts). This scrubber is the second line of defence for the two
7
+ * surfaces that CAN still carry free-form data on the observer path: the
8
+ * `WalletError.message` forwarded to `observer.onEvent`, and the `args` forwarded
9
+ * to `observer.onLog`. (The console / custom-logger sink receives raw data — it's
10
+ * local, not egress.)
11
+ *
12
+ * Two detection layers:
13
+ * - KEY-based redaction of sensitive object keys — substring match (not regex),
14
+ * avoiding the ReDoS class that Wiz flags on user-input regexes.
15
+ * - CONTENT-based redaction of secrets embedded in free-form strings (hex blobs,
16
+ * secret k/v params, bearer tokens) — via linear regexes with no nested or
17
+ * overlapping quantifiers (see SECRET_CONTENT_PATTERNS), so they carry no
18
+ * catastrophic-backtracking risk.
19
+ */
20
+ /**
21
+ * Recursively copy `value`, redacting sensitive object-keys, capping long
22
+ * strings, and rendering non-serializable values inert. The returned objects use
23
+ * a null prototype so a malicious `__proto__`/`constructor` key in attacker-shaped
24
+ * input can't pollute `Object.prototype` downstream.
25
+ */
26
+ export declare function scrubValue(value: unknown, depth?: number): unknown;
27
+ /** Scrub a free-form error message (string in → capped string out). */
28
+ export declare function scrubMessage(message: string): string;
29
+ /** Scrub a variadic `args` array before it reaches `observer.onLog`. */
30
+ export declare function scrubArgs(args: unknown[]): unknown[];
31
+ //# sourceMappingURL=scrub.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scrub.d.ts","sourceRoot":"","sources":["../../src/observability/scrub.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAmGH;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,OAAO,EAAE,KAAK,SAAI,GAAG,OAAO,CAwC7D;AAED,uEAAuE;AACvE,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAEpD;AAED,wEAAwE;AACxE,wBAAgB,SAAS,CAAC,IAAI,EAAE,OAAO,EAAE,GAAG,OAAO,EAAE,CAEpD"}
@@ -0,0 +1,167 @@
1
+ /**
2
+ * PII / secret scrubber for data leaving UWC through the observer seam.
3
+ *
4
+ * Security-first: the normalized `UWCTelemetryEvent` excludes signing payloads
5
+ * and addresses by construction — its STRUCTURED fields have no slot for them
6
+ * (§ telemetry.ts). This scrubber is the second line of defence for the two
7
+ * surfaces that CAN still carry free-form data on the observer path: the
8
+ * `WalletError.message` forwarded to `observer.onEvent`, and the `args` forwarded
9
+ * to `observer.onLog`. (The console / custom-logger sink receives raw data — it's
10
+ * local, not egress.)
11
+ *
12
+ * Two detection layers:
13
+ * - KEY-based redaction of sensitive object keys — substring match (not regex),
14
+ * avoiding the ReDoS class that Wiz flags on user-input regexes.
15
+ * - CONTENT-based redaction of secrets embedded in free-form strings (hex blobs,
16
+ * secret k/v params, bearer tokens) — via linear regexes with no nested or
17
+ * overlapping quantifiers (see SECRET_CONTENT_PATTERNS), so they carry no
18
+ * catastrophic-backtracking risk.
19
+ */
20
+ /**
21
+ * Separator-free, lower-cased fragments that mark a value as sensitive. Keys are
22
+ * normalized (separators stripped) before matching, so `walletPrivateKey`,
23
+ * `private_key`, `x-api-key`, and `authToken` all hit the same short list. This
24
+ * over-redacts by design (a `security`-first scrubber prefers a false redaction
25
+ * to a leak).
26
+ */
27
+ const SENSITIVE_KEY_FRAGMENTS = [
28
+ 'seed',
29
+ 'mnemonic',
30
+ 'privatekey',
31
+ 'secret',
32
+ 'password',
33
+ 'passphrase',
34
+ 'signature',
35
+ 'message',
36
+ 'payload',
37
+ 'token',
38
+ 'apikey',
39
+ 'authorization',
40
+ 'auth'
41
+ ];
42
+ const MAX_STRING_LENGTH = 500;
43
+ const MAX_DEPTH = 4;
44
+ const REDACTED = '[Redacted]';
45
+ function isSensitiveKey(key) {
46
+ // Strip separators so `private_key` / `x-api-key` normalize to the bare form.
47
+ const normalized = key.toLowerCase().split('-').join('').split('_').join('');
48
+ return SENSITIVE_KEY_FRAGMENTS.some(fragment => normalized.includes(fragment));
49
+ }
50
+ // Content patterns for secrets embedded in FREE-FORM strings (error messages,
51
+ // log args) where key-based redaction can't reach. All are linear expressions
52
+ // with no nested/overlapping quantifiers, so they carry no catastrophic-
53
+ // backtracking (ReDoS) risk on attacker-controlled input.
54
+ const SECRET_CONTENT_PATTERNS = [
55
+ // 0x-prefixed hex blobs: EVM addresses (40 nibbles) and longer
56
+ // signatures/keys/hashes/calldata (64+). Over-redacts public hashes by design.
57
+ [/0x[a-fA-F0-9]{40,}/g, '0x[redacted-hex]'],
58
+ // `secret=…` / `token=…` / `key=…` query-string or k/v fragments.
59
+ [
60
+ /\b(token|key|secret|auth|sig|signature|password|passphrase|mnemonic|seed|apikey)=[^\s&"']+/gi,
61
+ '$1=[redacted]'
62
+ ],
63
+ // `Bearer <opaque>` / `Basic <opaque>` authorization values.
64
+ [/\b(bearer|basic)\s+[A-Za-z0-9._~+/-]+=*/gi, '$1 [redacted]'],
65
+ // TON user-friendly addresses: `EQ…` / `UQ…` + 46 base64url chars (48 total).
66
+ // Trailing edge is a negative lookahead, not `\b`: a base64url address can end
67
+ // in `-`/`_`, where `\b` fails to anchor and the address would slip through.
68
+ [/\b[EU]Q[A-Za-z0-9_-]{46}(?![A-Za-z0-9_-])/g, '[redacted-ton-address]'],
69
+ // TON raw-form addresses: `workchain:64-hex` (`0:…`, `-1:…`). Before base58 so
70
+ // the hex tail gets the specific TON marker, not a generic base58 hit.
71
+ // Anchor-free like base58 below: digits and `_` are word chars, so a `\b`
72
+ // never anchors when the address is glued to one (`account_0:…` leaked).
73
+ // The trailing edge is a hex negative lookahead — a LONGER contiguous hex
74
+ // run is a different blob, not a 64-hex address.
75
+ [/-?\d+:[0-9a-fA-F]{64}(?![0-9a-fA-F])/g, '[redacted-ton-address]'],
76
+ // Solana / generic base58, 32 chars or MORE — bare addresses AND longer blobs
77
+ // (a signature is ~88 chars, a serialized tx longer still). No upper bound: a
78
+ // capped quantifier can never match inside a longer contiguous run. No `\b`
79
+ // anchors either: `0`/`O`/`I`/`l`/`_` are word chars OUTSIDE the base58
80
+ // alphabet, so they break the class AND defeat a boundary anchor — an address
81
+ // glued to one (`account_<addr>`) would slip through anchored matching.
82
+ // Over-redaction inside longer tokens is accepted by design. Placed LAST: it
83
+ // is the broadest pattern and would otherwise swallow narrower matches above.
84
+ [/[1-9A-HJ-NP-Za-km-z]{32,}/g, '[redacted-base58]']
85
+ ];
86
+ /**
87
+ * Redact secrets that live INSIDE a free-form string. Best-effort and
88
+ * deliberately conservative toward over-redaction: a wallet error string can
89
+ * embed an address, a reverted-tx calldata blob, or a URL with a token, none of
90
+ * which key-based redaction sees.
91
+ *
92
+ * Base58 (Solana) and TON addresses (user-friendly `EQ…`/`UQ…` AND the raw
93
+ * `workchain:hex` form) are redacted as a security gate: a leaked
94
+ * recipient/wallet address on the egress path is treated as a defect, so we
95
+ * accept that any base58-looking token of 32+ chars (some ids, tx hashes,
96
+ * IPFS CIDs) is over-redacted by design. The narrower TON patterns run before
97
+ * the broad base58 one so TON addresses get the specific marker.
98
+ *
99
+ * One documented gap remains: BARE (non-`0x`) hex blobs — a raw 64-char hex
100
+ * string is far more often a legitimate id/hash than a secret, so only
101
+ * `0x`-prefixed hex is redacted. Structured telemetry fields never carry
102
+ * addresses regardless (see telemetry.ts); this pass only hardens the free-form
103
+ * `error.message` / log-arg surface.
104
+ */
105
+ function redactSecretsInString(input) {
106
+ let out = input;
107
+ for (const [pattern, replacement] of SECRET_CONTENT_PATTERNS) {
108
+ out = out.replace(pattern, replacement);
109
+ }
110
+ return out;
111
+ }
112
+ /**
113
+ * Recursively copy `value`, redacting sensitive object-keys, capping long
114
+ * strings, and rendering non-serializable values inert. The returned objects use
115
+ * a null prototype so a malicious `__proto__`/`constructor` key in attacker-shaped
116
+ * input can't pollute `Object.prototype` downstream.
117
+ */
118
+ export function scrubValue(value, depth = 0) {
119
+ if (depth > MAX_DEPTH)
120
+ return '[Truncated: max depth]';
121
+ if (value === null || value === undefined)
122
+ return value;
123
+ const type = typeof value;
124
+ if (type === 'string') {
125
+ // Redact embedded secrets BEFORE capping, so the cap can't slice a pattern
126
+ // in half and leak its tail.
127
+ const str = redactSecretsInString(value);
128
+ return str.length > MAX_STRING_LENGTH
129
+ ? str.slice(0, MAX_STRING_LENGTH) + '…[truncated]'
130
+ : str;
131
+ }
132
+ if (type === 'number' || type === 'boolean')
133
+ return value;
134
+ if (type === 'bigint')
135
+ return value.toString();
136
+ if (type === 'function' || type === 'symbol')
137
+ return `[${type}]`;
138
+ if (Array.isArray(value)) {
139
+ return value.map(item => scrubValue(item, depth + 1));
140
+ }
141
+ if (type === 'object') {
142
+ // Error instances carry a useful message but also a stack we don't want.
143
+ // Null prototype here too — the doc above promises it for every returned
144
+ // object, and an Error-shaped attacker value lands in this branch.
145
+ if (value instanceof Error) {
146
+ const out = Object.create(null);
147
+ out['name'] = value.name;
148
+ out['message'] = scrubValue(value.message, depth + 1);
149
+ return out;
150
+ }
151
+ const out = Object.create(null);
152
+ for (const [key, val] of Object.entries(value)) {
153
+ out[key] = isSensitiveKey(key) ? REDACTED : scrubValue(val, depth + 1);
154
+ }
155
+ return out;
156
+ }
157
+ return '[Unserializable]';
158
+ }
159
+ /** Scrub a free-form error message (string in → capped string out). */
160
+ export function scrubMessage(message) {
161
+ return scrubValue(message);
162
+ }
163
+ /** Scrub a variadic `args` array before it reaches `observer.onLog`. */
164
+ export function scrubArgs(args) {
165
+ return args.map(arg => scrubValue(arg));
166
+ }
167
+ //# sourceMappingURL=scrub.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scrub.js","sourceRoot":"","sources":["../../src/observability/scrub.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,157 @@
1
+ import type { ConnectionMode, WalletError, WalletFlowType, Platform, LogLevel, Namespace, NetworkId, FailureKind, ErrorSource, LifecycleUnavailableReason } from '@meshconnect/uwc-types';
2
+ import type { UWCEventName, UWCEventMap, UWCOperation } from '../events';
3
+ /**
4
+ * Telemetry record schema version. Bump on any breaking change to the
5
+ * `UWCTelemetryEvent` shape so a downstream consumer can branch on it.
6
+ */
7
+ export declare const TELEMETRY_SCHEMA_VERSION = 1;
8
+ /**
9
+ * A normalized, PII-safe telemetry record derived from a `UWCEventMap` event.
10
+ *
11
+ * This is the ONLY shape that reaches `observer.onEvent`. It is the PII boundary:
12
+ * it has no field for a signing payload, signature, raw transaction, or wallet
13
+ * address — leaking those is structurally impossible, not merely discouraged.
14
+ */
15
+ export interface UWCTelemetryEvent {
16
+ /**
17
+ * Record schema version — the literal type IS the version this package
18
+ * emits, so a consumer can branch exhaustively (`event.schemaVersion === 1`)
19
+ * and gets a compile break when a bump lands. Import the runtime value as
20
+ * `TELEMETRY_SCHEMA_VERSION`.
21
+ */
22
+ schemaVersion: typeof TELEMETRY_SCHEMA_VERSION;
23
+ /**
24
+ * The `@meshconnect/uwc-core` version that produced the record (generated
25
+ * from package.json at build time) — aggregate failures per released
26
+ * version to spot version-specific regressions.
27
+ */
28
+ uwcVersion: string;
29
+ /** The originating event name. */
30
+ name: UWCEventName;
31
+ /** Per-connector-instance id (stamped on every event). */
32
+ sdkSessionId: string;
33
+ /**
34
+ * Consumer-supplied correlation id (e.g. their `link_sess_id`), present only
35
+ * when supplied — never aliased to `sdkSessionId`. Lets UWC events join the
36
+ * consumer's existing telemetry without a separate mapping.
37
+ */
38
+ correlationId?: string | undefined;
39
+ /** ms since epoch when the record was produced. */
40
+ timestamp: number;
41
+ /** Wallet-boundary op, when the event relates to one. */
42
+ operation?: UWCOperation | undefined;
43
+ connectionMode?: ConnectionMode | undefined;
44
+ /** CAIP namespace (eip155 | solana | tron | tvm …) — never an address. */
45
+ namespace?: Namespace | undefined;
46
+ /** CAIP chain id (e.g. `eip155:1`) — never an address. */
47
+ chainId?: NetworkId | undefined;
48
+ /** Catalog wallet id — never an address. */
49
+ walletId?: string | undefined;
50
+ /** Correlates a handoff start with its terminal. */
51
+ handoffId?: string | undefined;
52
+ durationMs?: number | undefined;
53
+ walletFlowType?: WalletFlowType | undefined;
54
+ platform?: Platform | undefined;
55
+ /** A `walletTimedOut` marker fired before the terminal (late completion). */
56
+ timedOut?: boolean | undefined;
57
+ /** Marker deadline — present on `walletTimedOut` and on a late terminal. */
58
+ timeoutMs?: number | undefined;
59
+ /** switchNetwork only: target chain was already the active one (no-op switch). */
60
+ targetAlreadyActive?: boolean | undefined;
61
+ /** Non-success outcomes: stringified error code when reliably known. */
62
+ errorCode?: string | undefined;
63
+ /** Non-success outcomes: coarse cause bucket. `unknown` when not classifiable. */
64
+ failureKind?: FailureKind | undefined;
65
+ /** Non-success outcomes: where the error originated, when inferable. */
66
+ source?: ErrorSource | undefined;
67
+ /**
68
+ * Connect `no_common_network` failures only: the wallet's advertised vs the
69
+ * configured CAIP-2 id lists (non-PII). Lets a dashboard show "wallet offered
70
+ * X, token configured Y" — the diagnosis for the #1 prod connection error.
71
+ */
72
+ networkMismatch?: {
73
+ walletNetworkIds: string[];
74
+ configuredNetworkIds: string[];
75
+ } | undefined;
76
+ /** Present on failure events. `message` is scrubbed before egress. */
77
+ error?: WalletError | undefined;
78
+ /** Log level, present only for `name === 'log'`. */
79
+ level?: LogLevel | undefined;
80
+ /**
81
+ * Per-chain supported capability names (e.g. `{ 'eip155:8453': ['atomic'] }`),
82
+ * present only for `capabilitiesUpdated`. Feature flags + chain ids — not PII.
83
+ */
84
+ capabilities?: Record<string, string[]> | undefined;
85
+ /**
86
+ * Per-namespace requested-vs-matched discovery summary, present only on
87
+ * `walletsDetected`. Counts only — never a wallet id or address.
88
+ */
89
+ discovery?: Record<string, {
90
+ requested: number;
91
+ matched: number;
92
+ }> | undefined;
93
+ /**
94
+ * Per-namespace count of the session's available addresses, present on
95
+ * `connected` / `sessionChanged`. Counts only — never an address.
96
+ */
97
+ addressNamespaces?: Record<string, number> | undefined;
98
+ /**
99
+ * Namespaces the connected wallet supports for this session's configured
100
+ * networks but returned NO address for — present on `connected` /
101
+ * `sessionChanged` only when non-empty. A RAW FACT (supported-but-absent), not
102
+ * an asserted bug: it's the observable symptom of a silently dropped
103
+ * secondary-namespace gather — e.g. injected-connector's framed-embed skip,
104
+ * where the wallet supports Solana but the session has zero Solana addresses.
105
+ * The consumer interprets it (link-v2 deposit ⇒ lost holdings; a single-
106
+ * namespace deposit ⇒ intended). Pairs with `discovery` for the full picture.
107
+ */
108
+ missingNamespaces?: string[] | undefined;
109
+ /** `lifecycleTelemetryUnavailable` only: why lifecycle telemetry couldn't attach. */
110
+ reason?: LifecycleUnavailableReason | undefined;
111
+ /** `lifecycleTelemetryUnavailable` only: direct page vs production iframe. */
112
+ runtime?: 'direct' | 'iframe' | undefined;
113
+ /**
114
+ * `error` only: a handoff terminal already reported this failure — this
115
+ * record is the back-compat duplicate. Count terminals + `error` where NOT
116
+ * handoffCovered to avoid double-counting.
117
+ */
118
+ handoffCovered?: boolean | undefined;
119
+ }
120
+ /**
121
+ * Vendor-agnostic telemetry sink. Consumers implement; UWC ships no vendor SDK.
122
+ * Both methods are optional and called best-effort (throws are swallowed upstream).
123
+ *
124
+ * CONTRACT — both methods are invoked **synchronously on the wallet-operation
125
+ * path**. A terminal event (e.g. `walletSucceeded`) fires between the wallet
126
+ * returning a result and that result reaching the caller, so a slow handler adds
127
+ * latency to connect / sign / sendTransaction. Keep handlers **non-blocking**:
128
+ * hand off to a queue-backed sink (Datadog `addAction`, Segment `track`, etc. are
129
+ * already non-blocking) or defer heavy work yourself — e.g.
130
+ * `onEvent: e => queueMicrotask(() => doHeavyWork(e))`. Do not perform synchronous
131
+ * network/IO or expensive serialization inline.
132
+ */
133
+ export interface UWCObserver {
134
+ /** A normalized, PII-safe event. Forward to Datadog / Amplitude / Segment / … (non-blocking). */
135
+ onEvent?(event: UWCTelemetryEvent): void;
136
+ /** A log line (all levels). `args` is already scrubbed. Keep non-blocking. */
137
+ onLog?(level: LogLevel, message: string, args: unknown[]): void;
138
+ }
139
+ /**
140
+ * Best-effort coarse runtime surface. HEURISTIC — user agents lie and in-app
141
+ * browsers are hard to detect; treat `webview` as a hint, not a guarantee.
142
+ * Never returns the raw UA (PII / high-cardinality).
143
+ */
144
+ export declare function derivePlatform(): Platform;
145
+ /**
146
+ * Derive the flow taxonomy from connection mode + surface. HEURISTIC — UWC does
147
+ * not always know whether a WalletConnect handoff used a deeplink vs a QR scan;
148
+ * we infer from the platform (mobile ⇒ deeplink, desktop ⇒ QR), matching link-v2's
149
+ * historical intent. Consumers with better context may override downstream.
150
+ */
151
+ export declare function deriveWalletFlowType(connectionMode: ConnectionMode, platform: Platform): WalletFlowType;
152
+ /**
153
+ * Normalize a bus event into a PII-safe `UWCTelemetryEvent`, dropping undefined
154
+ * optionals so the emitted shape is consistent across event kinds.
155
+ */
156
+ export declare function normalizeEvent<K extends UWCEventName>(name: K, data: UWCEventMap[K], sdkSessionId: string, now: number, correlationId?: string): UWCTelemetryEvent;
157
+ //# sourceMappingURL=telemetry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"telemetry.d.ts","sourceRoot":"","sources":["../../src/observability/telemetry.ts"],"names":[],"mappings":"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"}