@milaboratories/pl-client 2.10.1 → 2.11.0

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.1",
3
+ "version": "2.11.0",
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/pl-http": "^1.1.2",
36
- "@milaboratories/ts-helpers": "^1.3.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",
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
@@ -61,6 +61,7 @@ function isRecoverable(status: Status): boolean {
61
61
  }
62
62
 
63
63
  export class RethrowError extends Error {
64
+ name = 'RethrowError';
64
65
  constructor(public readonly rethrowLambda: () => never) {
65
66
  super('Rethrow error, you should never see this one.');
66
67
  }
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 {
@@ -126,7 +127,9 @@ export function field(resourceId: AnyResourceRef, fieldName: string): AnyFieldRe
126
127
  }
127
128
 
128
129
  /** If transaction commit failed due to write conflicts */
129
- export class TxCommitConflict extends Error {}
130
+ export class TxCommitConflict extends Error {
131
+ name = 'TxCommitConflict';
132
+ }
130
133
 
131
134
  async function notFoundToUndefined<T>(cb: () => Promise<T>): Promise<T | undefined> {
132
135
  try {
@@ -166,7 +169,14 @@ export class PlTransaction {
166
169
 
167
170
  private globalTxIdWasAwaited: boolean = false;
168
171
 
169
- 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
+ }
170
180
 
171
181
  constructor(
172
182
  private readonly ll: LLPlTransaction,
@@ -198,7 +208,7 @@ export class PlTransaction {
198
208
  });
199
209
 
200
210
  // Adding stats
201
- this.stat.txCount++;
211
+ this._stat.txCount++;
202
212
  }
203
213
 
204
214
  private async drainAndAwaitPendingOps(): Promise<void> {
@@ -378,7 +388,7 @@ export class PlTransaction {
378
388
  }
379
389
 
380
390
  public createRoot(type: ResourceType): ResourceRef {
381
- this.stat.rootsCreated++;
391
+ this._stat.rootsCreated++;
382
392
  return this.createResource(
383
393
  true,
384
394
  (localId) => ({ oneofKind: 'resourceCreateRoot', resourceCreateRoot: { type, id: localId } }),
@@ -387,8 +397,8 @@ export class PlTransaction {
387
397
  }
388
398
 
389
399
  public createStruct(type: ResourceType, data?: Uint8Array | string): ResourceRef {
390
- this.stat.structsCreated++;
391
- this.stat.structsCreatedDataBytes += data?.length ?? 0;
400
+ this._stat.structsCreated++;
401
+ this._stat.structsCreatedDataBytes += data?.length ?? 0;
392
402
  return this.createResource(
393
403
  false,
394
404
  (localId) => ({
@@ -404,8 +414,8 @@ export class PlTransaction {
404
414
  }
405
415
 
406
416
  public createEphemeral(type: ResourceType, data?: Uint8Array | string): ResourceRef {
407
- this.stat.ephemeralsCreated++;
408
- this.stat.ephemeralsCreatedDataBytes += data?.length ?? 0;
417
+ this._stat.ephemeralsCreated++;
418
+ this._stat.ephemeralsCreatedDataBytes += data?.length ?? 0;
409
419
  return this.createResource(
410
420
  false,
411
421
  (localId) => ({
@@ -425,8 +435,8 @@ export class PlTransaction {
425
435
  data: Uint8Array | string,
426
436
  errorIfExists: boolean = false,
427
437
  ): ResourceRef {
428
- this.stat.valuesCreated++;
429
- this.stat.valuesCreatedDataBytes += data?.length ?? 0;
438
+ this._stat.valuesCreated++;
439
+ this._stat.valuesCreatedDataBytes += data?.length ?? 0;
430
440
  return this.createResource(
431
441
  false,
432
442
  (localId) => ({
@@ -442,6 +452,16 @@ export class PlTransaction {
442
452
  );
443
453
  }
444
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
+
445
465
  public createError(message: string): ResourceRef {
446
466
  return this.createValue(ErrorResourceType, JSON.stringify({ message } satisfies ErrorResourceData));
447
467
  }
@@ -519,13 +539,13 @@ export class PlTransaction {
519
539
  const fromCache = this.sharedResourceDataCache.get(rId);
520
540
  if (fromCache && fromCache.cacheTxOpenTimestamp < this.txOpenTimestamp) {
521
541
  if (!loadFields) {
522
- this.stat.rGetDataCacheHits++;
523
- this.stat.rGetDataCacheBytes += fromCache.basicData.data?.length ?? 0;
542
+ this._stat.rGetDataCacheHits++;
543
+ this._stat.rGetDataCacheBytes += fromCache.basicData.data?.length ?? 0;
524
544
  return fromCache.basicData;
525
545
  } else if (fromCache.data) {
526
- this.stat.rGetDataCacheHits++;
527
- this.stat.rGetDataCacheBytes += fromCache.basicData.data?.length ?? 0;
528
- 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;
529
549
  return fromCache.data;
530
550
  }
531
551
  }
@@ -539,9 +559,9 @@ export class PlTransaction {
539
559
  (r) => protoToResource(notEmpty(r.resourceGet.resource)),
540
560
  );
541
561
 
542
- this.stat.rGetDataNetRequests++;
543
- this.stat.rGetDataNetBytes += result.data?.length ?? 0;
544
- 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;
545
565
 
546
566
  // we will cache only final resource data states
547
567
  // caching result even if we were ignore the cache
@@ -612,7 +632,7 @@ export class PlTransaction {
612
632
  * have their values, if inputs list is not locked.
613
633
  */
614
634
  public lockInputs(rId: AnyResourceRef): void {
615
- this.stat.inputsLocked++;
635
+ this._stat.inputsLocked++;
616
636
  this.sendVoidAsync({
617
637
  oneofKind: 'resourceLockInputs',
618
638
  resourceLockInputs: { resourceId: toResourceId(rId) },
@@ -624,7 +644,7 @@ export class PlTransaction {
624
644
  * This is required for resource to pass deduplication.
625
645
  */
626
646
  public lockOutputs(rId: AnyResourceRef): void {
627
- this.stat.outputsLocked++;
647
+ this._stat.outputsLocked++;
628
648
  this.sendVoidAsync({
629
649
  oneofKind: 'resourceLockOutputs',
630
650
  resourceLockOutputs: { resourceId: toResourceId(rId) },
@@ -648,7 +668,7 @@ export class PlTransaction {
648
668
  //
649
669
 
650
670
  public createField(fId: AnyFieldRef, fieldType: FieldType, value?: AnyRef): void {
651
- this.stat.fieldsCreated++;
671
+ this._stat.fieldsCreated++;
652
672
  this.sendVoidAsync({
653
673
  oneofKind: 'fieldCreate',
654
674
  fieldCreate: { type: fieldTypeToProto(fieldType), id: toFieldId(fId) },
@@ -667,7 +687,7 @@ export class PlTransaction {
667
687
  }
668
688
 
669
689
  public setField(fId: AnyFieldRef, ref: AnyRef): void {
670
- this.stat.fieldsSet++;
690
+ this._stat.fieldsSet++;
671
691
  if (isResource(ref))
672
692
  this.sendVoidAsync({
673
693
  oneofKind: 'fieldSet',
@@ -690,7 +710,7 @@ export class PlTransaction {
690
710
  }
691
711
 
692
712
  public setFieldError(fId: AnyFieldRef, ref: AnyResourceRef): void {
693
- this.stat.fieldsSet++;
713
+ this._stat.fieldsSet++;
694
714
  this.sendVoidAsync({
695
715
  oneofKind: 'fieldSetError',
696
716
  fieldSetError: { field: toFieldId(fId), errResourceId: toResourceId(ref) },
@@ -698,7 +718,7 @@ export class PlTransaction {
698
718
  }
699
719
 
700
720
  public async getField(fId: AnyFieldRef): Promise<FieldData> {
701
- this.stat.fieldsGet++;
721
+ this._stat.fieldsGet++;
702
722
  return await this.sendSingleAndParse(
703
723
  { oneofKind: 'fieldGet', fieldGet: { field: toFieldId(fId) } },
704
724
  (r) => protoToField(notEmpty(r.fieldGet.field)),
@@ -730,9 +750,9 @@ export class PlTransaction {
730
750
  (r) => r.map((e) => e.resourceKeyValueList.record!),
731
751
  );
732
752
 
733
- this.stat.kvListRequests++;
734
- this.stat.kvListEntries += result.length;
735
- 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;
736
756
 
737
757
  return result;
738
758
  }
@@ -755,8 +775,8 @@ export class PlTransaction {
755
775
  }
756
776
 
757
777
  public setKValue(rId: AnyResourceRef, key: string, value: Uint8Array | string): void {
758
- this.stat.kvSetRequests++;
759
- this.stat.kvSetBytes++;
778
+ this._stat.kvSetRequests++;
779
+ this._stat.kvSetBytes++;
760
780
  this.sendVoidAsync({
761
781
  oneofKind: 'resourceKeyValueSet',
762
782
  resourceKeyValueSet: {
@@ -786,8 +806,8 @@ export class PlTransaction {
786
806
  (r) => r.resourceKeyValueGet.value,
787
807
  );
788
808
 
789
- this.stat.kvGetRequests++;
790
- this.stat.kvGetBytes += result.length;
809
+ this._stat.kvGetRequests++;
810
+ this._stat.kvGetBytes += result.length;
791
811
 
792
812
  return result;
793
813
  }
@@ -813,8 +833,8 @@ export class PlTransaction {
813
833
  r.resourceKeyValueGetIfExists.exists ? r.resourceKeyValueGetIfExists.value : undefined,
814
834
  );
815
835
 
816
- this.stat.kvGetRequests++;
817
- this.stat.kvGetBytes += result?.length ?? 0;
836
+ this._stat.kvGetRequests++;
837
+ this._stat.kvGetBytes += result?.length ?? 0;
818
838
 
819
839
  return result;
820
840
  }
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');
@@ -21,7 +21,9 @@ import type { PlTransaction } from '../core/transaction';
21
21
  import * as tp from 'node:timers/promises';
22
22
 
23
23
  /** This error tells state assertion mechanism that required state is not yet ready */
24
- export class ContinuePolling extends Error {}
24
+ export class ContinuePolling extends Error {
25
+ name = 'ContinuePolling';
26
+ }
25
27
 
26
28
  export type PollFieldTraverseOps = {
27
29
  expectedType?: FieldType;