@milaboratories/pl-client 2.10.2 → 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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@milaboratories/pl-client",
3
- "version": "2.10.2",
3
+ "version": "2.11.1",
4
4
  "engines": {
5
5
  "node": ">=20.3.0"
6
6
  },
@@ -20,26 +20,26 @@
20
20
  "./src/**/*"
21
21
  ],
22
22
  "dependencies": {
23
- "@grpc/grpc-js": "~1.13.1",
24
- "@protobuf-ts/grpc-transport": "2.9.6",
25
- "@protobuf-ts/runtime": "2.9.6",
26
- "@protobuf-ts/runtime-rpc": "2.9.6",
23
+ "@grpc/grpc-js": "~1.13.4",
24
+ "@protobuf-ts/grpc-transport": "2.11.0",
25
+ "@protobuf-ts/runtime": "2.11.0",
26
+ "@protobuf-ts/runtime-rpc": "2.11.0",
27
27
  "canonicalize": "~2.1.0",
28
28
  "denque": "^2.1.0",
29
29
  "lru-cache": "^11.1.0",
30
30
  "https-proxy-agent": "^7.0.6",
31
- "long": "^5.3.1",
32
- "undici": "~7.5.0",
31
+ "long": "^5.3.2",
32
+ "undici": "~7.10.0",
33
33
  "utility-types": "^3.11.0",
34
34
  "yaml": "^2.7.0",
35
- "@milaboratories/ts-helpers": "^1.3.3",
36
- "@milaboratories/pl-http": "^1.1.2"
35
+ "@milaboratories/pl-http": "^1.1.3",
36
+ "@milaboratories/ts-helpers": "^1.4.0"
37
37
  },
38
38
  "devDependencies": {
39
39
  "typescript": "~5.5.4",
40
40
  "vite": "^6.3.5",
41
41
  "@types/node": "~20.16.15",
42
- "@protobuf-ts/plugin": "2.9.6",
42
+ "@protobuf-ts/plugin": "2.11.0",
43
43
  "@types/http-proxy": "^1.17.16",
44
44
  "@types/jest": "^29.5.14",
45
45
  "jest": "^29.7.0",
@@ -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, grpcTransport: GrpcTransport, httpDispatcher: Dispatcher): SimpleDriver {
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', {
@@ -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.grpcTransport, this.httpDispatcher);
345
+ const driver = definition.init(this, this._ll, this.httpDispatcher);
346
346
  this.drivers.set(definition.name, driver);
347
347
  return driver;
348
348
  }
@@ -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, grpcTransport: GrpcTransport, httpDispatcher: Dispatcher): Drv;
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/final.ts CHANGED
@@ -71,6 +71,7 @@ export const DefaultFinalResourceDataPredicate: FinalResourceDataPredicate = (r)
71
71
  case 'json/resourceError':
72
72
  return r.type.version === '1';
73
73
  case 'json/object':
74
+ case 'json-gz/object':
74
75
  case 'json/string':
75
76
  case 'json/array':
76
77
  case 'json/number':
@@ -79,6 +80,7 @@ export const DefaultFinalResourceDataPredicate: FinalResourceDataPredicate = (r)
79
80
  case 'Frontend/FromFolder':
80
81
  case 'BObjectSpec':
81
82
  case 'Blob':
83
+ case 'Null':
82
84
  case 'binary':
83
85
  case 'LSProvider':
84
86
  return true;
@@ -92,6 +94,8 @@ export const DefaultFinalResourceDataPredicate: FinalResourceDataPredicate = (r)
92
94
  return readyAndHasAllOutputsFilled(r);
93
95
  } else if (r.type.name.startsWith('PColumnData/')) {
94
96
  return readyOrDuplicateOrError(r);
97
+ } else if (r.type.name.startsWith('StreamWorkdir/')) {
98
+ return readyOrDuplicateOrError(r);
95
99
  } else {
96
100
  // Unknonw resource type detected
97
101
  // Set used to log this message only once
@@ -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
+ }
@@ -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 _grpcPl!: PlatformClient;
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
- this._grpcPl = new PlatformClient(this._grpcTransport);
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
- public get grpcTransport(): GrpcTransport {
134
- return this._grpcTransport;
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 grpcPl(): PlatformClient {
138
- return this._grpcPl;
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
package/src/core/stat.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  export type TxStat = {
2
2
  txCount: number;
3
+ timeMs: number;
3
4
 
4
5
  rootsCreated: number;
5
6
  structsCreated: number;
@@ -33,7 +34,9 @@ export type TxStat = {
33
34
  kvGetBytes: number;
34
35
  };
35
36
 
36
- export function initialTxStat(): TxStat {
37
+ export type TxStatWithoutTime = Omit<TxStat, 'timeMs'>;
38
+
39
+ export function initialTxStatWithoutTime(): TxStatWithoutTime {
37
40
  return {
38
41
  txCount: 0,
39
42
  rootsCreated: 0,
@@ -63,10 +66,17 @@ export function initialTxStat(): TxStat {
63
66
  kvGetBytes: 0,
64
67
  };
65
68
  }
69
+ export function initialTxStat(): TxStat {
70
+ return {
71
+ ...initialTxStatWithoutTime(),
72
+ timeMs: 0,
73
+ };
74
+ }
66
75
 
67
76
  export function addStat(a: TxStat, b: TxStat): TxStat {
68
77
  return {
69
78
  txCount: a.txCount + b.txCount,
79
+ timeMs: a.timeMs + b.timeMs,
70
80
  rootsCreated: a.rootsCreated + b.rootsCreated,
71
81
  structsCreated: a.structsCreated + b.structsCreated,
72
82
  structsCreatedDataBytes: a.structsCreatedDataBytes + b.structsCreatedDataBytes,
@@ -29,15 +29,16 @@ import { TxAPI_Open_Request_WritableTx } from '../proto/github.com/milaboratory/
29
29
  import type { NonUndefined } from 'utility-types';
30
30
  import { toBytes } from '../util/util';
31
31
  import { fieldTypeToProto, protoToField, protoToResource } from './type_conversion';
32
- import { deepFreeze, notEmpty } from '@milaboratories/ts-helpers';
32
+ import { canonicalJsonBytes, canonicalJsonGzBytes, deepFreeze, notEmpty } from '@milaboratories/ts-helpers';
33
33
  import { isNotFoundError } from './errors';
34
34
  import type { FinalResourceDataPredicate } from './final';
35
35
  import type { LRUCache } from 'lru-cache';
36
36
  import type { ResourceDataCacheRecord } from './cache';
37
37
  import type { TxStat } from './stat';
38
- import { initialTxStat } from './stat';
38
+ import { initialTxStatWithoutTime } from './stat';
39
39
  import type { ErrorResourceData } from './error_resource';
40
40
  import { ErrorResourceType } from './error_resource';
41
+ import { JsonGzObject, JsonObject } from '../helpers/pl';
41
42
 
42
43
  /** Reference to resource, used only within transaction */
43
44
  export interface ResourceRef {
@@ -168,7 +169,14 @@ export class PlTransaction {
168
169
 
169
170
  private globalTxIdWasAwaited: boolean = false;
170
171
 
171
- public readonly stat: TxStat = initialTxStat();
172
+ private readonly _startTime = Date.now();
173
+ private readonly _stat = initialTxStatWithoutTime();
174
+ public get stat(): TxStat {
175
+ return {
176
+ ...this._stat,
177
+ timeMs: Date.now() - this._startTime,
178
+ };
179
+ }
172
180
 
173
181
  constructor(
174
182
  private readonly ll: LLPlTransaction,
@@ -200,7 +208,7 @@ export class PlTransaction {
200
208
  });
201
209
 
202
210
  // Adding stats
203
- this.stat.txCount++;
211
+ this._stat.txCount++;
204
212
  }
205
213
 
206
214
  private async drainAndAwaitPendingOps(): Promise<void> {
@@ -380,7 +388,7 @@ export class PlTransaction {
380
388
  }
381
389
 
382
390
  public createRoot(type: ResourceType): ResourceRef {
383
- this.stat.rootsCreated++;
391
+ this._stat.rootsCreated++;
384
392
  return this.createResource(
385
393
  true,
386
394
  (localId) => ({ oneofKind: 'resourceCreateRoot', resourceCreateRoot: { type, id: localId } }),
@@ -389,8 +397,8 @@ export class PlTransaction {
389
397
  }
390
398
 
391
399
  public createStruct(type: ResourceType, data?: Uint8Array | string): ResourceRef {
392
- this.stat.structsCreated++;
393
- this.stat.structsCreatedDataBytes += data?.length ?? 0;
400
+ this._stat.structsCreated++;
401
+ this._stat.structsCreatedDataBytes += data?.length ?? 0;
394
402
  return this.createResource(
395
403
  false,
396
404
  (localId) => ({
@@ -406,8 +414,8 @@ export class PlTransaction {
406
414
  }
407
415
 
408
416
  public createEphemeral(type: ResourceType, data?: Uint8Array | string): ResourceRef {
409
- this.stat.ephemeralsCreated++;
410
- this.stat.ephemeralsCreatedDataBytes += data?.length ?? 0;
417
+ this._stat.ephemeralsCreated++;
418
+ this._stat.ephemeralsCreatedDataBytes += data?.length ?? 0;
411
419
  return this.createResource(
412
420
  false,
413
421
  (localId) => ({
@@ -427,8 +435,8 @@ export class PlTransaction {
427
435
  data: Uint8Array | string,
428
436
  errorIfExists: boolean = false,
429
437
  ): ResourceRef {
430
- this.stat.valuesCreated++;
431
- this.stat.valuesCreatedDataBytes += data?.length ?? 0;
438
+ this._stat.valuesCreated++;
439
+ this._stat.valuesCreatedDataBytes += data?.length ?? 0;
432
440
  return this.createResource(
433
441
  false,
434
442
  (localId) => ({
@@ -444,6 +452,16 @@ export class PlTransaction {
444
452
  );
445
453
  }
446
454
 
455
+ public createJsonValue(data: unknown): ResourceRef {
456
+ const jsonData = canonicalJsonBytes(data);
457
+ return this.createValue(JsonObject, jsonData, false);
458
+ }
459
+
460
+ public createJsonGzValue(data: unknown, minSizeToGzip: number | undefined = 16_384): ResourceRef {
461
+ const { data: jsonData, isGzipped } = canonicalJsonGzBytes(data, minSizeToGzip);
462
+ return this.createValue(isGzipped ? JsonGzObject : JsonObject, jsonData, false);
463
+ }
464
+
447
465
  public createError(message: string): ResourceRef {
448
466
  return this.createValue(ErrorResourceType, JSON.stringify({ message } satisfies ErrorResourceData));
449
467
  }
@@ -521,13 +539,13 @@ export class PlTransaction {
521
539
  const fromCache = this.sharedResourceDataCache.get(rId);
522
540
  if (fromCache && fromCache.cacheTxOpenTimestamp < this.txOpenTimestamp) {
523
541
  if (!loadFields) {
524
- this.stat.rGetDataCacheHits++;
525
- this.stat.rGetDataCacheBytes += fromCache.basicData.data?.length ?? 0;
542
+ this._stat.rGetDataCacheHits++;
543
+ this._stat.rGetDataCacheBytes += fromCache.basicData.data?.length ?? 0;
526
544
  return fromCache.basicData;
527
545
  } else if (fromCache.data) {
528
- this.stat.rGetDataCacheHits++;
529
- this.stat.rGetDataCacheBytes += fromCache.basicData.data?.length ?? 0;
530
- this.stat.rGetDataCacheFields += fromCache.data.fields.length;
546
+ this._stat.rGetDataCacheHits++;
547
+ this._stat.rGetDataCacheBytes += fromCache.basicData.data?.length ?? 0;
548
+ this._stat.rGetDataCacheFields += fromCache.data.fields.length;
531
549
  return fromCache.data;
532
550
  }
533
551
  }
@@ -541,9 +559,9 @@ export class PlTransaction {
541
559
  (r) => protoToResource(notEmpty(r.resourceGet.resource)),
542
560
  );
543
561
 
544
- this.stat.rGetDataNetRequests++;
545
- this.stat.rGetDataNetBytes += result.data?.length ?? 0;
546
- this.stat.rGetDataNetFields += result.fields.length;
562
+ this._stat.rGetDataNetRequests++;
563
+ this._stat.rGetDataNetBytes += result.data?.length ?? 0;
564
+ this._stat.rGetDataNetFields += result.fields.length;
547
565
 
548
566
  // we will cache only final resource data states
549
567
  // caching result even if we were ignore the cache
@@ -614,7 +632,7 @@ export class PlTransaction {
614
632
  * have their values, if inputs list is not locked.
615
633
  */
616
634
  public lockInputs(rId: AnyResourceRef): void {
617
- this.stat.inputsLocked++;
635
+ this._stat.inputsLocked++;
618
636
  this.sendVoidAsync({
619
637
  oneofKind: 'resourceLockInputs',
620
638
  resourceLockInputs: { resourceId: toResourceId(rId) },
@@ -626,7 +644,7 @@ export class PlTransaction {
626
644
  * This is required for resource to pass deduplication.
627
645
  */
628
646
  public lockOutputs(rId: AnyResourceRef): void {
629
- this.stat.outputsLocked++;
647
+ this._stat.outputsLocked++;
630
648
  this.sendVoidAsync({
631
649
  oneofKind: 'resourceLockOutputs',
632
650
  resourceLockOutputs: { resourceId: toResourceId(rId) },
@@ -650,7 +668,7 @@ export class PlTransaction {
650
668
  //
651
669
 
652
670
  public createField(fId: AnyFieldRef, fieldType: FieldType, value?: AnyRef): void {
653
- this.stat.fieldsCreated++;
671
+ this._stat.fieldsCreated++;
654
672
  this.sendVoidAsync({
655
673
  oneofKind: 'fieldCreate',
656
674
  fieldCreate: { type: fieldTypeToProto(fieldType), id: toFieldId(fId) },
@@ -669,7 +687,7 @@ export class PlTransaction {
669
687
  }
670
688
 
671
689
  public setField(fId: AnyFieldRef, ref: AnyRef): void {
672
- this.stat.fieldsSet++;
690
+ this._stat.fieldsSet++;
673
691
  if (isResource(ref))
674
692
  this.sendVoidAsync({
675
693
  oneofKind: 'fieldSet',
@@ -692,7 +710,7 @@ export class PlTransaction {
692
710
  }
693
711
 
694
712
  public setFieldError(fId: AnyFieldRef, ref: AnyResourceRef): void {
695
- this.stat.fieldsSet++;
713
+ this._stat.fieldsSet++;
696
714
  this.sendVoidAsync({
697
715
  oneofKind: 'fieldSetError',
698
716
  fieldSetError: { field: toFieldId(fId), errResourceId: toResourceId(ref) },
@@ -700,7 +718,7 @@ export class PlTransaction {
700
718
  }
701
719
 
702
720
  public async getField(fId: AnyFieldRef): Promise<FieldData> {
703
- this.stat.fieldsGet++;
721
+ this._stat.fieldsGet++;
704
722
  return await this.sendSingleAndParse(
705
723
  { oneofKind: 'fieldGet', fieldGet: { field: toFieldId(fId) } },
706
724
  (r) => protoToField(notEmpty(r.fieldGet.field)),
@@ -732,9 +750,9 @@ export class PlTransaction {
732
750
  (r) => r.map((e) => e.resourceKeyValueList.record!),
733
751
  );
734
752
 
735
- this.stat.kvListRequests++;
736
- this.stat.kvListEntries += result.length;
737
- for (const kv of result) this.stat.kvListBytes += kv.key.length + kv.value.length;
753
+ this._stat.kvListRequests++;
754
+ this._stat.kvListEntries += result.length;
755
+ for (const kv of result) this._stat.kvListBytes += kv.key.length + kv.value.length;
738
756
 
739
757
  return result;
740
758
  }
@@ -757,8 +775,8 @@ export class PlTransaction {
757
775
  }
758
776
 
759
777
  public setKValue(rId: AnyResourceRef, key: string, value: Uint8Array | string): void {
760
- this.stat.kvSetRequests++;
761
- this.stat.kvSetBytes++;
778
+ this._stat.kvSetRequests++;
779
+ this._stat.kvSetBytes++;
762
780
  this.sendVoidAsync({
763
781
  oneofKind: 'resourceKeyValueSet',
764
782
  resourceKeyValueSet: {
@@ -788,8 +806,8 @@ export class PlTransaction {
788
806
  (r) => r.resourceKeyValueGet.value,
789
807
  );
790
808
 
791
- this.stat.kvGetRequests++;
792
- this.stat.kvGetBytes += result.length;
809
+ this._stat.kvGetRequests++;
810
+ this._stat.kvGetBytes += result.length;
793
811
 
794
812
  return result;
795
813
  }
@@ -815,8 +833,8 @@ export class PlTransaction {
815
833
  r.resourceKeyValueGetIfExists.exists ? r.resourceKeyValueGetIfExists.value : undefined,
816
834
  );
817
835
 
818
- this.stat.kvGetRequests++;
819
- this.stat.kvGetBytes += result?.length ?? 0;
836
+ this._stat.kvGetRequests++;
837
+ this._stat.kvGetBytes += result?.length ?? 0;
820
838
 
821
839
  return result;
822
840
  }
@@ -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/helpers/pl.ts CHANGED
@@ -23,6 +23,7 @@ export const ValueTestResource = rt('ValueTest', '1');
23
23
  export const JsonString = rt('json/string', '1');
24
24
  export const JsonBool = rt('json/bool', '1');
25
25
  export const JsonObject = rt('json/object', '1');
26
+ export const JsonGzObject = rt('json-gz/object', '1');
26
27
  export const JsonArray = rt('json/array', '1');
27
28
  export const JsonNumber = rt('json/number', '1');
28
29
  export const JsonNull = rt('json/null', '1');
package/src/index.ts CHANGED
@@ -9,6 +9,7 @@ export * from './core/default_client';
9
9
  export * from './core/unauth_client';
10
10
  export * from './core/auth';
11
11
  export * from './core/final';
12
+ export * from './core/grpc';
12
13
  export * from './helpers/tx_helpers';
13
14
  export * from './helpers/poll';
14
15