viem 2.18.1 → 2.18.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (50) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/_cjs/chains/definitions/gravity.js +28 -0
  3. package/_cjs/chains/definitions/gravity.js.map +1 -0
  4. package/_cjs/chains/index.js +7 -5
  5. package/_cjs/chains/index.js.map +1 -1
  6. package/_cjs/clients/transports/webSocket.js +5 -2
  7. package/_cjs/clients/transports/webSocket.js.map +1 -1
  8. package/_cjs/errors/base.js +27 -27
  9. package/_cjs/errors/base.js.map +1 -1
  10. package/_cjs/errors/version.js +1 -1
  11. package/_cjs/utils/rpc/socket.js +27 -6
  12. package/_cjs/utils/rpc/socket.js.map +1 -1
  13. package/_cjs/utils/rpc/webSocket.js +22 -5
  14. package/_cjs/utils/rpc/webSocket.js.map +1 -1
  15. package/_esm/chains/definitions/gravity.js +25 -0
  16. package/_esm/chains/definitions/gravity.js.map +1 -0
  17. package/_esm/chains/index.js +1 -0
  18. package/_esm/chains/index.js.map +1 -1
  19. package/_esm/clients/transports/webSocket.js +5 -2
  20. package/_esm/clients/transports/webSocket.js.map +1 -1
  21. package/_esm/errors/base.js +27 -27
  22. package/_esm/errors/base.js.map +1 -1
  23. package/_esm/errors/version.js +1 -1
  24. package/_esm/utils/rpc/socket.js +29 -6
  25. package/_esm/utils/rpc/socket.js.map +1 -1
  26. package/_esm/utils/rpc/webSocket.js +22 -5
  27. package/_esm/utils/rpc/webSocket.js.map +1 -1
  28. package/_types/chains/definitions/gravity.d.ts +34 -0
  29. package/_types/chains/definitions/gravity.d.ts.map +1 -0
  30. package/_types/chains/index.d.ts +1 -0
  31. package/_types/chains/index.d.ts.map +1 -1
  32. package/_types/clients/transports/webSocket.d.ts +5 -0
  33. package/_types/clients/transports/webSocket.d.ts.map +1 -1
  34. package/_types/errors/base.d.ts +1 -1
  35. package/_types/errors/base.d.ts.map +1 -1
  36. package/_types/errors/version.d.ts +1 -1
  37. package/_types/utils/rpc/compat.d.ts +1 -0
  38. package/_types/utils/rpc/compat.d.ts.map +1 -1
  39. package/_types/utils/rpc/socket.d.ts +16 -2
  40. package/_types/utils/rpc/socket.d.ts.map +1 -1
  41. package/_types/utils/rpc/webSocket.d.ts +1 -1
  42. package/_types/utils/rpc/webSocket.d.ts.map +1 -1
  43. package/chains/definitions/gravity.ts +25 -0
  44. package/chains/index.ts +1 -0
  45. package/clients/transports/webSocket.ts +10 -1
  46. package/errors/base.ts +7 -6
  47. package/errors/version.ts +1 -1
  48. package/package.json +1 -1
  49. package/utils/rpc/socket.ts +59 -11
  50. package/utils/rpc/webSocket.ts +25 -6
package/errors/base.ts CHANGED
@@ -15,13 +15,11 @@ export class BaseError extends Error {
15
15
  docsPath?: string | undefined
16
16
  metaMessages?: string[] | undefined
17
17
  shortMessage: string
18
+ version: string
18
19
 
19
20
  override name = 'ViemError'
20
- version = getVersion()
21
21
 
22
22
  constructor(shortMessage: string, args: BaseErrorParameters = {}) {
23
- super()
24
-
25
23
  const details =
26
24
  args.cause instanceof BaseError
27
25
  ? args.cause.details
@@ -32,8 +30,9 @@ export class BaseError extends Error {
32
30
  args.cause instanceof BaseError
33
31
  ? args.cause.docsPath || args.docsPath
34
32
  : args.docsPath
33
+ const version = getVersion()
35
34
 
36
- this.message = [
35
+ const message = [
37
36
  shortMessage || 'An error occurred.',
38
37
  '',
39
38
  ...(args.metaMessages ? [...args.metaMessages, ''] : []),
@@ -45,14 +44,16 @@ export class BaseError extends Error {
45
44
  ]
46
45
  : []),
47
46
  ...(details ? [`Details: ${details}`] : []),
48
- `Version: ${this.version}`,
47
+ `Version: ${version}`,
49
48
  ].join('\n')
50
49
 
51
- if (args.cause) this.cause = args.cause
50
+ super(message, args.cause ? { cause: args.cause } : undefined)
51
+
52
52
  this.details = details
53
53
  this.docsPath = docsPath
54
54
  this.metaMessages = args.metaMessages
55
55
  this.shortMessage = shortMessage
56
+ this.version = version
56
57
  }
57
58
 
58
59
  walk(): Error
package/errors/version.ts CHANGED
@@ -1 +1 @@
1
- export const version = '2.18.1'
1
+ export const version = '2.18.4'
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "viem",
3
3
  "description": "TypeScript Interface for Ethereum",
4
- "version": "2.18.1",
4
+ "version": "2.18.4",
5
5
  "main": "./_cjs/index.js",
6
6
  "module": "./_esm/index.js",
7
7
  "types": "./_types/index.d.ts",
@@ -16,6 +16,7 @@ type CallbackFn = {
16
16
  type CallbackMap = Map<Id, CallbackFn>
17
17
 
18
18
  export type GetSocketParameters = {
19
+ onClose: () => void
19
20
  onError: (error?: Error | Event | undefined) => void
20
21
  onOpen: () => void
21
22
  onResponse: (data: RpcResponse) => void
@@ -23,9 +24,8 @@ export type GetSocketParameters = {
23
24
 
24
25
  export type Socket<socket extends {}> = socket & {
25
26
  close(): void
26
- request(params: {
27
- body: RpcRequest
28
- }): void
27
+ ping?: (() => void) | undefined
28
+ request(params: { body: RpcRequest }): void
29
29
  }
30
30
 
31
31
  export type SocketRpcClient<socket extends {}> = {
@@ -47,9 +47,23 @@ export type SocketRpcClient<socket extends {}> = {
47
47
 
48
48
  export type GetSocketRpcClientParameters<socket extends {} = {}> = {
49
49
  getSocket(params: GetSocketParameters): Promise<Socket<socket>>
50
+ /**
51
+ * Whether or not to send keep-alive messages.
52
+ * @default true
53
+ */
54
+ keepAlive?:
55
+ | boolean
56
+ | {
57
+ /**
58
+ * The interval (in ms) to send keep-alive messages.
59
+ * @default 30_000
60
+ */
61
+ interval?: number | undefined
62
+ }
63
+ | undefined
50
64
  key?: string
51
65
  /**
52
- * Whether or not to attempt to reconnect on socket failure.
66
+ * Whether or not to attempt to reconnect on socket failure or closure.
53
67
  * @default true
54
68
  */
55
69
  reconnect?:
@@ -80,9 +94,17 @@ export const socketClientCache = /*#__PURE__*/ new Map<
80
94
  >()
81
95
 
82
96
  export async function getSocketRpcClient<socket extends {}>(
83
- params: GetSocketRpcClientParameters<socket>,
97
+ parameters: GetSocketRpcClientParameters<socket>,
84
98
  ): Promise<SocketRpcClient<socket>> {
85
- const { getSocket, key = 'socket', reconnect = true, url } = params
99
+ const {
100
+ getSocket,
101
+ keepAlive = true,
102
+ key = 'socket',
103
+ reconnect = true,
104
+ url,
105
+ } = parameters
106
+ const { interval: keepAliveInterval = 30_000 } =
107
+ typeof keepAlive === 'object' ? keepAlive : {}
86
108
  const { attempts = 5, delay = 2_000 } =
87
109
  typeof reconnect === 'object' ? reconnect : {}
88
110
 
@@ -105,10 +127,24 @@ export async function getSocketRpcClient<socket extends {}>(
105
127
  const subscriptions = new Map<Id, CallbackFn>()
106
128
 
107
129
  let error: Error | Event | undefined
108
- let socket: Socket<any>
130
+ let socket: Socket<{}>
131
+ let keepAliveTimer: Timer | undefined
132
+
109
133
  // Set up socket implementation.
110
134
  async function setup() {
111
- return getSocket({
135
+ const result = await getSocket({
136
+ onClose() {
137
+ // Clear all requests and subscriptions.
138
+ requests.clear()
139
+ subscriptions.clear()
140
+
141
+ // Attempt to reconnect.
142
+ if (reconnect && reconnectCount < attempts)
143
+ setTimeout(async () => {
144
+ reconnectCount++
145
+ await setup().catch(console.error)
146
+ }, delay)
147
+ },
112
148
  onError(error_) {
113
149
  error = error_
114
150
 
@@ -125,7 +161,7 @@ export async function getSocketRpcClient<socket extends {}>(
125
161
  if (reconnect && reconnectCount < attempts)
126
162
  setTimeout(async () => {
127
163
  reconnectCount++
128
- socket = await setup().catch(console.error)
164
+ await setup().catch(console.error)
129
165
  }, delay)
130
166
  },
131
167
  onOpen() {
@@ -141,17 +177,29 @@ export async function getSocketRpcClient<socket extends {}>(
141
177
  if (!isSubscription) cache.delete(id)
142
178
  },
143
179
  })
180
+
181
+ socket = result
182
+
183
+ if (keepAlive) {
184
+ if (keepAliveTimer) clearInterval(keepAliveTimer)
185
+ keepAliveTimer = setInterval(() => socket.ping?.(), keepAliveInterval)
186
+ }
187
+
188
+ return result
144
189
  }
145
- socket = await setup()
190
+ await setup()
146
191
  error = undefined
147
192
 
148
193
  // Create a new socket instance.
149
194
  socketClient = {
150
195
  close() {
196
+ keepAliveTimer && clearInterval(keepAliveTimer)
151
197
  socket.close()
152
198
  socketClientCache.delete(`${key}:${url}`)
153
199
  },
154
- socket,
200
+ get socket() {
201
+ return socket
202
+ },
155
203
  request({ body, onError, onResponse }) {
156
204
  if (error && onError) onError(error)
157
205
 
@@ -10,22 +10,23 @@ import {
10
10
 
11
11
  export type GetWebSocketRpcClientOptions = Pick<
12
12
  GetSocketRpcClientParameters,
13
- 'reconnect'
13
+ 'keepAlive' | 'reconnect'
14
14
  >
15
15
 
16
16
  export async function getWebSocketRpcClient(
17
17
  url: string,
18
18
  options: GetWebSocketRpcClientOptions | undefined = {},
19
19
  ): Promise<SocketRpcClient<WebSocket>> {
20
- const { reconnect } = options
20
+ const { keepAlive, reconnect } = options
21
21
 
22
22
  return getSocketRpcClient({
23
- async getSocket({ onError, onOpen, onResponse }) {
23
+ async getSocket({ onClose, onError, onOpen, onResponse }) {
24
24
  const WebSocket = await import('isows').then((module) => module.WebSocket)
25
25
  const socket = new WebSocket(url)
26
26
 
27
- function onClose() {
28
- socket.removeEventListener('close', onClose)
27
+ function onClose_() {
28
+ onClose()
29
+ socket.removeEventListener('close', onClose_)
29
30
  socket.removeEventListener('message', onMessage)
30
31
  socket.removeEventListener('error', onError)
31
32
  socket.removeEventListener('open', onOpen)
@@ -35,7 +36,7 @@ export async function getWebSocketRpcClient(
35
36
  }
36
37
 
37
38
  // Setup event listeners for RPC & subscription responses.
38
- socket.addEventListener('close', onClose)
39
+ socket.addEventListener('close', onClose_)
39
40
  socket.addEventListener('message', onMessage)
40
41
  socket.addEventListener('error', onError)
41
42
  socket.addEventListener('open', onOpen)
@@ -56,6 +57,23 @@ export async function getWebSocketRpcClient(
56
57
  close_.bind(socket)()
57
58
  onClose()
58
59
  },
60
+ ping() {
61
+ try {
62
+ if (
63
+ socket.readyState === socket.CLOSED ||
64
+ socket.readyState === socket.CLOSING
65
+ )
66
+ throw new WebSocketRequestError({
67
+ body: {},
68
+ url: socket.url,
69
+ details: 'Socket is closed.',
70
+ })
71
+
72
+ socket.send('ping')
73
+ } catch (error) {
74
+ onError(error as Error)
75
+ }
76
+ },
59
77
  request({ body }) {
60
78
  if (
61
79
  socket.readyState === socket.CLOSED ||
@@ -71,6 +89,7 @@ export async function getWebSocketRpcClient(
71
89
  },
72
90
  } as Socket<WebSocket>)
73
91
  },
92
+ keepAlive,
74
93
  reconnect,
75
94
  url,
76
95
  })