@blackglory/cache-js 0.10.2 → 0.10.4

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/lib/contract.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { JSONValue } from '@blackglory/prelude';
2
- export declare const expectedVersion = "^0.9.0";
2
+ export declare const expectedVersion = "^0.9.0 || ^0.10.0";
3
3
  export interface INamespaceStats {
4
4
  items: number;
5
5
  }
package/lib/contract.js CHANGED
@@ -1,2 +1,2 @@
1
- export const expectedVersion = '^0.9.0';
1
+ export const expectedVersion = '^0.9.0 || ^0.10.0';
2
2
  //# sourceMappingURL=contract.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"contract.js","sourceRoot":"","sources":["../src/contract.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,eAAe,GAAG,QAAQ,CAAA"}
1
+ {"version":3,"file":"contract.js","sourceRoot":"","sources":["../src/contract.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,eAAe,GAAG,mBAAmB,CAAA"}
package/package.json CHANGED
@@ -1,10 +1,11 @@
1
1
  {
2
2
  "name": "@blackglory/cache-js",
3
- "version": "0.10.2",
3
+ "version": "0.10.4",
4
4
  "description": "",
5
5
  "keywords": [],
6
6
  "files": [
7
- "lib"
7
+ "lib",
8
+ "src"
8
9
  ],
9
10
  "type": "module",
10
11
  "main": "lib/index.js",
@@ -0,0 +1,154 @@
1
+ import { createRPCClient } from '@utils/rpc-client.js'
2
+ import { ClientProxy, BatchClient, BatchClientProxy } from 'delight-rpc'
3
+ import { IAPI, INamespaceStats, IItem } from './contract.js'
4
+ import { timeoutSignal, withAbortSignal } from 'extra-abort'
5
+ import { JSONValue } from '@blackglory/prelude'
6
+ export { INamespaceStats, IItem, IItemMetadata } from './contract.js'
7
+
8
+ export interface ICacheClientOptions {
9
+ server: string
10
+ timeout?: number
11
+ retryIntervalForReconnection?: number
12
+ }
13
+
14
+ export class CacheClient {
15
+ static async create(options: ICacheClientOptions): Promise<CacheClient> {
16
+ const { client, batchClient, proxy, close } = await createRPCClient(
17
+ options.server
18
+ , options.retryIntervalForReconnection
19
+ )
20
+ return new CacheClient(client, batchClient, proxy, close, options.timeout)
21
+ }
22
+
23
+ private constructor(
24
+ private client: ClientProxy<IAPI>
25
+ , private batchClient: BatchClient
26
+ , private batchProxy: BatchClientProxy<IAPI, unknown>
27
+ , private closeClients: () => Promise<void>
28
+ , private timeout?: number
29
+ ) {}
30
+
31
+ async close(): Promise<void> {
32
+ await this.closeClients()
33
+ }
34
+
35
+ async getNamespaceStats(
36
+ namespace: string
37
+ , timeout?: number
38
+ ): Promise<INamespaceStats> {
39
+ return await this.withTimeout(
40
+ () => this.client.getNamespaceStats(namespace)
41
+ , timeout ?? this.timeout
42
+ )
43
+ }
44
+
45
+ async getAllNamespaces(timeout?: number): Promise<string[]> {
46
+ return await this.withTimeout(
47
+ () => this.client.getAllNamespaces()
48
+ , timeout ?? this.timeout
49
+ )
50
+ }
51
+
52
+ async getAllItemKeys(namespace: string, timeout?: number): Promise<string[]> {
53
+ return await this.withTimeout(
54
+ () => this.client.getAllItemKeys(namespace)
55
+ , timeout ?? this.timeout
56
+ )
57
+ }
58
+
59
+ async hasItem(
60
+ namespace: string
61
+ , itemKey: string
62
+ , timeout?: number
63
+ ): Promise<boolean> {
64
+ return await this.withTimeout(
65
+ () => this.client.hasItem(namespace, itemKey)
66
+ , timeout ?? this.timeout
67
+ )
68
+ }
69
+
70
+ async getItem(
71
+ namespace: string
72
+ , itemKey: string
73
+ , timeout?: number
74
+ ): Promise<IItem | null> {
75
+ return await this.withTimeout(
76
+ () => this.client.getItem(namespace, itemKey)
77
+ , timeout ?? this.timeout
78
+ )
79
+ }
80
+
81
+ async getItemValue(
82
+ namespace: string
83
+ , itemKey: string
84
+ , timeout?: number
85
+ ): Promise<JSONValue | null> {
86
+ return await this.withTimeout(
87
+ () => this.client.getItemValue(namespace, itemKey)
88
+ , timeout ?? this.timeout
89
+ )
90
+ }
91
+
92
+ async getItemValues(
93
+ namespace: string
94
+ , itemKeys: string[]
95
+ , timeout?: number
96
+ ): Promise<Array<JSONValue | null>> {
97
+ return await this.withTimeout(
98
+ async () => {
99
+ const results = await this.batchClient.parallel(
100
+ ...itemKeys.map(key => this.batchProxy.getItemValue(namespace, key))
101
+ )
102
+ return results.map(result => result.unwrap())
103
+ }
104
+ , timeout ?? this.timeout
105
+ )
106
+ }
107
+
108
+ async setItem(
109
+ namespace: string
110
+ , itemKey: string
111
+ , itemValue: JSONValue
112
+ , timeToLive: number | null
113
+ , timeout?: number
114
+ ): Promise<void> {
115
+ await this.withTimeout(
116
+ () => this.client.setItem(
117
+ namespace
118
+ , itemKey
119
+ , itemValue
120
+ , timeToLive
121
+ )
122
+ , timeout ?? this.timeout
123
+ )
124
+ }
125
+
126
+ async removeItem(
127
+ namespace: string
128
+ , itemKey: string
129
+ , timeout?: number
130
+ ): Promise<void> {
131
+ await this.withTimeout(
132
+ () => this.client.removeItem(namespace, itemKey)
133
+ , timeout ?? this.timeout
134
+ )
135
+ }
136
+
137
+ async clearItemsByNamespace(namespace: string, timeout?: number): Promise<void> {
138
+ await this.withTimeout(
139
+ () => this.client.clearItemsByNamespace(namespace)
140
+ , timeout ?? this.timeout
141
+ )
142
+ }
143
+
144
+ private async withTimeout<T>(
145
+ fn: () => PromiseLike<T>
146
+ , timeout: number | undefined = this.timeout
147
+ ): Promise<T> {
148
+ if (timeout) {
149
+ return await withAbortSignal(timeoutSignal(timeout), fn)
150
+ } else {
151
+ return await fn()
152
+ }
153
+ }
154
+ }
@@ -0,0 +1,40 @@
1
+ import { JSONValue } from '@blackglory/prelude'
2
+
3
+ export const expectedVersion = '^0.9.0 || ^0.10.0'
4
+
5
+ export interface INamespaceStats {
6
+ items: number
7
+ }
8
+
9
+ export interface IItem {
10
+ value: JSONValue
11
+ metadata: IItemMetadata
12
+ }
13
+
14
+ export interface IItemMetadata {
15
+ updatedAt: number
16
+ timeToLive: number | null
17
+ }
18
+
19
+ export interface IAPI {
20
+ getAllNamespaces(): string[]
21
+ getAllItemKeys(namespace: string): string[]
22
+
23
+ getNamespaceStats(namespace: string): INamespaceStats
24
+
25
+ hasItem(namespace: string, itemKey: string): boolean
26
+
27
+ getItem(namespace: string, itemKey: string): IItem | null
28
+ getItemValue(namespace: string, itemKey: string): JSONValue | null
29
+
30
+ setItem(
31
+ namespace: string
32
+ , itemKey: string
33
+ , itemValue: JSONValue
34
+ , timeToLive: number | null /* ms */
35
+ ): null
36
+
37
+ removeItem(namespace: string, itemKey: string): null
38
+
39
+ clearItemsByNamespace(namespace: string): null
40
+ }
package/src/index.ts ADDED
@@ -0,0 +1 @@
1
+ export * from './cache-client.js'
@@ -0,0 +1,34 @@
1
+ import { IAPI, expectedVersion } from '@src/contract.js'
2
+ import { ClientProxy, BatchClient, BatchClientProxy, createBatchProxy } from 'delight-rpc'
3
+ import { createClient, createBatchClient } 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
+ batchClient: BatchClient<IAPI>
12
+ proxy: BatchClientProxy<IAPI, unknown>
13
+ close: () => Promise<void>
14
+ }> {
15
+ const ws = new ExtraNativeWebSocket(() => new WebSocket(url))
16
+ const cancelAutoReconnect = autoReconnect(ws, retryIntervalForReconnection)
17
+ await ws.connect()
18
+
19
+ const [client, closeClient] = createClient<IAPI>(ws, { expectedVersion })
20
+ const [batchClient, closeBatchClient] = createBatchClient(ws, { expectedVersion })
21
+ const proxy = createBatchProxy<IAPI>()
22
+
23
+ return {
24
+ client
25
+ , batchClient
26
+ , proxy
27
+ , close: async () => {
28
+ closeClient()
29
+ closeBatchClient()
30
+ cancelAutoReconnect()
31
+ await ws.close()
32
+ }
33
+ }
34
+ }
@@ -0,0 +1,35 @@
1
+ import { IAPI, expectedVersion } from '@src/contract.js'
2
+ import { ClientProxy, BatchClient, BatchClientProxy, createBatchProxy } from 'delight-rpc'
3
+ import { createClient, createBatchClient } 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
+ batchClient: BatchClient<IAPI>
13
+ proxy: BatchClientProxy<IAPI, unknown>
14
+ close: () => Promise<void>
15
+ }> {
16
+ const ws = new ExtraWebSocket(() => new WebSocket(url))
17
+ const cancelAutoReconnect = autoReconnect(ws, retryIntervalForReconnection)
18
+ await ws.connect()
19
+
20
+ const [client, closeClient] = createClient<IAPI>(ws, { expectedVersion })
21
+ const [batchClient, closeBatchClient] = createBatchClient(ws, { expectedVersion })
22
+ const proxy = createBatchProxy<IAPI>()
23
+
24
+ return {
25
+ client
26
+ , batchClient
27
+ , proxy
28
+ , close: async () => {
29
+ closeClient()
30
+ closeBatchClient()
31
+ cancelAutoReconnect()
32
+ await ws.close()
33
+ }
34
+ }
35
+ }