@milaboratories/pl-tree 1.5.1 → 1.5.3

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/src/state.ts CHANGED
@@ -1,26 +1,29 @@
1
- import {
1
+ import type {
2
2
  BasicResourceData,
3
3
  FieldData,
4
4
  FieldStatus,
5
5
  FieldType,
6
- isNotNullResourceId,
7
- isNullResourceId,
8
6
  KeyValue,
9
- NullResourceId,
10
7
  OptionalResourceId,
11
8
  ResourceData,
12
9
  ResourceId,
13
- resourceIdToString,
14
10
  ResourceKind,
15
- ResourceType,
16
- stringifyWithResourceId
11
+ ResourceType } from '@milaboratories/pl-client';
12
+ import {
13
+ isNotNullResourceId,
14
+ isNullResourceId,
15
+ NullResourceId,
16
+ resourceIdToString,
17
+ stringifyWithResourceId,
17
18
  } from '@milaboratories/pl-client';
18
- import { ChangeSource, Watcher } from '@milaboratories/computable';
19
+ import type { Watcher } from '@milaboratories/computable';
20
+ import { ChangeSource } from '@milaboratories/computable';
19
21
  import { PlTreeEntry } from './accessors';
20
- import { ValueAndError } from './value_and_error';
21
- import { MiLogger, notEmpty } from '@milaboratories/ts-helpers';
22
- import { FieldTraversalStep, GetFieldStep } from './traversal_ops';
23
- import { FinalResourceDataPredicate } from '@milaboratories/pl-client';
22
+ import type { ValueAndError } from './value_and_error';
23
+ import type { MiLogger } from '@milaboratories/ts-helpers';
24
+ import { notEmpty } from '@milaboratories/ts-helpers';
25
+ import type { FieldTraversalStep, GetFieldStep } from './traversal_ops';
26
+ import type { FinalResourceDataPredicate } from '@milaboratories/pl-client';
24
27
 
25
28
  export type ExtendedResourceData = ResourceData & {
26
29
  kv: KeyValue[];
@@ -43,7 +46,7 @@ class PlTreeField implements FieldData {
43
46
  public status: FieldStatus,
44
47
  public valueIsFinal: boolean,
45
48
  /** Last version of resource this field was observed, used to garbage collect fields in tree patching procedure */
46
- public resourceVersion: number
49
+ public resourceVersion: number,
47
50
  ) {}
48
51
 
49
52
  get state(): FieldData {
@@ -53,7 +56,7 @@ class PlTreeField implements FieldData {
53
56
  status: this.status,
54
57
  value: this.value,
55
58
  error: this.error,
56
- valueIsFinal: this.valueIsFinal
59
+ valueIsFinal: this.valueIsFinal,
57
60
  };
58
61
  }
59
62
  }
@@ -168,7 +171,7 @@ export class PlTreeResource implements ResourceDataWithFinalState {
168
171
  public getField(
169
172
  watcher: Watcher,
170
173
  _step: string | GetFieldStep,
171
- onUnstable: (marker: string) => void = () => {}
174
+ onUnstable: (marker: string) => void = () => {},
172
175
  ): ValueAndError<ResourceId> | undefined {
173
176
  const step: FieldTraversalStep = typeof _step === 'string' ? { field: _step } : _step;
174
177
 
@@ -176,7 +179,7 @@ export class PlTreeResource implements ResourceDataWithFinalState {
176
179
  if (field === undefined) {
177
180
  if (step.errorIfFieldNotFound || step.errorIfFieldNotSet)
178
181
  throw new Error(
179
- `Field "${step.field}" not found in resource ${resourceIdToString(this.id)}`
182
+ `Field "${step.field}" not found in resource ${resourceIdToString(this.id)}`,
180
183
  );
181
184
 
182
185
  if (!this.inputsLocked) this.inputAndServiceFieldListChanged?.attachWatcher(watcher);
@@ -202,7 +205,7 @@ export class PlTreeResource implements ResourceDataWithFinalState {
202
205
  } else {
203
206
  if (step.assertFieldType !== undefined && field.type !== step.assertFieldType)
204
207
  throw new Error(
205
- `Unexpected field type: expected ${step.assertFieldType} but got ${field.type} for the field name ${step.field}`
208
+ `Unexpected field type: expected ${step.assertFieldType} but got ${field.type} for the field name ${step.field}`,
206
209
  );
207
210
 
208
211
  const ret = {} as ValueAndError<ResourceId>;
@@ -234,9 +237,9 @@ export class PlTreeResource implements ResourceDataWithFinalState {
234
237
 
235
238
  public get isReadyOrError(): boolean {
236
239
  return (
237
- this.error !== NullResourceId ||
238
- this.resourceReady ||
239
- this.originalResourceId !== NullResourceId
240
+ this.error !== NullResourceId
241
+ || this.resourceReady
242
+ || this.originalResourceId !== NullResourceId
240
243
  );
241
244
  }
242
245
 
@@ -331,7 +334,7 @@ export class PlTreeResource implements ResourceDataWithFinalState {
331
334
  outputsLocked: this.outputsLocked,
332
335
  error: this.error,
333
336
  originalResourceId: this.originalResourceId,
334
- final: this.finalFlag
337
+ final: this.finalFlag,
335
338
  };
336
339
  }
337
340
 
@@ -339,7 +342,7 @@ export class PlTreeResource implements ResourceDataWithFinalState {
339
342
  return {
340
343
  ...this.basicState,
341
344
  fields: this.fields,
342
- kv: Array.from(this.kv.entries()).map(([key, value]) => ({ key, value }))
345
+ kv: Array.from(this.kv.entries()).map(([key, value]) => ({ key, value })),
343
346
  };
344
347
  }
345
348
 
@@ -383,7 +386,7 @@ export class PlTreeState {
383
386
  constructor(
384
387
  /** This will be the only resource not deleted during GC round */
385
388
  public readonly root: ResourceId,
386
- public readonly isFinalPredicate: FinalResourceDataPredicate
389
+ public readonly isFinalPredicate: FinalResourceDataPredicate,
387
390
  ) {}
388
391
 
389
392
  public forEachResource(cb: (res: ResourceDataWithFinalState) => void): void {
@@ -420,12 +423,13 @@ export class PlTreeState {
420
423
 
421
424
  const statBeforeMutation = resource?.basicState;
422
425
  const unexpectedTransitionError = (reason: string): never => {
426
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
423
427
  const { fields, ...rdWithoutFields } = rd;
424
428
  this.invalidateTree();
425
429
  throw new TreeStateUpdateError(
426
430
  `Unexpected resource state transition (${reason}): ${stringifyWithResourceId(
427
- rdWithoutFields
428
- )} -> ${stringifyWithResourceId(statBeforeMutation)}`
431
+ rdWithoutFields,
432
+ )} -> ${stringifyWithResourceId(statBeforeMutation)}`,
429
433
  );
430
434
  };
431
435
 
@@ -442,7 +446,7 @@ export class PlTreeState {
442
446
  // duplicate / original
443
447
  if (resource.originalResourceId !== rd.originalResourceId) {
444
448
  if (resource.originalResourceId !== NullResourceId)
445
- unexpectedTransitionError("originalResourceId can't change after it is set");
449
+ unexpectedTransitionError('originalResourceId can\'t change after it is set');
446
450
  resource.originalResourceId = rd.originalResourceId;
447
451
  // duplicate status of the resource counts as ready for the external observer
448
452
  notEmpty(resource.resourceStateChange).markChanged();
@@ -452,7 +456,7 @@ export class PlTreeState {
452
456
  // error
453
457
  if (resource.error !== rd.error) {
454
458
  if (isNotNullResourceId(resource.error))
455
- unexpectedTransitionError("resource can't change attached error after it is set");
459
+ unexpectedTransitionError('resource can\'t change attached error after it is set');
456
460
  resource.error = rd.error;
457
461
  incrementRefs.push(resource.error as ResourceId);
458
462
  notEmpty(resource.resourceStateChange).markChanged();
@@ -473,7 +477,7 @@ export class PlTreeState {
473
477
  fd.error,
474
478
  fd.status,
475
479
  fd.valueIsFinal,
476
- resource.version
480
+ resource.version,
477
481
  );
478
482
  if (isNotNullResourceId(fd.value)) incrementRefs.push(fd.value);
479
483
  if (isNotNullResourceId(fd.error)) incrementRefs.push(fd.error);
@@ -481,13 +485,13 @@ export class PlTreeState {
481
485
  if (fd.type === 'Input' || fd.type === 'Service') {
482
486
  if (resource.inputsLocked)
483
487
  unexpectedTransitionError(
484
- `adding ${fd.type} (${fd.name}) field while inputs locked`
488
+ `adding ${fd.type} (${fd.name}) field while inputs locked`,
485
489
  );
486
490
  notEmpty(resource.inputAndServiceFieldListChanged).markChanged();
487
491
  } else if (fd.type === 'Output') {
488
492
  if (resource.outputsLocked)
489
493
  unexpectedTransitionError(
490
- `adding ${fd.type} (${fd.name}) field while outputs locked`
494
+ `adding ${fd.type} (${fd.name}) field while outputs locked`,
491
495
  );
492
496
  notEmpty(resource.outputFieldListChanged).markChanged();
493
497
  } else {
@@ -508,14 +512,14 @@ export class PlTreeState {
508
512
  if (field.type === 'Input' || field.type === 'Service') {
509
513
  if (resource.inputsLocked)
510
514
  unexpectedTransitionError(
511
- `adding input field "${fd.name}", while corresponding list is locked`
515
+ `adding input field "${fd.name}", while corresponding list is locked`,
512
516
  );
513
517
  notEmpty(resource.inputAndServiceFieldListChanged).markChanged();
514
518
  }
515
519
  if (field.type === 'Output') {
516
520
  if (resource.outputsLocked)
517
521
  unexpectedTransitionError(
518
- `adding output field "${fd.name}", while corresponding list is locked`
522
+ `adding output field "${fd.name}", while corresponding list is locked`,
519
523
  );
520
524
  notEmpty(resource.outputFieldListChanged).markChanged();
521
525
  }
@@ -599,7 +603,7 @@ export class PlTreeState {
599
603
  resource.verifyReadyState();
600
604
  if (!resource.isReadyOrError)
601
605
  unexpectedTransitionError(
602
- `resource can't lose it's ready or error state (ready state before ${readyStateBefore})`
606
+ `resource can't lose it's ready or error state (ready state before ${readyStateBefore})`,
603
607
  );
604
608
  notEmpty(resource.resourceStateChange).markChanged();
605
609
  changed = true;
@@ -623,7 +627,7 @@ export class PlTreeState {
623
627
  const newStateKeys = new Set(rd.kv.map((kv) => kv.key));
624
628
 
625
629
  // deleting keys not present in resource anymore
626
- resource.kv.forEach((value, key, map) => {
630
+ resource.kv.forEach((_value, key, map) => {
627
631
  if (!newStateKeys.has(key)) map.delete(key);
628
632
  });
629
633
 
@@ -651,7 +655,7 @@ export class PlTreeState {
651
655
  fd.error,
652
656
  fd.status,
653
657
  fd.valueIsFinal,
654
- InitialResourceVersion
658
+ InitialResourceVersion,
655
659
  );
656
660
  if (isNotNullResourceId(fd.value)) incrementRefs.push(fd.value);
657
661
  if (isNotNullResourceId(fd.error)) incrementRefs.push(fd.error);
package/src/sync.ts CHANGED
@@ -1,13 +1,14 @@
1
- import {
1
+ import type {
2
2
  FieldData,
3
- isNotNullResourceId,
4
- isNullResourceId,
5
3
  OptionalResourceId,
6
4
  PlTransaction,
7
- ResourceId
5
+ ResourceId,
6
+ } from '@milaboratories/pl-client';
7
+ import {
8
+ isNullResourceId,
8
9
  } from '@milaboratories/pl-client';
9
10
  import Denque from 'denque';
10
- import { ExtendedResourceData, PlTreeState } from './state';
11
+ import type { ExtendedResourceData, PlTreeState } from './state';
11
12
  import { msToHumanReadable } from '@milaboratories/ts-helpers';
12
13
 
13
14
  /** Applied to list of fields in resource data. */
@@ -36,7 +37,7 @@ export interface TreeLoadingRequest {
36
37
  * {@link loadTreeState} to load updated state. */
37
38
  export function constructTreeLoadingRequest(
38
39
  tree: PlTreeState,
39
- pruningFunction?: PruningFunction
40
+ pruningFunction?: PruningFunction,
40
41
  ): TreeLoadingRequest {
41
42
  const seedResources: ResourceId[] = [];
42
43
  const finalResources = new Set<ResourceId>();
@@ -75,7 +76,7 @@ export function initialTreeLoadingStat(): TreeLoadingStat {
75
76
  retrievedKeyValueBytes: 0,
76
77
  prunedFields: 0,
77
78
  finalResourcesSkipped: 0,
78
- millisSpent: 0
79
+ millisSpent: 0,
79
80
  };
80
81
  }
81
82
 
@@ -99,7 +100,7 @@ export function formatTreeLoadingStat(stat: TreeLoadingStat): string {
99
100
  export async function loadTreeState(
100
101
  tx: PlTransaction,
101
102
  loadingRequest: TreeLoadingRequest,
102
- stats?: TreeLoadingStat
103
+ stats?: TreeLoadingStat,
103
104
  ): Promise<ExtendedResourceData[]> {
104
105
  // saving start timestamp to add time spent in this function to the stats at the end of the method
105
106
  const startTimestamp = Date.now();
@@ -159,7 +160,7 @@ export async function loadTreeState(
159
160
  if (kv === undefined) throw new Error('Inconsistent replies');
160
161
 
161
162
  return { ...resource, kv };
162
- })()
163
+ })(),
163
164
  );
164
165
  };
165
166
 
@@ -1,22 +1,27 @@
1
1
  import { PollingComputableHooks } from '@milaboratories/computable';
2
2
  import { PlTreeEntry } from './accessors';
3
- import {
3
+ import type {
4
4
  FinalResourceDataPredicate,
5
- isTimeoutOrCancelError,
6
5
  PlClient,
7
6
  ResourceId,
8
- TxOps
7
+ TxOps,
8
+ } from '@milaboratories/pl-client';
9
+ import {
10
+ isTimeoutOrCancelError,
9
11
  } from '@milaboratories/pl-client';
10
- import { ExtendedResourceData, PlTreeState, TreeStateUpdateError } from './state';
12
+ import type { ExtendedResourceData } from './state';
13
+ import { PlTreeState, TreeStateUpdateError } from './state';
14
+ import type {
15
+ PruningFunction,
16
+ TreeLoadingStat,
17
+ } from './sync';
11
18
  import {
12
19
  constructTreeLoadingRequest,
13
20
  initialTreeLoadingStat,
14
21
  loadTreeState,
15
- PruningFunction,
16
- TreeLoadingStat
17
22
  } from './sync';
18
23
  import * as tp from 'node:timers/promises';
19
- import { MiLogger } from '@milaboratories/ts-helpers';
24
+ import type { MiLogger } from '@milaboratories/ts-helpers';
20
25
 
21
26
  type StatLoggingMode = 'cumulative' | 'per-request';
22
27
 
@@ -58,7 +63,7 @@ export class SynchronizedTreeState {
58
63
  private readonly pl: PlClient,
59
64
  private readonly root: ResourceId,
60
65
  ops: SynchronizedTreeOps,
61
- private readonly logger?: MiLogger
66
+ private readonly logger?: MiLogger,
62
67
  ) {
63
68
  const { finalPredicateOverride, pruning, pollingInterval, stopPollingDelay, logStat } = ops;
64
69
  this.pruning = pruning;
@@ -70,7 +75,7 @@ export class SynchronizedTreeState {
70
75
  () => this.startUpdating(),
71
76
  () => this.stopUpdating(),
72
77
  { stopDebounce: stopPollingDelay },
73
- (resolve, reject) => this.scheduleOnNextState(resolve, reject)
78
+ (resolve, reject) => this.scheduleOnNextState(resolve, reject),
74
79
  );
75
80
  }
76
81
 
@@ -231,24 +236,24 @@ export class SynchronizedTreeState {
231
236
  pl: PlClient,
232
237
  root: ResourceId,
233
238
  ops: SynchronizedTreeOps,
234
- logger?: MiLogger
239
+ logger?: MiLogger,
235
240
  ) {
236
241
  const tree = new SynchronizedTreeState(pl, root, ops, logger);
237
242
 
238
- let stat = ops.logStat ? initialTreeLoadingStat() : undefined;
243
+ const stat = ops.logStat ? initialTreeLoadingStat() : undefined;
239
244
 
240
245
  let ok = false;
241
246
 
242
247
  try {
243
248
  await tree.refresh(stat, {
244
- timeout: ops.initialTreeLoadingTimeout
249
+ timeout: ops.initialTreeLoadingTimeout,
245
250
  });
246
251
  ok = true;
247
252
  } finally {
248
253
  // logging stats if we were asked to (even if error occured)
249
254
  if (stat && logger)
250
255
  logger.info(
251
- `Tree stat (initial load, ${ok ? 'success' : 'failure'}): ${JSON.stringify(stat)}`
256
+ `Tree stat (initial load, ${ok ? 'success' : 'failure'}): ${JSON.stringify(stat)}`,
252
257
  );
253
258
  }
254
259
 
package/src/test_utils.ts CHANGED
@@ -1,49 +1,49 @@
1
- import { Optional } from 'utility-types';
2
- import {
1
+ import type {
3
2
  BasicResourceData,
4
3
  FieldData,
5
4
  FieldType,
6
- NullResourceId,
7
5
  OptionalResourceId,
8
- ResourceData,
9
6
  ResourceId,
10
- ResourceType
7
+ ResourceType,
11
8
  } from '@milaboratories/pl-client';
12
- import { ExtendedResourceData } from './state';
9
+ import {
10
+ NullResourceId,
11
+ } from '@milaboratories/pl-client';
12
+ import type { ExtendedResourceData } from './state';
13
13
 
14
14
  export const TestRootType1: ResourceType = {
15
15
  name: 'TestRootResource1',
16
- version: '0'
16
+ version: '0',
17
17
  };
18
18
 
19
19
  export const TestRootType2: ResourceType = {
20
20
  name: 'TestRootResource2',
21
- version: '0'
21
+ version: '0',
22
22
  };
23
23
 
24
24
  export const TestStructuralResourceType1: ResourceType = {
25
25
  name: 'TestStructuralResource1',
26
- version: '0'
26
+ version: '0',
27
27
  };
28
28
 
29
29
  export const TestStructuralResourceType2: ResourceType = {
30
30
  name: 'TestStructuralResource2',
31
- version: '0'
31
+ version: '0',
32
32
  };
33
33
 
34
34
  export const TestValueResourceType1: ResourceType = {
35
35
  name: 'TestValueResource1',
36
- version: '0'
36
+ version: '0',
37
37
  };
38
38
 
39
39
  export const TestValueResourceType2: ResourceType = {
40
40
  name: 'TestValueResource2',
41
- version: '0'
41
+ version: '0',
42
42
  };
43
43
 
44
44
  export const TestErrorResourceType1: ResourceType = {
45
45
  name: 'json/resourceError',
46
- version: '1'
46
+ version: '1',
47
47
  };
48
48
 
49
49
  export const ResourceReady: Pick<
@@ -53,11 +53,11 @@ export const ResourceReady: Pick<
53
53
  inputsLocked: true,
54
54
  outputsLocked: true,
55
55
  resourceReady: true,
56
- final: true
56
+ final: true,
57
57
  };
58
58
 
59
- export const InitialStructuralResourceState: Omit<ExtendedResourceData, 'id' | 'type' | 'fields'> =
60
- {
59
+ export const InitialStructuralResourceState: Omit<ExtendedResourceData, 'id' | 'type' | 'fields'>
60
+ = {
61
61
  kind: 'Structural',
62
62
  originalResourceId: NullResourceId,
63
63
  error: NullResourceId,
@@ -65,7 +65,7 @@ export const InitialStructuralResourceState: Omit<ExtendedResourceData, 'id' | '
65
65
  outputsLocked: false,
66
66
  resourceReady: false,
67
67
  final: false,
68
- kv: []
68
+ kv: [],
69
69
  };
70
70
 
71
71
  export const InitialValueResourceState: Omit<ExtendedResourceData, 'id' | 'type' | 'data'> = {
@@ -74,32 +74,32 @@ export const InitialValueResourceState: Omit<ExtendedResourceData, 'id' | 'type'
74
74
  error: NullResourceId,
75
75
  ...ResourceReady,
76
76
  fields: [],
77
- kv: []
77
+ kv: [],
78
78
  };
79
79
 
80
80
  export const TestStructuralResourceState1: Omit<ExtendedResourceData, 'id' | 'fields'> = {
81
81
  ...InitialStructuralResourceState,
82
- type: TestStructuralResourceType1
82
+ type: TestStructuralResourceType1,
83
83
  };
84
84
 
85
85
  export const TestStructuralResourceState2: Omit<ExtendedResourceData, 'id' | 'fields'> = {
86
86
  ...InitialStructuralResourceState,
87
- type: TestStructuralResourceType2
87
+ type: TestStructuralResourceType2,
88
88
  };
89
89
 
90
90
  export const TestValueResourceState1: Omit<ExtendedResourceData, 'id' | 'data'> = {
91
91
  ...InitialValueResourceState,
92
- type: TestValueResourceType1
92
+ type: TestValueResourceType1,
93
93
  };
94
94
 
95
95
  export const TestValueResourceState2: Omit<ExtendedResourceData, 'id' | 'data'> = {
96
96
  ...InitialValueResourceState,
97
- type: TestValueResourceType2
97
+ type: TestValueResourceType2,
98
98
  };
99
99
 
100
100
  export const TestErrorResourceState2: Omit<ExtendedResourceData, 'id' | 'data'> = {
101
101
  ...InitialValueResourceState,
102
- type: TestErrorResourceType1
102
+ type: TestErrorResourceType1,
103
103
  };
104
104
 
105
105
  export const TestDynamicRootId1 = 1000001n as ResourceId;
@@ -109,7 +109,7 @@ export const TestDynamicRootState1: Omit<ExtendedResourceData, 'fields'> = {
109
109
  outputsLocked: true,
110
110
  resourceReady: true,
111
111
  type: TestRootType1,
112
- id: TestDynamicRootId1
112
+ id: TestDynamicRootId1,
113
113
  };
114
114
 
115
115
  export const TestDynamicRootId2 = 1000002n as ResourceId;
@@ -119,7 +119,7 @@ export const TestDynamicRootState2: Omit<ExtendedResourceData, 'fields'> = {
119
119
  outputsLocked: true,
120
120
  resourceReady: true,
121
121
  type: TestRootType2,
122
- id: TestDynamicRootId2
122
+ id: TestDynamicRootId2,
123
123
  };
124
124
 
125
125
  export function field(
@@ -127,7 +127,7 @@ export function field(
127
127
  name: string,
128
128
  value: OptionalResourceId = NullResourceId,
129
129
  error: OptionalResourceId = NullResourceId,
130
- valueIsFinal: boolean = false
130
+ valueIsFinal: boolean = false,
131
131
  ): FieldData {
132
132
  return {
133
133
  name,
@@ -135,14 +135,14 @@ export function field(
135
135
  value,
136
136
  error,
137
137
  status: value !== NullResourceId ? 'Resolved' : error !== NullResourceId ? 'Assigned' : 'Empty',
138
- valueIsFinal
138
+ valueIsFinal,
139
139
  };
140
140
  }
141
141
 
142
142
  export function dField(
143
143
  name: string,
144
144
  value: OptionalResourceId = NullResourceId,
145
- error: OptionalResourceId = NullResourceId
145
+ error: OptionalResourceId = NullResourceId,
146
146
  ): FieldData {
147
147
  return field('Dynamic', name, value, error);
148
148
  }
@@ -150,7 +150,7 @@ export function dField(
150
150
  export function iField(
151
151
  name: string,
152
152
  value: OptionalResourceId = NullResourceId,
153
- error: OptionalResourceId = NullResourceId
153
+ error: OptionalResourceId = NullResourceId,
154
154
  ): FieldData {
155
155
  return field('Input', name, value, error);
156
156
  }
@@ -5,7 +5,7 @@ export interface ValueAndError<T> {
5
5
 
6
6
  export function mapValueAndErrorIfDefined<T1, T2>(
7
7
  input: ValueAndError<T1> | undefined,
8
- mapping: (v: T1) => T2
8
+ mapping: (v: T1) => T2,
9
9
  ): ValueAndError<T2> | undefined {
10
10
  if (input === undefined) return undefined;
11
11
  else return mapValueAndError(input, mapping);
@@ -1,9 +1,9 @@
1
1
  export type ValueOrError<V, E> =
2
2
  | {
3
- ok: true;
4
- value: V;
5
- }
3
+ ok: true;
4
+ value: V;
5
+ }
6
6
  | {
7
- ok: false;
8
- error: E;
9
- };
7
+ ok: false;
8
+ error: E;
9
+ };