@milaboratories/pl-client 2.11.0 → 2.11.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/dist/core/driver.d.ts +2 -2
- package/dist/core/driver.d.ts.map +1 -1
- package/dist/core/grpc.d.ts +16 -0
- package/dist/core/grpc.d.ts.map +1 -0
- package/dist/core/ll_client.d.ts +11 -3
- package/dist/core/ll_client.d.ts.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +118 -88
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/core/client.test.ts +2 -2
- package/src/core/client.ts +3 -3
- package/src/core/driver.ts +2 -2
- package/src/core/grpc.ts +17 -0
- package/src/core/ll_client.ts +65 -9
- package/src/core/unauth_client.ts +3 -3
- package/src/index.ts +1 -0
package/package.json
CHANGED
package/src/core/client.test.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { getTestClient, getTestClientConf } from '../test/test_config';
|
|
2
2
|
import { PlClient } from './client';
|
|
3
3
|
import { PlDriver, PlDriverDefinition } from './driver';
|
|
4
|
-
import { GrpcTransport } from '@protobuf-ts/grpc-transport';
|
|
5
4
|
import { Dispatcher, request } from 'undici';
|
|
5
|
+
import { GrpcClientProviderFactory } from './grpc';
|
|
6
6
|
|
|
7
7
|
test('test client init', async () => {
|
|
8
8
|
const client = await getTestClient(undefined);
|
|
@@ -27,7 +27,7 @@ interface SimpleDriver extends PlDriver {
|
|
|
27
27
|
|
|
28
28
|
const SimpleDriverDefinition: PlDriverDefinition<SimpleDriver> = {
|
|
29
29
|
name: 'SimpleDriver',
|
|
30
|
-
init(pl: PlClient,
|
|
30
|
+
init(pl: PlClient, grpcClientProviderFactory: GrpcClientProviderFactory, httpDispatcher: Dispatcher): SimpleDriver {
|
|
31
31
|
return {
|
|
32
32
|
async ping(): Promise<string> {
|
|
33
33
|
const response = await request('https://cdn.milaboratory.com/ping', {
|
package/src/core/client.ts
CHANGED
|
@@ -142,7 +142,7 @@ export class PlClient {
|
|
|
142
142
|
}
|
|
143
143
|
|
|
144
144
|
public async ping(): Promise<MaintenanceAPI_Ping_Response> {
|
|
145
|
-
return (await this._ll.grpcPl.ping({})).response;
|
|
145
|
+
return (await this._ll.grpcPl.get().ping({})).response;
|
|
146
146
|
}
|
|
147
147
|
|
|
148
148
|
public get conf(): PlClientConfig {
|
|
@@ -296,7 +296,7 @@ export class PlClient {
|
|
|
296
296
|
if (ok) {
|
|
297
297
|
// syncing on transaction if requested
|
|
298
298
|
if (ops?.sync === undefined ? this.forceSync : ops?.sync)
|
|
299
|
-
await this._ll.grpcPl.txSync({ txId });
|
|
299
|
+
await this._ll.grpcPl.get().txSync({ txId });
|
|
300
300
|
|
|
301
301
|
// introducing artificial delay, if requested
|
|
302
302
|
if (writable && this.txDelay > 0)
|
|
@@ -342,7 +342,7 @@ export class PlClient {
|
|
|
342
342
|
public getDriver<Drv extends PlDriver>(definition: PlDriverDefinition<Drv>): Drv {
|
|
343
343
|
const attached = this.drivers.get(definition.name);
|
|
344
344
|
if (attached !== undefined) return attached as Drv;
|
|
345
|
-
const driver = definition.init(this, this.
|
|
345
|
+
const driver = definition.init(this, this._ll, this.httpDispatcher);
|
|
346
346
|
this.drivers.set(definition.name, driver);
|
|
347
347
|
return driver;
|
|
348
348
|
}
|
package/src/core/driver.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import type { PlClient } from './client';
|
|
2
|
-
import type { GrpcTransport } from '@protobuf-ts/grpc-transport';
|
|
3
2
|
import type { RpcOptions } from '@protobuf-ts/runtime-rpc';
|
|
4
3
|
import type { Dispatcher } from 'undici';
|
|
5
4
|
import type { ResourceType } from './types';
|
|
5
|
+
import type { GrpcClientProviderFactory } from './grpc';
|
|
6
6
|
|
|
7
7
|
/** Drivers must implement this interface */
|
|
8
8
|
export interface PlDriver {
|
|
@@ -15,7 +15,7 @@ export interface PlDriverDefinition<Drv extends PlDriver> {
|
|
|
15
15
|
readonly name: string;
|
|
16
16
|
|
|
17
17
|
/** Initialization routine, will be executed only once for each driver in a specific client */
|
|
18
|
-
init(pl: PlClient,
|
|
18
|
+
init(pl: PlClient, grpcClientProviderFactory: GrpcClientProviderFactory, httpDispatcher: Dispatcher): Drv;
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
// addRTypeToMetadata adds a metadata with resource type
|
package/src/core/grpc.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { GrpcTransport } from '@protobuf-ts/grpc-transport';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* A provider for a grpc client.
|
|
5
|
+
* The client is created on demand, and is reset when the transport is reset.
|
|
6
|
+
* This is useful for cases where the client is used in a loop, and the transport is reset after each iteration.
|
|
7
|
+
*/
|
|
8
|
+
export interface GrpcClientProvider<Client> {
|
|
9
|
+
get(): Client;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* A factory for grpc client providers.
|
|
14
|
+
*/
|
|
15
|
+
export interface GrpcClientProviderFactory {
|
|
16
|
+
createGrpcClientProvider<Client>(clientConstructor: (transport: GrpcTransport) => Client): GrpcClientProvider<Client>;
|
|
17
|
+
}
|
package/src/core/ll_client.ts
CHANGED
|
@@ -21,14 +21,31 @@ import { parsePlJwt } from '../util/pl';
|
|
|
21
21
|
import type { Dispatcher } from 'undici';
|
|
22
22
|
import { inferAuthRefreshTime } from './auth';
|
|
23
23
|
import { defaultHttpDispatcher } from '@milaboratories/pl-http';
|
|
24
|
+
import type { GrpcClientProvider, GrpcClientProviderFactory } from './grpc';
|
|
24
25
|
|
|
25
26
|
export interface PlCallOps {
|
|
26
27
|
timeout?: number;
|
|
27
28
|
abortSignal?: AbortSignal;
|
|
28
29
|
}
|
|
29
30
|
|
|
31
|
+
class GrpcClientProviderImpl<Client> implements GrpcClientProvider<Client> {
|
|
32
|
+
private client: Client | undefined = undefined;
|
|
33
|
+
|
|
34
|
+
constructor(private readonly grpcTransport: () => GrpcTransport, private readonly clientConstructor: (transport: GrpcTransport) => Client) {}
|
|
35
|
+
|
|
36
|
+
public reset(): void {
|
|
37
|
+
this.client = undefined;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
public get(): Client {
|
|
41
|
+
if (this.client === undefined)
|
|
42
|
+
this.client = this.clientConstructor(this.grpcTransport());
|
|
43
|
+
return this.client;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
30
47
|
/** Abstract out low level networking and authorization details */
|
|
31
|
-
export class LLPlClient {
|
|
48
|
+
export class LLPlClient implements GrpcClientProviderFactory {
|
|
32
49
|
public readonly conf: PlClientConfig;
|
|
33
50
|
|
|
34
51
|
/** Initial authorization information */
|
|
@@ -47,7 +64,9 @@ export class LLPlClient {
|
|
|
47
64
|
|
|
48
65
|
private readonly grpcInterceptors: Interceptor[];
|
|
49
66
|
private _grpcTransport!: GrpcTransport;
|
|
50
|
-
private
|
|
67
|
+
private readonly providers: WeakRef<GrpcClientProviderImpl<any>>[] = [];
|
|
68
|
+
|
|
69
|
+
public readonly grpcPl: GrpcClientProvider<PlatformClient>;
|
|
51
70
|
|
|
52
71
|
public readonly httpDispatcher: Dispatcher;
|
|
53
72
|
|
|
@@ -89,6 +108,8 @@ export class LLPlClient {
|
|
|
89
108
|
this.statusListener = statusListener;
|
|
90
109
|
statusListener(this._status);
|
|
91
110
|
}
|
|
111
|
+
|
|
112
|
+
this.grpcPl = this.createGrpcClientProvider((transport) => new PlatformClient(transport));
|
|
92
113
|
}
|
|
93
114
|
|
|
94
115
|
/**
|
|
@@ -125,17 +146,52 @@ export class LLPlClient {
|
|
|
125
146
|
const oldTransport = this._grpcTransport;
|
|
126
147
|
|
|
127
148
|
this._grpcTransport = new GrpcTransport(grpcOptions);
|
|
128
|
-
|
|
149
|
+
|
|
150
|
+
// Reset all providers to let them reinitialize their clients
|
|
151
|
+
for (let i = 0; i < this.providers.length; i++) {
|
|
152
|
+
const provider = this.providers[i].deref();
|
|
153
|
+
if (provider === undefined) {
|
|
154
|
+
// at the same time we need to remove providers that are no longer valid
|
|
155
|
+
this.providers.splice(i, 1);
|
|
156
|
+
i--;
|
|
157
|
+
} else {
|
|
158
|
+
provider.reset();
|
|
159
|
+
}
|
|
160
|
+
}
|
|
129
161
|
|
|
130
162
|
if (oldTransport !== undefined) oldTransport.close();
|
|
131
163
|
}
|
|
132
164
|
|
|
133
|
-
|
|
134
|
-
|
|
165
|
+
private providerCleanupCounter = 0;
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Creates a provider for a grpc client. Returned provider will create fresh client whenever the underlying transport is reset.
|
|
169
|
+
*
|
|
170
|
+
* @param clientConstructor - a factory function that creates a grpc client
|
|
171
|
+
*/
|
|
172
|
+
public createGrpcClientProvider<Client>(clientConstructor: (transport: GrpcTransport) => Client): GrpcClientProvider<Client> {
|
|
173
|
+
// We need to cleanup providers periodically to avoid memory leaks.
|
|
174
|
+
// This is a simple heuristic to avoid memory leaks.
|
|
175
|
+
// We could use a more sophisticated algorithm, but this is good enough for now.
|
|
176
|
+
this.providerCleanupCounter++;
|
|
177
|
+
if (this.providerCleanupCounter >= 16) {
|
|
178
|
+
for (let i = 0; i < this.providers.length; i++) {
|
|
179
|
+
const provider = this.providers[i].deref();
|
|
180
|
+
if (provider === undefined) {
|
|
181
|
+
this.providers.splice(i, 1);
|
|
182
|
+
i--;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
this.providerCleanupCounter = 0;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
const provider = new GrpcClientProviderImpl<Client>(() => this._grpcTransport, clientConstructor);
|
|
189
|
+
this.providers.push(new WeakRef(provider));
|
|
190
|
+
return provider;
|
|
135
191
|
}
|
|
136
192
|
|
|
137
|
-
public get
|
|
138
|
-
return this.
|
|
193
|
+
public get grpcTransport(): GrpcTransport {
|
|
194
|
+
return this._grpcTransport;
|
|
139
195
|
}
|
|
140
196
|
|
|
141
197
|
/** Returns true if client is authenticated. Even with anonymous auth information
|
|
@@ -182,7 +238,7 @@ export class LLPlClient {
|
|
|
182
238
|
this.authRefreshInProgress = true;
|
|
183
239
|
void (async () => {
|
|
184
240
|
try {
|
|
185
|
-
const response = await this.grpcPl.getJWTToken({
|
|
241
|
+
const response = await this.grpcPl.get().getJWTToken({
|
|
186
242
|
expiration: {
|
|
187
243
|
seconds: BigInt(this.conf.authTTLSeconds),
|
|
188
244
|
nanos: 0,
|
|
@@ -244,7 +300,7 @@ export class LLPlClient {
|
|
|
244
300
|
return new LLPlTransaction((abortSignal) => {
|
|
245
301
|
let totalAbortSignal = abortSignal;
|
|
246
302
|
if (ops.abortSignal) totalAbortSignal = AbortSignal.any([totalAbortSignal, ops.abortSignal]);
|
|
247
|
-
return this.grpcPl.tx({
|
|
303
|
+
return this.grpcPl.get().tx({
|
|
248
304
|
abort: totalAbortSignal,
|
|
249
305
|
timeout:
|
|
250
306
|
ops.timeout
|
|
@@ -16,11 +16,11 @@ export class UnauthenticatedPlClient {
|
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
public async ping(): Promise<MaintenanceAPI_Ping_Response> {
|
|
19
|
-
return (await this.ll.grpcPl.ping({})).response;
|
|
19
|
+
return (await this.ll.grpcPl.get().ping({})).response;
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
public async authMethods(): Promise<AuthAPI_ListMethods_Response> {
|
|
23
|
-
return (await this.ll.grpcPl.authMethods({})).response;
|
|
23
|
+
return (await this.ll.grpcPl.get().authMethods({})).response;
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
public async requireAuth(): Promise<boolean> {
|
|
@@ -29,7 +29,7 @@ export class UnauthenticatedPlClient {
|
|
|
29
29
|
|
|
30
30
|
public async login(user: string, password: string): Promise<AuthInformation> {
|
|
31
31
|
try {
|
|
32
|
-
const response = await this.ll.grpcPl.getJWTToken(
|
|
32
|
+
const response = await this.ll.grpcPl.get().getJWTToken(
|
|
33
33
|
{ expiration: { seconds: BigInt(this.ll.conf.authTTLSeconds), nanos: 0 } },
|
|
34
34
|
{
|
|
35
35
|
meta: {
|
package/src/index.ts
CHANGED