@milaboratories/pl-client 3.4.1 → 3.5.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/dist/core/final.cjs.map +1 -1
- package/dist/core/final.js.map +1 -1
- package/dist/core/ll_client.cjs +7 -1
- package/dist/core/ll_client.cjs.map +1 -1
- package/dist/core/ll_client.d.ts.map +1 -1
- package/dist/core/ll_client.js +7 -1
- package/dist/core/ll_client.js.map +1 -1
- package/dist/core/ll_transaction.cjs +151 -26
- package/dist/core/ll_transaction.cjs.map +1 -1
- package/dist/core/ll_transaction.d.ts +1 -0
- package/dist/core/ll_transaction.d.ts.map +1 -1
- package/dist/core/ll_transaction.js +151 -26
- package/dist/core/ll_transaction.js.map +1 -1
- package/dist/core/transaction.cjs +89 -0
- package/dist/core/transaction.cjs.map +1 -1
- package/dist/core/transaction.d.ts +47 -1
- package/dist/core/transaction.d.ts.map +1 -1
- package/dist/core/transaction.js +90 -1
- package/dist/core/transaction.js.map +1 -1
- package/dist/core/tree_filter.cjs +106 -0
- package/dist/core/tree_filter.cjs.map +1 -0
- package/dist/core/tree_filter.d.ts +85 -0
- package/dist/core/tree_filter.d.ts.map +1 -0
- package/dist/core/tree_filter.js +106 -0
- package/dist/core/tree_filter.js.map +1 -0
- package/dist/core/type_conversion.cjs +1 -0
- package/dist/core/type_conversion.cjs.map +1 -1
- package/dist/core/type_conversion.js +1 -1
- package/dist/core/type_conversion.js.map +1 -1
- package/dist/index.cjs +5 -0
- package/dist/index.d.ts +4 -2
- package/dist/index.js +3 -1
- package/dist/proto-grpc/github.com/googleapis/googleapis/google/rpc/status.cjs.map +1 -1
- package/dist/proto-grpc/github.com/googleapis/googleapis/google/rpc/status.js.map +1 -1
- package/dist/proto-grpc/github.com/milaboratory/pl/plapi/plapiproto/api.cjs +450 -4
- package/dist/proto-grpc/github.com/milaboratory/pl/plapi/plapiproto/api.cjs.map +1 -1
- package/dist/proto-grpc/github.com/milaboratory/pl/plapi/plapiproto/api.d.ts +328 -2
- package/dist/proto-grpc/github.com/milaboratory/pl/plapi/plapiproto/api.d.ts.map +1 -1
- package/dist/proto-grpc/github.com/milaboratory/pl/plapi/plapiproto/api.js +449 -5
- package/dist/proto-grpc/github.com/milaboratory/pl/plapi/plapiproto/api.js.map +1 -1
- package/dist/proto-grpc/google/protobuf/timestamp.cjs.map +1 -1
- package/dist/proto-grpc/google/protobuf/timestamp.d.ts +9 -8
- package/dist/proto-grpc/google/protobuf/timestamp.d.ts.map +1 -1
- package/dist/proto-grpc/google/protobuf/timestamp.js.map +1 -1
- package/dist/proto-grpc/google/rpc/code.cjs.map +1 -1
- package/dist/proto-grpc/google/rpc/code.js.map +1 -1
- package/package.json +5 -5
- package/src/core/final.ts +1 -1
- package/src/core/ll_client.ts +11 -1
- package/src/core/ll_transaction.test.ts +13 -18
- package/src/core/ll_transaction.ts +237 -60
- package/src/core/transaction.test.ts +38 -0
- package/src/core/transaction.ts +136 -1
- package/src/core/tree_filter.test.ts +217 -0
- package/src/core/tree_filter.ts +182 -0
- package/src/core/type_conversion.ts +1 -1
- package/src/index.ts +1 -0
- package/src/proto-grpc/github.com/googleapis/googleapis/google/rpc/status.ts +1 -1
- package/src/proto-grpc/github.com/milaboratory/pl/plapi/plapiproto/api.ts +604 -6
- package/src/proto-grpc/google/api/http.ts +1 -1
- package/src/proto-grpc/google/protobuf/descriptor.ts +242 -12
- package/src/proto-grpc/google/protobuf/timestamp.ts +9 -8
- package/src/proto-grpc/google/protobuf/wrappers.ts +38 -4
- package/src/proto-grpc/google/rpc/code.ts +1 -1
- package/src/proto-grpc/google/rpc/error_details.ts +5 -5
- package/src/proto-grpc/google/rpc/http.ts +1 -1
- package/src/proto-grpc/google/rpc/status.ts +1 -1
|
@@ -4,14 +4,96 @@ const require_StatefulPromise = require("./StatefulPromise.cjs");
|
|
|
4
4
|
let denque = require("denque");
|
|
5
5
|
denque = require_runtime.__toESM(denque);
|
|
6
6
|
//#region src/core/ll_transaction.ts
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
7
|
+
var AsyncMessageStream = class {
|
|
8
|
+
queue = new denque.default();
|
|
9
|
+
done = false;
|
|
10
|
+
cancelled = false;
|
|
11
|
+
failure;
|
|
12
|
+
waitingResolve;
|
|
13
|
+
waitingReject;
|
|
14
|
+
push(value) {
|
|
15
|
+
if (this.done || this.cancelled) return;
|
|
16
|
+
if (this.waitingResolve) {
|
|
17
|
+
const resolve = this.waitingResolve;
|
|
18
|
+
this.waitingResolve = void 0;
|
|
19
|
+
this.waitingReject = void 0;
|
|
20
|
+
resolve({
|
|
21
|
+
value,
|
|
22
|
+
done: false
|
|
23
|
+
});
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
this.queue.push(value);
|
|
27
|
+
}
|
|
28
|
+
end() {
|
|
29
|
+
if (this.done) return;
|
|
30
|
+
this.done = true;
|
|
31
|
+
if (this.waitingResolve) {
|
|
32
|
+
const resolve = this.waitingResolve;
|
|
33
|
+
this.waitingResolve = void 0;
|
|
34
|
+
this.waitingReject = void 0;
|
|
35
|
+
resolve({
|
|
36
|
+
value: void 0,
|
|
37
|
+
done: true
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
fail(error) {
|
|
42
|
+
if (this.done) return;
|
|
43
|
+
this.failure = error;
|
|
44
|
+
this.done = true;
|
|
45
|
+
if (this.waitingReject) {
|
|
46
|
+
const reject = this.waitingReject;
|
|
47
|
+
this.waitingResolve = void 0;
|
|
48
|
+
this.waitingReject = void 0;
|
|
49
|
+
reject(error);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
cancel() {
|
|
53
|
+
this.cancelled = true;
|
|
54
|
+
this.done = true;
|
|
55
|
+
this.queue.clear();
|
|
56
|
+
if (this.waitingResolve) {
|
|
57
|
+
const resolve = this.waitingResolve;
|
|
58
|
+
this.waitingResolve = void 0;
|
|
59
|
+
this.waitingReject = void 0;
|
|
60
|
+
resolve({
|
|
61
|
+
value: void 0,
|
|
62
|
+
done: true
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
async next() {
|
|
67
|
+
if (this.failure) throw this.failure;
|
|
68
|
+
const value = this.queue.shift();
|
|
69
|
+
if (value !== void 0) return {
|
|
70
|
+
value,
|
|
71
|
+
done: false
|
|
72
|
+
};
|
|
73
|
+
if (this.done) return {
|
|
74
|
+
value: void 0,
|
|
75
|
+
done: true
|
|
76
|
+
};
|
|
77
|
+
return await new Promise((resolve, reject) => {
|
|
78
|
+
this.waitingResolve = resolve;
|
|
79
|
+
this.waitingReject = reject;
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
async return() {
|
|
83
|
+
this.cancel();
|
|
84
|
+
return {
|
|
85
|
+
value: void 0,
|
|
86
|
+
done: true
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
async throw(error) {
|
|
90
|
+
this.cancel();
|
|
91
|
+
throw error;
|
|
92
|
+
}
|
|
93
|
+
[Symbol.asyncIterator]() {
|
|
94
|
+
return this;
|
|
95
|
+
}
|
|
96
|
+
};
|
|
15
97
|
function isRecoverable(status) {
|
|
16
98
|
return status.code === require_errors.PlErrorCodeNotFound;
|
|
17
99
|
}
|
|
@@ -62,6 +144,7 @@ var LLPlTransaction = class {
|
|
|
62
144
|
let expectedId = -1;
|
|
63
145
|
let currentHandler = void 0;
|
|
64
146
|
let responseAggregator = void 0;
|
|
147
|
+
let currentMultiIdx = 0;
|
|
65
148
|
try {
|
|
66
149
|
for await (const message of this.stream.responses) {
|
|
67
150
|
if (currentHandler === void 0) {
|
|
@@ -72,7 +155,8 @@ var LLPlTransaction = class {
|
|
|
72
155
|
});
|
|
73
156
|
break;
|
|
74
157
|
}
|
|
75
|
-
if (currentHandler.
|
|
158
|
+
if (currentHandler.mode === "multiBuffered") responseAggregator = [];
|
|
159
|
+
currentMultiIdx = 0;
|
|
76
160
|
expectedId++;
|
|
77
161
|
}
|
|
78
162
|
if (message.requestId !== expectedId) {
|
|
@@ -85,9 +169,11 @@ var LLPlTransaction = class {
|
|
|
85
169
|
if (message.error !== void 0) {
|
|
86
170
|
const status = message.error;
|
|
87
171
|
if (isRecoverable(status)) {
|
|
88
|
-
|
|
172
|
+
const recoverableError = new RethrowError(() => {
|
|
89
173
|
throw new require_errors.RecoverablePlError(status);
|
|
90
|
-
})
|
|
174
|
+
});
|
|
175
|
+
if (currentHandler.mode === "single" || currentHandler.mode === "multiBuffered") currentHandler.reject(recoverableError);
|
|
176
|
+
else currentHandler.stream.fail(recoverableError);
|
|
91
177
|
currentHandler = void 0;
|
|
92
178
|
if (message.multiMessage !== void 0 && !message.multiMessage.isLast) {
|
|
93
179
|
this.assignErrorFactoryIfNotSet(() => {
|
|
@@ -99,7 +185,8 @@ var LLPlTransaction = class {
|
|
|
99
185
|
} else {
|
|
100
186
|
this.assignErrorFactoryIfNotSet(() => {
|
|
101
187
|
throw new require_errors.UnrecoverablePlError(status);
|
|
102
|
-
}, currentHandler.reject);
|
|
188
|
+
}, currentHandler.mode === "single" || currentHandler.mode === "multiBuffered" ? currentHandler.reject : void 0);
|
|
189
|
+
if (currentHandler.mode === "multiStream") currentHandler.stream.fail(new require_errors.UnrecoverablePlError(status));
|
|
103
190
|
currentHandler = void 0;
|
|
104
191
|
break;
|
|
105
192
|
}
|
|
@@ -108,33 +195,41 @@ var LLPlTransaction = class {
|
|
|
108
195
|
const errorMessage = `inconsistent request response types: ${currentHandler.kind} !== ${message.response.oneofKind}`;
|
|
109
196
|
this.assignErrorFactoryIfNotSet(() => {
|
|
110
197
|
throw new Error(errorMessage);
|
|
111
|
-
}, currentHandler.reject);
|
|
198
|
+
}, currentHandler.mode === "single" || currentHandler.mode === "multiBuffered" ? currentHandler.reject : void 0);
|
|
199
|
+
if (currentHandler.mode === "multiStream") currentHandler.stream.fail(new Error(errorMessage));
|
|
112
200
|
currentHandler = void 0;
|
|
113
201
|
break;
|
|
114
202
|
}
|
|
115
|
-
|
|
116
|
-
|
|
203
|
+
const expectMultiResponse = currentHandler.mode === "multiBuffered" || currentHandler.mode === "multiStream";
|
|
204
|
+
if (expectMultiResponse !== (message.multiMessage !== void 0)) {
|
|
205
|
+
const errorMessage = `inconsistent multi state: ${expectMultiResponse} !== ${message.multiMessage !== void 0}`;
|
|
117
206
|
this.assignErrorFactoryIfNotSet(() => {
|
|
118
207
|
throw new Error(errorMessage);
|
|
119
|
-
}, currentHandler.reject);
|
|
208
|
+
}, currentHandler.mode === "single" || currentHandler.mode === "multiBuffered" ? currentHandler.reject : void 0);
|
|
209
|
+
if (currentHandler.mode === "multiStream") currentHandler.stream.fail(new Error(errorMessage));
|
|
120
210
|
currentHandler = void 0;
|
|
121
211
|
break;
|
|
122
212
|
}
|
|
123
213
|
if (message.multiMessage !== void 0) {
|
|
124
214
|
if (!message.multiMessage.isEmpty) {
|
|
125
|
-
if (message.multiMessage.id !==
|
|
126
|
-
const errorMessage = `inconsistent multi id: ${message.multiMessage.id} !== ${
|
|
215
|
+
if (message.multiMessage.id !== currentMultiIdx + 1) {
|
|
216
|
+
const errorMessage = `inconsistent multi id: ${message.multiMessage.id} !== ${currentMultiIdx + 1}`;
|
|
127
217
|
this.assignErrorFactoryIfNotSet(() => {
|
|
128
218
|
throw new Error(errorMessage);
|
|
129
|
-
}, currentHandler.reject);
|
|
219
|
+
}, currentHandler.mode === "multiBuffered" ? currentHandler.reject : void 0);
|
|
220
|
+
if (currentHandler.mode === "multiStream") currentHandler.stream.fail(new Error(errorMessage));
|
|
130
221
|
currentHandler = void 0;
|
|
131
222
|
break;
|
|
132
223
|
}
|
|
133
|
-
|
|
224
|
+
currentMultiIdx++;
|
|
225
|
+
if (currentHandler.mode === "multiBuffered") responseAggregator.push(message.response);
|
|
226
|
+
else if (currentHandler.mode === "multiStream") currentHandler.stream.push(message.response);
|
|
134
227
|
}
|
|
135
228
|
if (message.multiMessage.isLast) {
|
|
136
|
-
currentHandler.
|
|
137
|
-
|
|
229
|
+
if (currentHandler.mode === "multiBuffered") {
|
|
230
|
+
currentHandler.resolve(responseAggregator);
|
|
231
|
+
responseAggregator = void 0;
|
|
232
|
+
} else if (currentHandler.mode === "multiStream") currentHandler.stream.end();
|
|
138
233
|
currentHandler = void 0;
|
|
139
234
|
}
|
|
140
235
|
} else {
|
|
@@ -146,7 +241,7 @@ var LLPlTransaction = class {
|
|
|
146
241
|
} catch (e) {
|
|
147
242
|
return this.assignErrorFactoryIfNotSet(() => {
|
|
148
243
|
require_errors.rethrowMeaningfulError(e, true);
|
|
149
|
-
}, currentHandler
|
|
244
|
+
}, currentHandler && (currentHandler.mode === "single" || currentHandler.mode === "multiBuffered") ? currentHandler.reject : void 0);
|
|
150
245
|
} finally {
|
|
151
246
|
await this.close();
|
|
152
247
|
}
|
|
@@ -159,8 +254,9 @@ var LLPlTransaction = class {
|
|
|
159
254
|
while (true) {
|
|
160
255
|
const handler = this.responseHandlerQueue.shift();
|
|
161
256
|
if (!handler) break;
|
|
162
|
-
|
|
163
|
-
|
|
257
|
+
const noReplyError = this.errorFactory ? new RethrowError(this.errorFactory) : /* @__PURE__ */ new Error("no reply");
|
|
258
|
+
if (handler.mode === "single" || handler.mode === "multiBuffered") handler.reject(noReplyError);
|
|
259
|
+
else handler.stream.fail(noReplyError);
|
|
164
260
|
}
|
|
165
261
|
await this.stream.requests.complete();
|
|
166
262
|
}
|
|
@@ -181,7 +277,18 @@ var LLPlTransaction = class {
|
|
|
181
277
|
if (this.errorFactory) return Promise.reject(new RethrowError(this.errorFactory));
|
|
182
278
|
if (this.closed) return Promise.reject(/* @__PURE__ */ new Error("Transaction already closed"));
|
|
183
279
|
const result = require_StatefulPromise.StatefulPromise.fromDeferredReject(new Promise((resolve, reject) => {
|
|
184
|
-
this.responseHandlerQueue.push(
|
|
280
|
+
if (expectMultiResponse) this.responseHandlerQueue.push({
|
|
281
|
+
mode: "multiBuffered",
|
|
282
|
+
kind: r.oneofKind,
|
|
283
|
+
resolve,
|
|
284
|
+
reject
|
|
285
|
+
});
|
|
286
|
+
else this.responseHandlerQueue.push({
|
|
287
|
+
mode: "single",
|
|
288
|
+
kind: r.oneofKind,
|
|
289
|
+
resolve,
|
|
290
|
+
reject
|
|
291
|
+
});
|
|
185
292
|
}));
|
|
186
293
|
await this.stream.requests.send({
|
|
187
294
|
requestId: this.requestIdxCounter++,
|
|
@@ -194,6 +301,24 @@ var LLPlTransaction = class {
|
|
|
194
301
|
throw new Error("Error while waiting for response", { cause: e });
|
|
195
302
|
}
|
|
196
303
|
}
|
|
304
|
+
sendStream(r) {
|
|
305
|
+
if (this.errorFactory) throw new RethrowError(this.errorFactory);
|
|
306
|
+
if (this.closed) throw new Error("Transaction already closed");
|
|
307
|
+
const stream = new AsyncMessageStream();
|
|
308
|
+
this.responseHandlerQueue.push({
|
|
309
|
+
mode: "multiStream",
|
|
310
|
+
kind: r.oneofKind,
|
|
311
|
+
stream
|
|
312
|
+
});
|
|
313
|
+
this.stream.requests.send({
|
|
314
|
+
requestId: this.requestIdxCounter++,
|
|
315
|
+
request: r
|
|
316
|
+
}).catch((e) => {
|
|
317
|
+
if (e instanceof Error) stream.fail(e);
|
|
318
|
+
else stream.fail(new Error("Error while sending request", { cause: e }));
|
|
319
|
+
});
|
|
320
|
+
return stream;
|
|
321
|
+
}
|
|
197
322
|
_completed = false;
|
|
198
323
|
/** Safe to call multiple times */
|
|
199
324
|
async complete() {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ll_transaction.cjs","names":["PlErrorCodeNotFound","Denque","RecoverablePlError","UnrecoverablePlError","StatefulPromise"],"sources":["../../src/core/ll_transaction.ts"],"sourcesContent":["import type {\n TxAPI_ClientMessage,\n TxAPI_ServerMessage,\n} from \"../proto-grpc/github.com/milaboratory/pl/plapi/plapiproto/api\";\nimport type { BiDiStream } from \"./abstract_stream\";\nimport Denque from \"denque\";\nimport type { Status } from \"../proto-grpc/github.com/googleapis/googleapis/google/rpc/status\";\nimport {\n PlErrorCodeNotFound,\n RecoverablePlError,\n rethrowMeaningfulError,\n UnrecoverablePlError,\n} from \"./errors\";\nimport { StatefulPromise } from \"./StatefulPromise\";\n\nexport type ClientMessageRequest = TxAPI_ClientMessage[\"request\"];\n\nexport type ServerMessageResponse = TxAPI_ServerMessage[\"response\"];\n\ntype TxStream = BiDiStream<TxAPI_ClientMessage, TxAPI_ServerMessage>;\n\nexport type OneOfKind<T extends { oneofKind: unknown }, Kind extends T[\"oneofKind\"]> = Extract<\n T,\n { oneofKind: Kind }\n>;\n\ninterface SingleResponseHandler<Kind extends ServerMessageResponse[\"oneofKind\"]> {\n kind: Kind;\n expectMultiResponse: false;\n resolve: (v: OneOfKind<ServerMessageResponse, Kind>) => void;\n reject: (e: Error) => void;\n}\n\ninterface MultiResponseHandler<Kind extends ServerMessageResponse[\"oneofKind\"]> {\n kind: Kind;\n expectMultiResponse: true;\n resolve: (v: OneOfKind<ServerMessageResponse, Kind>[]) => void;\n reject: (e: Error) => void;\n}\n\ntype AnySingleResponseHandler = SingleResponseHandler<ServerMessageResponse[\"oneofKind\"]>;\n\ntype AnyMultiResponseHandler = MultiResponseHandler<ServerMessageResponse[\"oneofKind\"]>;\n\ntype AnyResponseHandler =\n | SingleResponseHandler<ServerMessageResponse[\"oneofKind\"]>\n | MultiResponseHandler<ServerMessageResponse[\"oneofKind\"]>;\n\nfunction createResponseHandler<Kind extends ServerMessageResponse[\"oneofKind\"]>(\n kind: Kind,\n expectMultiResponse: boolean,\n resolve:\n | ((v: OneOfKind<ServerMessageResponse, Kind>) => void)\n | ((v: OneOfKind<ServerMessageResponse, Kind>[]) => void),\n reject: (e: Error) => void,\n): AnyResponseHandler {\n return { kind, expectMultiResponse, resolve, reject } as AnyResponseHandler;\n}\n\nfunction isRecoverable(status: Status): boolean {\n return status.code === PlErrorCodeNotFound;\n}\n\nexport class RethrowError extends Error {\n name = \"RethrowError\";\n constructor(public readonly rethrowLambda: () => never) {\n super(\"Rethrow error, you should never see this one.\");\n }\n}\n\nexport class LLPlTransaction {\n /** Bidirectional channel through which transaction communicates with the server */\n private readonly stream: TxStream;\n\n /** Used to abort ongoing transaction stream */\n private readonly abortController = new AbortController();\n\n /** Counter of sent requests, used to calculate which future response will correspond to this request.\n * Incremented on each sent request. */\n private requestIdxCounter = 0;\n\n /** Queue from which incoming message processor takes handlers to which pass incoming messages */\n private readonly responseHandlerQueue = new Denque<AnyResponseHandler>();\n\n /** Each new resource, created by the transaction, is assigned with virtual (local) resource id, to make it possible\n * to populate its fields without awaiting actual resource id. This counter tracks those ids on client side, the\n * same way it is tracked on the server, so client can synchronously return such ids to the user. */\n private localResourceIdCounter = 0n;\n\n /** Switches to true, when this transaction closes due to normal or exceptional conditions. Prevents any new messages\n * to be sent to the stream. */\n private closed = false;\n /** Whether the outgoing stream was already closed. */\n private completed = false;\n\n /** If this transaction was terminated due to error, this is a generator to create new errors if corresponding response is required. */\n private errorFactory?: () => never;\n\n /** Timestamp when transaction was opened */\n private readonly openTimestamp = Date.now();\n\n private readonly incomingProcessorResult: Promise<(() => never) | null>;\n\n constructor(streamFactory: (abortSignal: AbortSignal) => TxStream) {\n this.stream = streamFactory(this.abortController.signal);\n\n // Starting incoming event processor\n this.incomingProcessorResult = this.incomingEventProcessor();\n }\n\n private assignErrorFactoryIfNotSet(\n errorFactory: () => never,\n reject?: (e: Error) => void,\n ): () => never {\n if (reject !== undefined) reject(new RethrowError(errorFactory));\n if (this.errorFactory) return errorFactory;\n this.errorFactory = errorFactory;\n return errorFactory;\n }\n\n private async incomingEventProcessor(): Promise<(() => never) | null> {\n /** Counter of received responses, used to check consistency of responses.\n * Increments on each received message. */\n let expectedId = -1;\n\n // defined externally to make possible to communicate any processing errors\n // to the specific request on which it happened\n let currentHandler: AnyResponseHandler | undefined = undefined;\n let responseAggregator: ServerMessageResponse[] | undefined = undefined;\n try {\n for await (const message of this.stream.responses) {\n if (currentHandler === undefined) {\n currentHandler = this.responseHandlerQueue.shift();\n\n if (currentHandler === undefined) {\n this.assignErrorFactoryIfNotSet(() => {\n throw new Error(`orphan incoming message`);\n });\n break;\n }\n\n // allocating response aggregator array\n if (currentHandler.expectMultiResponse) responseAggregator = [];\n\n expectedId++;\n }\n\n if (message.requestId !== expectedId) {\n const errorMessage = `out of order messages, ${message.requestId} !== ${expectedId}`;\n this.assignErrorFactoryIfNotSet(() => {\n throw new Error(errorMessage);\n });\n break;\n }\n\n if (message.error !== undefined) {\n const status = message.error;\n\n if (isRecoverable(status)) {\n currentHandler.reject(\n new RethrowError(() => {\n throw new RecoverablePlError(status);\n }),\n );\n currentHandler = undefined;\n\n if (message.multiMessage !== undefined && !message.multiMessage.isLast) {\n this.assignErrorFactoryIfNotSet(() => {\n throw new Error(\"Unexpected message sequence.\");\n });\n break;\n }\n\n // We can continue to work after recoverable errors\n continue;\n } else {\n this.assignErrorFactoryIfNotSet(() => {\n throw new UnrecoverablePlError(status);\n }, currentHandler.reject);\n currentHandler = undefined;\n\n // In case of unrecoverable errors we close the transaction\n break;\n }\n }\n\n if (\n currentHandler.kind !== message.response.oneofKind &&\n message?.multiMessage?.isEmpty !== true\n ) {\n const errorMessage = `inconsistent request response types: ${currentHandler.kind} !== ${message.response.oneofKind}`;\n\n this.assignErrorFactoryIfNotSet(() => {\n throw new Error(errorMessage);\n }, currentHandler.reject);\n currentHandler = undefined;\n\n break;\n }\n\n if (currentHandler.expectMultiResponse !== (message.multiMessage !== undefined)) {\n const errorMessage = `inconsistent multi state: ${currentHandler.expectMultiResponse} !== ${message.multiMessage !== undefined}`;\n\n this.assignErrorFactoryIfNotSet(() => {\n throw new Error(errorMessage);\n }, currentHandler.reject);\n currentHandler = undefined;\n\n break;\n }\n\n // <- at this point we validated everything we can at this level\n\n if (message.multiMessage !== undefined) {\n if (!message.multiMessage.isEmpty) {\n if (message.multiMessage.id !== responseAggregator!.length + 1) {\n const errorMessage = `inconsistent multi id: ${message.multiMessage.id} !== ${responseAggregator!.length + 1}`;\n\n this.assignErrorFactoryIfNotSet(() => {\n throw new Error(errorMessage);\n }, currentHandler.reject);\n currentHandler = undefined;\n\n break;\n }\n\n responseAggregator!.push(message.response);\n }\n\n if (message.multiMessage.isLast) {\n (currentHandler as AnyMultiResponseHandler).resolve(responseAggregator!);\n responseAggregator = undefined;\n currentHandler = undefined;\n }\n } else {\n (currentHandler as AnySingleResponseHandler).resolve(message.response);\n currentHandler = undefined;\n }\n\n // After receiving a terminal response (txCommit or txDiscard), we proactively close the client stream.\n // This ensures consistent behavior between the gRPC and WebSocket transports,\n // since the server closes the connection automatically upon transaction completion in both cases.\n if (this.isTerminalResponse(message) && this.responseHandlerQueue.length === 0) {\n await this.stream.requests.complete();\n }\n }\n } catch (e: any) {\n return this.assignErrorFactoryIfNotSet(() => {\n rethrowMeaningfulError(e, true);\n }, currentHandler?.reject);\n } finally {\n await this.close();\n }\n return null;\n }\n\n /** Executed after termination of incoming message processor */\n private async close(): Promise<void> {\n if (this.closed) return;\n\n this.closed = true;\n\n // Rejecting all messages\n\n while (true) {\n const handler = this.responseHandlerQueue.shift();\n if (!handler) break;\n if (this.errorFactory) handler.reject(new RethrowError(this.errorFactory));\n else handler.reject(new Error(\"no reply\"));\n }\n\n // closing outgoing stream\n await this.stream.requests.complete();\n }\n\n /** Forcefully close the transaction, terminate all connections and reject all pending requests */\n public abort(cause?: Error) {\n this.assignErrorFactoryIfNotSet(() => {\n throw new Error(`transaction aborted`, { cause });\n });\n this.abortController.abort(cause);\n }\n\n /** Await incoming message loop termination and throw any leftover errors if it was unsuccessful */\n public async await(): Promise<void> {\n // for those who want to understand \"why?\":\n // this way there is no hanging promise that will complete with rejection\n // until await is implicitly requested, the this.incomingProcessorResult\n // always resolves with success\n\n const processingResult = await this.incomingProcessorResult;\n if (processingResult !== null) processingResult();\n }\n\n public async send<Kind extends ClientMessageRequest[\"oneofKind\"]>(\n r: OneOfKind<ClientMessageRequest, Kind>,\n expectMultiResponse: false,\n ): Promise<OneOfKind<ServerMessageResponse, Kind>>;\n public async send<Kind extends ClientMessageRequest[\"oneofKind\"]>(\n r: OneOfKind<ClientMessageRequest, Kind>,\n expectMultiResponse: true,\n ): Promise<OneOfKind<ServerMessageResponse, Kind>[]>;\n /** Generate proper client message and send it to the server, and returns a promise of future response. */\n public async send<Kind extends ClientMessageRequest[\"oneofKind\"]>(\n r: OneOfKind<ClientMessageRequest, Kind>,\n expectMultiResponse: boolean,\n ): Promise<OneOfKind<ServerMessageResponse, Kind> | OneOfKind<ServerMessageResponse, Kind>[]> {\n if (this.errorFactory) return Promise.reject(new RethrowError(this.errorFactory));\n\n if (this.closed) return Promise.reject(new Error(\"Transaction already closed\"));\n\n // Note: Promise synchronously executes a callback passed to a constructor\n const result = StatefulPromise.fromDeferredReject(\n new Promise<OneOfKind<ServerMessageResponse, Kind>>((resolve, reject) => {\n this.responseHandlerQueue.push(\n createResponseHandler(r.oneofKind, expectMultiResponse, resolve, reject),\n );\n }),\n );\n\n // Awaiting message dispatch to catch any associated errors.\n // There is no hurry, we are not going to receive a response until message is sent.\n await this.stream.requests.send({\n requestId: this.requestIdxCounter++,\n request: r,\n });\n\n try {\n return await result;\n } catch (e: any) {\n if (e instanceof RethrowError) e.rethrowLambda();\n throw new Error(\"Error while waiting for response\", { cause: e });\n }\n }\n\n private _completed = false;\n\n /** Safe to call multiple times */\n public async complete() {\n if (this._completed) return;\n this._completed = true;\n await this.stream.requests.complete();\n }\n\n private isTerminalResponse(message: TxAPI_ServerMessage): boolean {\n const kind = message.response.oneofKind;\n return kind === \"txCommit\" || kind === \"txDiscard\";\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ll_transaction.cjs","names":["Denque","PlErrorCodeNotFound","RecoverablePlError","UnrecoverablePlError","StatefulPromise"],"sources":["../../src/core/ll_transaction.ts"],"sourcesContent":["import type {\n TxAPI_ClientMessage,\n TxAPI_ServerMessage,\n} from \"../proto-grpc/github.com/milaboratory/pl/plapi/plapiproto/api\";\nimport type { BiDiStream } from \"./abstract_stream\";\nimport Denque from \"denque\";\nimport type { Status } from \"../proto-grpc/github.com/googleapis/googleapis/google/rpc/status\";\nimport {\n PlErrorCodeNotFound,\n RecoverablePlError,\n rethrowMeaningfulError,\n UnrecoverablePlError,\n} from \"./errors\";\nimport { StatefulPromise } from \"./StatefulPromise\";\n\nexport type ClientMessageRequest = TxAPI_ClientMessage[\"request\"];\n\nexport type ServerMessageResponse = TxAPI_ServerMessage[\"response\"];\n\ntype TxStream = BiDiStream<TxAPI_ClientMessage, TxAPI_ServerMessage>;\n\nexport type OneOfKind<T extends { oneofKind: unknown }, Kind extends T[\"oneofKind\"]> = Extract<\n T,\n { oneofKind: Kind }\n>;\n\ninterface SingleResponseHandler {\n mode: \"single\";\n kind: ServerMessageResponse[\"oneofKind\"];\n resolve: (v: ServerMessageResponse) => void;\n reject: (e: Error) => void;\n}\n\ninterface MultiResponseHandler {\n mode: \"multiBuffered\";\n kind: ServerMessageResponse[\"oneofKind\"];\n resolve: (v: ServerMessageResponse[]) => void;\n reject: (e: Error) => void;\n}\n\ninterface MultiStreamResponseHandler {\n mode: \"multiStream\";\n kind: ServerMessageResponse[\"oneofKind\"];\n stream: AsyncMessageStream<ServerMessageResponse>;\n}\n\ntype AnySingleResponseHandler = SingleResponseHandler;\ntype AnyMultiStreamResponseHandler = MultiStreamResponseHandler;\n\ntype AnyResponseHandler =\n | AnySingleResponseHandler\n | MultiResponseHandler\n | AnyMultiStreamResponseHandler;\n\n// Implements both AsyncIterable and AsyncIterator: `[Symbol.asyncIterator]()` returns\n// `this`, so multiple `for await` loops over the same stream share state instead of\n// creating independent iterators that would race on `waitingResolve`/`waitingReject`.\n// Only one active consumer is supported; concurrent `next()` calls are not safe.\nclass AsyncMessageStream<T> implements AsyncIterable<T>, AsyncIterator<T> {\n private readonly queue = new Denque<T>();\n private done = false;\n private cancelled = false;\n private failure?: Error;\n private waitingResolve?: (value: IteratorResult<T>) => void;\n private waitingReject?: (reason?: unknown) => void;\n\n public push(value: T): void {\n if (this.done || this.cancelled) return;\n if (this.waitingResolve) {\n const resolve = this.waitingResolve;\n this.waitingResolve = undefined;\n this.waitingReject = undefined;\n resolve({ value, done: false });\n return;\n }\n this.queue.push(value);\n }\n\n public end(): void {\n if (this.done) return;\n this.done = true;\n if (this.waitingResolve) {\n const resolve = this.waitingResolve;\n this.waitingResolve = undefined;\n this.waitingReject = undefined;\n resolve({ value: undefined, done: true });\n }\n }\n\n // Fail-fast: any frames still buffered in `this.queue` are intentionally dropped.\n // `next()` checks `this.failure` before draining the queue, so consumers see the\n // error immediately. This differs from `end()`, which drains buffered frames first.\n // Rationale: when the upstream stream errors, the in-flight response is treated as\n // corrupt and partial frames must not be surfaced.\n public fail(error: Error): void {\n if (this.done) return;\n this.failure = error;\n this.done = true;\n if (this.waitingReject) {\n const reject = this.waitingReject;\n this.waitingResolve = undefined;\n this.waitingReject = undefined;\n reject(error);\n }\n }\n\n public cancel(): void {\n this.cancelled = true;\n this.done = true;\n this.queue.clear();\n if (this.waitingResolve) {\n const resolve = this.waitingResolve;\n this.waitingResolve = undefined;\n this.waitingReject = undefined;\n resolve({ value: undefined, done: true });\n }\n }\n\n public async next(): Promise<IteratorResult<T>> {\n if (this.failure) throw this.failure;\n\n const value = this.queue.shift();\n if (value !== undefined) return { value, done: false };\n\n if (this.done) return { value: undefined, done: true };\n\n return await new Promise<IteratorResult<T>>((resolve, reject) => {\n this.waitingResolve = resolve;\n this.waitingReject = reject;\n });\n }\n\n public async return(): Promise<IteratorResult<T>> {\n this.cancel();\n return { value: undefined, done: true };\n }\n\n public async throw(error?: unknown): Promise<IteratorResult<T>> {\n this.cancel();\n throw error;\n }\n\n [Symbol.asyncIterator](): this {\n return this;\n }\n}\n\nfunction isRecoverable(status: Status): boolean {\n return status.code === PlErrorCodeNotFound;\n}\n\nexport class RethrowError extends Error {\n name = \"RethrowError\";\n constructor(public readonly rethrowLambda: () => never) {\n super(\"Rethrow error, you should never see this one.\");\n }\n}\n\nexport class LLPlTransaction {\n /** Bidirectional channel through which transaction communicates with the server */\n private readonly stream: TxStream;\n\n /** Used to abort ongoing transaction stream */\n private readonly abortController = new AbortController();\n\n /** Counter of sent requests, used to calculate which future response will correspond to this request.\n * Incremented on each sent request. */\n private requestIdxCounter = 0;\n\n /** Queue from which incoming message processor takes handlers to which pass incoming messages */\n private readonly responseHandlerQueue = new Denque<AnyResponseHandler>();\n\n /** Each new resource, created by the transaction, is assigned with virtual (local) resource id, to make it possible\n * to populate its fields without awaiting actual resource id. This counter tracks those ids on client side, the\n * same way it is tracked on the server, so client can synchronously return such ids to the user. */\n private localResourceIdCounter = 0n;\n\n /** Switches to true, when this transaction closes due to normal or exceptional conditions. Prevents any new messages\n * to be sent to the stream. */\n private closed = false;\n /** Whether the outgoing stream was already closed. */\n private completed = false;\n\n /** If this transaction was terminated due to error, this is a generator to create new errors if corresponding response is required. */\n private errorFactory?: () => never;\n\n /** Timestamp when transaction was opened */\n private readonly openTimestamp = Date.now();\n\n private readonly incomingProcessorResult: Promise<(() => never) | null>;\n\n constructor(streamFactory: (abortSignal: AbortSignal) => TxStream) {\n this.stream = streamFactory(this.abortController.signal);\n\n // Starting incoming event processor\n this.incomingProcessorResult = this.incomingEventProcessor();\n }\n\n private assignErrorFactoryIfNotSet(\n errorFactory: () => never,\n reject?: (e: Error) => void,\n ): () => never {\n if (reject !== undefined) reject(new RethrowError(errorFactory));\n if (this.errorFactory) return errorFactory;\n this.errorFactory = errorFactory;\n return errorFactory;\n }\n\n private async incomingEventProcessor(): Promise<(() => never) | null> {\n /** Counter of received responses, used to check consistency of responses.\n * Increments on each received message. */\n let expectedId = -1;\n\n // defined externally to make possible to communicate any processing errors\n // to the specific request on which it happened\n let currentHandler: AnyResponseHandler | undefined = undefined;\n let responseAggregator: ServerMessageResponse[] | undefined = undefined;\n let currentMultiIdx = 0;\n try {\n for await (const message of this.stream.responses) {\n if (currentHandler === undefined) {\n currentHandler = this.responseHandlerQueue.shift();\n\n if (currentHandler === undefined) {\n this.assignErrorFactoryIfNotSet(() => {\n throw new Error(`orphan incoming message`);\n });\n break;\n }\n\n if (currentHandler.mode === \"multiBuffered\") responseAggregator = [];\n currentMultiIdx = 0;\n expectedId++;\n }\n\n if (message.requestId !== expectedId) {\n const errorMessage = `out of order messages, ${message.requestId} !== ${expectedId}`;\n this.assignErrorFactoryIfNotSet(() => {\n throw new Error(errorMessage);\n });\n break;\n }\n\n if (message.error !== undefined) {\n const status = message.error;\n\n if (isRecoverable(status)) {\n const recoverableError = new RethrowError(() => {\n throw new RecoverablePlError(status);\n });\n if (currentHandler.mode === \"single\" || currentHandler.mode === \"multiBuffered\") {\n currentHandler.reject(recoverableError);\n } else {\n currentHandler.stream.fail(recoverableError);\n }\n currentHandler = undefined;\n\n if (message.multiMessage !== undefined && !message.multiMessage.isLast) {\n this.assignErrorFactoryIfNotSet(() => {\n throw new Error(\"Unexpected message sequence.\");\n });\n break;\n }\n\n // We can continue to work after recoverable errors\n continue;\n } else {\n this.assignErrorFactoryIfNotSet(\n () => {\n throw new UnrecoverablePlError(status);\n },\n currentHandler.mode === \"single\" || currentHandler.mode === \"multiBuffered\"\n ? currentHandler.reject\n : undefined,\n );\n if (currentHandler.mode === \"multiStream\") {\n currentHandler.stream.fail(new UnrecoverablePlError(status));\n }\n currentHandler = undefined;\n\n // In case of unrecoverable errors we close the transaction\n break;\n }\n }\n\n if (\n currentHandler.kind !== message.response.oneofKind &&\n message?.multiMessage?.isEmpty !== true\n ) {\n const errorMessage = `inconsistent request response types: ${currentHandler.kind} !== ${message.response.oneofKind}`;\n\n this.assignErrorFactoryIfNotSet(\n () => {\n throw new Error(errorMessage);\n },\n currentHandler.mode === \"single\" || currentHandler.mode === \"multiBuffered\"\n ? currentHandler.reject\n : undefined,\n );\n if (currentHandler.mode === \"multiStream\") {\n currentHandler.stream.fail(new Error(errorMessage));\n }\n currentHandler = undefined;\n\n break;\n }\n\n const expectMultiResponse =\n currentHandler.mode === \"multiBuffered\" || currentHandler.mode === \"multiStream\";\n if (expectMultiResponse !== (message.multiMessage !== undefined)) {\n const errorMessage = `inconsistent multi state: ${expectMultiResponse} !== ${message.multiMessage !== undefined}`;\n\n this.assignErrorFactoryIfNotSet(\n () => {\n throw new Error(errorMessage);\n },\n currentHandler.mode === \"single\" || currentHandler.mode === \"multiBuffered\"\n ? currentHandler.reject\n : undefined,\n );\n if (currentHandler.mode === \"multiStream\") {\n currentHandler.stream.fail(new Error(errorMessage));\n }\n currentHandler = undefined;\n\n break;\n }\n\n // <- at this point we validated everything we can at this level\n\n if (message.multiMessage !== undefined) {\n if (!message.multiMessage.isEmpty) {\n if (message.multiMessage.id !== currentMultiIdx + 1) {\n const errorMessage = `inconsistent multi id: ${message.multiMessage.id} !== ${currentMultiIdx + 1}`;\n\n this.assignErrorFactoryIfNotSet(\n () => {\n throw new Error(errorMessage);\n },\n currentHandler.mode === \"multiBuffered\" ? currentHandler.reject : undefined,\n );\n if (currentHandler.mode === \"multiStream\") {\n currentHandler.stream.fail(new Error(errorMessage));\n }\n currentHandler = undefined;\n\n break;\n }\n\n currentMultiIdx++;\n if (currentHandler.mode === \"multiBuffered\") {\n responseAggregator!.push(message.response);\n } else if (currentHandler.mode === \"multiStream\") {\n currentHandler.stream.push(message.response);\n }\n }\n\n if (message.multiMessage.isLast) {\n if (currentHandler.mode === \"multiBuffered\") {\n currentHandler.resolve(responseAggregator!);\n responseAggregator = undefined;\n } else if (currentHandler.mode === \"multiStream\") {\n currentHandler.stream.end();\n }\n currentHandler = undefined;\n }\n } else {\n (currentHandler as AnySingleResponseHandler).resolve(message.response);\n currentHandler = undefined;\n }\n\n // After receiving a terminal response (txCommit or txDiscard), we proactively close the client stream.\n // This ensures consistent behavior between the gRPC and WebSocket transports,\n // since the server closes the connection automatically upon transaction completion in both cases.\n if (this.isTerminalResponse(message) && this.responseHandlerQueue.length === 0) {\n await this.stream.requests.complete();\n }\n }\n } catch (e: any) {\n return this.assignErrorFactoryIfNotSet(\n () => {\n rethrowMeaningfulError(e, true);\n },\n currentHandler &&\n (currentHandler.mode === \"single\" || currentHandler.mode === \"multiBuffered\")\n ? currentHandler.reject\n : undefined,\n );\n } finally {\n await this.close();\n }\n return null;\n }\n\n /** Executed after termination of incoming message processor */\n private async close(): Promise<void> {\n if (this.closed) return;\n\n this.closed = true;\n\n // Rejecting all messages\n\n while (true) {\n const handler = this.responseHandlerQueue.shift();\n if (!handler) break;\n const noReplyError = this.errorFactory\n ? new RethrowError(this.errorFactory)\n : new Error(\"no reply\");\n if (handler.mode === \"single\" || handler.mode === \"multiBuffered\") {\n handler.reject(noReplyError);\n } else {\n handler.stream.fail(noReplyError);\n }\n }\n\n // closing outgoing stream\n await this.stream.requests.complete();\n }\n\n /** Forcefully close the transaction, terminate all connections and reject all pending requests */\n public abort(cause?: Error) {\n this.assignErrorFactoryIfNotSet(() => {\n throw new Error(`transaction aborted`, { cause });\n });\n this.abortController.abort(cause);\n }\n\n /** Await incoming message loop termination and throw any leftover errors if it was unsuccessful */\n public async await(): Promise<void> {\n // for those who want to understand \"why?\":\n // this way there is no hanging promise that will complete with rejection\n // until await is implicitly requested, the this.incomingProcessorResult\n // always resolves with success\n\n const processingResult = await this.incomingProcessorResult;\n if (processingResult !== null) processingResult();\n }\n\n public async send<Kind extends ClientMessageRequest[\"oneofKind\"]>(\n r: OneOfKind<ClientMessageRequest, Kind>,\n expectMultiResponse: false,\n ): Promise<OneOfKind<ServerMessageResponse, Kind>>;\n public async send<Kind extends ClientMessageRequest[\"oneofKind\"]>(\n r: OneOfKind<ClientMessageRequest, Kind>,\n expectMultiResponse: true,\n ): Promise<OneOfKind<ServerMessageResponse, Kind>[]>;\n /** Generate proper client message and send it to the server, and returns a promise of future response. */\n public async send<Kind extends ClientMessageRequest[\"oneofKind\"]>(\n r: OneOfKind<ClientMessageRequest, Kind>,\n expectMultiResponse: boolean,\n ): Promise<OneOfKind<ServerMessageResponse, Kind> | OneOfKind<ServerMessageResponse, Kind>[]> {\n if (this.errorFactory) return Promise.reject(new RethrowError(this.errorFactory));\n\n if (this.closed) return Promise.reject(new Error(\"Transaction already closed\"));\n\n // Note: Promise synchronously executes a callback passed to a constructor\n const result = StatefulPromise.fromDeferredReject(\n new Promise<\n OneOfKind<ServerMessageResponse, Kind> | OneOfKind<ServerMessageResponse, Kind>[]\n >((resolve, reject) => {\n if (expectMultiResponse) {\n this.responseHandlerQueue.push({\n mode: \"multiBuffered\",\n kind: r.oneofKind,\n resolve: resolve as (v: ServerMessageResponse[]) => void,\n reject,\n });\n } else {\n this.responseHandlerQueue.push({\n mode: \"single\",\n kind: r.oneofKind,\n resolve: resolve as (v: ServerMessageResponse) => void,\n reject,\n });\n }\n }),\n );\n\n // Awaiting message dispatch to catch any associated errors.\n // There is no hurry, we are not going to receive a response until message is sent.\n await this.stream.requests.send({\n requestId: this.requestIdxCounter++,\n request: r,\n });\n\n try {\n return await result;\n } catch (e: any) {\n if (e instanceof RethrowError) e.rethrowLambda();\n throw new Error(\"Error while waiting for response\", { cause: e });\n }\n }\n\n public sendStream<Kind extends ClientMessageRequest[\"oneofKind\"]>(\n r: OneOfKind<ClientMessageRequest, Kind>,\n ): AsyncIterable<OneOfKind<ServerMessageResponse, Kind>> {\n if (this.errorFactory) throw new RethrowError(this.errorFactory);\n if (this.closed) throw new Error(\"Transaction already closed\");\n\n const stream = new AsyncMessageStream<ServerMessageResponse>();\n this.responseHandlerQueue.push({ mode: \"multiStream\", kind: r.oneofKind, stream });\n\n void this.stream.requests\n .send({ requestId: this.requestIdxCounter++, request: r })\n .catch((e: unknown) => {\n if (e instanceof Error) stream.fail(e);\n else stream.fail(new Error(\"Error while sending request\", { cause: e }));\n });\n\n return stream as AsyncIterable<OneOfKind<ServerMessageResponse, Kind>>;\n }\n\n private _completed = false;\n\n /** Safe to call multiple times */\n public async complete() {\n if (this._completed) return;\n this._completed = true;\n await this.stream.requests.complete();\n }\n\n private isTerminalResponse(message: TxAPI_ServerMessage): boolean {\n const kind = message.response.oneofKind;\n return kind === \"txCommit\" || kind === \"txDiscard\";\n }\n}\n"],"mappings":";;;;;;AA0DA,IAAM,qBAAN,MAA0E;CACxE,QAAyB,IAAIA,OAAAA,SAAW;CACxC,OAAe;CACf,YAAoB;CACpB;CACA;CACA;CAEA,KAAY,OAAgB;AAC1B,MAAI,KAAK,QAAQ,KAAK,UAAW;AACjC,MAAI,KAAK,gBAAgB;GACvB,MAAM,UAAU,KAAK;AACrB,QAAK,iBAAiB,KAAA;AACtB,QAAK,gBAAgB,KAAA;AACrB,WAAQ;IAAE;IAAO,MAAM;IAAO,CAAC;AAC/B;;AAEF,OAAK,MAAM,KAAK,MAAM;;CAGxB,MAAmB;AACjB,MAAI,KAAK,KAAM;AACf,OAAK,OAAO;AACZ,MAAI,KAAK,gBAAgB;GACvB,MAAM,UAAU,KAAK;AACrB,QAAK,iBAAiB,KAAA;AACtB,QAAK,gBAAgB,KAAA;AACrB,WAAQ;IAAE,OAAO,KAAA;IAAW,MAAM;IAAM,CAAC;;;CAS7C,KAAY,OAAoB;AAC9B,MAAI,KAAK,KAAM;AACf,OAAK,UAAU;AACf,OAAK,OAAO;AACZ,MAAI,KAAK,eAAe;GACtB,MAAM,SAAS,KAAK;AACpB,QAAK,iBAAiB,KAAA;AACtB,QAAK,gBAAgB,KAAA;AACrB,UAAO,MAAM;;;CAIjB,SAAsB;AACpB,OAAK,YAAY;AACjB,OAAK,OAAO;AACZ,OAAK,MAAM,OAAO;AAClB,MAAI,KAAK,gBAAgB;GACvB,MAAM,UAAU,KAAK;AACrB,QAAK,iBAAiB,KAAA;AACtB,QAAK,gBAAgB,KAAA;AACrB,WAAQ;IAAE,OAAO,KAAA;IAAW,MAAM;IAAM,CAAC;;;CAI7C,MAAa,OAAmC;AAC9C,MAAI,KAAK,QAAS,OAAM,KAAK;EAE7B,MAAM,QAAQ,KAAK,MAAM,OAAO;AAChC,MAAI,UAAU,KAAA,EAAW,QAAO;GAAE;GAAO,MAAM;GAAO;AAEtD,MAAI,KAAK,KAAM,QAAO;GAAE,OAAO,KAAA;GAAW,MAAM;GAAM;AAEtD,SAAO,MAAM,IAAI,SAA4B,SAAS,WAAW;AAC/D,QAAK,iBAAiB;AACtB,QAAK,gBAAgB;IACrB;;CAGJ,MAAa,SAAqC;AAChD,OAAK,QAAQ;AACb,SAAO;GAAE,OAAO,KAAA;GAAW,MAAM;GAAM;;CAGzC,MAAa,MAAM,OAA6C;AAC9D,OAAK,QAAQ;AACb,QAAM;;CAGR,CAAC,OAAO,iBAAuB;AAC7B,SAAO;;;AAIX,SAAS,cAAc,QAAyB;AAC9C,QAAO,OAAO,SAASC,eAAAA;;AAGzB,IAAa,eAAb,cAAkC,MAAM;CACtC,OAAO;CACP,YAAY,eAA4C;AACtD,QAAM,gDAAgD;AAD5B,OAAA,gBAAA;;;AAK9B,IAAa,kBAAb,MAA6B;;CAE3B;;CAGA,kBAAmC,IAAI,iBAAiB;;;CAIxD,oBAA4B;;CAG5B,uBAAwC,IAAID,OAAAA,SAA4B;;;;CAKxE,yBAAiC;;;CAIjC,SAAiB;;CAEjB,YAAoB;;CAGpB;;CAGA,gBAAiC,KAAK,KAAK;CAE3C;CAEA,YAAY,eAAuD;AACjE,OAAK,SAAS,cAAc,KAAK,gBAAgB,OAAO;AAGxD,OAAK,0BAA0B,KAAK,wBAAwB;;CAG9D,2BACE,cACA,QACa;AACb,MAAI,WAAW,KAAA,EAAW,QAAO,IAAI,aAAa,aAAa,CAAC;AAChE,MAAI,KAAK,aAAc,QAAO;AAC9B,OAAK,eAAe;AACpB,SAAO;;CAGT,MAAc,yBAAwD;;;EAGpE,IAAI,aAAa;EAIjB,IAAI,iBAAiD,KAAA;EACrD,IAAI,qBAA0D,KAAA;EAC9D,IAAI,kBAAkB;AACtB,MAAI;AACF,cAAW,MAAM,WAAW,KAAK,OAAO,WAAW;AACjD,QAAI,mBAAmB,KAAA,GAAW;AAChC,sBAAiB,KAAK,qBAAqB,OAAO;AAElD,SAAI,mBAAmB,KAAA,GAAW;AAChC,WAAK,iCAAiC;AACpC,aAAM,IAAI,MAAM,0BAA0B;QAC1C;AACF;;AAGF,SAAI,eAAe,SAAS,gBAAiB,sBAAqB,EAAE;AACpE,uBAAkB;AAClB;;AAGF,QAAI,QAAQ,cAAc,YAAY;KACpC,MAAM,eAAe,0BAA0B,QAAQ,UAAU,OAAO;AACxE,UAAK,iCAAiC;AACpC,YAAM,IAAI,MAAM,aAAa;OAC7B;AACF;;AAGF,QAAI,QAAQ,UAAU,KAAA,GAAW;KAC/B,MAAM,SAAS,QAAQ;AAEvB,SAAI,cAAc,OAAO,EAAE;MACzB,MAAM,mBAAmB,IAAI,mBAAmB;AAC9C,aAAM,IAAIE,eAAAA,mBAAmB,OAAO;QACpC;AACF,UAAI,eAAe,SAAS,YAAY,eAAe,SAAS,gBAC9D,gBAAe,OAAO,iBAAiB;UAEvC,gBAAe,OAAO,KAAK,iBAAiB;AAE9C,uBAAiB,KAAA;AAEjB,UAAI,QAAQ,iBAAiB,KAAA,KAAa,CAAC,QAAQ,aAAa,QAAQ;AACtE,YAAK,iCAAiC;AACpC,cAAM,IAAI,MAAM,+BAA+B;SAC/C;AACF;;AAIF;YACK;AACL,WAAK,iCACG;AACJ,aAAM,IAAIC,eAAAA,qBAAqB,OAAO;SAExC,eAAe,SAAS,YAAY,eAAe,SAAS,kBACxD,eAAe,SACf,KAAA,EACL;AACD,UAAI,eAAe,SAAS,cAC1B,gBAAe,OAAO,KAAK,IAAIA,eAAAA,qBAAqB,OAAO,CAAC;AAE9D,uBAAiB,KAAA;AAGjB;;;AAIJ,QACE,eAAe,SAAS,QAAQ,SAAS,aACzC,SAAS,cAAc,YAAY,MACnC;KACA,MAAM,eAAe,wCAAwC,eAAe,KAAK,OAAO,QAAQ,SAAS;AAEzG,UAAK,iCACG;AACJ,YAAM,IAAI,MAAM,aAAa;QAE/B,eAAe,SAAS,YAAY,eAAe,SAAS,kBACxD,eAAe,SACf,KAAA,EACL;AACD,SAAI,eAAe,SAAS,cAC1B,gBAAe,OAAO,KAAK,IAAI,MAAM,aAAa,CAAC;AAErD,sBAAiB,KAAA;AAEjB;;IAGF,MAAM,sBACJ,eAAe,SAAS,mBAAmB,eAAe,SAAS;AACrE,QAAI,yBAAyB,QAAQ,iBAAiB,KAAA,IAAY;KAChE,MAAM,eAAe,6BAA6B,oBAAoB,OAAO,QAAQ,iBAAiB,KAAA;AAEtG,UAAK,iCACG;AACJ,YAAM,IAAI,MAAM,aAAa;QAE/B,eAAe,SAAS,YAAY,eAAe,SAAS,kBACxD,eAAe,SACf,KAAA,EACL;AACD,SAAI,eAAe,SAAS,cAC1B,gBAAe,OAAO,KAAK,IAAI,MAAM,aAAa,CAAC;AAErD,sBAAiB,KAAA;AAEjB;;AAKF,QAAI,QAAQ,iBAAiB,KAAA,GAAW;AACtC,SAAI,CAAC,QAAQ,aAAa,SAAS;AACjC,UAAI,QAAQ,aAAa,OAAO,kBAAkB,GAAG;OACnD,MAAM,eAAe,0BAA0B,QAAQ,aAAa,GAAG,OAAO,kBAAkB;AAEhG,YAAK,iCACG;AACJ,cAAM,IAAI,MAAM,aAAa;UAE/B,eAAe,SAAS,kBAAkB,eAAe,SAAS,KAAA,EACnE;AACD,WAAI,eAAe,SAAS,cAC1B,gBAAe,OAAO,KAAK,IAAI,MAAM,aAAa,CAAC;AAErD,wBAAiB,KAAA;AAEjB;;AAGF;AACA,UAAI,eAAe,SAAS,gBAC1B,oBAAoB,KAAK,QAAQ,SAAS;eACjC,eAAe,SAAS,cACjC,gBAAe,OAAO,KAAK,QAAQ,SAAS;;AAIhD,SAAI,QAAQ,aAAa,QAAQ;AAC/B,UAAI,eAAe,SAAS,iBAAiB;AAC3C,sBAAe,QAAQ,mBAAoB;AAC3C,4BAAqB,KAAA;iBACZ,eAAe,SAAS,cACjC,gBAAe,OAAO,KAAK;AAE7B,uBAAiB,KAAA;;WAEd;AACJ,oBAA4C,QAAQ,QAAQ,SAAS;AACtE,sBAAiB,KAAA;;AAMnB,QAAI,KAAK,mBAAmB,QAAQ,IAAI,KAAK,qBAAqB,WAAW,EAC3E,OAAM,KAAK,OAAO,SAAS,UAAU;;WAGlC,GAAQ;AACf,UAAO,KAAK,iCACJ;AACJ,mBAAA,uBAAuB,GAAG,KAAK;MAEjC,mBACG,eAAe,SAAS,YAAY,eAAe,SAAS,mBAC3D,eAAe,SACf,KAAA,EACL;YACO;AACR,SAAM,KAAK,OAAO;;AAEpB,SAAO;;;CAIT,MAAc,QAAuB;AACnC,MAAI,KAAK,OAAQ;AAEjB,OAAK,SAAS;AAId,SAAO,MAAM;GACX,MAAM,UAAU,KAAK,qBAAqB,OAAO;AACjD,OAAI,CAAC,QAAS;GACd,MAAM,eAAe,KAAK,eACtB,IAAI,aAAa,KAAK,aAAa,mBACnC,IAAI,MAAM,WAAW;AACzB,OAAI,QAAQ,SAAS,YAAY,QAAQ,SAAS,gBAChD,SAAQ,OAAO,aAAa;OAE5B,SAAQ,OAAO,KAAK,aAAa;;AAKrC,QAAM,KAAK,OAAO,SAAS,UAAU;;;CAIvC,MAAa,OAAe;AAC1B,OAAK,iCAAiC;AACpC,SAAM,IAAI,MAAM,uBAAuB,EAAE,OAAO,CAAC;IACjD;AACF,OAAK,gBAAgB,MAAM,MAAM;;;CAInC,MAAa,QAAuB;EAMlC,MAAM,mBAAmB,MAAM,KAAK;AACpC,MAAI,qBAAqB,KAAM,mBAAkB;;;CAYnD,MAAa,KACX,GACA,qBAC4F;AAC5F,MAAI,KAAK,aAAc,QAAO,QAAQ,OAAO,IAAI,aAAa,KAAK,aAAa,CAAC;AAEjF,MAAI,KAAK,OAAQ,QAAO,QAAQ,uBAAO,IAAI,MAAM,6BAA6B,CAAC;EAG/E,MAAM,SAASC,wBAAAA,gBAAgB,mBAC7B,IAAI,SAED,SAAS,WAAW;AACrB,OAAI,oBACF,MAAK,qBAAqB,KAAK;IAC7B,MAAM;IACN,MAAM,EAAE;IACC;IACT;IACD,CAAC;OAEF,MAAK,qBAAqB,KAAK;IAC7B,MAAM;IACN,MAAM,EAAE;IACC;IACT;IACD,CAAC;IAEJ,CACH;AAID,QAAM,KAAK,OAAO,SAAS,KAAK;GAC9B,WAAW,KAAK;GAChB,SAAS;GACV,CAAC;AAEF,MAAI;AACF,UAAO,MAAM;WACN,GAAQ;AACf,OAAI,aAAa,aAAc,GAAE,eAAe;AAChD,SAAM,IAAI,MAAM,oCAAoC,EAAE,OAAO,GAAG,CAAC;;;CAIrE,WACE,GACuD;AACvD,MAAI,KAAK,aAAc,OAAM,IAAI,aAAa,KAAK,aAAa;AAChE,MAAI,KAAK,OAAQ,OAAM,IAAI,MAAM,6BAA6B;EAE9D,MAAM,SAAS,IAAI,oBAA2C;AAC9D,OAAK,qBAAqB,KAAK;GAAE,MAAM;GAAe,MAAM,EAAE;GAAW;GAAQ,CAAC;AAE7E,OAAK,OAAO,SACd,KAAK;GAAE,WAAW,KAAK;GAAqB,SAAS;GAAG,CAAC,CACzD,OAAO,MAAe;AACrB,OAAI,aAAa,MAAO,QAAO,KAAK,EAAE;OACjC,QAAO,KAAK,IAAI,MAAM,+BAA+B,EAAE,OAAO,GAAG,CAAC,CAAC;IACxE;AAEJ,SAAO;;CAGT,aAAqB;;CAGrB,MAAa,WAAW;AACtB,MAAI,KAAK,WAAY;AACrB,OAAK,aAAa;AAClB,QAAM,KAAK,OAAO,SAAS,UAAU;;CAGvC,mBAA2B,SAAuC;EAChE,MAAM,OAAO,QAAQ,SAAS;AAC9B,SAAO,SAAS,cAAc,SAAS"}
|
|
@@ -45,6 +45,7 @@ declare class LLPlTransaction {
|
|
|
45
45
|
await(): Promise<void>;
|
|
46
46
|
send<Kind extends ClientMessageRequest["oneofKind"]>(r: OneOfKind<ClientMessageRequest, Kind>, expectMultiResponse: false): Promise<OneOfKind<ServerMessageResponse, Kind>>;
|
|
47
47
|
send<Kind extends ClientMessageRequest["oneofKind"]>(r: OneOfKind<ClientMessageRequest, Kind>, expectMultiResponse: true): Promise<OneOfKind<ServerMessageResponse, Kind>[]>;
|
|
48
|
+
sendStream<Kind extends ClientMessageRequest["oneofKind"]>(r: OneOfKind<ClientMessageRequest, Kind>): AsyncIterable<OneOfKind<ServerMessageResponse, Kind>>;
|
|
48
49
|
private _completed;
|
|
49
50
|
/** Safe to call multiple times */
|
|
50
51
|
complete(): Promise<void>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ll_transaction.d.ts","names":[],"sources":["../../src/core/ll_transaction.ts"],"mappings":";;;;KAeY,oBAAA,GAAuB,mBAAA;AAAA,KAEvB,qBAAA,GAAwB,mBAAA;AAAA,KAE/B,QAAA,GAAW,UAAA,CAAW,mBAAA,EAAqB,mBAAA;AAAA,KAEpC,SAAA;EAAsB,SAAA;AAAA,gBAAmC,CAAA,iBAAkB,OAAA,CACrF,CAAA;EACE,SAAA,EAAW,IAAA;AAAA;AAAA,
|
|
1
|
+
{"version":3,"file":"ll_transaction.d.ts","names":[],"sources":["../../src/core/ll_transaction.ts"],"mappings":";;;;KAeY,oBAAA,GAAuB,mBAAA;AAAA,KAEvB,qBAAA,GAAwB,mBAAA;AAAA,KAE/B,QAAA,GAAW,UAAA,CAAW,mBAAA,EAAqB,mBAAA;AAAA,KAEpC,SAAA;EAAsB,SAAA;AAAA,gBAAmC,CAAA,iBAAkB,OAAA,CACrF,CAAA;EACE,SAAA,EAAW,IAAA;AAAA;AAAA,cAuIF,eAAA;;mBAEM,MAAA;EA7I6B;EAAA,iBAgJ7B,eAAA;EAhJO;;EAAA,QAoJhB,iBAAA;EApJiB;EAAA,iBAuJR,oBAAA;EAvJgD;;AAEnE;EAFmE,QA4JzD,sBAAA;EA1JW;;EAAA,QA8JX,MAAA;EA5JK;EAAA,QA8JL,SAAA;EAhKoF;EAAA,QAmKpF,YAAA;EAnKY;EAAA,iBAsKH,aAAA;EAAA,iBAEA,uBAAA;cAEL,aAAA,GAAgB,WAAA,EAAa,WAAA,KAAgB,QAAA;EAAA,QAOjD,0BAAA;EAAA,QAUM,sBAAA;EAzLZ;EAAA,QAoXY,KAAA;EApXG;EA6YV,KAAA,CAAM,KAAA,GAAQ,KAAA;EAtQV;EA8QE,KAAA,CAAA,GAAS,OAAA;EAUT,IAAA,cAAkB,oBAAA,cAAA,CAC7B,CAAA,EAAG,SAAA,CAAU,oBAAA,EAAsB,IAAA,GACnC,mBAAA,UACC,OAAA,CAAQ,SAAA,CAAU,qBAAA,EAAuB,IAAA;EAC/B,IAAA,cAAkB,oBAAA,cAAA,CAC7B,CAAA,EAAG,SAAA,CAAU,oBAAA,EAAsB,IAAA,GACnC,mBAAA,SACC,OAAA,CAAQ,SAAA,CAAU,qBAAA,EAAuB,IAAA;EAgDrC,UAAA,cAAwB,oBAAA,cAAA,CAC7B,CAAA,EAAG,SAAA,CAAU,oBAAA,EAAsB,IAAA,IAClC,aAAA,CAAc,SAAA,CAAU,qBAAA,EAAuB,IAAA;EAAA,QAiB1C,UAAA;EApFc;EAuFT,QAAA,CAAA,GAAQ,OAAA;EAAA,QAMb,kBAAA;AAAA"}
|
|
@@ -2,14 +2,96 @@ import { PlErrorCodeNotFound, RecoverablePlError, UnrecoverablePlError, rethrowM
|
|
|
2
2
|
import { StatefulPromise } from "./StatefulPromise.js";
|
|
3
3
|
import Denque from "denque";
|
|
4
4
|
//#region src/core/ll_transaction.ts
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
5
|
+
var AsyncMessageStream = class {
|
|
6
|
+
queue = new Denque();
|
|
7
|
+
done = false;
|
|
8
|
+
cancelled = false;
|
|
9
|
+
failure;
|
|
10
|
+
waitingResolve;
|
|
11
|
+
waitingReject;
|
|
12
|
+
push(value) {
|
|
13
|
+
if (this.done || this.cancelled) return;
|
|
14
|
+
if (this.waitingResolve) {
|
|
15
|
+
const resolve = this.waitingResolve;
|
|
16
|
+
this.waitingResolve = void 0;
|
|
17
|
+
this.waitingReject = void 0;
|
|
18
|
+
resolve({
|
|
19
|
+
value,
|
|
20
|
+
done: false
|
|
21
|
+
});
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
this.queue.push(value);
|
|
25
|
+
}
|
|
26
|
+
end() {
|
|
27
|
+
if (this.done) return;
|
|
28
|
+
this.done = true;
|
|
29
|
+
if (this.waitingResolve) {
|
|
30
|
+
const resolve = this.waitingResolve;
|
|
31
|
+
this.waitingResolve = void 0;
|
|
32
|
+
this.waitingReject = void 0;
|
|
33
|
+
resolve({
|
|
34
|
+
value: void 0,
|
|
35
|
+
done: true
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
fail(error) {
|
|
40
|
+
if (this.done) return;
|
|
41
|
+
this.failure = error;
|
|
42
|
+
this.done = true;
|
|
43
|
+
if (this.waitingReject) {
|
|
44
|
+
const reject = this.waitingReject;
|
|
45
|
+
this.waitingResolve = void 0;
|
|
46
|
+
this.waitingReject = void 0;
|
|
47
|
+
reject(error);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
cancel() {
|
|
51
|
+
this.cancelled = true;
|
|
52
|
+
this.done = true;
|
|
53
|
+
this.queue.clear();
|
|
54
|
+
if (this.waitingResolve) {
|
|
55
|
+
const resolve = this.waitingResolve;
|
|
56
|
+
this.waitingResolve = void 0;
|
|
57
|
+
this.waitingReject = void 0;
|
|
58
|
+
resolve({
|
|
59
|
+
value: void 0,
|
|
60
|
+
done: true
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
async next() {
|
|
65
|
+
if (this.failure) throw this.failure;
|
|
66
|
+
const value = this.queue.shift();
|
|
67
|
+
if (value !== void 0) return {
|
|
68
|
+
value,
|
|
69
|
+
done: false
|
|
70
|
+
};
|
|
71
|
+
if (this.done) return {
|
|
72
|
+
value: void 0,
|
|
73
|
+
done: true
|
|
74
|
+
};
|
|
75
|
+
return await new Promise((resolve, reject) => {
|
|
76
|
+
this.waitingResolve = resolve;
|
|
77
|
+
this.waitingReject = reject;
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
async return() {
|
|
81
|
+
this.cancel();
|
|
82
|
+
return {
|
|
83
|
+
value: void 0,
|
|
84
|
+
done: true
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
async throw(error) {
|
|
88
|
+
this.cancel();
|
|
89
|
+
throw error;
|
|
90
|
+
}
|
|
91
|
+
[Symbol.asyncIterator]() {
|
|
92
|
+
return this;
|
|
93
|
+
}
|
|
94
|
+
};
|
|
13
95
|
function isRecoverable(status) {
|
|
14
96
|
return status.code === PlErrorCodeNotFound;
|
|
15
97
|
}
|
|
@@ -60,6 +142,7 @@ var LLPlTransaction = class {
|
|
|
60
142
|
let expectedId = -1;
|
|
61
143
|
let currentHandler = void 0;
|
|
62
144
|
let responseAggregator = void 0;
|
|
145
|
+
let currentMultiIdx = 0;
|
|
63
146
|
try {
|
|
64
147
|
for await (const message of this.stream.responses) {
|
|
65
148
|
if (currentHandler === void 0) {
|
|
@@ -70,7 +153,8 @@ var LLPlTransaction = class {
|
|
|
70
153
|
});
|
|
71
154
|
break;
|
|
72
155
|
}
|
|
73
|
-
if (currentHandler.
|
|
156
|
+
if (currentHandler.mode === "multiBuffered") responseAggregator = [];
|
|
157
|
+
currentMultiIdx = 0;
|
|
74
158
|
expectedId++;
|
|
75
159
|
}
|
|
76
160
|
if (message.requestId !== expectedId) {
|
|
@@ -83,9 +167,11 @@ var LLPlTransaction = class {
|
|
|
83
167
|
if (message.error !== void 0) {
|
|
84
168
|
const status = message.error;
|
|
85
169
|
if (isRecoverable(status)) {
|
|
86
|
-
|
|
170
|
+
const recoverableError = new RethrowError(() => {
|
|
87
171
|
throw new RecoverablePlError(status);
|
|
88
|
-
})
|
|
172
|
+
});
|
|
173
|
+
if (currentHandler.mode === "single" || currentHandler.mode === "multiBuffered") currentHandler.reject(recoverableError);
|
|
174
|
+
else currentHandler.stream.fail(recoverableError);
|
|
89
175
|
currentHandler = void 0;
|
|
90
176
|
if (message.multiMessage !== void 0 && !message.multiMessage.isLast) {
|
|
91
177
|
this.assignErrorFactoryIfNotSet(() => {
|
|
@@ -97,7 +183,8 @@ var LLPlTransaction = class {
|
|
|
97
183
|
} else {
|
|
98
184
|
this.assignErrorFactoryIfNotSet(() => {
|
|
99
185
|
throw new UnrecoverablePlError(status);
|
|
100
|
-
}, currentHandler.reject);
|
|
186
|
+
}, currentHandler.mode === "single" || currentHandler.mode === "multiBuffered" ? currentHandler.reject : void 0);
|
|
187
|
+
if (currentHandler.mode === "multiStream") currentHandler.stream.fail(new UnrecoverablePlError(status));
|
|
101
188
|
currentHandler = void 0;
|
|
102
189
|
break;
|
|
103
190
|
}
|
|
@@ -106,33 +193,41 @@ var LLPlTransaction = class {
|
|
|
106
193
|
const errorMessage = `inconsistent request response types: ${currentHandler.kind} !== ${message.response.oneofKind}`;
|
|
107
194
|
this.assignErrorFactoryIfNotSet(() => {
|
|
108
195
|
throw new Error(errorMessage);
|
|
109
|
-
}, currentHandler.reject);
|
|
196
|
+
}, currentHandler.mode === "single" || currentHandler.mode === "multiBuffered" ? currentHandler.reject : void 0);
|
|
197
|
+
if (currentHandler.mode === "multiStream") currentHandler.stream.fail(new Error(errorMessage));
|
|
110
198
|
currentHandler = void 0;
|
|
111
199
|
break;
|
|
112
200
|
}
|
|
113
|
-
|
|
114
|
-
|
|
201
|
+
const expectMultiResponse = currentHandler.mode === "multiBuffered" || currentHandler.mode === "multiStream";
|
|
202
|
+
if (expectMultiResponse !== (message.multiMessage !== void 0)) {
|
|
203
|
+
const errorMessage = `inconsistent multi state: ${expectMultiResponse} !== ${message.multiMessage !== void 0}`;
|
|
115
204
|
this.assignErrorFactoryIfNotSet(() => {
|
|
116
205
|
throw new Error(errorMessage);
|
|
117
|
-
}, currentHandler.reject);
|
|
206
|
+
}, currentHandler.mode === "single" || currentHandler.mode === "multiBuffered" ? currentHandler.reject : void 0);
|
|
207
|
+
if (currentHandler.mode === "multiStream") currentHandler.stream.fail(new Error(errorMessage));
|
|
118
208
|
currentHandler = void 0;
|
|
119
209
|
break;
|
|
120
210
|
}
|
|
121
211
|
if (message.multiMessage !== void 0) {
|
|
122
212
|
if (!message.multiMessage.isEmpty) {
|
|
123
|
-
if (message.multiMessage.id !==
|
|
124
|
-
const errorMessage = `inconsistent multi id: ${message.multiMessage.id} !== ${
|
|
213
|
+
if (message.multiMessage.id !== currentMultiIdx + 1) {
|
|
214
|
+
const errorMessage = `inconsistent multi id: ${message.multiMessage.id} !== ${currentMultiIdx + 1}`;
|
|
125
215
|
this.assignErrorFactoryIfNotSet(() => {
|
|
126
216
|
throw new Error(errorMessage);
|
|
127
|
-
}, currentHandler.reject);
|
|
217
|
+
}, currentHandler.mode === "multiBuffered" ? currentHandler.reject : void 0);
|
|
218
|
+
if (currentHandler.mode === "multiStream") currentHandler.stream.fail(new Error(errorMessage));
|
|
128
219
|
currentHandler = void 0;
|
|
129
220
|
break;
|
|
130
221
|
}
|
|
131
|
-
|
|
222
|
+
currentMultiIdx++;
|
|
223
|
+
if (currentHandler.mode === "multiBuffered") responseAggregator.push(message.response);
|
|
224
|
+
else if (currentHandler.mode === "multiStream") currentHandler.stream.push(message.response);
|
|
132
225
|
}
|
|
133
226
|
if (message.multiMessage.isLast) {
|
|
134
|
-
currentHandler.
|
|
135
|
-
|
|
227
|
+
if (currentHandler.mode === "multiBuffered") {
|
|
228
|
+
currentHandler.resolve(responseAggregator);
|
|
229
|
+
responseAggregator = void 0;
|
|
230
|
+
} else if (currentHandler.mode === "multiStream") currentHandler.stream.end();
|
|
136
231
|
currentHandler = void 0;
|
|
137
232
|
}
|
|
138
233
|
} else {
|
|
@@ -144,7 +239,7 @@ var LLPlTransaction = class {
|
|
|
144
239
|
} catch (e) {
|
|
145
240
|
return this.assignErrorFactoryIfNotSet(() => {
|
|
146
241
|
rethrowMeaningfulError(e, true);
|
|
147
|
-
}, currentHandler
|
|
242
|
+
}, currentHandler && (currentHandler.mode === "single" || currentHandler.mode === "multiBuffered") ? currentHandler.reject : void 0);
|
|
148
243
|
} finally {
|
|
149
244
|
await this.close();
|
|
150
245
|
}
|
|
@@ -157,8 +252,9 @@ var LLPlTransaction = class {
|
|
|
157
252
|
while (true) {
|
|
158
253
|
const handler = this.responseHandlerQueue.shift();
|
|
159
254
|
if (!handler) break;
|
|
160
|
-
|
|
161
|
-
|
|
255
|
+
const noReplyError = this.errorFactory ? new RethrowError(this.errorFactory) : /* @__PURE__ */ new Error("no reply");
|
|
256
|
+
if (handler.mode === "single" || handler.mode === "multiBuffered") handler.reject(noReplyError);
|
|
257
|
+
else handler.stream.fail(noReplyError);
|
|
162
258
|
}
|
|
163
259
|
await this.stream.requests.complete();
|
|
164
260
|
}
|
|
@@ -179,7 +275,18 @@ var LLPlTransaction = class {
|
|
|
179
275
|
if (this.errorFactory) return Promise.reject(new RethrowError(this.errorFactory));
|
|
180
276
|
if (this.closed) return Promise.reject(/* @__PURE__ */ new Error("Transaction already closed"));
|
|
181
277
|
const result = StatefulPromise.fromDeferredReject(new Promise((resolve, reject) => {
|
|
182
|
-
this.responseHandlerQueue.push(
|
|
278
|
+
if (expectMultiResponse) this.responseHandlerQueue.push({
|
|
279
|
+
mode: "multiBuffered",
|
|
280
|
+
kind: r.oneofKind,
|
|
281
|
+
resolve,
|
|
282
|
+
reject
|
|
283
|
+
});
|
|
284
|
+
else this.responseHandlerQueue.push({
|
|
285
|
+
mode: "single",
|
|
286
|
+
kind: r.oneofKind,
|
|
287
|
+
resolve,
|
|
288
|
+
reject
|
|
289
|
+
});
|
|
183
290
|
}));
|
|
184
291
|
await this.stream.requests.send({
|
|
185
292
|
requestId: this.requestIdxCounter++,
|
|
@@ -192,6 +299,24 @@ var LLPlTransaction = class {
|
|
|
192
299
|
throw new Error("Error while waiting for response", { cause: e });
|
|
193
300
|
}
|
|
194
301
|
}
|
|
302
|
+
sendStream(r) {
|
|
303
|
+
if (this.errorFactory) throw new RethrowError(this.errorFactory);
|
|
304
|
+
if (this.closed) throw new Error("Transaction already closed");
|
|
305
|
+
const stream = new AsyncMessageStream();
|
|
306
|
+
this.responseHandlerQueue.push({
|
|
307
|
+
mode: "multiStream",
|
|
308
|
+
kind: r.oneofKind,
|
|
309
|
+
stream
|
|
310
|
+
});
|
|
311
|
+
this.stream.requests.send({
|
|
312
|
+
requestId: this.requestIdxCounter++,
|
|
313
|
+
request: r
|
|
314
|
+
}).catch((e) => {
|
|
315
|
+
if (e instanceof Error) stream.fail(e);
|
|
316
|
+
else stream.fail(new Error("Error while sending request", { cause: e }));
|
|
317
|
+
});
|
|
318
|
+
return stream;
|
|
319
|
+
}
|
|
195
320
|
_completed = false;
|
|
196
321
|
/** Safe to call multiple times */
|
|
197
322
|
async complete() {
|