@meshconnect/uwc-core 1.0.0 → 1.0.1-snapshot.c7e65ce

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 (76) hide show
  1. package/README.md +211 -66
  2. package/dist/events.d.ts +69 -2
  3. package/dist/events.d.ts.map +1 -1
  4. package/dist/events.js +40 -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 +1 -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 +69 -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 +22 -0
  18. package/dist/observability/connect-observer.d.ts.map +1 -0
  19. package/dist/observability/connect-observer.js +60 -0
  20. package/dist/observability/connect-observer.js.map +1 -0
  21. package/dist/observability/instrument-handoff.d.ts +39 -0
  22. package/dist/observability/instrument-handoff.d.ts.map +1 -0
  23. package/dist/observability/instrument-handoff.js +86 -0
  24. package/dist/observability/instrument-handoff.js.map +1 -0
  25. package/dist/observability/scrub.d.ts +30 -0
  26. package/dist/observability/scrub.d.ts.map +1 -0
  27. package/dist/observability/scrub.js +135 -0
  28. package/dist/observability/scrub.js.map +1 -0
  29. package/dist/observability/telemetry.d.ts +77 -0
  30. package/dist/observability/telemetry.d.ts.map +1 -0
  31. package/dist/observability/telemetry.js +138 -0
  32. package/dist/observability/telemetry.js.map +1 -0
  33. package/dist/services/connection-service.d.ts +2 -1
  34. package/dist/services/connection-service.d.ts.map +1 -1
  35. package/dist/services/connection-service.js +35 -15
  36. package/dist/services/connection-service.js.map +1 -1
  37. package/dist/services/network-switch-service.d.ts +2 -1
  38. package/dist/services/network-switch-service.d.ts.map +1 -1
  39. package/dist/services/network-switch-service.js +33 -15
  40. package/dist/services/network-switch-service.js.map +1 -1
  41. package/dist/universal-wallet-connector.d.ts +51 -2
  42. package/dist/universal-wallet-connector.d.ts.map +1 -1
  43. package/dist/universal-wallet-connector.js +163 -61
  44. package/dist/universal-wallet-connector.js.map +1 -1
  45. package/dist/utils/id.d.ts +2 -0
  46. package/dist/utils/id.d.ts.map +1 -0
  47. package/dist/utils/id.js +41 -0
  48. package/dist/utils/id.js.map +1 -0
  49. package/dist/utils/to-wallet-error.d.ts +16 -0
  50. package/dist/utils/to-wallet-error.d.ts.map +1 -0
  51. package/dist/utils/to-wallet-error.js +33 -0
  52. package/dist/utils/to-wallet-error.js.map +1 -0
  53. package/package.json +5 -5
  54. package/src/events.ts +114 -1
  55. package/src/index.ts +6 -0
  56. package/src/logger/create-logger.test.ts +111 -0
  57. package/src/logger/create-logger.ts +101 -0
  58. package/src/managers/event-manager.test.ts +25 -0
  59. package/src/managers/event-manager.ts +5 -1
  60. package/src/observability/connect-observer.test.ts +100 -0
  61. package/src/observability/connect-observer.ts +71 -0
  62. package/src/observability/instrument-handoff.test.ts +150 -0
  63. package/src/observability/instrument-handoff.ts +122 -0
  64. package/src/observability/scrub.test.ts +109 -0
  65. package/src/observability/scrub.ts +142 -0
  66. package/src/observability/telemetry.test.ts +134 -0
  67. package/src/observability/telemetry.ts +211 -0
  68. package/src/services/connection-service.test.ts +66 -1
  69. package/src/services/connection-service.ts +54 -32
  70. package/src/services/network-switch-service.test.ts +51 -1
  71. package/src/services/network-switch-service.ts +43 -23
  72. package/src/universal-wallet-connector.observability.test.ts +265 -0
  73. package/src/universal-wallet-connector.ts +244 -71
  74. package/src/utils/id.test.ts +15 -0
  75. package/src/utils/id.ts +48 -0
  76. package/src/utils/to-wallet-error.ts +36 -0
package/README.md CHANGED
@@ -121,11 +121,11 @@ await connector.connect('injected', 'metamask', 'eip155:1')
121
121
 
122
122
  Three mutually exclusive ways a user can connect:
123
123
 
124
- | Mode | Description | Typical wallets |
125
- | --------------- | ------------------------------------------------------------------ | -------------------------------- |
124
+ | Mode | Description | Typical wallets |
125
+ | --------------- | ----------------------------------------------------------------------------------------------------- | -------------------------------------- |
126
126
  | `injected` | Browser-extension or in-app injected provider (EIP-6963, Solana Wallet Standard, Tron, TON JS Bridge) | MetaMask, Phantom, Tonkeeper extension |
127
- | `walletConnect` | WalletConnect v2 relay + QR code / deeplink | Any WC-compatible mobile wallet |
128
- | `tonConnect` | TON Connect JS Bridge + QR code / universal link | Tonkeeper, OKX TON Wallet |
127
+ | `walletConnect` | WalletConnect v2 relay + QR code / deeplink | Any WC-compatible mobile wallet |
128
+ | `tonConnect` | TON Connect JS Bridge + QR code / universal link | Tonkeeper, OKX TON Wallet |
129
129
 
130
130
  A wallet can support multiple modes. `isConnectionModeAvailable(mode, walletId)`
131
131
  tells you which ones are actually viable in the current runtime.
@@ -141,7 +141,7 @@ interface Session {
141
141
  activeNetwork: Network | null
142
142
  activeAddress: string | null
143
143
  publicKey: string | null
144
- availableNetworks: Network[] // networks the current wallet supports
144
+ availableNetworks: Network[] // networks the current wallet supports
145
145
  availableAddresses: AvailableAddress[] // one per chain the wallet exposed
146
146
  activeWalletCapabilities: Record<string, EVMCapabilities> | null
147
147
  activeNetworkWalletCapabilities: EVMCapabilities | null
@@ -153,24 +153,30 @@ a `sessionChanged` event fires.
153
153
 
154
154
  ### Observer / event model
155
155
 
156
- The connector is an event emitter. There are twelve typed events:
157
-
158
- | Event | When it fires |
159
- | ----------------------- | ------------------------------------------------------------ |
160
- | `ready` | Initial wallet detection has completed |
161
- | `walletsDetected` | Detection completed, payload is the enriched wallet list |
162
- | `connecting` | `connect()` started |
163
- | `connectionUri` | A WC/TonConnect pairing URI is now available |
164
- | `connected` | `connect()` finished successfully |
165
- | `disconnected` | `disconnect()` finished (session cleared) |
166
- | `sessionChanged` | Any session field changed |
167
- | `networkSwitching` | Network switch started or ended |
168
- | `networkSwitched` | Network switch finished successfully |
169
- | `capabilitiesUpdated` | `getWalletCapabilities()` refreshed data |
170
- | `error` | A user-initiated op threw |
171
- | `change` | Catch-all — fires after every other event |
172
-
173
- Use `on(eventName, listener)` for targeted updates. Every typed event also
156
+ The connector is an event emitter. The state-change events are:
157
+
158
+ | Event | When it fires |
159
+ | --------------------- | -------------------------------------------------------- |
160
+ | `ready` | Initial wallet detection has completed |
161
+ | `walletsDetected` | Detection completed, payload is the enriched wallet list |
162
+ | `connecting` | `connect()` started |
163
+ | `connectionUri` | A WC/TonConnect pairing URI is now available |
164
+ | `connected` | `connect()` finished successfully |
165
+ | `disconnected` | `disconnect()` finished (session cleared) |
166
+ | `sessionChanged` | Any session field changed |
167
+ | `networkSwitching` | Network switch started or ended |
168
+ | `networkSwitched` | Network switch finished successfully |
169
+ | `capabilitiesUpdated` | `getWalletCapabilities()` refreshed data |
170
+ | `error` | A user-initiated op threw |
171
+ | `change` | Catch-all — fires after every other state-change event |
172
+
173
+ Plus a set of **telemetry-only** events used for logging and wallet-handoff
174
+ visibility (see [Logging & observability](#logging--observability)). These
175
+ deliberately do **not** cascade to `change` (so they never trigger re-renders):
176
+ `log`, `awaitingWallet`, `walletSucceeded`, `walletRejected`, `walletFailed`,
177
+ `walletTimedOut`.
178
+
179
+ Use `on(eventName, listener)` for targeted updates. Every state-change event also
174
180
  cascades to `change`, so the legacy `subscribe(listener)` pattern still works.
175
181
 
176
182
  ### AbortSignal
@@ -296,8 +302,8 @@ const connector = new UniversalWalletConnector(
296
302
  )
297
303
  ```
298
304
 
299
- Constructing a **second** instance logs a `console.error` warning it's
300
- almost always a mistake. For tests and full-logout flows, call
305
+ Constructing a **second** instance logs an error through the configured logger
306
+ — it's almost always a mistake. For tests and full-logout flows, call
301
307
  `UniversalWalletConnector.resetInstance()` to clear the cached reference.
302
308
 
303
309
  ---
@@ -307,15 +313,15 @@ almost always a mistake. For tests and full-logout flows, call
307
313
  All reads are synchronous and cheap. Call them any time.
308
314
 
309
315
  ```ts
310
- connector.getSession() // current Session
311
- connector.getState() // { session } — parity with older API
312
- connector.isReady() // true once detection completed
313
- connector.getWallets() // wallets with `installed` flags set
314
- connector.getNetworks() // the configured networks array
316
+ connector.getSession() // current Session
317
+ connector.getState() // { session } — parity with older API
318
+ connector.isReady() // true once detection completed
319
+ connector.getWallets() // wallets with `installed` flags set
320
+ connector.getNetworks() // the configured networks array
315
321
  connector.isConnectionModeAvailable('walletConnect', 'metamask')
316
- connector.getConnectionURI() // WC/TonConnect pairing URI, if any
322
+ connector.getConnectionURI() // WC/TonConnect pairing URI, if any
317
323
  connector.getNetworkSwitchLoadingState()
318
- // { isLoading, isWaitingForUserApproval }
324
+ // { isLoading, isWaitingForUserApproval }
319
325
  connector.getActiveWalletCapabilities() // Record<NetworkId, EVMCapabilities> | null
320
326
  ```
321
327
 
@@ -363,18 +369,21 @@ connector.off('connectionUri', handler)
363
369
  import type { UWCEventMap } from '@meshconnect/uwc-core'
364
370
 
365
371
  // Narrowed per event:
366
- connector.on('connecting', ({ connectionMode, walletId }) => {})
367
- connector.on('connected', ({ session }) => {})
368
- connector.on('disconnected', () => {})
372
+ connector.on('connecting', ({ connectionMode, walletId }) => {})
373
+ connector.on('connected', ({ session }) => {})
374
+ connector.on('disconnected', () => {})
369
375
  connector.on('connectionUri', ({ uri, connectionMode }) => {})
370
- connector.on('networkSwitching', ({ isLoading, isWaitingForUserApproval }) => {})
371
- connector.on('networkSwitched', ({ network }) => {})
372
- connector.on('sessionChanged', ({ session }) => {})
376
+ connector.on(
377
+ 'networkSwitching',
378
+ ({ isLoading, isWaitingForUserApproval }) => {}
379
+ )
380
+ connector.on('networkSwitched', ({ network }) => {})
381
+ connector.on('sessionChanged', ({ session }) => {})
373
382
  connector.on('capabilitiesUpdated', ({ capabilities }) => {})
374
- connector.on('walletsDetected', ({ wallets }) => {})
383
+ connector.on('walletsDetected', ({ wallets }) => {})
375
384
  connector.on('ready', () => {})
376
385
  connector.on('error', ({ error, operation }) => {})
377
- connector.on('change', () => {}) // catch-all; no payload
386
+ connector.on('change', () => {}) // catch-all; no payload
378
387
  ```
379
388
 
380
389
  ### Legacy `subscribe()`
@@ -473,8 +482,8 @@ await connector.switchNetwork('eip155:8453') // Base
473
482
  // Track loading for UI
474
483
  connector.on('networkSwitching', ({ isLoading, isWaitingForUserApproval }) => {
475
484
  if (isWaitingForUserApproval) showBanner('Approve in your wallet…')
476
- else if (isLoading) showBanner('Switching network…')
477
- else hideBanner()
485
+ else if (isLoading) showBanner('Switching network…')
486
+ else hideBanner()
478
487
  })
479
488
  ```
480
489
 
@@ -647,8 +656,8 @@ interface WalletError {
647
656
  ```
648
657
 
649
658
  - `rejected` — user dismissed the prompt.
650
- - `expired` — WalletConnect pairing proposal timed out.
651
- - `unknown` — everything else (network errors, bad wallet state, …).
659
+ - `expired` — WalletConnect pairing proposal timed out.
660
+ - `unknown` — everything else (network errors, bad wallet state, …).
652
661
 
653
662
  ### Per-call handling
654
663
 
@@ -670,20 +679,20 @@ try {
670
679
  ```ts
671
680
  import type { UWCOperation } from '@meshconnect/uwc-core'
672
681
 
673
- connector.on('error', ({ error, operation }: {
674
- error: WalletError
675
- operation: UWCOperation
676
- }) => {
677
- analytics.track('wallet.error', {
678
- operation,
679
- type: error.type,
680
- message: error.message
681
- })
682
- })
682
+ connector.on(
683
+ 'error',
684
+ ({ error, operation }: { error: WalletError; operation: UWCOperation }) => {
685
+ analytics.track('wallet.error', {
686
+ operation,
687
+ type: error.type,
688
+ message: error.message
689
+ })
690
+ }
691
+ )
683
692
  ```
684
693
 
685
694
  `operation` is one of:
686
- `'connect' | 'disconnect' | 'switchNetwork' | 'signMessage' | 'sendTransaction' | 'getWalletCapabilities' | 'initialize'`.
695
+ `'connect' | 'disconnect' | 'switchNetwork' | 'signMessage' | 'signTypedData' | 'sendTransaction' | 'signSolanaTransaction' | 'getWalletCapabilities' | 'initialize'`.
687
696
 
688
697
  ### AbortError
689
698
 
@@ -705,6 +714,122 @@ try {
705
714
 
706
715
  ---
707
716
 
717
+ ## Logging & observability
718
+
719
+ UWC ships **no** third-party telemetry SDK. Instead it exposes two opt-in seams
720
+ you wire to whatever stack you run (Datadog, Amplitude, Segment, a plain
721
+ console). With neither configured, there is **zero external egress** — the
722
+ security-first default for a sign-only wallet layer.
723
+
724
+ ### Logging
725
+
726
+ A pluggable, level-gated logger backs UWC's internal logging. Configure it via
727
+ `UWCConfig`:
728
+
729
+ ```ts
730
+ const connector = new UniversalWalletConnector({
731
+ networks,
732
+ wallets,
733
+ logLevel: 'info', // console threshold; default 'warn'
734
+ logger: myAppLogger // optional: redirect output into your own logger
735
+ })
736
+ ```
737
+
738
+ `logger` (any object with `debug`/`info`/`warn`/`error`) receives the raw
739
+ `(message, ...args)`; omit it and a console sink is used with an ISO/level prefix.
740
+
741
+ Tune the threshold at runtime — useful when triaging in a wallet's in-app
742
+ browser where you can't redeploy:
743
+
744
+ ```ts
745
+ connector.setLogLevel('debug') // programmatic
746
+ window.UWC_DEBUG = true // or from the console: surfaces everything
747
+ window.UWC_DEBUG = 'info' // or a specific level (only ever lowers it)
748
+ ```
749
+
750
+ ### Observer (telemetry)
751
+
752
+ Implement `UWCObserver` to receive a normalized, PII-safe event stream plus all
753
+ log lines. The vendor code lives in **your** app, never in UWC:
754
+
755
+ ```ts
756
+ import type { UWCObserver } from '@meshconnect/uwc-core'
757
+
758
+ const observer: UWCObserver = {
759
+ onEvent: e => datadogRum.addAction(`uwc.${e.name}`, e), // or amplitude.track / …
760
+ onLog: (level, message, args) =>
761
+ myAppLogger[level](`[uwc] ${message}`, ...args)
762
+ }
763
+
764
+ const connector = new UniversalWalletConnector({ networks, wallets, observer })
765
+ ```
766
+
767
+ > **Keep `onEvent` / `onLog` non-blocking.** They are called synchronously on the
768
+ > wallet-operation path — a terminal event fires between the wallet returning a
769
+ > result and that result reaching your `await`, so a slow handler adds latency to
770
+ > connect / sign / sendTransaction. Queue-backed sinks (Datadog `addAction`,
771
+ > Segment `track`) are already non-blocking; for anything heavy, defer it yourself
772
+ > (`onEvent: e => queueMicrotask(() => …)`).
773
+
774
+ Every `onEvent` record is a `UWCTelemetryEvent` — only routing facets
775
+ (`operation`, `connectionMode`, `namespace`, `chainId`, `walletId`, `durationMs`,
776
+ outcome…) plus a `sdkSessionId` correlation id. By default `sdkSessionId` is a
777
+ generated per-instance id; pass `correlationId` (a string or getter) to align it
778
+ to your own session key (e.g. `link_sess_id`) so UWC events join your existing
779
+ Datadog/RUM telemetry without a separate mapping:
780
+
781
+ ````ts
782
+ new UniversalWalletConnector({
783
+ ...config,
784
+ observer,
785
+ correlationId: () => myLinkSession.id // resolved per-event; getter may be lazy/dynamic
786
+ })
787
+ ``` **Structured fields
788
+ never carry a signing payload or wallet address** — the shape has no slot for
789
+ them. The two free-form surfaces (`error.message` and `onLog` args) additionally
790
+ pass through a key + content scrubber that redacts sensitive keys, EVM hex blobs
791
+ (addresses / signatures / keys), secret URL params, and bearer tokens. Note this
792
+ last pass is best-effort: a base58 (Solana) or TON address embedded in a
793
+ free-form error string is a documented gap (it can't be matched without
794
+ false-positives), so don't rely on it for non-EVM address redaction.
795
+
796
+ ### Wallet-handoff funnel
797
+
798
+ The moment UWC hands control to a wallet (extension popup, WC deeplink, TON
799
+ sheet) it emits `awaitingWallet`; when the wallet answers it emits exactly one
800
+ terminal — all sharing a `handoffId` and carrying `durationMs`:
801
+
802
+ ````
803
+
804
+ awaitingWallet ─┬─ walletSucceeded success
805
+ ├─ walletRejected user said no (4001 / rejected)
806
+ └─ walletFailed any other error
807
+
808
+ ````
809
+
810
+ A non-cancelling `walletTimedOut` marker also fires if a non-TON handoff is still
811
+ pending past `walletResponseTimeoutMs` (default 60s, `0` disables). It is purely
812
+ observational — it never rejects, aborts, or mutates state, and is cleared the
813
+ instant the op settles, so a fast op never false-fires it. This turns the
814
+ previously-blind connect/sign/approve window into a per-(chain × wallet × flow)
815
+ timed funnel.
816
+
817
+ > A failed or rejected op emits **both** a handoff terminal
818
+ > (`walletFailed` / `walletRejected`) **and** the back-compat `error` event. For
819
+ > failure metrics, count the handoff terminals and treat `error` as the legacy
820
+ > channel — counting both double-counts each failure.
821
+
822
+ ### Teardown
823
+
824
+ Call `dispose()` on full logout to release observer subscriptions and clear all
825
+ listeners:
826
+
827
+ ```ts
828
+ connector.dispose()
829
+ ````
830
+
831
+ ---
832
+
708
833
  ## Testing
709
834
 
710
835
  The shared singleton can leak state between tests. Reset it in `beforeEach`:
@@ -726,7 +851,9 @@ When unit-testing consumer code, mock the whole module:
726
851
  vi.mock('@meshconnect/uwc-core', () => ({
727
852
  UniversalWalletConnector: class {
728
853
  static getInstance = () => new this()
729
- getSession = () => ({ /* fixture */ })
854
+ getSession = () => ({
855
+ /* fixture */
856
+ })
730
857
  on = () => () => {}
731
858
  once = () => () => {}
732
859
  off = () => {}
@@ -757,12 +884,12 @@ connector.on('sessionChanged', ({ session }) => {
757
884
  session.activeAddress ?? 'not connected'
758
885
  })
759
886
 
760
- document.querySelector('#connect')!.addEventListener('click', () =>
761
- connector.connect('injected', 'metamask')
762
- )
763
- document.querySelector('#disconnect')!.addEventListener('click', () =>
764
- connector.disconnect()
765
- )
887
+ document
888
+ .querySelector('#connect')!
889
+ .addEventListener('click', () => connector.connect('injected', 'metamask'))
890
+ document
891
+ .querySelector('#disconnect')!
892
+ .addEventListener('click', () => connector.disconnect())
766
893
  ```
767
894
 
768
895
  ### Show QR and auto-close modal on pair
@@ -805,7 +932,9 @@ const controller = new AbortController()
805
932
  window.addEventListener('beforeunload', () => controller.abort())
806
933
 
807
934
  try {
808
- const sig = await connector.signMessage('Sign in', { signal: controller.signal })
935
+ const sig = await connector.signMessage('Sign in', {
936
+ signal: controller.signal
937
+ })
809
938
  submitLogin(sig)
810
939
  } catch (error) {
811
940
  if ((error as { name?: string }).name === 'AbortError') return
@@ -828,6 +957,12 @@ export interface UWCConfig {
828
957
  walletConnectConfig?: WalletConnectConfig
829
958
  tonConnectConfig?: TonConnectConfig
830
959
  tronConnectorConfig?: TronConnectorConfig
960
+ // Observability (all optional; defaults preserve current behaviour)
961
+ observer?: UWCObserver // vendor-agnostic telemetry sink; default = no egress
962
+ logger?: Logger // redirect internal logging; default = console
963
+ logLevel?: LogLevel // console threshold; default 'warn'
964
+ walletResponseTimeoutMs?: number // walletTimedOut deadline; default 60_000, 0 disables
965
+ correlationId?: string | (() => string) // align sdkSessionId to your session key
831
966
  }
832
967
 
833
968
  export interface OperationOptions {
@@ -836,9 +971,15 @@ export interface OperationOptions {
836
971
 
837
972
  export type UWCEventName = keyof UWCEventMap
838
973
  export type UWCOperation =
839
- | 'connect' | 'disconnect' | 'switchNetwork'
840
- | 'signMessage' | 'sendTransaction'
841
- | 'getWalletCapabilities' | 'initialize'
974
+ | 'connect'
975
+ | 'disconnect'
976
+ | 'switchNetwork'
977
+ | 'signMessage'
978
+ | 'signTypedData'
979
+ | 'sendTransaction'
980
+ | 'signSolanaTransaction'
981
+ | 'getWalletCapabilities'
982
+ | 'initialize'
842
983
  ```
843
984
 
844
985
  ### Static methods
@@ -879,6 +1020,10 @@ getNetworkSwitchLoadingState(): {
879
1020
  getActiveWalletCapabilities(): Record<string, EVMCapabilities> | null
880
1021
  isConnectionModeAvailable(mode: ConnectionMode, walletId: string): boolean
881
1022
 
1023
+ // Observability
1024
+ setLogLevel(level: LogLevel): void // tune the console threshold at runtime
1025
+ dispose(): void // release observer subs + clear listeners
1026
+
882
1027
  // Events
883
1028
  on<K extends UWCEventName>(event: K, listener: UWCEventListener<K>): () => void
884
1029
  once<K extends UWCEventName>(event: K, listener: UWCEventListener<K>): () => void
package/dist/events.d.ts CHANGED
@@ -1,8 +1,8 @@
1
- import type { Session, Network, WalletMetadata, ConnectionMode, WalletError, EVMCapabilities } from '@meshconnect/uwc-types';
1
+ import type { Session, Network, WalletMetadata, ConnectionMode, WalletError, EVMCapabilities, LogLevel, WalletFlowType, Platform, Namespace, NetworkId } from '@meshconnect/uwc-types';
2
2
  /**
3
3
  * User-initiated operations that can surface through the `error` event.
4
4
  */
5
- export type UWCOperation = 'connect' | 'disconnect' | 'switchNetwork' | 'signMessage' | 'sendTransaction' | 'getWalletCapabilities' | 'initialize';
5
+ export type UWCOperation = 'connect' | 'disconnect' | 'switchNetwork' | 'signMessage' | 'signTypedData' | 'sendTransaction' | 'signSolanaTransaction' | 'getWalletCapabilities' | 'initialize';
6
6
  /**
7
7
  * Typed event map emitted by UniversalWalletConnector.
8
8
  *
@@ -60,12 +60,79 @@ export interface UWCEventMap {
60
60
  error: WalletError;
61
61
  operation: UWCOperation;
62
62
  };
63
+ /**
64
+ * A line emitted through the internal `Logger`. Fires for ALL levels regardless
65
+ * of the console threshold so an observer can capture `debug`/`info` even when
66
+ * the console is quiet. `args` is scrubbed before reaching `observer.onLog`.
67
+ */
68
+ log: {
69
+ level: LogLevel;
70
+ message: string;
71
+ args: unknown[];
72
+ };
73
+ /**
74
+ * Emitted immediately BEFORE control is handed to the wallet for a boundary op
75
+ * (the moment the blind window opens). Pair with exactly one terminal event via
76
+ * `handoffId`. Carries no signing payload and no address — only routing facets.
77
+ */
78
+ awaitingWallet: {
79
+ operation: UWCOperation;
80
+ connectionMode: ConnectionMode;
81
+ walletId?: string | undefined;
82
+ /** CAIP namespace (eip155 | solana | tron | tvm …) — not an address. */
83
+ namespace?: Namespace | undefined;
84
+ /** CAIP chain id (e.g. `eip155:1`) — not an address. */
85
+ chainId?: NetworkId | undefined;
86
+ handoffId: string;
87
+ /** Derived, best-effort flow taxonomy (heuristic — see deriveWalletFlowType). */
88
+ walletFlowType: WalletFlowType;
89
+ /** Coarse runtime surface (heuristic — see derivePlatform). */
90
+ platform: Platform;
91
+ timestamp: number;
92
+ };
93
+ /** The wallet resolved the op successfully. Closes a handoff. */
94
+ walletSucceeded: {
95
+ handoffId: string;
96
+ operation: UWCOperation;
97
+ durationMs: number;
98
+ };
99
+ /** The user explicitly rejected (4001 / WalletError.type === 'rejected'). Closes a handoff. */
100
+ walletRejected: {
101
+ handoffId: string;
102
+ operation: UWCOperation;
103
+ durationMs: number;
104
+ };
105
+ /** The op failed for any non-rejection reason. Closes a handoff. */
106
+ walletFailed: {
107
+ handoffId: string;
108
+ operation: UWCOperation;
109
+ durationMs: number;
110
+ error: WalletError;
111
+ };
112
+ /**
113
+ * Side marker: the deadline passed with the op still pending. OBSERVATIONAL —
114
+ * it does NOT reject, abort, or mutate state; the wallet may still complete and
115
+ * produce the real terminal. Never fires for an op that settled before the
116
+ * deadline. Not itself a terminal.
117
+ */
118
+ walletTimedOut: {
119
+ handoffId: string;
120
+ operation: UWCOperation;
121
+ durationMs: number;
122
+ };
63
123
  /**
64
124
  * Back-compat catch-all. Fires after every other event above.
65
125
  * New code should prefer the specific events.
66
126
  */
67
127
  change: void;
68
128
  }
129
+ /**
130
+ * Events that describe telemetry rather than a state change. `EventManager.emit`
131
+ * skips the legacy `change` cascade for these so high-frequency logging / handoff
132
+ * instrumentation can't trigger React re-renders through `subscribe()`.
133
+ */
134
+ export declare const TELEMETRY_ONLY_EVENTS: ReadonlySet<UWCEventName>;
69
135
  export type UWCEventName = keyof UWCEventMap;
70
136
  export type UWCEventListener<K extends UWCEventName> = (data: UWCEventMap[K]) => void;
137
+ export declare const UWC_EVENT_NAMES: UWCEventName[];
71
138
  //# sourceMappingURL=events.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"events.d.ts","sourceRoot":"","sources":["../src/events.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,OAAO,EACP,OAAO,EACP,cAAc,EACd,cAAc,EACd,WAAW,EACX,eAAe,EAChB,MAAM,wBAAwB,CAAA;AAE/B;;GAEG;AACH,MAAM,MAAM,YAAY,GACpB,SAAS,GACT,YAAY,GACZ,eAAe,GACf,aAAa,GACb,iBAAiB,GACjB,uBAAuB,GACvB,YAAY,CAAA;AAEhB;;;;;;;;GAQG;AACH,MAAM,WAAW,WAAW;IAC1B,kFAAkF;IAClF,KAAK,EAAE,IAAI,CAAA;IACX,mEAAmE;IACnE,eAAe,EAAE;QAAE,OAAO,EAAE,cAAc,EAAE,CAAA;KAAE,CAAA;IAE9C,+BAA+B;IAC/B,UAAU,EAAE;QAAE,cAAc,EAAE,cAAc,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAA;IAChE,0CAA0C;IAC1C,SAAS,EAAE;QAAE,OAAO,EAAE,OAAO,CAAA;KAAE,CAAA;IAC/B,4DAA4D;IAC5D,YAAY,EAAE,IAAI,CAAA;IAClB;;;OAGG;IACH,aAAa,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,cAAc,EAAE,cAAc,CAAA;KAAE,CAAA;IAE9D,sEAAsE;IACtE,cAAc,EAAE;QAAE,OAAO,EAAE,OAAO,CAAA;KAAE,CAAA;IAEpC,gEAAgE;IAChE,gBAAgB,EAAE;QAAE,SAAS,EAAE,OAAO,CAAC;QAAC,wBAAwB,EAAE,OAAO,CAAA;KAAE,CAAA;IAC3E,gCAAgC;IAChC,eAAe,EAAE;QAAE,OAAO,EAAE,OAAO,CAAA;KAAE,CAAA;IAErC,0CAA0C;IAC1C,mBAAmB,EAAE;QAAE,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,CAAA;KAAE,CAAA;IAEtE,yCAAyC;IACzC,KAAK,EAAE;QAAE,KAAK,EAAE,WAAW,CAAC;QAAC,SAAS,EAAE,YAAY,CAAA;KAAE,CAAA;IAEtD;;;OAGG;IACH,MAAM,EAAE,IAAI,CAAA;CACb;AAED,MAAM,MAAM,YAAY,GAAG,MAAM,WAAW,CAAA;AAC5C,MAAM,MAAM,gBAAgB,CAAC,CAAC,SAAS,YAAY,IAAI,CACrD,IAAI,EAAE,WAAW,CAAC,CAAC,CAAC,KACjB,IAAI,CAAA"}
1
+ {"version":3,"file":"events.d.ts","sourceRoot":"","sources":["../src/events.ts"],"names":[],"mappings":"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"}
package/dist/events.js CHANGED
@@ -1,2 +1,41 @@
1
- export {};
1
+ /**
2
+ * Events that describe telemetry rather than a state change. `EventManager.emit`
3
+ * skips the legacy `change` cascade for these so high-frequency logging / handoff
4
+ * instrumentation can't trigger React re-renders through `subscribe()`.
5
+ */
6
+ export const TELEMETRY_ONLY_EVENTS = new Set([
7
+ 'log',
8
+ 'awaitingWallet',
9
+ 'walletSucceeded',
10
+ 'walletRejected',
11
+ 'walletFailed',
12
+ 'walletTimedOut'
13
+ ]);
14
+ /**
15
+ * Runtime list of every event name. The `Record<UWCEventName, true>` literal
16
+ * forces this to stay exhaustive — adding an event to `UWCEventMap` without
17
+ * listing it here is a compile error — so the observer bridge can never silently
18
+ * drop a new event.
19
+ */
20
+ const EVENT_NAME_FLAGS = {
21
+ ready: true,
22
+ walletsDetected: true,
23
+ connecting: true,
24
+ connected: true,
25
+ disconnected: true,
26
+ connectionUri: true,
27
+ sessionChanged: true,
28
+ networkSwitching: true,
29
+ networkSwitched: true,
30
+ capabilitiesUpdated: true,
31
+ error: true,
32
+ log: true,
33
+ awaitingWallet: true,
34
+ walletSucceeded: true,
35
+ walletRejected: true,
36
+ walletFailed: true,
37
+ walletTimedOut: true,
38
+ change: true
39
+ };
40
+ export const UWC_EVENT_NAMES = Object.keys(EVENT_NAME_FLAGS);
2
41
  //# sourceMappingURL=events.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"events.js","sourceRoot":"","sources":["../src/events.ts"],"names":[],"mappings":""}
1
+ {"version":3,"file":"events.js","sourceRoot":"","sources":["../src/events.ts"],"names":[],"mappings":"AA2IA;;;;GAIG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAA8B,IAAI,GAAG,CAAC;IACtE,KAAK;IACL,gBAAgB;IAChB,iBAAiB;IACjB,gBAAgB;IAChB,cAAc;IACd,gBAAgB;CACjB,CAAC,CAAA;AAOF;;;;;GAKG;AACH,MAAM,gBAAgB,GAA+B;IACnD,KAAK,EAAE,IAAI;IACX,eAAe,EAAE,IAAI;IACrB,UAAU,EAAE,IAAI;IAChB,SAAS,EAAE,IAAI;IACf,YAAY,EAAE,IAAI;IAClB,aAAa,EAAE,IAAI;IACnB,cAAc,EAAE,IAAI;IACpB,gBAAgB,EAAE,IAAI;IACtB,eAAe,EAAE,IAAI;IACrB,mBAAmB,EAAE,IAAI;IACzB,KAAK,EAAE,IAAI;IACX,GAAG,EAAE,IAAI;IACT,cAAc,EAAE,IAAI;IACpB,eAAe,EAAE,IAAI;IACrB,cAAc,EAAE,IAAI;IACpB,YAAY,EAAE,IAAI;IAClB,cAAc,EAAE,IAAI;IACpB,MAAM,EAAE,IAAI;CACb,CAAA;AAED,MAAM,CAAC,MAAM,eAAe,GAAG,MAAM,CAAC,IAAI,CAAC,gBAAgB,CAAmB,CAAA"}
package/dist/index.d.ts CHANGED
@@ -1,4 +1,6 @@
1
1
  export { UniversalWalletConnector, type UWCConfig, type OperationOptions } from './universal-wallet-connector';
2
2
  export type { UWCEventMap, UWCEventName, UWCEventListener, UWCOperation } from './events';
3
+ export { createLogger, type ManagedLogger, type CreateLoggerOptions } from './logger/create-logger';
4
+ export type { UWCObserver, UWCTelemetryEvent } from './observability/telemetry';
3
5
  export { toBaseUnits } from './utils/to-base-units';
4
6
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,wBAAwB,EACxB,KAAK,SAAS,EACd,KAAK,gBAAgB,EACtB,MAAM,8BAA8B,CAAA;AACrC,YAAY,EACV,WAAW,EACX,YAAY,EACZ,gBAAgB,EAChB,YAAY,EACb,MAAM,UAAU,CAAA;AACjB,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,wBAAwB,EACxB,KAAK,SAAS,EACd,KAAK,gBAAgB,EACtB,MAAM,8BAA8B,CAAA;AACrC,YAAY,EACV,WAAW,EACX,YAAY,EACZ,gBAAgB,EAChB,YAAY,EACb,MAAM,UAAU,CAAA;AACjB,OAAO,EACL,YAAY,EACZ,KAAK,aAAa,EAClB,KAAK,mBAAmB,EACzB,MAAM,wBAAwB,CAAA;AAC/B,YAAY,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAA;AAC/E,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAA"}
package/dist/index.js CHANGED
@@ -1,3 +1,4 @@
1
1
  export { UniversalWalletConnector } from './universal-wallet-connector';
2
+ export { createLogger } from './logger/create-logger';
2
3
  export { toBaseUnits } from './utils/to-base-units';
3
4
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,wBAAwB,EAGzB,MAAM,8BAA8B,CAAA;AAOrC,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,wBAAwB,EAGzB,MAAM,8BAA8B,CAAA;AAOrC,OAAO,EACL,YAAY,EAGb,MAAM,wBAAwB,CAAA;AAE/B,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAA"}
@@ -0,0 +1,32 @@
1
+ import type { LogLevel, Logger } from '@meshconnect/uwc-types';
2
+ /** The logger returned by `createLogger`, with runtime-tunable level controls. */
3
+ export interface ManagedLogger extends Logger {
4
+ /** Lower/raise the console threshold at runtime (e.g. from a support toggle). */
5
+ setLevel(level: LogLevel): void;
6
+ /** The currently configured threshold (ignores the global override). */
7
+ getLevel(): LogLevel;
8
+ }
9
+ export interface CreateLoggerOptions {
10
+ /** Console threshold. Default `'warn'` — UWC stays quiet unless asked. */
11
+ level?: LogLevel | undefined;
12
+ /**
13
+ * Pre-gate tap: invoked for EVERY log line regardless of the console threshold.
14
+ * The observer bridge subscribes here so `debug`/`info` reach remote telemetry
15
+ * even when the console is silent. Throws are swallowed — a bad sink must not
16
+ * break a log call.
17
+ */
18
+ onLog?: ((level: LogLevel, message: string, args: unknown[]) => void) | undefined;
19
+ /**
20
+ * Output sink for gated lines. When provided (e.g. a consumer's own logger), it
21
+ * receives the RAW `(message, ...args)` so it can apply its own formatting. When
22
+ * omitted, the default `console` sink is used with an ISO/level prefix.
23
+ */
24
+ sink?: Logger | undefined;
25
+ }
26
+ /**
27
+ * Console-backed, level-gated logger. ~zero-dep. Two seams:
28
+ * - `onLog` fires pre-gate (all levels) → drives the observer.
29
+ * - the console sink fires only at/above the threshold.
30
+ */
31
+ export declare function createLogger(options?: CreateLoggerOptions): ManagedLogger;
32
+ //# sourceMappingURL=create-logger.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create-logger.d.ts","sourceRoot":"","sources":["../../src/logger/create-logger.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAA;AAS9D,kFAAkF;AAClF,MAAM,WAAW,aAAc,SAAQ,MAAM;IAC3C,iFAAiF;IACjF,QAAQ,CAAC,KAAK,EAAE,QAAQ,GAAG,IAAI,CAAA;IAC/B,wEAAwE;IACxE,QAAQ,IAAI,QAAQ,CAAA;CACrB;AAED,MAAM,WAAW,mBAAmB;IAClC,0EAA0E;IAC1E,KAAK,CAAC,EAAE,QAAQ,GAAG,SAAS,CAAA;IAC5B;;;;;OAKG;IACH,KAAK,CAAC,EACF,CAAC,CAAC,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,IAAI,CAAC,GAC7D,SAAS,CAAA;IACb;;;;OAIG;IACH,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;CAC1B;AA2BD;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,OAAO,GAAE,mBAAwB,GAAG,aAAa,CAiC7E"}