@blackglory/geyser-js 0.9.0 → 0.9.1
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/package.json +3 -2
- package/src/contract.ts +34 -0
- package/src/geyser-client.ts +99 -0
- package/src/index.ts +1 -0
- package/src/utils/rpc-client.browser.ts +27 -0
- package/src/utils/rpc-client.ts +28 -0
package/package.json
CHANGED
package/src/contract.ts
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { JSONObject } from 'justypes'
|
|
2
|
+
import { CustomError } from '@blackglory/errors'
|
|
3
|
+
|
|
4
|
+
export const expectedVersion = '^0.5.0'
|
|
5
|
+
|
|
6
|
+
export interface IRateLimiterConfig extends JSONObject {
|
|
7
|
+
duration: number | null
|
|
8
|
+
limit: number | null
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface IAPI {
|
|
12
|
+
getAllRateLimiterIds(): string[]
|
|
13
|
+
|
|
14
|
+
getRateLimiter(rateLimiterId: string): IRateLimiterConfig | null
|
|
15
|
+
setRateLimiter(rateLimiterId: string, config: IRateLimiterConfig): null
|
|
16
|
+
removeRateLimiter(rateLimiterId: string): null
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* 重置速率限制器的状态.
|
|
20
|
+
*
|
|
21
|
+
* @throws {RateLimiterNotFound}
|
|
22
|
+
*/
|
|
23
|
+
resetRateLimiter(rateLimiterId: string): null
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* @throws {RateLimiterNotFound}
|
|
27
|
+
*/
|
|
28
|
+
acquireToken(rateLimiterId: string): null
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* 速率限制器在未经配置的情况下, 相当于不存在.
|
|
33
|
+
*/
|
|
34
|
+
export class RateLimiterNotFound extends CustomError {}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { createRPCClient } from '@utils/rpc-client.js'
|
|
2
|
+
import { ClientProxy } from 'delight-rpc'
|
|
3
|
+
import { timeoutSignal, withAbortSignal } from 'extra-abort'
|
|
4
|
+
import { IAPI, IRateLimiterConfig } from './contract.js'
|
|
5
|
+
export { IRateLimiterConfig, RateLimiterNotFound } from './contract.js'
|
|
6
|
+
|
|
7
|
+
export interface IGeyserClientOptions {
|
|
8
|
+
server: string
|
|
9
|
+
timeout?: number
|
|
10
|
+
retryIntervalForReconnection?: number
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export class GeyserClient {
|
|
14
|
+
static async create(options: IGeyserClientOptions): Promise<GeyserClient> {
|
|
15
|
+
const { client, close } = await createRPCClient(
|
|
16
|
+
options.server
|
|
17
|
+
, options.retryIntervalForReconnection
|
|
18
|
+
)
|
|
19
|
+
return new GeyserClient(client, close, options.timeout)
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
private constructor(
|
|
23
|
+
private client: ClientProxy<IAPI>
|
|
24
|
+
, private closeClients: () => Promise<void>
|
|
25
|
+
, private timeout?: number
|
|
26
|
+
) {}
|
|
27
|
+
|
|
28
|
+
async close(): Promise<void> {
|
|
29
|
+
await this.closeClients()
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
async getAllRateLimiterIds(timeout?: number): Promise<string[]> {
|
|
33
|
+
return await this.withTimeout(
|
|
34
|
+
() => this.client.getAllRateLimiterIds()
|
|
35
|
+
, timeout ?? this.timeout
|
|
36
|
+
)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
async getRateLimiter(
|
|
40
|
+
rateLimiterId: string
|
|
41
|
+
, timeout?: number
|
|
42
|
+
): Promise<IRateLimiterConfig | null> {
|
|
43
|
+
return await this.withTimeout(
|
|
44
|
+
() => this.client.getRateLimiter(rateLimiterId)
|
|
45
|
+
, timeout ?? this.timeout
|
|
46
|
+
)
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
async setRateLimiter(
|
|
50
|
+
rateLimiterId: string
|
|
51
|
+
, config: IRateLimiterConfig
|
|
52
|
+
, timeout?: number
|
|
53
|
+
): Promise<void> {
|
|
54
|
+
await this.withTimeout(
|
|
55
|
+
() => this.client.setRateLimiter(rateLimiterId, config)
|
|
56
|
+
, timeout ?? this.timeout
|
|
57
|
+
)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
async removeRateLimiter(rateLimiterId: string, timeout?: number): Promise<void> {
|
|
61
|
+
await this.withTimeout(
|
|
62
|
+
() => this.client.removeRateLimiter(rateLimiterId)
|
|
63
|
+
, timeout ?? this.timeout
|
|
64
|
+
)
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* 重置速率限制器的状态.
|
|
69
|
+
*
|
|
70
|
+
* @throws {RateLimiterNotFound}
|
|
71
|
+
*/
|
|
72
|
+
async resetRateLimiter(rateLimiterId: string, timeout?: number): Promise<void> {
|
|
73
|
+
await this.withTimeout(
|
|
74
|
+
() => this.client.removeRateLimiter(rateLimiterId)
|
|
75
|
+
, timeout ?? this.timeout
|
|
76
|
+
)
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* @throws {RateLimiterNotFound}
|
|
81
|
+
*/
|
|
82
|
+
async acquireToken(rateLimiterId: string, timeout?: number): Promise<void> {
|
|
83
|
+
await this.withTimeout(
|
|
84
|
+
() => this.client.acquireToken(rateLimiterId)
|
|
85
|
+
, timeout ?? this.timeout
|
|
86
|
+
)
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
private async withTimeout<T>(
|
|
90
|
+
fn: () => PromiseLike<T>
|
|
91
|
+
, timeout: number | undefined = this.timeout
|
|
92
|
+
): Promise<T> {
|
|
93
|
+
if (timeout) {
|
|
94
|
+
return await withAbortSignal(timeoutSignal(timeout), fn)
|
|
95
|
+
} else {
|
|
96
|
+
return await fn()
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './geyser-client.js'
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { IAPI, expectedVersion } from '@src/contract.js'
|
|
2
|
+
import { ClientProxy } from 'delight-rpc'
|
|
3
|
+
import { createClient } from '@delight-rpc/extra-native-websocket'
|
|
4
|
+
import { ExtraNativeWebSocket, autoReconnect } from 'extra-native-websocket'
|
|
5
|
+
|
|
6
|
+
export async function createRPCClient(
|
|
7
|
+
url: string
|
|
8
|
+
, retryIntervalForReconnection?: number
|
|
9
|
+
): Promise<{
|
|
10
|
+
client: ClientProxy<IAPI>
|
|
11
|
+
close: () => Promise<void>
|
|
12
|
+
}> {
|
|
13
|
+
const ws = new ExtraNativeWebSocket(() => new WebSocket(url))
|
|
14
|
+
const cancelAutoReconnect = autoReconnect(ws, retryIntervalForReconnection)
|
|
15
|
+
await ws.connect()
|
|
16
|
+
|
|
17
|
+
const [client, closeClient] = createClient<IAPI>(ws, { expectedVersion })
|
|
18
|
+
|
|
19
|
+
return {
|
|
20
|
+
client
|
|
21
|
+
, close: async () => {
|
|
22
|
+
closeClient()
|
|
23
|
+
cancelAutoReconnect()
|
|
24
|
+
await ws.close()
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { IAPI, expectedVersion } from '@src/contract.js'
|
|
2
|
+
import { ClientProxy } from 'delight-rpc'
|
|
3
|
+
import { createClient } from '@delight-rpc/extra-websocket'
|
|
4
|
+
import { WebSocket } from 'ws'
|
|
5
|
+
import { ExtraWebSocket, autoReconnect } from 'extra-websocket'
|
|
6
|
+
|
|
7
|
+
export async function createRPCClient(
|
|
8
|
+
url: string
|
|
9
|
+
, retryIntervalForReconnection?: number
|
|
10
|
+
): Promise<{
|
|
11
|
+
client: ClientProxy<IAPI>
|
|
12
|
+
close: () => Promise<void>
|
|
13
|
+
}> {
|
|
14
|
+
const ws = new ExtraWebSocket(() => new WebSocket(url))
|
|
15
|
+
const cancelAutoReconnect = autoReconnect(ws, retryIntervalForReconnection)
|
|
16
|
+
await ws.connect()
|
|
17
|
+
|
|
18
|
+
const [client, closeClient] = createClient<IAPI>(ws, { expectedVersion })
|
|
19
|
+
|
|
20
|
+
return {
|
|
21
|
+
client
|
|
22
|
+
, close: async () => {
|
|
23
|
+
closeClient()
|
|
24
|
+
cancelAutoReconnect()
|
|
25
|
+
await ws.close()
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|