@milaboratories/pl-client 2.16.1 → 2.16.2

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.
Files changed (58) hide show
  1. package/dist/__external/.pnpm/@rollup_plugin-typescript@12.1.4_rollup@4.52.4_tslib@2.7.0_typescript@5.6.3/__external/tslib/tslib.es6.cjs +61 -0
  2. package/dist/__external/.pnpm/@rollup_plugin-typescript@12.1.4_rollup@4.52.4_tslib@2.7.0_typescript@5.6.3/__external/tslib/tslib.es6.cjs.map +1 -0
  3. package/dist/__external/.pnpm/@rollup_plugin-typescript@12.1.4_rollup@4.52.4_tslib@2.7.0_typescript@5.6.3/__external/tslib/tslib.es6.js +58 -0
  4. package/dist/__external/.pnpm/@rollup_plugin-typescript@12.1.4_rollup@4.52.4_tslib@2.7.0_typescript@5.6.3/__external/tslib/tslib.es6.js.map +1 -0
  5. package/dist/core/PromiseTracker.cjs +39 -0
  6. package/dist/core/PromiseTracker.cjs.map +1 -0
  7. package/dist/core/PromiseTracker.d.ts +14 -0
  8. package/dist/core/PromiseTracker.d.ts.map +1 -0
  9. package/dist/core/PromiseTracker.js +37 -0
  10. package/dist/core/PromiseTracker.js.map +1 -0
  11. package/dist/core/StatefulPromise.cjs +65 -0
  12. package/dist/core/StatefulPromise.cjs.map +1 -0
  13. package/dist/core/StatefulPromise.d.ts +39 -0
  14. package/dist/core/StatefulPromise.d.ts.map +1 -0
  15. package/dist/core/StatefulPromise.js +63 -0
  16. package/dist/core/StatefulPromise.js.map +1 -0
  17. package/dist/core/ll_transaction.cjs +3 -2
  18. package/dist/core/ll_transaction.cjs.map +1 -1
  19. package/dist/core/ll_transaction.d.ts.map +1 -1
  20. package/dist/core/ll_transaction.js +3 -2
  21. package/dist/core/ll_transaction.js.map +1 -1
  22. package/dist/core/transaction.cjs +577 -515
  23. package/dist/core/transaction.cjs.map +1 -1
  24. package/dist/core/transaction.d.ts +6 -4
  25. package/dist/core/transaction.d.ts.map +1 -1
  26. package/dist/core/transaction.js +578 -516
  27. package/dist/core/transaction.js.map +1 -1
  28. package/dist/test/tcp-proxy.cjs +129 -0
  29. package/dist/test/tcp-proxy.cjs.map +1 -0
  30. package/dist/test/tcp-proxy.d.ts +17 -0
  31. package/dist/test/tcp-proxy.d.ts.map +1 -0
  32. package/dist/test/tcp-proxy.js +107 -0
  33. package/dist/test/tcp-proxy.js.map +1 -0
  34. package/dist/test/test_config.cjs +54 -7
  35. package/dist/test/test_config.cjs.map +1 -1
  36. package/dist/test/test_config.d.ts +18 -2
  37. package/dist/test/test_config.d.ts.map +1 -1
  38. package/dist/test/test_config.js +54 -7
  39. package/dist/test/test_config.js.map +1 -1
  40. package/package.json +5 -6
  41. package/src/core/PromiseTracker.ts +39 -0
  42. package/src/core/StatefulPromise.ts +92 -0
  43. package/src/core/client.test.ts +1 -1
  44. package/src/core/config.test.ts +1 -1
  45. package/src/core/connectivity.test.ts +70 -0
  46. package/src/core/error.test.ts +1 -1
  47. package/src/core/ll_client.test.ts +1 -1
  48. package/src/core/ll_transaction.test.ts +1 -1
  49. package/src/core/ll_transaction.ts +3 -2
  50. package/src/core/transaction.test.ts +6 -1
  51. package/src/core/transaction.ts +91 -48
  52. package/src/core/types.test.ts +1 -1
  53. package/src/core/unauth_client.test.ts +1 -1
  54. package/src/helpers/rich_resource_types.test.ts +1 -1
  55. package/src/test/tcp-proxy.ts +126 -0
  56. package/src/test/test_config.test.ts +1 -1
  57. package/src/test/test_config.ts +82 -7
  58. package/src/util/util.test.ts +1 -1
@@ -1,4 +1,5 @@
1
- import { isLocalResourceId, isNullResourceId, ensureResourceIdNotNull, extractBasicResourceData, createLocalResourceId, MaxTxId } from './types.js';
1
+ import { __esDecorate, __runInitializers } from '../__external/.pnpm/@rollup_plugin-typescript@12.1.4_rollup@4.52.4_tslib@2.7.0_typescript@5.6.3/__external/tslib/tslib.es6.js';
2
+ import { ensureResourceIdNotNull, isLocalResourceId, extractBasicResourceData, createLocalResourceId, MaxTxId, isNullResourceId } from './types.js';
2
3
  import { TxAPI_Open_Request_WritableTx } from '../proto/github.com/milaboratory/pl/plapi/plapiproto/api.js';
3
4
  import { toBytes } from '../util/util.js';
4
5
  import { protoToResource, fieldTypeToProto, protoToField } from './type_conversion.js';
@@ -7,6 +8,7 @@ import { isNotFoundError } from './errors.js';
7
8
  import { initialTxStatWithoutTime } from './stat.js';
8
9
  import { ErrorResourceType } from './error_resource.js';
9
10
  import { JsonObject, JsonGzObject } from '../helpers/pl.js';
11
+ import { PromiseTracker } from './PromiseTracker.js';
10
12
 
11
13
  function isField(ref) {
12
14
  return ref.hasOwnProperty('resourceId') && ref.hasOwnProperty('fieldName');
@@ -64,6 +66,15 @@ async function notFoundToUndefined(cb) {
64
66
  throw e;
65
67
  }
66
68
  }
69
+ /**
70
+ * Decorator that wraps the method's returned promise with this.track()
71
+ * This ensures that the promise will be awaited before the transaction is completed.
72
+ */
73
+ function tracked(value, _context) {
74
+ return function (...args) {
75
+ return this.track(value.apply(this, args));
76
+ };
77
+ }
67
78
  /**
68
79
  * Each platform transaction has 3 stages:
69
80
  * - initialization (txOpen message -> txInfo response)
@@ -72,542 +83,593 @@ async function notFoundToUndefined(cb) {
72
83
  *
73
84
  * This class encapsulates finalization stage and provides ready-to-communication transaction object.
74
85
  * */
75
- class PlTransaction {
76
- ll;
77
- name;
78
- writable;
79
- _clientRoot;
80
- finalPredicate;
81
- sharedResourceDataCache;
82
- enableFormattedErrors;
83
- globalTxId;
84
- localTxId = PlTransaction.nextLocalTxId();
85
- /** Used in caching */
86
- txOpenTimestamp = Date.now();
87
- localResourceIdCounter = 0;
88
- /** Store logical tx open / closed state to prevent invalid sequence of requests.
89
- * True means output stream was completed.
90
- * Contract: there must be no async operations between setting this field to true and sending complete signal to stream. */
91
- _completed = false;
92
- /** Void operation futures are placed into this pool, and corresponding method return immediately.
93
- * This is done to save number of round-trips. Next operation producing result will also await those
94
- * pending ops, to throw any pending errors. */
95
- pendingVoidOps = [];
96
- globalTxIdWasAwaited = false;
97
- _startTime = Date.now();
98
- _stat = initialTxStatWithoutTime();
99
- get stat() {
100
- return {
101
- ...this._stat,
102
- timeMs: Date.now() - this._startTime,
103
- };
104
- }
105
- constructor(ll, name, writable, _clientRoot, finalPredicate, sharedResourceDataCache, enableFormattedErrors = false) {
106
- this.ll = ll;
107
- this.name = name;
108
- this.writable = writable;
109
- this._clientRoot = _clientRoot;
110
- this.finalPredicate = finalPredicate;
111
- this.sharedResourceDataCache = sharedResourceDataCache;
112
- this.enableFormattedErrors = enableFormattedErrors;
113
- // initiating transaction
114
- this.globalTxId = this.sendSingleAndParse({
115
- oneofKind: 'txOpen',
116
- txOpen: {
117
- name,
118
- enableFormattedErrors,
119
- writable: writable
120
- ? TxAPI_Open_Request_WritableTx.WRITABLE
121
- : TxAPI_Open_Request_WritableTx.NOT_WRITABLE,
122
- },
123
- }, (r) => notEmpty(r.txOpen.tx?.id));
124
- // To avoid floating promise
125
- this.globalTxId.catch((err) => {
126
- if (!this.globalTxIdWasAwaited) {
127
- console.warn(err);
86
+ let PlTransaction = (() => {
87
+ let _instanceExtraInitializers = [];
88
+ let _getResourceData_decorators;
89
+ let _getResourceDataIfExists_decorators;
90
+ let _getFieldIfExists_decorators;
91
+ let _listKeyValues_decorators;
92
+ let _listKeyValuesString_decorators;
93
+ let _listKeyValuesIfResourceExists_decorators;
94
+ let _listKeyValuesStringIfResourceExists_decorators;
95
+ let _getKValue_decorators;
96
+ let _getKValueString_decorators;
97
+ let _getKValueJson_decorators;
98
+ let _getKValueIfExists_decorators;
99
+ let _getKValueStringIfExists_decorators;
100
+ let _getKValueJsonIfExists_decorators;
101
+ return class PlTransaction {
102
+ static {
103
+ const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(null) : void 0;
104
+ _getResourceData_decorators = [tracked];
105
+ _getResourceDataIfExists_decorators = [tracked];
106
+ _getFieldIfExists_decorators = [tracked];
107
+ _listKeyValues_decorators = [tracked];
108
+ _listKeyValuesString_decorators = [tracked];
109
+ _listKeyValuesIfResourceExists_decorators = [tracked];
110
+ _listKeyValuesStringIfResourceExists_decorators = [tracked];
111
+ _getKValue_decorators = [tracked];
112
+ _getKValueString_decorators = [tracked];
113
+ _getKValueJson_decorators = [tracked];
114
+ _getKValueIfExists_decorators = [tracked];
115
+ _getKValueStringIfExists_decorators = [tracked];
116
+ _getKValueJsonIfExists_decorators = [tracked];
117
+ __esDecorate(this, null, _getResourceData_decorators, { kind: "method", name: "getResourceData", static: false, private: false, access: { has: obj => "getResourceData" in obj, get: obj => obj.getResourceData }, metadata: _metadata }, null, _instanceExtraInitializers);
118
+ __esDecorate(this, null, _getResourceDataIfExists_decorators, { kind: "method", name: "getResourceDataIfExists", static: false, private: false, access: { has: obj => "getResourceDataIfExists" in obj, get: obj => obj.getResourceDataIfExists }, metadata: _metadata }, null, _instanceExtraInitializers);
119
+ __esDecorate(this, null, _getFieldIfExists_decorators, { kind: "method", name: "getFieldIfExists", static: false, private: false, access: { has: obj => "getFieldIfExists" in obj, get: obj => obj.getFieldIfExists }, metadata: _metadata }, null, _instanceExtraInitializers);
120
+ __esDecorate(this, null, _listKeyValues_decorators, { kind: "method", name: "listKeyValues", static: false, private: false, access: { has: obj => "listKeyValues" in obj, get: obj => obj.listKeyValues }, metadata: _metadata }, null, _instanceExtraInitializers);
121
+ __esDecorate(this, null, _listKeyValuesString_decorators, { kind: "method", name: "listKeyValuesString", static: false, private: false, access: { has: obj => "listKeyValuesString" in obj, get: obj => obj.listKeyValuesString }, metadata: _metadata }, null, _instanceExtraInitializers);
122
+ __esDecorate(this, null, _listKeyValuesIfResourceExists_decorators, { kind: "method", name: "listKeyValuesIfResourceExists", static: false, private: false, access: { has: obj => "listKeyValuesIfResourceExists" in obj, get: obj => obj.listKeyValuesIfResourceExists }, metadata: _metadata }, null, _instanceExtraInitializers);
123
+ __esDecorate(this, null, _listKeyValuesStringIfResourceExists_decorators, { kind: "method", name: "listKeyValuesStringIfResourceExists", static: false, private: false, access: { has: obj => "listKeyValuesStringIfResourceExists" in obj, get: obj => obj.listKeyValuesStringIfResourceExists }, metadata: _metadata }, null, _instanceExtraInitializers);
124
+ __esDecorate(this, null, _getKValue_decorators, { kind: "method", name: "getKValue", static: false, private: false, access: { has: obj => "getKValue" in obj, get: obj => obj.getKValue }, metadata: _metadata }, null, _instanceExtraInitializers);
125
+ __esDecorate(this, null, _getKValueString_decorators, { kind: "method", name: "getKValueString", static: false, private: false, access: { has: obj => "getKValueString" in obj, get: obj => obj.getKValueString }, metadata: _metadata }, null, _instanceExtraInitializers);
126
+ __esDecorate(this, null, _getKValueJson_decorators, { kind: "method", name: "getKValueJson", static: false, private: false, access: { has: obj => "getKValueJson" in obj, get: obj => obj.getKValueJson }, metadata: _metadata }, null, _instanceExtraInitializers);
127
+ __esDecorate(this, null, _getKValueIfExists_decorators, { kind: "method", name: "getKValueIfExists", static: false, private: false, access: { has: obj => "getKValueIfExists" in obj, get: obj => obj.getKValueIfExists }, metadata: _metadata }, null, _instanceExtraInitializers);
128
+ __esDecorate(this, null, _getKValueStringIfExists_decorators, { kind: "method", name: "getKValueStringIfExists", static: false, private: false, access: { has: obj => "getKValueStringIfExists" in obj, get: obj => obj.getKValueStringIfExists }, metadata: _metadata }, null, _instanceExtraInitializers);
129
+ __esDecorate(this, null, _getKValueJsonIfExists_decorators, { kind: "method", name: "getKValueJsonIfExists", static: false, private: false, access: { has: obj => "getKValueJsonIfExists" in obj, get: obj => obj.getKValueJsonIfExists }, metadata: _metadata }, null, _instanceExtraInitializers);
130
+ if (_metadata) Object.defineProperty(this, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
131
+ }
132
+ ll = __runInitializers(this, _instanceExtraInitializers);
133
+ name;
134
+ writable;
135
+ _clientRoot;
136
+ finalPredicate;
137
+ sharedResourceDataCache;
138
+ enableFormattedErrors;
139
+ globalTxId;
140
+ localTxId = PlTransaction.nextLocalTxId();
141
+ /** Used in caching */
142
+ txOpenTimestamp = Date.now();
143
+ localResourceIdCounter = 0;
144
+ /** Store logical tx open / closed state to prevent invalid sequence of requests.
145
+ * True means output stream was completed.
146
+ * Contract: there must be no async operations between setting this field to true and sending complete signal to stream. */
147
+ _completed = false;
148
+ globalTxIdWasAwaited = false;
149
+ pending = new PromiseTracker();
150
+ _startTime = Date.now();
151
+ _stat = initialTxStatWithoutTime();
152
+ get stat() {
153
+ return {
154
+ ...this._stat,
155
+ timeMs: Date.now() - this._startTime,
156
+ };
157
+ }
158
+ constructor(ll, name, writable, _clientRoot, finalPredicate, sharedResourceDataCache, enableFormattedErrors = false) {
159
+ this.ll = ll;
160
+ this.name = name;
161
+ this.writable = writable;
162
+ this._clientRoot = _clientRoot;
163
+ this.finalPredicate = finalPredicate;
164
+ this.sharedResourceDataCache = sharedResourceDataCache;
165
+ this.enableFormattedErrors = enableFormattedErrors;
166
+ // initiating transaction
167
+ this.globalTxId = this.sendSingleAndParse({
168
+ oneofKind: 'txOpen',
169
+ txOpen: {
170
+ name,
171
+ enableFormattedErrors,
172
+ writable: writable
173
+ ? TxAPI_Open_Request_WritableTx.WRITABLE
174
+ : TxAPI_Open_Request_WritableTx.NOT_WRITABLE,
175
+ },
176
+ }, (r) => notEmpty(r.txOpen.tx?.id));
177
+ void this.track(this.globalTxId);
178
+ // To avoid floating promise
179
+ this.globalTxId.catch((err) => {
180
+ if (!this.globalTxIdWasAwaited) {
181
+ console.warn(err);
182
+ }
183
+ });
184
+ // Adding stats
185
+ this._stat.txCount++;
186
+ }
187
+ /**
188
+ * Collect all pending promises for the transaction finalization.
189
+ */
190
+ track(promiseOrCallback) {
191
+ return this.pending.track(promiseOrCallback);
192
+ }
193
+ async drainAndAwaitPendingOps() {
194
+ // awaiting these pending operations first, to catch any errors
195
+ await this.pending.awaitAll();
196
+ }
197
+ sendSingleAndParse(r, parser) {
198
+ return this.pending.track(async () => {
199
+ const rawResponsePromise = this.ll.send(r, false);
200
+ void this.pending.track(rawResponsePromise);
201
+ await this.drainAndAwaitPendingOps();
202
+ // awaiting our result, and parsing the response
203
+ return parser(await rawResponsePromise);
204
+ });
205
+ }
206
+ sendMultiAndParse(r, parser) {
207
+ return this.pending.track(async () => {
208
+ const rawResponsePromise = this.ll.send(r, true);
209
+ void this.pending.track(rawResponsePromise);
210
+ await this.drainAndAwaitPendingOps();
211
+ // awaiting our result, and parsing the response
212
+ return parser(await rawResponsePromise);
213
+ });
214
+ }
215
+ async sendVoidSync(r) {
216
+ await this.ll.send(r, false);
217
+ }
218
+ /** Requests sent with this method should never produce recoverable errors */
219
+ sendVoidAsync(r) {
220
+ void this.track(this.sendVoidSync(r));
221
+ }
222
+ checkTxOpen() {
223
+ if (this._completed)
224
+ throw new Error('Transaction already closed');
225
+ }
226
+ get completed() {
227
+ return this._completed;
228
+ }
229
+ /** Commit & closes transaction. {@link TxCommitConflict} is thrown on
230
+ * commit conflicts. */
231
+ async commit() {
232
+ this.checkTxOpen();
233
+ // tx will accept no requests after this one
234
+ this._completed = true;
235
+ if (!this.writable) {
236
+ // no need to explicitly commit or reject read-only tx
237
+ const completeResult = this.track(this.ll.complete());
238
+ await this.drainAndAwaitPendingOps();
239
+ await completeResult;
240
+ await this.ll.await();
241
+ }
242
+ else {
243
+ const commitResponse = this.track(this.sendSingleAndParse({ oneofKind: 'txCommit', txCommit: {} }, (r) => r.txCommit.success));
244
+ // send closing frame right after commit to save some time on round-trips
245
+ const completeResult = this.track(this.ll.complete());
246
+ // now when we pushed all packets into the stream, we should wait for any
247
+ // pending void operations from before, to catch any errors
248
+ await this.drainAndAwaitPendingOps();
249
+ if (!(await commitResponse))
250
+ throw new TxCommitConflict();
251
+ await completeResult;
252
+ // await event-loop completion
253
+ await this.ll.await();
128
254
  }
129
- });
130
- // Adding stats
131
- this._stat.txCount++;
132
- }
133
- async drainAndAwaitPendingOps() {
134
- if (this.pendingVoidOps.length === 0)
135
- return;
136
- // drain pending operations into temp array
137
- const pending = this.pendingVoidOps;
138
- this.pendingVoidOps = [];
139
- // awaiting these pending operations first, to catch any errors
140
- await Promise.all(pending);
141
- }
142
- async sendSingleAndParse(r, parser) {
143
- // pushing operation packet to server
144
- const rawResponsePromise = this.ll.send(r, false);
145
- await this.drainAndAwaitPendingOps();
146
- // awaiting our result, and parsing the response
147
- return parser(await rawResponsePromise);
148
- }
149
- async sendMultiAndParse(r, parser) {
150
- // pushing operation packet to server
151
- const rawResponsePromise = this.ll.send(r, true);
152
- await this.drainAndAwaitPendingOps();
153
- // awaiting our result, and parsing the response
154
- return parser(await rawResponsePromise);
155
- }
156
- async sendVoidSync(r) {
157
- await this.ll.send(r, false);
158
- }
159
- /** Requests sent with this method should never produce recoverable errors */
160
- sendVoidAsync(r) {
161
- this.pendingVoidOps.push(this.sendVoidSync(r));
162
- }
163
- checkTxOpen() {
164
- if (this._completed)
165
- throw new Error('Transaction already closed');
166
- }
167
- get completed() {
168
- return this._completed;
169
- }
170
- /** Commit & closes transaction. {@link TxCommitConflict} is thrown on
171
- * commit conflicts. */
172
- async commit() {
173
- this.checkTxOpen();
174
- // tx will accept no requests after this one
175
- this._completed = true;
176
- if (!this.writable) {
177
- // no need to explicitly commit or reject read-only tx
178
- const completeResult = this.ll.complete();
179
- await this.drainAndAwaitPendingOps();
180
- await completeResult;
181
- await this.ll.await();
182
255
  }
183
- else {
184
- // @TODO, also floating promises
185
- const commitResponse = this.sendSingleAndParse({ oneofKind: 'txCommit', txCommit: {} }, (r) => r.txCommit.success);
256
+ async discard() {
257
+ this.checkTxOpen();
258
+ // tx will accept no requests after this one
259
+ this._completed = true;
260
+ const discardResponse = this.sendVoidSync({ oneofKind: 'txDiscard', txDiscard: {} });
261
+ void this.track(discardResponse);
186
262
  // send closing frame right after commit to save some time on round-trips
187
- const completeResult = this.ll.complete();
263
+ const completeResult = this.track(this.ll.complete());
188
264
  // now when we pushed all packets into the stream, we should wait for any
189
265
  // pending void operations from before, to catch any errors
190
266
  await this.drainAndAwaitPendingOps();
191
- if (!(await commitResponse))
192
- throw new TxCommitConflict();
267
+ await discardResponse;
193
268
  await completeResult;
194
- // await event-loop completion
195
269
  await this.ll.await();
196
270
  }
197
- }
198
- async discard() {
199
- this.checkTxOpen();
200
- // tx will accept no requests after this one
201
- this._completed = true;
202
- const discardResponse = this.sendVoidSync({ oneofKind: 'txDiscard', txDiscard: {} });
203
- // send closing frame right after commit to save some time on round-trips
204
- const completeResult = this.ll.complete();
205
- // now when we pushed all packets into the stream, we should wait for any
206
- // pending void operations from before, to catch any errors
207
- await this.drainAndAwaitPendingOps();
208
- await discardResponse;
209
- await completeResult;
210
- await this.ll.await();
211
- }
212
- //
213
- // Main tx methods
214
- //
215
- get clientRoot() {
216
- return ensureResourceIdNotNull(this._clientRoot);
217
- }
218
- //
219
- // Resources
220
- //
221
- createSingleton(name, type, errorIfExists = false) {
222
- const localId = this.nextLocalResourceId(false);
223
- const globalId = this.sendSingleAndParse({
224
- oneofKind: 'resourceCreateSingleton',
225
- resourceCreateSingleton: {
226
- type,
227
- id: localId,
228
- data: Buffer.from(name),
229
- errorIfExists,
230
- },
231
- }, (r) => r.resourceCreateSingleton.resourceId);
232
- return { globalId, localId };
233
- }
234
- async getSingleton(name, loadFields = true) {
235
- return await this.sendSingleAndParse({
236
- oneofKind: 'resourceGetSingleton',
237
- resourceGetSingleton: {
238
- data: Buffer.from(name),
239
- loadFields,
240
- },
241
- }, (r) => protoToResource(notEmpty(r.resourceGetSingleton.resource)));
242
- }
243
- createResource(root, req, parser) {
244
- const localId = this.nextLocalResourceId(root);
245
- const globalId = this.sendSingleAndParse(req(localId), (r) => parser(r));
246
- return { globalId, localId };
247
- }
248
- createRoot(type) {
249
- this._stat.rootsCreated++;
250
- return this.createResource(true, (localId) => ({ oneofKind: 'resourceCreateRoot', resourceCreateRoot: { type, id: localId } }), (r) => r.resourceCreateRoot.resourceId);
251
- }
252
- createStruct(type, data) {
253
- this._stat.structsCreated++;
254
- this._stat.structsCreatedDataBytes += data?.length ?? 0;
255
- return this.createResource(false, (localId) => ({
256
- oneofKind: 'resourceCreateStruct',
257
- resourceCreateStruct: {
258
- type,
259
- id: localId,
260
- data: data === undefined ? undefined : typeof data === 'string' ? Buffer.from(data) : data,
261
- },
262
- }), (r) => r.resourceCreateStruct.resourceId);
263
- }
264
- createEphemeral(type, data) {
265
- this._stat.ephemeralsCreated++;
266
- this._stat.ephemeralsCreatedDataBytes += data?.length ?? 0;
267
- return this.createResource(false, (localId) => ({
268
- oneofKind: 'resourceCreateEphemeral',
269
- resourceCreateEphemeral: {
270
- type,
271
- id: localId,
272
- data: data === undefined ? undefined : typeof data === 'string' ? Buffer.from(data) : data,
273
- },
274
- }), (r) => r.resourceCreateEphemeral.resourceId);
275
- }
276
- createValue(type, data, errorIfExists = false) {
277
- this._stat.valuesCreated++;
278
- this._stat.valuesCreatedDataBytes += data?.length ?? 0;
279
- return this.createResource(false, (localId) => ({
280
- oneofKind: 'resourceCreateValue',
281
- resourceCreateValue: {
282
- type,
283
- id: localId,
284
- data: typeof data === 'string' ? Buffer.from(data) : data,
285
- errorIfExists,
286
- },
287
- }), (r) => r.resourceCreateValue.resourceId);
288
- }
289
- createJsonValue(data) {
290
- const jsonData = canonicalJsonBytes(data);
291
- return this.createValue(JsonObject, jsonData, false);
292
- }
293
- createJsonGzValue(data, minSizeToGzip = 16_384) {
294
- const { data: jsonData, isGzipped } = canonicalJsonGzBytes(data, minSizeToGzip);
295
- return this.createValue(isGzipped ? JsonGzObject : JsonObject, jsonData, false);
296
- }
297
- createError(message) {
298
- return this.createValue(ErrorResourceType, JSON.stringify({ message }));
299
- }
300
- setResourceName(name, rId) {
301
- this.sendVoidAsync({
302
- oneofKind: 'resourceNameSet',
303
- resourceNameSet: { resourceId: toResourceId(rId), name },
304
- });
305
- }
306
- deleteResourceName(name) {
307
- this.sendVoidAsync({ oneofKind: 'resourceNameDelete', resourceNameDelete: { name } });
308
- }
309
- async getResourceByName(name) {
310
- return await this.sendSingleAndParse({ oneofKind: 'resourceNameGet', resourceNameGet: { name } }, (r) => ensureResourceIdNotNull(r.resourceNameGet.resourceId));
311
- }
312
- async checkResourceNameExists(name) {
313
- return await this.sendSingleAndParse({ oneofKind: 'resourceNameExists', resourceNameExists: { name } }, (r) => r.resourceNameExists.exists);
314
- }
315
- removeResource(rId) {
316
- this.sendVoidAsync({ oneofKind: 'resourceRemove', resourceRemove: { id: rId } });
317
- }
318
- async resourceExists(rId) {
319
- return await this.sendSingleAndParse({ oneofKind: 'resourceExists', resourceExists: { resourceId: rId } }, (r) => r.resourceExists.exists);
320
- }
321
- async getResourceData(rId, loadFields = true, ignoreCache = false) {
322
- if (!ignoreCache && !isResourceRef(rId) && !isLocalResourceId(rId)) {
323
- // checking if we can return result from cache
324
- const fromCache = this.sharedResourceDataCache.get(rId);
325
- if (fromCache && fromCache.cacheTxOpenTimestamp < this.txOpenTimestamp) {
326
- if (!loadFields) {
327
- this._stat.rGetDataCacheHits++;
328
- this._stat.rGetDataCacheBytes += fromCache.basicData.data?.length ?? 0;
329
- return fromCache.basicData;
330
- }
331
- else if (fromCache.data) {
332
- this._stat.rGetDataCacheHits++;
333
- this._stat.rGetDataCacheBytes += fromCache.basicData.data?.length ?? 0;
334
- this._stat.rGetDataCacheFields += fromCache.data.fields.length;
335
- return fromCache.data;
336
- }
337
- }
271
+ //
272
+ // Main tx methods
273
+ //
274
+ get clientRoot() {
275
+ return ensureResourceIdNotNull(this._clientRoot);
276
+ }
277
+ //
278
+ // Resources
279
+ //
280
+ createSingleton(name, type, errorIfExists = false) {
281
+ const localId = this.nextLocalResourceId(false);
282
+ const globalId = this.sendSingleAndParse({
283
+ oneofKind: 'resourceCreateSingleton',
284
+ resourceCreateSingleton: {
285
+ type,
286
+ id: localId,
287
+ data: Buffer.from(name),
288
+ errorIfExists,
289
+ },
290
+ }, (r) => r.resourceCreateSingleton.resourceId);
291
+ void this.track(globalId);
292
+ return { globalId, localId };
293
+ }
294
+ getSingleton(name, loadFields = true) {
295
+ return this.sendSingleAndParse({
296
+ oneofKind: 'resourceGetSingleton',
297
+ resourceGetSingleton: {
298
+ data: Buffer.from(name),
299
+ loadFields,
300
+ },
301
+ }, (r) => protoToResource(notEmpty(r.resourceGetSingleton.resource)));
302
+ }
303
+ createResource(root, req, parser) {
304
+ const localId = this.nextLocalResourceId(root);
305
+ const globalId = this.sendSingleAndParse(req(localId), (r) => parser(r));
306
+ void this.track(globalId);
307
+ return { globalId, localId };
308
+ }
309
+ createRoot(type) {
310
+ this._stat.rootsCreated++;
311
+ return this.createResource(true, (localId) => ({ oneofKind: 'resourceCreateRoot', resourceCreateRoot: { type, id: localId } }), (r) => r.resourceCreateRoot.resourceId);
312
+ }
313
+ createStruct(type, data) {
314
+ this._stat.structsCreated++;
315
+ this._stat.structsCreatedDataBytes += data?.length ?? 0;
316
+ return this.createResource(false, (localId) => ({
317
+ oneofKind: 'resourceCreateStruct',
318
+ resourceCreateStruct: {
319
+ type,
320
+ id: localId,
321
+ data: data === undefined ? undefined : typeof data === 'string' ? Buffer.from(data) : data,
322
+ },
323
+ }), (r) => r.resourceCreateStruct.resourceId);
324
+ }
325
+ createEphemeral(type, data) {
326
+ this._stat.ephemeralsCreated++;
327
+ this._stat.ephemeralsCreatedDataBytes += data?.length ?? 0;
328
+ return this.createResource(false, (localId) => ({
329
+ oneofKind: 'resourceCreateEphemeral',
330
+ resourceCreateEphemeral: {
331
+ type,
332
+ id: localId,
333
+ data: data === undefined ? undefined : typeof data === 'string' ? Buffer.from(data) : data,
334
+ },
335
+ }), (r) => r.resourceCreateEphemeral.resourceId);
336
+ }
337
+ createValue(type, data, errorIfExists = false) {
338
+ this._stat.valuesCreated++;
339
+ this._stat.valuesCreatedDataBytes += data?.length ?? 0;
340
+ return this.createResource(false, (localId) => ({
341
+ oneofKind: 'resourceCreateValue',
342
+ resourceCreateValue: {
343
+ type,
344
+ id: localId,
345
+ data: typeof data === 'string' ? Buffer.from(data) : data,
346
+ errorIfExists,
347
+ },
348
+ }), (r) => r.resourceCreateValue.resourceId);
349
+ }
350
+ createJsonValue(data) {
351
+ const jsonData = canonicalJsonBytes(data);
352
+ return this.createValue(JsonObject, jsonData, false);
353
+ }
354
+ createJsonGzValue(data, minSizeToGzip = 16_384) {
355
+ const { data: jsonData, isGzipped } = canonicalJsonGzBytes(data, minSizeToGzip);
356
+ return this.createValue(isGzipped ? JsonGzObject : JsonObject, jsonData, false);
357
+ }
358
+ createError(message) {
359
+ return this.createValue(ErrorResourceType, JSON.stringify({ message }));
360
+ }
361
+ setResourceName(name, rId) {
362
+ this.sendVoidAsync({
363
+ oneofKind: 'resourceNameSet',
364
+ resourceNameSet: { resourceId: toResourceId(rId), name },
365
+ });
366
+ }
367
+ deleteResourceName(name) {
368
+ this.sendVoidAsync({ oneofKind: 'resourceNameDelete', resourceNameDelete: { name } });
369
+ }
370
+ getResourceByName(name) {
371
+ return this.sendSingleAndParse({ oneofKind: 'resourceNameGet', resourceNameGet: { name } }, (r) => ensureResourceIdNotNull(r.resourceNameGet.resourceId));
372
+ }
373
+ checkResourceNameExists(name) {
374
+ return this.sendSingleAndParse({ oneofKind: 'resourceNameExists', resourceNameExists: { name } }, (r) => r.resourceNameExists.exists);
375
+ }
376
+ removeResource(rId) {
377
+ this.sendVoidAsync({ oneofKind: 'resourceRemove', resourceRemove: { id: rId } });
338
378
  }
339
- const result = await this.sendSingleAndParse({
340
- oneofKind: 'resourceGet',
341
- resourceGet: { resourceId: toResourceId(rId), loadFields: loadFields },
342
- }, (r) => protoToResource(notEmpty(r.resourceGet.resource)));
343
- this._stat.rGetDataNetRequests++;
344
- this._stat.rGetDataNetBytes += result.data?.length ?? 0;
345
- this._stat.rGetDataNetFields += result.fields.length;
346
- // we will cache only final resource data states
347
- // caching result even if we were ignore the cache
348
- if (!isResourceRef(rId) && !isLocalResourceId(rId) && this.finalPredicate(result)) {
349
- deepFreeze(result);
350
- const fromCache = this.sharedResourceDataCache.get(rId);
351
- if (fromCache) {
352
- if (loadFields && !fromCache.data) {
353
- fromCache.data = result;
354
- // updating timestamp becuse we updated the record
355
- fromCache.cacheTxOpenTimestamp = this.txOpenTimestamp;
379
+ resourceExists(rId) {
380
+ return this.sendSingleAndParse({ oneofKind: 'resourceExists', resourceExists: { resourceId: rId } }, (r) => r.resourceExists.exists);
381
+ }
382
+ async getResourceData(rId, loadFields = true, ignoreCache = false) {
383
+ if (!ignoreCache && !isResourceRef(rId) && !isLocalResourceId(rId)) {
384
+ // checking if we can return result from cache
385
+ const fromCache = this.sharedResourceDataCache.get(rId);
386
+ if (fromCache && fromCache.cacheTxOpenTimestamp < this.txOpenTimestamp) {
387
+ if (!loadFields) {
388
+ this._stat.rGetDataCacheHits++;
389
+ this._stat.rGetDataCacheBytes += fromCache.basicData.data?.length ?? 0;
390
+ return fromCache.basicData;
391
+ }
392
+ else if (fromCache.data) {
393
+ this._stat.rGetDataCacheHits++;
394
+ this._stat.rGetDataCacheBytes += fromCache.basicData.data?.length ?? 0;
395
+ this._stat.rGetDataCacheFields += fromCache.data.fields.length;
396
+ return fromCache.data;
397
+ }
356
398
  }
357
399
  }
358
- else {
359
- const basicData = extractBasicResourceData(result);
360
- deepFreeze(basicData);
361
- if (loadFields)
362
- this.sharedResourceDataCache.set(rId, {
363
- basicData,
364
- data: result,
365
- cacheTxOpenTimestamp: this.txOpenTimestamp,
366
- });
367
- else
368
- this.sharedResourceDataCache.set(rId, {
369
- basicData,
370
- data: undefined,
371
- cacheTxOpenTimestamp: this.txOpenTimestamp,
372
- });
400
+ const result = await this.sendSingleAndParse({
401
+ oneofKind: 'resourceGet',
402
+ resourceGet: { resourceId: toResourceId(rId), loadFields: loadFields },
403
+ }, (r) => protoToResource(notEmpty(r.resourceGet.resource)));
404
+ this._stat.rGetDataNetRequests++;
405
+ this._stat.rGetDataNetBytes += result.data?.length ?? 0;
406
+ this._stat.rGetDataNetFields += result.fields.length;
407
+ // we will cache only final resource data states
408
+ // caching result even if we were ignore the cache
409
+ if (!isResourceRef(rId) && !isLocalResourceId(rId) && this.finalPredicate(result)) {
410
+ deepFreeze(result);
411
+ const fromCache = this.sharedResourceDataCache.get(rId);
412
+ if (fromCache) {
413
+ if (loadFields && !fromCache.data) {
414
+ fromCache.data = result;
415
+ // updating timestamp because we updated the record
416
+ fromCache.cacheTxOpenTimestamp = this.txOpenTimestamp;
417
+ }
418
+ }
419
+ else {
420
+ const basicData = extractBasicResourceData(result);
421
+ deepFreeze(basicData);
422
+ if (loadFields)
423
+ this.sharedResourceDataCache.set(rId, {
424
+ basicData,
425
+ data: result,
426
+ cacheTxOpenTimestamp: this.txOpenTimestamp,
427
+ });
428
+ else
429
+ this.sharedResourceDataCache.set(rId, {
430
+ basicData,
431
+ data: undefined,
432
+ cacheTxOpenTimestamp: this.txOpenTimestamp,
433
+ });
434
+ }
373
435
  }
436
+ return result;
374
437
  }
375
- return result;
376
- }
377
- async getResourceDataIfExists(rId, loadFields = true) {
378
- // calling this mehtod will ignore cache, because user intention is to detect resource absence
379
- // which cache will prevent
380
- const result = await notFoundToUndefined(async () => await this.getResourceData(rId, loadFields, true));
381
- // cleaning cache record if resorce was removed from the db
382
- if (result === undefined && !isResourceRef(rId) && !isLocalResourceId(rId))
383
- this.sharedResourceDataCache.delete(rId);
384
- return result;
385
- }
386
- /**
387
- * Inform platform that resource will not get any new input fields.
388
- * This is required, when client creates resource without schema and wants
389
- * controller to start calculations.
390
- * Most controllers will not start calculations even when all inputs
391
- * have their values, if inputs list is not locked.
392
- */
393
- lockInputs(rId) {
394
- this._stat.inputsLocked++;
395
- this.sendVoidAsync({
396
- oneofKind: 'resourceLockInputs',
397
- resourceLockInputs: { resourceId: toResourceId(rId) },
398
- });
399
- }
400
- /**
401
- * Inform platform that resource will not get any new output fields.
402
- * This is required for resource to pass deduplication.
403
- */
404
- lockOutputs(rId) {
405
- this._stat.outputsLocked++;
406
- this.sendVoidAsync({
407
- oneofKind: 'resourceLockOutputs',
408
- resourceLockOutputs: { resourceId: toResourceId(rId) },
409
- });
410
- }
411
- lock(rID) {
412
- this.lockInputs(rID);
413
- this.lockOutputs(rID);
414
- }
415
- setResourceError(rId, ref) {
416
- this.sendVoidAsync({
417
- oneofKind: 'resourceSetError',
418
- resourceSetError: { resourceId: toResourceId(rId), errorResourceId: toResourceId(ref) },
419
- });
420
- }
421
- //
422
- // Fields
423
- //
424
- createField(fId, fieldType, value) {
425
- this._stat.fieldsCreated++;
426
- this.sendVoidAsync({
427
- oneofKind: 'fieldCreate',
428
- fieldCreate: { type: fieldTypeToProto(fieldType), id: toFieldId(fId) },
429
- });
430
- if (value !== undefined)
431
- this.setField(fId, value);
432
- }
433
- async fieldExists(fId) {
434
- return await this.sendSingleAndParse({
435
- oneofKind: 'fieldExists',
436
- fieldExists: { field: toFieldId(fId) },
437
- }, (r) => r.fieldExists.exists);
438
- }
439
- setField(fId, ref) {
440
- this._stat.fieldsSet++;
441
- if (isResource(ref))
438
+ async getResourceDataIfExists(rId, loadFields = true) {
439
+ // calling this method will ignore cache, because user intention is to detect resource absence
440
+ // which cache will prevent
441
+ const result = await notFoundToUndefined(async () => await this.getResourceData(rId, loadFields, true));
442
+ // cleaning cache record if resource was removed from the db
443
+ if (result === undefined && !isResourceRef(rId) && !isLocalResourceId(rId))
444
+ this.sharedResourceDataCache.delete(rId);
445
+ return result;
446
+ }
447
+ /**
448
+ * Inform platform that resource will not get any new input fields.
449
+ * This is required, when client creates resource without schema and wants
450
+ * controller to start calculations.
451
+ * Most controllers will not start calculations even when all inputs
452
+ * have their values, if inputs list is not locked.
453
+ */
454
+ lockInputs(rId) {
455
+ this._stat.inputsLocked++;
456
+ this.sendVoidAsync({
457
+ oneofKind: 'resourceLockInputs',
458
+ resourceLockInputs: { resourceId: toResourceId(rId) },
459
+ });
460
+ }
461
+ /**
462
+ * Inform platform that resource will not get any new output fields.
463
+ * This is required for resource to pass deduplication.
464
+ */
465
+ lockOutputs(rId) {
466
+ this._stat.outputsLocked++;
442
467
  this.sendVoidAsync({
443
- oneofKind: 'fieldSet',
444
- fieldSet: {
445
- field: toFieldId(fId),
446
- value: {
447
- resourceId: toResourceId(ref),
448
- fieldName: '', // default value, read as undefined
468
+ oneofKind: 'resourceLockOutputs',
469
+ resourceLockOutputs: { resourceId: toResourceId(rId) },
470
+ });
471
+ }
472
+ lock(rID) {
473
+ this.lockInputs(rID);
474
+ this.lockOutputs(rID);
475
+ }
476
+ setResourceError(rId, ref) {
477
+ this.sendVoidAsync({
478
+ oneofKind: 'resourceSetError',
479
+ resourceSetError: { resourceId: toResourceId(rId), errorResourceId: toResourceId(ref) },
480
+ });
481
+ }
482
+ //
483
+ // Fields
484
+ //
485
+ createField(fId, fieldType, value) {
486
+ this._stat.fieldsCreated++;
487
+ this.sendVoidAsync({
488
+ oneofKind: 'fieldCreate',
489
+ fieldCreate: { type: fieldTypeToProto(fieldType), id: toFieldId(fId) },
490
+ });
491
+ if (value !== undefined)
492
+ this.setField(fId, value);
493
+ }
494
+ fieldExists(fId) {
495
+ return this.sendSingleAndParse({
496
+ oneofKind: 'fieldExists',
497
+ fieldExists: { field: toFieldId(fId) },
498
+ }, (r) => r.fieldExists.exists);
499
+ }
500
+ setField(fId, ref) {
501
+ this._stat.fieldsSet++;
502
+ if (isResource(ref))
503
+ this.sendVoidAsync({
504
+ oneofKind: 'fieldSet',
505
+ fieldSet: {
506
+ field: toFieldId(fId),
507
+ value: {
508
+ resourceId: toResourceId(ref),
509
+ fieldName: '', // default value, read as undefined
510
+ },
449
511
  },
512
+ });
513
+ else
514
+ this.sendVoidAsync({
515
+ oneofKind: 'fieldSet',
516
+ fieldSet: {
517
+ field: toFieldId(fId),
518
+ value: toFieldId(ref),
519
+ },
520
+ });
521
+ }
522
+ setFieldError(fId, ref) {
523
+ this._stat.fieldsSet++;
524
+ this.sendVoidAsync({
525
+ oneofKind: 'fieldSetError',
526
+ fieldSetError: { field: toFieldId(fId), errResourceId: toResourceId(ref) },
527
+ });
528
+ }
529
+ getField(fId) {
530
+ this._stat.fieldsGet++;
531
+ return this.sendSingleAndParse({ oneofKind: 'fieldGet', fieldGet: { field: toFieldId(fId) } }, (r) => protoToField(notEmpty(r.fieldGet.field)));
532
+ }
533
+ async getFieldIfExists(fId) {
534
+ return notFoundToUndefined(async () => await this.getField(fId));
535
+ }
536
+ resetField(fId) {
537
+ this.sendVoidAsync({ oneofKind: 'fieldReset', fieldReset: { field: toFieldId(fId) } });
538
+ }
539
+ removeField(fId) {
540
+ this.sendVoidAsync({ oneofKind: 'fieldRemove', fieldRemove: { field: toFieldId(fId) } });
541
+ }
542
+ //
543
+ // KV
544
+ //
545
+ async listKeyValues(rId) {
546
+ const result = await this.sendMultiAndParse({
547
+ oneofKind: 'resourceKeyValueList',
548
+ resourceKeyValueList: { resourceId: toResourceId(rId), startFrom: '', limit: 0 },
549
+ }, (r) => r.map((e) => e.resourceKeyValueList.record));
550
+ this._stat.kvListRequests++;
551
+ this._stat.kvListEntries += result.length;
552
+ for (const kv of result)
553
+ this._stat.kvListBytes += kv.key.length + kv.value.length;
554
+ return result;
555
+ }
556
+ async listKeyValuesString(rId) {
557
+ return (await this.listKeyValues(rId)).map(({ key, value }) => ({
558
+ key,
559
+ value: Buffer.from(value).toString(),
560
+ }));
561
+ }
562
+ async listKeyValuesIfResourceExists(rId) {
563
+ return notFoundToUndefined(async () => await this.listKeyValues(rId));
564
+ }
565
+ async listKeyValuesStringIfResourceExists(rId) {
566
+ return notFoundToUndefined(async () => await this.listKeyValuesString(rId));
567
+ }
568
+ setKValue(rId, key, value) {
569
+ this._stat.kvSetRequests++;
570
+ this._stat.kvSetBytes++;
571
+ this.sendVoidAsync({
572
+ oneofKind: 'resourceKeyValueSet',
573
+ resourceKeyValueSet: {
574
+ resourceId: toResourceId(rId),
575
+ key,
576
+ value: toBytes(value),
450
577
  },
451
578
  });
452
- else
579
+ }
580
+ deleteKValue(rId, key) {
453
581
  this.sendVoidAsync({
454
- oneofKind: 'fieldSet',
455
- fieldSet: {
456
- field: toFieldId(fId),
457
- value: toFieldId(ref),
582
+ oneofKind: 'resourceKeyValueDelete',
583
+ resourceKeyValueDelete: {
584
+ resourceId: toResourceId(rId),
585
+ key,
458
586
  },
459
587
  });
460
- }
461
- setFieldError(fId, ref) {
462
- this._stat.fieldsSet++;
463
- this.sendVoidAsync({
464
- oneofKind: 'fieldSetError',
465
- fieldSetError: { field: toFieldId(fId), errResourceId: toResourceId(ref) },
466
- });
467
- }
468
- async getField(fId) {
469
- this._stat.fieldsGet++;
470
- return await this.sendSingleAndParse({ oneofKind: 'fieldGet', fieldGet: { field: toFieldId(fId) } }, (r) => protoToField(notEmpty(r.fieldGet.field)));
471
- }
472
- async getFieldIfExists(fId) {
473
- return notFoundToUndefined(async () => await this.getField(fId));
474
- }
475
- resetField(fId) {
476
- this.sendVoidAsync({ oneofKind: 'fieldReset', fieldReset: { field: toFieldId(fId) } });
477
- }
478
- removeField(fId) {
479
- this.sendVoidAsync({ oneofKind: 'fieldRemove', fieldRemove: { field: toFieldId(fId) } });
480
- }
481
- //
482
- // KV
483
- //
484
- async listKeyValues(rId) {
485
- const result = await this.sendMultiAndParse({
486
- oneofKind: 'resourceKeyValueList',
487
- resourceKeyValueList: { resourceId: toResourceId(rId), startFrom: '', limit: 0 },
488
- }, (r) => r.map((e) => e.resourceKeyValueList.record));
489
- this._stat.kvListRequests++;
490
- this._stat.kvListEntries += result.length;
491
- for (const kv of result)
492
- this._stat.kvListBytes += kv.key.length + kv.value.length;
493
- return result;
494
- }
495
- async listKeyValuesString(rId) {
496
- return (await this.listKeyValues(rId)).map(({ key, value }) => ({
497
- key,
498
- value: Buffer.from(value).toString(),
499
- }));
500
- }
501
- async listKeyValuesIfResourceExists(rId) {
502
- return notFoundToUndefined(async () => await this.listKeyValues(rId));
503
- }
504
- async listKeyValuesStringIfResourceExists(rId) {
505
- return notFoundToUndefined(async () => await this.listKeyValuesString(rId));
506
- }
507
- setKValue(rId, key, value) {
508
- this._stat.kvSetRequests++;
509
- this._stat.kvSetBytes++;
510
- this.sendVoidAsync({
511
- oneofKind: 'resourceKeyValueSet',
512
- resourceKeyValueSet: {
513
- resourceId: toResourceId(rId),
514
- key,
515
- value: toBytes(value),
516
- },
517
- });
518
- }
519
- deleteKValue(rId, key) {
520
- this.sendVoidAsync({
521
- oneofKind: 'resourceKeyValueDelete',
522
- resourceKeyValueDelete: {
523
- resourceId: toResourceId(rId),
524
- key,
525
- },
526
- });
527
- }
528
- async getKValue(rId, key) {
529
- const result = await this.sendSingleAndParse({
530
- oneofKind: 'resourceKeyValueGet',
531
- resourceKeyValueGet: { resourceId: toResourceId(rId), key },
532
- }, (r) => r.resourceKeyValueGet.value);
533
- this._stat.kvGetRequests++;
534
- this._stat.kvGetBytes += result.length;
535
- return result;
536
- }
537
- async getKValueString(rId, key) {
538
- return Buffer.from(await this.getKValue(rId, key)).toString();
539
- }
540
- async getKValueJson(rId, key) {
541
- return JSON.parse(await this.getKValueString(rId, key));
542
- }
543
- async getKValueIfExists(rId, key) {
544
- const result = await this.sendSingleAndParse({
545
- oneofKind: 'resourceKeyValueGetIfExists',
546
- resourceKeyValueGetIfExists: { resourceId: toResourceId(rId), key },
547
- }, (r) => r.resourceKeyValueGetIfExists.exists ? r.resourceKeyValueGetIfExists.value : undefined);
548
- this._stat.kvGetRequests++;
549
- this._stat.kvGetBytes += result?.length ?? 0;
550
- return result;
551
- }
552
- async getKValueStringIfExists(rId, key) {
553
- const data = await this.getKValueIfExists(rId, key);
554
- return data === undefined ? undefined : Buffer.from(data).toString();
555
- }
556
- async getKValueJsonIfExists(rId, key) {
557
- const str = await this.getKValueString(rId, key);
558
- if (str === undefined)
559
- return undefined;
560
- return JSON.parse(str);
561
- }
562
- //
563
- // Cache
564
- //
565
- // TODO
566
- //
567
- // High level ops
568
- //
569
- /** Resolves existing or create first level resource from */
570
- getFutureFieldValue(rId, fieldName, fieldType) {
571
- const data = Buffer.from(JSON.stringify({ fieldName, fieldType }));
572
- const getFieldResource = this.createEphemeral({ name: 'json/getField', version: '1' }, data);
573
- this.setField({ resourceId: getFieldResource, fieldName: 'resource' }, rId);
574
- return { resourceId: getFieldResource, fieldName: 'result' };
575
- }
576
- //
577
- // Technical
578
- //
579
- async getGlobalTxId() {
580
- this.globalTxIdWasAwaited = true;
581
- return await this.globalTxId;
582
- }
583
- /** Closes output event stream */
584
- async complete() {
585
- if (this._completed)
586
- return;
587
- this._completed = true;
588
- const completeResult = this.ll.complete();
589
- await this.drainAndAwaitPendingOps();
590
- await completeResult;
591
- }
592
- /** Await incoming message loop termination and throw
593
- * any leftover errors if it was unsuccessful */
594
- async await() {
595
- await this.ll.await();
596
- }
597
- //
598
- // Helpers
599
- //
600
- nextLocalResourceId(root) {
601
- return createLocalResourceId(root, ++this.localResourceIdCounter, this.localTxId);
602
- }
603
- static localTxIdCounter = 0;
604
- static nextLocalTxId() {
605
- PlTransaction.localTxIdCounter++;
606
- if (PlTransaction.localTxIdCounter === MaxTxId)
607
- PlTransaction.localTxIdCounter = 1;
608
- return PlTransaction.localTxIdCounter;
609
- }
610
- }
588
+ }
589
+ async getKValue(rId, key) {
590
+ const result = await this.sendSingleAndParse({
591
+ oneofKind: 'resourceKeyValueGet',
592
+ resourceKeyValueGet: { resourceId: toResourceId(rId), key },
593
+ }, (r) => r.resourceKeyValueGet.value);
594
+ this._stat.kvGetRequests++;
595
+ this._stat.kvGetBytes += result.length;
596
+ return result;
597
+ }
598
+ async getKValueString(rId, key) {
599
+ return Buffer.from(await this.getKValue(rId, key)).toString();
600
+ }
601
+ async getKValueJson(rId, key) {
602
+ return JSON.parse(await this.getKValueString(rId, key));
603
+ }
604
+ async getKValueIfExists(rId, key) {
605
+ const result = await this.sendSingleAndParse({
606
+ oneofKind: 'resourceKeyValueGetIfExists',
607
+ resourceKeyValueGetIfExists: { resourceId: toResourceId(rId), key },
608
+ }, (r) => r.resourceKeyValueGetIfExists.exists ? r.resourceKeyValueGetIfExists.value : undefined);
609
+ this._stat.kvGetRequests++;
610
+ this._stat.kvGetBytes += result?.length ?? 0;
611
+ return result;
612
+ }
613
+ async getKValueStringIfExists(rId, key) {
614
+ const data = await this.getKValueIfExists(rId, key);
615
+ return data === undefined ? undefined : Buffer.from(data).toString();
616
+ }
617
+ async getKValueJsonIfExists(rId, key) {
618
+ const str = await this.getKValueString(rId, key);
619
+ if (str === undefined)
620
+ return undefined;
621
+ return JSON.parse(str);
622
+ }
623
+ //
624
+ // Cache
625
+ //
626
+ // TODO
627
+ //
628
+ // High level ops
629
+ //
630
+ /** Resolves existing or create first level resource from */
631
+ getFutureFieldValue(rId, fieldName, fieldType) {
632
+ const data = Buffer.from(JSON.stringify({ fieldName, fieldType }));
633
+ const getFieldResource = this.createEphemeral({ name: 'json/getField', version: '1' }, data);
634
+ this.setField({ resourceId: getFieldResource, fieldName: 'resource' }, rId);
635
+ return { resourceId: getFieldResource, fieldName: 'result' };
636
+ }
637
+ //
638
+ // Technical
639
+ //
640
+ async getGlobalTxId() {
641
+ this.globalTxIdWasAwaited = true;
642
+ return await this.globalTxId;
643
+ }
644
+ /** Closes output event stream */
645
+ async complete() {
646
+ if (this._completed)
647
+ return;
648
+ this._completed = true;
649
+ const completeResult = this.track(this.ll.complete());
650
+ await this.drainAndAwaitPendingOps();
651
+ await completeResult;
652
+ }
653
+ /** Await incoming message loop termination and throw
654
+ * any leftover errors if it was unsuccessful */
655
+ async await() {
656
+ await this.ll.await();
657
+ }
658
+ //
659
+ // Helpers
660
+ //
661
+ nextLocalResourceId(root) {
662
+ return createLocalResourceId(root, ++this.localResourceIdCounter, this.localTxId);
663
+ }
664
+ static localTxIdCounter = 0;
665
+ static nextLocalTxId() {
666
+ PlTransaction.localTxIdCounter++;
667
+ if (PlTransaction.localTxIdCounter === MaxTxId)
668
+ PlTransaction.localTxIdCounter = 1;
669
+ return PlTransaction.localTxIdCounter;
670
+ }
671
+ };
672
+ })();
611
673
 
612
674
  export { PlTransaction, TxCommitConflict, field, isField, isFieldRef, isResource, isResourceId, isResourceRef, toFieldId, toGlobalFieldId, toGlobalResourceId, toResourceId };
613
675
  //# sourceMappingURL=transaction.js.map