@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.
- package/README.md +211 -66
- package/dist/events.d.ts +69 -2
- package/dist/events.d.ts.map +1 -1
- package/dist/events.js +40 -1
- package/dist/events.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/logger/create-logger.d.ts +32 -0
- package/dist/logger/create-logger.d.ts.map +1 -0
- package/dist/logger/create-logger.js +69 -0
- package/dist/logger/create-logger.js.map +1 -0
- package/dist/managers/event-manager.d.ts.map +1 -1
- package/dist/managers/event-manager.js +5 -1
- package/dist/managers/event-manager.js.map +1 -1
- package/dist/observability/connect-observer.d.ts +22 -0
- package/dist/observability/connect-observer.d.ts.map +1 -0
- package/dist/observability/connect-observer.js +60 -0
- package/dist/observability/connect-observer.js.map +1 -0
- package/dist/observability/instrument-handoff.d.ts +39 -0
- package/dist/observability/instrument-handoff.d.ts.map +1 -0
- package/dist/observability/instrument-handoff.js +86 -0
- package/dist/observability/instrument-handoff.js.map +1 -0
- package/dist/observability/scrub.d.ts +30 -0
- package/dist/observability/scrub.d.ts.map +1 -0
- package/dist/observability/scrub.js +135 -0
- package/dist/observability/scrub.js.map +1 -0
- package/dist/observability/telemetry.d.ts +77 -0
- package/dist/observability/telemetry.d.ts.map +1 -0
- package/dist/observability/telemetry.js +138 -0
- package/dist/observability/telemetry.js.map +1 -0
- package/dist/services/connection-service.d.ts +2 -1
- package/dist/services/connection-service.d.ts.map +1 -1
- package/dist/services/connection-service.js +35 -15
- package/dist/services/connection-service.js.map +1 -1
- package/dist/services/network-switch-service.d.ts +2 -1
- package/dist/services/network-switch-service.d.ts.map +1 -1
- package/dist/services/network-switch-service.js +33 -15
- package/dist/services/network-switch-service.js.map +1 -1
- package/dist/universal-wallet-connector.d.ts +51 -2
- package/dist/universal-wallet-connector.d.ts.map +1 -1
- package/dist/universal-wallet-connector.js +163 -61
- package/dist/universal-wallet-connector.js.map +1 -1
- package/dist/utils/id.d.ts +2 -0
- package/dist/utils/id.d.ts.map +1 -0
- package/dist/utils/id.js +41 -0
- package/dist/utils/id.js.map +1 -0
- package/dist/utils/to-wallet-error.d.ts +16 -0
- package/dist/utils/to-wallet-error.d.ts.map +1 -0
- package/dist/utils/to-wallet-error.js +33 -0
- package/dist/utils/to-wallet-error.js.map +1 -0
- package/package.json +5 -5
- package/src/events.ts +114 -1
- package/src/index.ts +6 -0
- package/src/logger/create-logger.test.ts +111 -0
- package/src/logger/create-logger.ts +101 -0
- package/src/managers/event-manager.test.ts +25 -0
- package/src/managers/event-manager.ts +5 -1
- package/src/observability/connect-observer.test.ts +100 -0
- package/src/observability/connect-observer.ts +71 -0
- package/src/observability/instrument-handoff.test.ts +150 -0
- package/src/observability/instrument-handoff.ts +122 -0
- package/src/observability/scrub.test.ts +109 -0
- package/src/observability/scrub.ts +142 -0
- package/src/observability/telemetry.test.ts +134 -0
- package/src/observability/telemetry.ts +211 -0
- package/src/services/connection-service.test.ts +66 -1
- package/src/services/connection-service.ts +54 -32
- package/src/services/network-switch-service.test.ts +51 -1
- package/src/services/network-switch-service.ts +43 -23
- package/src/universal-wallet-connector.observability.test.ts +265 -0
- package/src/universal-wallet-connector.ts +244 -71
- package/src/utils/id.test.ts +15 -0
- package/src/utils/id.ts +48 -0
- package/src/utils/to-wallet-error.ts +36 -0
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, vi } from 'vitest'
|
|
2
|
+
import type {
|
|
3
|
+
Network,
|
|
4
|
+
WalletMetadata,
|
|
5
|
+
Logger,
|
|
6
|
+
Session
|
|
7
|
+
} from '@meshconnect/uwc-types'
|
|
8
|
+
import { UniversalWalletConnector } from './universal-wallet-connector'
|
|
9
|
+
import type { UWCObserver, UWCTelemetryEvent } from './observability/telemetry'
|
|
10
|
+
|
|
11
|
+
// Mock connectors + services so the façade resolves deterministically, but leave
|
|
12
|
+
// the EventManager + logger + observability wiring REAL so this exercises the
|
|
13
|
+
// actual end-to-end observer path through the public API.
|
|
14
|
+
//
|
|
15
|
+
// connect()/switchNetwork() instrumentation now lives INSIDE their services
|
|
16
|
+
// (after validation) — covered by the service tests. Here we use signMessage,
|
|
17
|
+
// which stays instrumented at the façade, to exercise the observer end-to-end.
|
|
18
|
+
const signMessageMock = vi.fn()
|
|
19
|
+
|
|
20
|
+
const connectedSession: Session = {
|
|
21
|
+
connectionMode: 'injected',
|
|
22
|
+
activeNetwork: {
|
|
23
|
+
internalId: 1,
|
|
24
|
+
id: 'eip155:1',
|
|
25
|
+
namespace: 'eip155',
|
|
26
|
+
name: 'Ethereum',
|
|
27
|
+
logoUrl: '',
|
|
28
|
+
nativeCurrency: { name: 'Ether', symbol: 'ETH', decimals: 18 },
|
|
29
|
+
networkType: 'evm'
|
|
30
|
+
},
|
|
31
|
+
activeWalletCapabilities: null,
|
|
32
|
+
activeNetworkWalletCapabilities: null,
|
|
33
|
+
activeAddress: '0xabc',
|
|
34
|
+
publicKey: null,
|
|
35
|
+
availableNetworks: [],
|
|
36
|
+
activeWallet: {
|
|
37
|
+
id: 'metamask',
|
|
38
|
+
name: 'MetaMask',
|
|
39
|
+
extensionInjectedProvider: {}
|
|
40
|
+
} as unknown as WalletMetadata,
|
|
41
|
+
availableAddresses: []
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
vi.mock('@meshconnect/uwc-injected-connector', () => ({
|
|
45
|
+
InjectedConnector: vi.fn().mockImplementation(function () {
|
|
46
|
+
return { getAvailableWallets: vi.fn().mockResolvedValue([]) }
|
|
47
|
+
})
|
|
48
|
+
}))
|
|
49
|
+
vi.mock('@meshconnect/uwc-wallet-connect-connector', () => ({
|
|
50
|
+
WalletConnectConnector: vi.fn().mockImplementation(function () {
|
|
51
|
+
return { getAvailableWallets: vi.fn().mockResolvedValue([]) }
|
|
52
|
+
})
|
|
53
|
+
}))
|
|
54
|
+
vi.mock('@meshconnect/uwc-ton-connector', () => ({
|
|
55
|
+
TonConnectConnector: vi.fn().mockImplementation(function () {
|
|
56
|
+
return { getAvailableWallets: vi.fn().mockResolvedValue([]) }
|
|
57
|
+
})
|
|
58
|
+
}))
|
|
59
|
+
vi.mock('./managers/session-manager', () => ({
|
|
60
|
+
SessionManager: vi.fn().mockImplementation(function () {
|
|
61
|
+
return { getSession: () => connectedSession, updateSession: vi.fn() }
|
|
62
|
+
})
|
|
63
|
+
}))
|
|
64
|
+
vi.mock('./services/connection-service', () => ({
|
|
65
|
+
ConnectionService: vi.fn().mockImplementation(function () {
|
|
66
|
+
return {
|
|
67
|
+
connect: vi.fn().mockResolvedValue(undefined),
|
|
68
|
+
disconnect: vi.fn().mockResolvedValue(undefined),
|
|
69
|
+
getWallets: vi.fn().mockReturnValue([]),
|
|
70
|
+
getNetworks: vi.fn().mockReturnValue([]),
|
|
71
|
+
getConnectionURI: vi.fn(),
|
|
72
|
+
updateWallets: vi.fn()
|
|
73
|
+
}
|
|
74
|
+
})
|
|
75
|
+
}))
|
|
76
|
+
vi.mock('./services/network-switch-service', () => ({
|
|
77
|
+
NetworkSwitchService: vi.fn().mockImplementation(function () {
|
|
78
|
+
return { switchNetwork: vi.fn(), getLoadingState: vi.fn() }
|
|
79
|
+
})
|
|
80
|
+
}))
|
|
81
|
+
vi.mock('./services/signature-service', () => ({
|
|
82
|
+
SignatureService: vi.fn().mockImplementation(function () {
|
|
83
|
+
return { signMessage: signMessageMock, signTypedData: vi.fn() }
|
|
84
|
+
})
|
|
85
|
+
}))
|
|
86
|
+
vi.mock('./services/transaction-service', () => ({
|
|
87
|
+
TransactionService: vi.fn().mockImplementation(function () {
|
|
88
|
+
return { sendTransaction: vi.fn() }
|
|
89
|
+
})
|
|
90
|
+
}))
|
|
91
|
+
vi.mock('./services/wallet-capabilities-service', () => ({
|
|
92
|
+
WalletCapabilitiesService: vi.fn().mockImplementation(function () {
|
|
93
|
+
return { getWalletCapabilities: vi.fn() }
|
|
94
|
+
})
|
|
95
|
+
}))
|
|
96
|
+
|
|
97
|
+
const networks = [
|
|
98
|
+
{
|
|
99
|
+
internalId: 1,
|
|
100
|
+
id: 'eip155:1',
|
|
101
|
+
namespace: 'eip155',
|
|
102
|
+
name: 'Ethereum',
|
|
103
|
+
logoUrl: '',
|
|
104
|
+
nativeCurrency: { name: 'Ether', symbol: 'ETH', decimals: 18 },
|
|
105
|
+
networkType: 'evm'
|
|
106
|
+
}
|
|
107
|
+
] as unknown as Network[]
|
|
108
|
+
|
|
109
|
+
const wallets = [
|
|
110
|
+
{ id: 'metamask', name: 'MetaMask' }
|
|
111
|
+
] as unknown as WalletMetadata[]
|
|
112
|
+
|
|
113
|
+
describe('UniversalWalletConnector — observability wiring', () => {
|
|
114
|
+
beforeEach(() => {
|
|
115
|
+
UniversalWalletConnector.resetInstance()
|
|
116
|
+
signMessageMock.mockReset()
|
|
117
|
+
signMessageMock.mockResolvedValue({ type: 'standard', signature: '0xsig' })
|
|
118
|
+
})
|
|
119
|
+
|
|
120
|
+
it('forwards a sign handoff to observer.onEvent (awaitingWallet → walletSucceeded)', async () => {
|
|
121
|
+
const events: UWCTelemetryEvent[] = []
|
|
122
|
+
const observer: UWCObserver = { onEvent: e => events.push(e) }
|
|
123
|
+
const uwc = new UniversalWalletConnector({ networks, wallets, observer })
|
|
124
|
+
|
|
125
|
+
await uwc.signMessage('hello')
|
|
126
|
+
|
|
127
|
+
const names = events.map(e => e.name)
|
|
128
|
+
expect(names).toContain('awaitingWallet')
|
|
129
|
+
expect(names).toContain('walletSucceeded')
|
|
130
|
+
|
|
131
|
+
const awaiting = events.find(e => e.name === 'awaitingWallet')!
|
|
132
|
+
const succeeded = events.find(e => e.name === 'walletSucceeded')!
|
|
133
|
+
// Same correlation id pairs start↔terminal, and the per-instance session id
|
|
134
|
+
// is stamped on every record.
|
|
135
|
+
expect(awaiting.handoffId).toBe(succeeded.handoffId)
|
|
136
|
+
expect(awaiting.sdkSessionId).toBe(succeeded.sdkSessionId)
|
|
137
|
+
expect(awaiting.operation).toBe('signMessage')
|
|
138
|
+
expect(signMessageMock).toHaveBeenCalledOnce()
|
|
139
|
+
})
|
|
140
|
+
|
|
141
|
+
it('stamps a consumer-supplied correlationId (string) on every record', async () => {
|
|
142
|
+
const events: UWCTelemetryEvent[] = []
|
|
143
|
+
const uwc = new UniversalWalletConnector({
|
|
144
|
+
networks,
|
|
145
|
+
wallets,
|
|
146
|
+
observer: { onEvent: e => events.push(e) },
|
|
147
|
+
correlationId: 'link-123'
|
|
148
|
+
})
|
|
149
|
+
|
|
150
|
+
await uwc.signMessage('hello')
|
|
151
|
+
|
|
152
|
+
expect(events.length).toBeGreaterThan(0)
|
|
153
|
+
expect(events.every(e => e.sdkSessionId === 'link-123')).toBe(true)
|
|
154
|
+
})
|
|
155
|
+
|
|
156
|
+
it('resolves a correlationId getter per-event (dynamic id)', async () => {
|
|
157
|
+
let id = 'a'
|
|
158
|
+
const events: UWCTelemetryEvent[] = []
|
|
159
|
+
const uwc = new UniversalWalletConnector({
|
|
160
|
+
networks,
|
|
161
|
+
wallets,
|
|
162
|
+
observer: { onEvent: e => events.push(e) },
|
|
163
|
+
correlationId: () => id
|
|
164
|
+
})
|
|
165
|
+
|
|
166
|
+
await uwc.signMessage('one')
|
|
167
|
+
id = 'b'
|
|
168
|
+
await uwc.signMessage('two')
|
|
169
|
+
|
|
170
|
+
const ids = new Set(events.map(e => e.sdkSessionId))
|
|
171
|
+
expect(ids.has('a')).toBe(true)
|
|
172
|
+
expect(ids.has('b')).toBe(true)
|
|
173
|
+
})
|
|
174
|
+
|
|
175
|
+
it('falls back to the generated id when the correlationId getter throws', async () => {
|
|
176
|
+
const events: UWCTelemetryEvent[] = []
|
|
177
|
+
const uwc = new UniversalWalletConnector({
|
|
178
|
+
networks,
|
|
179
|
+
wallets,
|
|
180
|
+
observer: { onEvent: e => events.push(e) },
|
|
181
|
+
correlationId: () => {
|
|
182
|
+
throw new Error('not ready')
|
|
183
|
+
}
|
|
184
|
+
})
|
|
185
|
+
|
|
186
|
+
await uwc.signMessage('hello')
|
|
187
|
+
|
|
188
|
+
expect(events.length).toBeGreaterThan(0)
|
|
189
|
+
// A stable, non-empty fallback id is stamped — never a throw.
|
|
190
|
+
expect(
|
|
191
|
+
events.every(
|
|
192
|
+
e => typeof e.sdkSessionId === 'string' && e.sdkSessionId.length > 0
|
|
193
|
+
)
|
|
194
|
+
).toBe(true)
|
|
195
|
+
})
|
|
196
|
+
|
|
197
|
+
it('emits walletFailed when the underlying op throws', async () => {
|
|
198
|
+
signMessageMock.mockRejectedValueOnce(new Error('relay down'))
|
|
199
|
+
const events: UWCTelemetryEvent[] = []
|
|
200
|
+
const uwc = new UniversalWalletConnector({
|
|
201
|
+
networks,
|
|
202
|
+
wallets,
|
|
203
|
+
observer: { onEvent: e => events.push(e) }
|
|
204
|
+
})
|
|
205
|
+
|
|
206
|
+
await expect(uwc.signMessage('hello')).rejects.toThrow('relay down')
|
|
207
|
+
const failed = events.find(e => e.name === 'walletFailed')
|
|
208
|
+
expect(failed?.error?.message).toBe('relay down')
|
|
209
|
+
// The back-compat `error` event still fires too.
|
|
210
|
+
expect(events.some(e => e.name === 'error')).toBe(true)
|
|
211
|
+
})
|
|
212
|
+
|
|
213
|
+
it('an aborted op emits awaitingWallet with no terminal and no error', async () => {
|
|
214
|
+
// A DOMException AbortError is an intentional cancellation, not a wallet
|
|
215
|
+
// outcome: instrumentHandoff emits no terminal, and emitError must skip it
|
|
216
|
+
// too (both paths share isAbortError).
|
|
217
|
+
signMessageMock.mockRejectedValueOnce(
|
|
218
|
+
new DOMException('Aborted', 'AbortError')
|
|
219
|
+
)
|
|
220
|
+
const events: UWCTelemetryEvent[] = []
|
|
221
|
+
const uwc = new UniversalWalletConnector({
|
|
222
|
+
networks,
|
|
223
|
+
wallets,
|
|
224
|
+
observer: { onEvent: e => events.push(e) }
|
|
225
|
+
})
|
|
226
|
+
|
|
227
|
+
await expect(uwc.signMessage('hello')).rejects.toThrow()
|
|
228
|
+
|
|
229
|
+
const names = events.map(e => e.name)
|
|
230
|
+
expect(names).toContain('awaitingWallet')
|
|
231
|
+
expect(names).not.toContain('walletSucceeded')
|
|
232
|
+
expect(names).not.toContain('walletRejected')
|
|
233
|
+
expect(names).not.toContain('walletFailed')
|
|
234
|
+
expect(names).not.toContain('error')
|
|
235
|
+
})
|
|
236
|
+
|
|
237
|
+
it('routes a custom logger as the sink and the multi-instance warning through it', () => {
|
|
238
|
+
const calls: string[] = []
|
|
239
|
+
const logger: Logger = {
|
|
240
|
+
debug: () => {},
|
|
241
|
+
info: () => {},
|
|
242
|
+
warn: () => {},
|
|
243
|
+
error: msg => calls.push(msg)
|
|
244
|
+
}
|
|
245
|
+
// First instance is fine; the SECOND triggers the multi-instance warning,
|
|
246
|
+
// which must travel through the injected logger, not raw console.
|
|
247
|
+
new UniversalWalletConnector({ networks, wallets, logger })
|
|
248
|
+
new UniversalWalletConnector({ networks, wallets, logger })
|
|
249
|
+
|
|
250
|
+
expect(calls.some(m => m.includes('Multiple instances'))).toBe(true)
|
|
251
|
+
})
|
|
252
|
+
|
|
253
|
+
it('dispose() stops further observer delivery', async () => {
|
|
254
|
+
const events: UWCTelemetryEvent[] = []
|
|
255
|
+
const uwc = new UniversalWalletConnector({
|
|
256
|
+
networks,
|
|
257
|
+
wallets,
|
|
258
|
+
observer: { onEvent: e => events.push(e) }
|
|
259
|
+
})
|
|
260
|
+
uwc.dispose()
|
|
261
|
+
events.length = 0
|
|
262
|
+
await uwc.signMessage('hello')
|
|
263
|
+
expect(events).toHaveLength(0)
|
|
264
|
+
})
|
|
265
|
+
})
|