spacetimedb 2.1.0 → 2.2.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/LICENSE.txt +2 -2
- package/dist/angular/index.cjs +7 -2
- package/dist/angular/index.cjs.map +1 -1
- package/dist/angular/index.mjs +7 -2
- package/dist/angular/index.mjs.map +1 -1
- package/dist/browser/angular/index.mjs +7 -2
- package/dist/browser/angular/index.mjs.map +1 -1
- package/dist/browser/react/index.mjs +57 -6
- package/dist/browser/react/index.mjs.map +1 -1
- package/dist/browser/svelte/index.mjs +7 -2
- package/dist/browser/svelte/index.mjs.map +1 -1
- package/dist/browser/vue/index.mjs +7 -2
- package/dist/browser/vue/index.mjs.map +1 -1
- package/dist/index.browser.mjs +459 -138
- package/dist/index.browser.mjs.map +1 -1
- package/dist/index.cjs +459 -138
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +459 -138
- package/dist/index.mjs.map +1 -1
- package/dist/lib/binary_reader.d.ts +1 -1
- package/dist/lib/binary_reader.d.ts.map +1 -1
- package/dist/lib/binary_writer.d.ts +2 -1
- package/dist/lib/binary_writer.d.ts.map +1 -1
- package/dist/lib/filter.d.ts +2 -1
- package/dist/lib/filter.d.ts.map +1 -1
- package/dist/lib/table.d.ts +6 -0
- package/dist/lib/table.d.ts.map +1 -1
- package/dist/min/index.browser.mjs +1 -1
- package/dist/min/index.browser.mjs.map +1 -1
- package/dist/min/react/index.mjs +1 -1
- package/dist/min/react/index.mjs.map +1 -1
- package/dist/min/sdk/index.browser.mjs +1 -1
- package/dist/min/sdk/index.browser.mjs.map +1 -1
- package/dist/react/index.cjs +57 -5
- package/dist/react/index.cjs.map +1 -1
- package/dist/react/index.d.ts +1 -0
- package/dist/react/index.d.ts.map +1 -1
- package/dist/react/index.mjs +57 -6
- package/dist/react/index.mjs.map +1 -1
- package/dist/react/useProcedure.d.ts +4 -0
- package/dist/react/useProcedure.d.ts.map +1 -0
- package/dist/react/useTable.d.ts +2 -0
- package/dist/react/useTable.d.ts.map +1 -1
- package/dist/sdk/db_connection_builder.d.ts +3 -3
- package/dist/sdk/db_connection_builder.d.ts.map +1 -1
- package/dist/sdk/db_connection_impl.d.ts +3 -3
- package/dist/sdk/db_connection_impl.d.ts.map +1 -1
- package/dist/sdk/decompress.d.ts +1 -1
- package/dist/sdk/decompress.d.ts.map +1 -1
- package/dist/sdk/index.browser.mjs +459 -138
- package/dist/sdk/index.browser.mjs.map +1 -1
- package/dist/sdk/index.cjs +459 -138
- package/dist/sdk/index.cjs.map +1 -1
- package/dist/sdk/index.mjs +459 -138
- package/dist/sdk/index.mjs.map +1 -1
- package/dist/sdk/table_cache.d.ts +1 -0
- package/dist/sdk/table_cache.d.ts.map +1 -1
- package/dist/sdk/type_utils.d.ts +4 -1
- package/dist/sdk/type_utils.d.ts.map +1 -1
- package/dist/sdk/websocket_decompress_adapter.d.ts +5 -21
- package/dist/sdk/websocket_decompress_adapter.d.ts.map +1 -1
- package/dist/sdk/websocket_protocols.d.ts +6 -0
- package/dist/sdk/websocket_protocols.d.ts.map +1 -0
- package/dist/sdk/websocket_test_adapter.d.ts +14 -18
- package/dist/sdk/websocket_test_adapter.d.ts.map +1 -1
- package/dist/sdk/websocket_v3_frames.d.ts +9 -0
- package/dist/sdk/websocket_v3_frames.d.ts.map +1 -0
- package/dist/sdk/ws.d.ts +26 -1
- package/dist/sdk/ws.d.ts.map +1 -1
- package/dist/server/http_internal.d.ts.map +1 -1
- package/dist/server/index.d.ts +1 -1
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.mjs +53 -6
- package/dist/server/index.mjs.map +1 -1
- package/dist/server/runtime.d.ts +29 -2
- package/dist/server/runtime.d.ts.map +1 -1
- package/dist/svelte/index.cjs +7 -2
- package/dist/svelte/index.cjs.map +1 -1
- package/dist/svelte/index.mjs +7 -2
- package/dist/svelte/index.mjs.map +1 -1
- package/dist/tanstack/index.cjs +7 -2
- package/dist/tanstack/index.cjs.map +1 -1
- package/dist/tanstack/index.mjs +7 -2
- package/dist/tanstack/index.mjs.map +1 -1
- package/dist/vue/index.cjs +7 -2
- package/dist/vue/index.cjs.map +1 -1
- package/dist/vue/index.mjs +7 -2
- package/dist/vue/index.mjs.map +1 -1
- package/package.json +2 -2
- package/src/lib/binary_reader.ts +5 -2
- package/src/lib/binary_writer.ts +7 -1
- package/src/lib/filter.ts +12 -1
- package/src/lib/table.ts +9 -1
- package/src/react/index.ts +1 -0
- package/src/react/useProcedure.ts +60 -0
- package/src/react/useTable.ts +17 -2
- package/src/sdk/db_connection_builder.ts +16 -7
- package/src/sdk/db_connection_impl.ts +404 -89
- package/src/sdk/decompress.ts +7 -23
- package/src/sdk/table_cache.ts +5 -5
- package/src/sdk/type_utils.ts +10 -1
- package/src/sdk/websocket_decompress_adapter.ts +15 -77
- package/src/sdk/websocket_protocols.ts +25 -0
- package/src/sdk/websocket_test_adapter.ts +65 -29
- package/src/sdk/websocket_v3_frames.ts +126 -0
- package/src/sdk/ws.ts +81 -3
- package/src/server/http_internal.ts +10 -1
- package/src/server/index.ts +1 -1
- package/src/server/runtime.ts +39 -1
- package/src/server/sys.d.ts +4 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { ConnectionId, ProductBuilder, ProductType } from '../';
|
|
2
2
|
import { AlgebraicType, type ComparablePrimitive } from '../';
|
|
3
|
-
import
|
|
4
|
-
import
|
|
3
|
+
import BinaryReader from '../lib/binary_reader.ts';
|
|
4
|
+
import BinaryWriter from '../lib/binary_writer.ts';
|
|
5
5
|
import {
|
|
6
6
|
BsatnRowList,
|
|
7
7
|
ClientMessage,
|
|
@@ -37,10 +37,6 @@ import {
|
|
|
37
37
|
type PendingCallback,
|
|
38
38
|
type TableUpdate as CacheTableUpdate,
|
|
39
39
|
} from './table_cache.ts';
|
|
40
|
-
import {
|
|
41
|
-
WebsocketDecompressAdapter,
|
|
42
|
-
type WebsocketAdapter,
|
|
43
|
-
} from './websocket_decompress_adapter.ts';
|
|
44
40
|
import {
|
|
45
41
|
SubscriptionBuilderImpl,
|
|
46
42
|
SubscriptionHandleImpl,
|
|
@@ -60,6 +56,19 @@ import type { ProceduresView } from './procedures.ts';
|
|
|
60
56
|
import type { Values } from '../lib/type_util.ts';
|
|
61
57
|
import type { TransactionUpdate } from './client_api/types.ts';
|
|
62
58
|
import { InternalError, SenderError } from '../lib/errors.ts';
|
|
59
|
+
import type { WebSocketAdapter, WebSocketFactory } from './ws.ts';
|
|
60
|
+
import {
|
|
61
|
+
normalizeWsProtocol,
|
|
62
|
+
PREFERRED_WS_PROTOCOLS,
|
|
63
|
+
V2_WS_PROTOCOL,
|
|
64
|
+
V3_WS_PROTOCOL,
|
|
65
|
+
type NegotiatedWsProtocol,
|
|
66
|
+
} from './websocket_protocols';
|
|
67
|
+
import {
|
|
68
|
+
countClientMessagesForV3Frame,
|
|
69
|
+
encodeClientMessagesV3,
|
|
70
|
+
forEachServerMessageV3,
|
|
71
|
+
} from './websocket_v3_frames.ts';
|
|
63
72
|
|
|
64
73
|
export {
|
|
65
74
|
DbConnectionBuilder,
|
|
@@ -89,8 +98,8 @@ export type DbConnectionConfig<RemoteModule extends UntypedRemoteModule> = {
|
|
|
89
98
|
identity?: Identity;
|
|
90
99
|
token?: string;
|
|
91
100
|
emitter: EventEmitter<ConnectionEvent>;
|
|
92
|
-
createWSFn:
|
|
93
|
-
compression: 'gzip' | 'none';
|
|
101
|
+
createWSFn: WebSocketFactory;
|
|
102
|
+
compression: 'gzip' | 'brotli' | 'none';
|
|
94
103
|
lightMode: boolean;
|
|
95
104
|
confirmedReads?: boolean;
|
|
96
105
|
remoteModule: RemoteModule;
|
|
@@ -98,6 +107,29 @@ export type DbConnectionConfig<RemoteModule extends UntypedRemoteModule> = {
|
|
|
98
107
|
|
|
99
108
|
type ProcedureCallback = (result: ProcedureResultMessage['result']) => void;
|
|
100
109
|
|
|
110
|
+
const TEXT_ENCODER = new TextEncoder();
|
|
111
|
+
|
|
112
|
+
function getClientMessageVariantTag(name: string): number {
|
|
113
|
+
if (ClientMessage.algebraicType.tag !== 'Sum') {
|
|
114
|
+
throw new TypeError('ClientMessage must be a sum type');
|
|
115
|
+
}
|
|
116
|
+
const tag = ClientMessage.algebraicType.value.variants.findIndex(
|
|
117
|
+
variant => variant.name === name
|
|
118
|
+
);
|
|
119
|
+
if (tag === -1) {
|
|
120
|
+
throw new RangeError(`Unknown ClientMessage variant: ${name}`);
|
|
121
|
+
}
|
|
122
|
+
return tag;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const CLIENT_MESSAGE_CALL_REDUCER_TAG =
|
|
126
|
+
getClientMessageVariantTag('CallReducer');
|
|
127
|
+
const CLIENT_MESSAGE_CALL_PROCEDURE_TAG =
|
|
128
|
+
getClientMessageVariantTag('CallProcedure');
|
|
129
|
+
// Keep individual v3 frames bounded so one burst does not monopolize the send
|
|
130
|
+
// path or create very large websocket writes.
|
|
131
|
+
const MAX_V3_OUTBOUND_FRAME_BYTES = 256 * 1024;
|
|
132
|
+
|
|
101
133
|
export class DbConnectionImpl<RemoteModule extends UntypedRemoteModule>
|
|
102
134
|
implements DbContext<RemoteModule>
|
|
103
135
|
{
|
|
@@ -141,14 +173,19 @@ export class DbConnectionImpl<RemoteModule extends UntypedRemoteModule>
|
|
|
141
173
|
* The `ConnectionId` of the connection to to the database.
|
|
142
174
|
*/
|
|
143
175
|
connectionId: ConnectionId = ConnectionId.random();
|
|
176
|
+
#connectionIdHex = this.connectionId.toHexString();
|
|
144
177
|
|
|
145
178
|
// These fields are meant to be strictly private.
|
|
146
179
|
#queryId = 0;
|
|
147
180
|
#requestId = 0;
|
|
148
181
|
#eventId = 0;
|
|
149
182
|
#emitter: EventEmitter<ConnectionEvent>;
|
|
150
|
-
#
|
|
151
|
-
#
|
|
183
|
+
#inboundQueue: Uint8Array[] = [];
|
|
184
|
+
#inboundQueueOffset = 0;
|
|
185
|
+
#isDrainingInboundQueue = false;
|
|
186
|
+
#outboundQueue: Uint8Array<ArrayBuffer>[] = [];
|
|
187
|
+
#isOutboundFlushScheduled = false;
|
|
188
|
+
#negotiatedWsProtocol: NegotiatedWsProtocol = V2_WS_PROTOCOL;
|
|
152
189
|
#subscriptionManager = new SubscriptionManager<RemoteModule>();
|
|
153
190
|
#remoteModule: RemoteModule;
|
|
154
191
|
#reducerCallbacks = new Map<
|
|
@@ -158,6 +195,10 @@ export class DbConnectionImpl<RemoteModule extends UntypedRemoteModule>
|
|
|
158
195
|
#reducerCallInfo = new Map<number, { name: string; args: object }>();
|
|
159
196
|
#procedureCallbacks = new Map<number, ProcedureCallback>();
|
|
160
197
|
#rowDeserializers: Record<string, Deserializer<any>>;
|
|
198
|
+
#rowIdMetadata: Record<
|
|
199
|
+
string,
|
|
200
|
+
{ primaryKeyColName?: string; primaryKeyColType?: AlgebraicType }
|
|
201
|
+
>;
|
|
161
202
|
#reducerArgsSerializers: Record<
|
|
162
203
|
string,
|
|
163
204
|
{ serialize: Serializer<any>; deserialize: Deserializer<any> }
|
|
@@ -166,15 +207,22 @@ export class DbConnectionImpl<RemoteModule extends UntypedRemoteModule>
|
|
|
166
207
|
string,
|
|
167
208
|
{ serializeArgs: Serializer<any>; deserializeReturn: Deserializer<any> }
|
|
168
209
|
>;
|
|
210
|
+
#reducerNameBytes: Record<string, Uint8Array>;
|
|
211
|
+
#procedureNameBytes: Record<string, Uint8Array>;
|
|
169
212
|
#sourceNameToTableDef: Record<string, Values<RemoteModule['tables']>>;
|
|
213
|
+
#messageReader = new BinaryReader(new Uint8Array());
|
|
214
|
+
#rowListReader = new BinaryReader(new Uint8Array());
|
|
215
|
+
#clientFrameEncoder = new BinaryWriter(1024);
|
|
216
|
+
#boundSubscriptionBuilder!: () => SubscriptionBuilderImpl<RemoteModule>;
|
|
217
|
+
#boundDisconnect!: () => void;
|
|
170
218
|
|
|
171
219
|
// These fields are not part of the public API, but in a pinch you
|
|
172
220
|
// could use JavaScript to access them by bypassing TypeScript's
|
|
173
221
|
// private fields.
|
|
174
222
|
// We use them in testing.
|
|
175
223
|
private clientCache: ClientCache<RemoteModule>;
|
|
176
|
-
private ws?:
|
|
177
|
-
private wsPromise: Promise<
|
|
224
|
+
private ws?: WebSocketAdapter;
|
|
225
|
+
private wsPromise: Promise<WebSocketAdapter | undefined>;
|
|
178
226
|
|
|
179
227
|
constructor({
|
|
180
228
|
uri,
|
|
@@ -203,8 +251,11 @@ export class DbConnectionImpl<RemoteModule extends UntypedRemoteModule>
|
|
|
203
251
|
|
|
204
252
|
this.#remoteModule = remoteModule;
|
|
205
253
|
this.#emitter = emitter;
|
|
254
|
+
this.#boundSubscriptionBuilder = this.subscriptionBuilder.bind(this);
|
|
255
|
+
this.#boundDisconnect = this.disconnect.bind(this);
|
|
206
256
|
|
|
207
257
|
this.#rowDeserializers = Object.create(null);
|
|
258
|
+
this.#rowIdMetadata = Object.create(null);
|
|
208
259
|
this.#sourceNameToTableDef = Object.create(null);
|
|
209
260
|
for (const table of Object.values(remoteModule.tables)) {
|
|
210
261
|
this.#rowDeserializers[table.sourceName] = ProductType.makeDeserializer(
|
|
@@ -213,17 +264,29 @@ export class DbConnectionImpl<RemoteModule extends UntypedRemoteModule>
|
|
|
213
264
|
this.#sourceNameToTableDef[table.sourceName] = table as Values<
|
|
214
265
|
RemoteModule['tables']
|
|
215
266
|
>;
|
|
267
|
+
const primaryKeyColumn = Object.entries(table.columns).find(
|
|
268
|
+
([, column]) => column.columnMetadata.isPrimaryKey
|
|
269
|
+
);
|
|
270
|
+
this.#rowIdMetadata[table.sourceName] = primaryKeyColumn
|
|
271
|
+
? {
|
|
272
|
+
primaryKeyColName: primaryKeyColumn[0],
|
|
273
|
+
primaryKeyColType: primaryKeyColumn[1].typeBuilder.algebraicType,
|
|
274
|
+
}
|
|
275
|
+
: {};
|
|
216
276
|
}
|
|
217
277
|
|
|
218
278
|
this.#reducerArgsSerializers = Object.create(null);
|
|
279
|
+
this.#reducerNameBytes = Object.create(null);
|
|
219
280
|
for (const reducer of remoteModule.reducers) {
|
|
220
281
|
this.#reducerArgsSerializers[reducer.name] = {
|
|
221
282
|
serialize: ProductType.makeSerializer(reducer.paramsType),
|
|
222
283
|
deserialize: ProductType.makeDeserializer(reducer.paramsType),
|
|
223
284
|
};
|
|
285
|
+
this.#reducerNameBytes[reducer.name] = TEXT_ENCODER.encode(reducer.name);
|
|
224
286
|
}
|
|
225
287
|
|
|
226
288
|
this.#procedureSerializers = Object.create(null);
|
|
289
|
+
this.#procedureNameBytes = Object.create(null);
|
|
227
290
|
for (const procedure of remoteModule.procedures) {
|
|
228
291
|
this.#procedureSerializers[procedure.name] = {
|
|
229
292
|
serializeArgs: ProductType.makeSerializer(
|
|
@@ -233,10 +296,12 @@ export class DbConnectionImpl<RemoteModule extends UntypedRemoteModule>
|
|
|
233
296
|
procedure.returnType.algebraicType
|
|
234
297
|
),
|
|
235
298
|
};
|
|
299
|
+
this.#procedureNameBytes[procedure.name] = TEXT_ENCODER.encode(
|
|
300
|
+
procedure.name
|
|
301
|
+
);
|
|
236
302
|
}
|
|
237
303
|
|
|
238
|
-
|
|
239
|
-
url.searchParams.set('connection_id', connectionId);
|
|
304
|
+
url.searchParams.set('connection_id', this.#connectionIdHex);
|
|
240
305
|
|
|
241
306
|
this.clientCache = new ClientCache<RemoteModule>();
|
|
242
307
|
this.db = this.#makeDbView();
|
|
@@ -246,7 +311,7 @@ export class DbConnectionImpl<RemoteModule extends UntypedRemoteModule>
|
|
|
246
311
|
this.wsPromise = createWSFn({
|
|
247
312
|
url,
|
|
248
313
|
nameOrAddress,
|
|
249
|
-
wsProtocol:
|
|
314
|
+
wsProtocol: [...PREFERRED_WS_PROTOCOLS],
|
|
250
315
|
authToken: token,
|
|
251
316
|
compression: compression,
|
|
252
317
|
lightMode: lightMode,
|
|
@@ -302,20 +367,25 @@ export class DbConnectionImpl<RemoteModule extends UntypedRemoteModule>
|
|
|
302
367
|
#makeReducers(def: RemoteModule): ReducersView<RemoteModule> {
|
|
303
368
|
const out: Record<string, unknown> = {};
|
|
304
369
|
|
|
305
|
-
const writer = new BinaryWriter(1024);
|
|
306
|
-
|
|
307
370
|
for (const reducer of def.reducers) {
|
|
308
371
|
const reducerName = reducer.name;
|
|
372
|
+
const encodedReducerName = this.#reducerNameBytes[reducerName];
|
|
309
373
|
const key = reducer.accessorName;
|
|
310
374
|
|
|
311
375
|
const { serialize: serializeArgs } =
|
|
312
376
|
this.#reducerArgsSerializers[reducerName];
|
|
313
377
|
|
|
314
378
|
(out as any)[key] = (params: InferTypeOfRow<typeof reducer.params>) => {
|
|
379
|
+
const writer = this.#reducerArgsEncoder;
|
|
315
380
|
writer.clear();
|
|
316
381
|
serializeArgs(writer, params);
|
|
317
382
|
const argsBuffer = writer.getBuffer();
|
|
318
|
-
return this
|
|
383
|
+
return this.#callReducerWithEncodedName(
|
|
384
|
+
reducerName,
|
|
385
|
+
encodedReducerName,
|
|
386
|
+
argsBuffer,
|
|
387
|
+
params
|
|
388
|
+
);
|
|
319
389
|
};
|
|
320
390
|
}
|
|
321
391
|
|
|
@@ -329,6 +399,7 @@ export class DbConnectionImpl<RemoteModule extends UntypedRemoteModule>
|
|
|
329
399
|
|
|
330
400
|
for (const procedure of def.procedures) {
|
|
331
401
|
const procedureName = procedure.name;
|
|
402
|
+
const encodedProcedureName = this.#procedureNameBytes[procedureName];
|
|
332
403
|
const key = procedure.accessorName;
|
|
333
404
|
|
|
334
405
|
const { serializeArgs, deserializeReturn } =
|
|
@@ -340,7 +411,11 @@ export class DbConnectionImpl<RemoteModule extends UntypedRemoteModule>
|
|
|
340
411
|
writer.clear();
|
|
341
412
|
serializeArgs(writer, params);
|
|
342
413
|
const argsBuffer = writer.getBuffer();
|
|
343
|
-
return this
|
|
414
|
+
return this.#callProcedureWithEncodedName(
|
|
415
|
+
procedureName,
|
|
416
|
+
encodedProcedureName,
|
|
417
|
+
argsBuffer
|
|
418
|
+
).then(returnBuf => {
|
|
344
419
|
return deserializeReturn(new BinaryReader(returnBuf));
|
|
345
420
|
});
|
|
346
421
|
};
|
|
@@ -357,13 +432,12 @@ export class DbConnectionImpl<RemoteModule extends UntypedRemoteModule>
|
|
|
357
432
|
>
|
|
358
433
|
>
|
|
359
434
|
): EventContextInterface<RemoteModule> {
|
|
360
|
-
// Bind methods to preserve `this` (#private fields safe)
|
|
361
435
|
return {
|
|
362
436
|
db: this.db,
|
|
363
437
|
reducers: this.reducers,
|
|
364
438
|
isActive: this.isActive,
|
|
365
|
-
subscriptionBuilder: this
|
|
366
|
-
disconnect: this
|
|
439
|
+
subscriptionBuilder: this.#boundSubscriptionBuilder,
|
|
440
|
+
disconnect: this.#boundDisconnect,
|
|
367
441
|
event,
|
|
368
442
|
};
|
|
369
443
|
}
|
|
@@ -424,24 +498,18 @@ export class DbConnectionImpl<RemoteModule extends UntypedRemoteModule>
|
|
|
424
498
|
rowList: BsatnRowList
|
|
425
499
|
): Operation[] {
|
|
426
500
|
const buffer = rowList.rowsData;
|
|
427
|
-
const reader =
|
|
501
|
+
const reader = this.#rowListReader;
|
|
502
|
+
reader.reset(buffer);
|
|
428
503
|
const rows: Operation[] = [];
|
|
429
504
|
|
|
430
505
|
const deserializeRow = this.#rowDeserializers[tableName];
|
|
431
|
-
const
|
|
432
|
-
|
|
433
|
-
const columnsArray = Object.entries(table.columns);
|
|
434
|
-
const primaryKeyColumnEntry = columnsArray.find(
|
|
435
|
-
col => col[1].columnMetadata.isPrimaryKey
|
|
436
|
-
);
|
|
506
|
+
const { primaryKeyColName, primaryKeyColType } =
|
|
507
|
+
this.#rowIdMetadata[tableName];
|
|
437
508
|
let previousOffset = 0;
|
|
438
509
|
while (reader.remaining > 0) {
|
|
439
510
|
const row = deserializeRow(reader);
|
|
440
511
|
let rowId: ComparablePrimitive | undefined = undefined;
|
|
441
|
-
if (
|
|
442
|
-
const primaryKeyColName = primaryKeyColumnEntry[0];
|
|
443
|
-
const primaryKeyColType =
|
|
444
|
-
primaryKeyColumnEntry[1].typeBuilder.algebraicType;
|
|
512
|
+
if (primaryKeyColName !== undefined && primaryKeyColType !== undefined) {
|
|
445
513
|
rowId = AlgebraicType.intoMapKey(
|
|
446
514
|
primaryKeyColType,
|
|
447
515
|
row[primaryKeyColName]
|
|
@@ -541,47 +609,175 @@ export class DbConnectionImpl<RemoteModule extends UntypedRemoteModule>
|
|
|
541
609
|
return this.#mergeTableUpdates(updates);
|
|
542
610
|
}
|
|
543
611
|
|
|
544
|
-
#flushOutboundQueue(wsResolved:
|
|
612
|
+
#flushOutboundQueue(wsResolved: WebSocketAdapter): void {
|
|
613
|
+
if (this.#negotiatedWsProtocol === V3_WS_PROTOCOL) {
|
|
614
|
+
this.#flushOutboundQueueV3(wsResolved);
|
|
615
|
+
return;
|
|
616
|
+
}
|
|
617
|
+
this.#flushOutboundQueueV2(wsResolved);
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
#flushOutboundQueueV2(wsResolved: WebSocketAdapter): void {
|
|
545
621
|
const pending = this.#outboundQueue.splice(0);
|
|
546
622
|
for (const message of pending) {
|
|
547
623
|
wsResolved.send(message);
|
|
548
624
|
}
|
|
549
625
|
}
|
|
550
626
|
|
|
627
|
+
#flushOutboundQueueV3(wsResolved: WebSocketAdapter): void {
|
|
628
|
+
if (this.#outboundQueue.length === 0) {
|
|
629
|
+
return;
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
// Emit at most one bounded frame per flush. If more encoded v2 messages
|
|
633
|
+
// remain in the queue, they are sent by a later scheduled flush so inbound
|
|
634
|
+
// traffic and other tasks get a chance to run between websocket writes.
|
|
635
|
+
const batchSize = countClientMessagesForV3Frame(
|
|
636
|
+
this.#outboundQueue,
|
|
637
|
+
MAX_V3_OUTBOUND_FRAME_BYTES
|
|
638
|
+
);
|
|
639
|
+
wsResolved.send(
|
|
640
|
+
encodeClientMessagesV3(
|
|
641
|
+
this.#clientFrameEncoder,
|
|
642
|
+
this.#outboundQueue,
|
|
643
|
+
batchSize
|
|
644
|
+
)
|
|
645
|
+
);
|
|
646
|
+
|
|
647
|
+
if (batchSize === this.#outboundQueue.length) {
|
|
648
|
+
this.#outboundQueue.length = 0;
|
|
649
|
+
return;
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
this.#outboundQueue.copyWithin(0, batchSize);
|
|
653
|
+
this.#outboundQueue.length -= batchSize;
|
|
654
|
+
if (this.#outboundQueue.length > 0) {
|
|
655
|
+
this.#scheduleDeferredOutboundFlush();
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
#scheduleOutboundFlush(): void {
|
|
660
|
+
this.#scheduleOutboundFlushWith('microtask');
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
#scheduleDeferredOutboundFlush(): void {
|
|
664
|
+
this.#scheduleOutboundFlushWith('next-task');
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
#scheduleOutboundFlushWith(schedule: 'microtask' | 'next-task'): void {
|
|
668
|
+
if (this.#isOutboundFlushScheduled) {
|
|
669
|
+
return;
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
this.#isOutboundFlushScheduled = true;
|
|
673
|
+
const flush = () => {
|
|
674
|
+
this.#isOutboundFlushScheduled = false;
|
|
675
|
+
if (this.ws && this.isActive) {
|
|
676
|
+
this.#flushOutboundQueue(this.ws);
|
|
677
|
+
}
|
|
678
|
+
};
|
|
679
|
+
|
|
680
|
+
// The first v3 flush stays on the current turn so same-tick sends coalesce.
|
|
681
|
+
// Follow-up flushes after a size-capped frame yield to the next task so we
|
|
682
|
+
// do not sit in a tight send loop while inbound websocket work is waiting.
|
|
683
|
+
if (schedule === 'next-task') {
|
|
684
|
+
setTimeout(flush, 0);
|
|
685
|
+
} else {
|
|
686
|
+
queueMicrotask(flush);
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
#reducerArgsEncoder = new BinaryWriter(1024);
|
|
551
691
|
#clientMessageEncoder = new BinaryWriter(1024);
|
|
692
|
+
#sendEncodedMessage(
|
|
693
|
+
encoded: Uint8Array<ArrayBuffer>,
|
|
694
|
+
describe: () => string
|
|
695
|
+
): void {
|
|
696
|
+
stdbLogger('trace', describe);
|
|
697
|
+
if (this.ws && this.isActive) {
|
|
698
|
+
if (this.#negotiatedWsProtocol === V2_WS_PROTOCOL) {
|
|
699
|
+
if (this.#outboundQueue.length) this.#flushOutboundQueue(this.ws);
|
|
700
|
+
this.ws.send(encoded);
|
|
701
|
+
return;
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
this.#outboundQueue.push(encoded.slice());
|
|
705
|
+
this.#scheduleOutboundFlush();
|
|
706
|
+
} else {
|
|
707
|
+
// Use slice() to copy, in case the clientMessageEncoder's buffer gets reused
|
|
708
|
+
// before the connection opens or before a v3 microbatch flush runs.
|
|
709
|
+
this.#outboundQueue.push(encoded.slice());
|
|
710
|
+
}
|
|
711
|
+
}
|
|
712
|
+
|
|
552
713
|
#sendMessage(message: ClientMessage): void {
|
|
553
714
|
const writer = this.#clientMessageEncoder;
|
|
554
715
|
writer.clear();
|
|
555
716
|
ClientMessage.serialize(writer, message);
|
|
556
717
|
const encoded = writer.getBuffer();
|
|
718
|
+
const isLive = !!(this.ws && this.isActive);
|
|
719
|
+
this.#sendEncodedMessage(encoded, () =>
|
|
720
|
+
isLive
|
|
721
|
+
? `Sending message to server: ${stringify(message)}`
|
|
722
|
+
: `Queuing message to server: ${stringify(message)}`
|
|
723
|
+
);
|
|
724
|
+
}
|
|
557
725
|
|
|
558
|
-
|
|
559
|
-
|
|
726
|
+
#sendCallReducerMessage(
|
|
727
|
+
requestId: number,
|
|
728
|
+
reducerNameBytes: Uint8Array,
|
|
729
|
+
argsBuffer: Uint8Array
|
|
730
|
+
): void {
|
|
731
|
+
const writer = this.#clientMessageEncoder;
|
|
732
|
+
writer.clear();
|
|
733
|
+
writer.writeByte(CLIENT_MESSAGE_CALL_REDUCER_TAG);
|
|
734
|
+
writer.writeU32(requestId);
|
|
735
|
+
writer.writeU8(0);
|
|
736
|
+
writer.writeUInt8Array(reducerNameBytes);
|
|
737
|
+
writer.writeUInt8Array(argsBuffer);
|
|
738
|
+
const encoded = writer.getBuffer();
|
|
739
|
+
this.#sendEncodedMessage(
|
|
740
|
+
encoded,
|
|
741
|
+
() => `Sending reducer call message to server: requestId=${requestId}`
|
|
742
|
+
);
|
|
743
|
+
}
|
|
560
744
|
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
745
|
+
#sendCallProcedureMessage(
|
|
746
|
+
requestId: number,
|
|
747
|
+
procedureNameBytes: Uint8Array,
|
|
748
|
+
argsBuffer: Uint8Array
|
|
749
|
+
): void {
|
|
750
|
+
const writer = this.#clientMessageEncoder;
|
|
751
|
+
writer.clear();
|
|
752
|
+
writer.writeByte(CLIENT_MESSAGE_CALL_PROCEDURE_TAG);
|
|
753
|
+
writer.writeU32(requestId);
|
|
754
|
+
writer.writeU8(0);
|
|
755
|
+
writer.writeUInt8Array(procedureNameBytes);
|
|
756
|
+
writer.writeUInt8Array(argsBuffer);
|
|
757
|
+
const encoded = writer.getBuffer();
|
|
758
|
+
this.#sendEncodedMessage(
|
|
759
|
+
encoded,
|
|
760
|
+
() => `Sending procedure call message to server: requestId=${requestId}`
|
|
761
|
+
);
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
#setConnectionId(connectionId: ConnectionId): void {
|
|
765
|
+
this.connectionId = connectionId;
|
|
766
|
+
this.#connectionIdHex = connectionId.toHexString();
|
|
574
767
|
}
|
|
575
768
|
|
|
576
769
|
#nextEventId(): string {
|
|
577
770
|
this.#eventId += 1;
|
|
578
|
-
return `${this
|
|
771
|
+
return `${this.#connectionIdHex}:${this.#eventId}`;
|
|
579
772
|
}
|
|
580
773
|
|
|
581
774
|
/**
|
|
582
775
|
* Handles WebSocket onOpen event.
|
|
583
776
|
*/
|
|
584
777
|
#handleOnOpen(): void {
|
|
778
|
+
if (this.ws) {
|
|
779
|
+
this.#negotiatedWsProtocol = normalizeWsProtocol(this.ws.protocol);
|
|
780
|
+
}
|
|
585
781
|
this.isActive = true;
|
|
586
782
|
if (this.ws) {
|
|
587
783
|
this.#flushOutboundQueue(this.ws);
|
|
@@ -629,8 +825,17 @@ export class DbConnectionImpl<RemoteModule extends UntypedRemoteModule>
|
|
|
629
825
|
);
|
|
630
826
|
}
|
|
631
827
|
|
|
632
|
-
|
|
633
|
-
|
|
828
|
+
#dispatchPendingCallbacks(callbacks: readonly PendingCallback[]): void {
|
|
829
|
+
stdbLogger(
|
|
830
|
+
'trace',
|
|
831
|
+
() => `Calling ${callbacks.length} triggered row callbacks`
|
|
832
|
+
);
|
|
833
|
+
for (const callback of callbacks) {
|
|
834
|
+
callback.cb();
|
|
835
|
+
}
|
|
836
|
+
}
|
|
837
|
+
|
|
838
|
+
#processServerMessage(serverMessage: ServerMessage): void {
|
|
634
839
|
stdbLogger(
|
|
635
840
|
'trace',
|
|
636
841
|
() => `Processing server message: ${stringify(serverMessage)}`
|
|
@@ -641,7 +846,7 @@ export class DbConnectionImpl<RemoteModule extends UntypedRemoteModule>
|
|
|
641
846
|
if (!this.token && serverMessage.value.token) {
|
|
642
847
|
this.token = serverMessage.value.token;
|
|
643
848
|
}
|
|
644
|
-
this
|
|
849
|
+
this.#setConnectionId(serverMessage.value.connectionId);
|
|
645
850
|
this.#emitter.emit('connect', this, this.identity, this.token);
|
|
646
851
|
break;
|
|
647
852
|
}
|
|
@@ -668,13 +873,7 @@ export class DbConnectionImpl<RemoteModule extends UntypedRemoteModule>
|
|
|
668
873
|
const callbacks = this.#applyTableUpdates(tableUpdates, eventContext);
|
|
669
874
|
const { event: _, ...subscriptionEventContext } = eventContext;
|
|
670
875
|
subscription.emitter.emit('applied', subscriptionEventContext);
|
|
671
|
-
|
|
672
|
-
'trace',
|
|
673
|
-
() => `Calling ${callbacks.length} triggered row callbacks`
|
|
674
|
-
);
|
|
675
|
-
for (const callback of callbacks) {
|
|
676
|
-
callback.cb();
|
|
677
|
-
}
|
|
876
|
+
this.#dispatchPendingCallbacks(callbacks);
|
|
678
877
|
break;
|
|
679
878
|
}
|
|
680
879
|
case 'UnsubscribeApplied': {
|
|
@@ -700,13 +899,7 @@ export class DbConnectionImpl<RemoteModule extends UntypedRemoteModule>
|
|
|
700
899
|
const { event: _, ...subscriptionEventContext } = eventContext;
|
|
701
900
|
subscription.emitter.emit('end', subscriptionEventContext);
|
|
702
901
|
this.#subscriptionManager.subscriptions.delete(querySetId);
|
|
703
|
-
|
|
704
|
-
'trace',
|
|
705
|
-
() => `Calling ${callbacks.length} triggered row callbacks`
|
|
706
|
-
);
|
|
707
|
-
for (const callback of callbacks) {
|
|
708
|
-
callback.cb();
|
|
709
|
-
}
|
|
902
|
+
this.#dispatchPendingCallbacks(callbacks);
|
|
710
903
|
break;
|
|
711
904
|
}
|
|
712
905
|
case 'SubscriptionError': {
|
|
@@ -760,13 +953,7 @@ export class DbConnectionImpl<RemoteModule extends UntypedRemoteModule>
|
|
|
760
953
|
eventContext,
|
|
761
954
|
serverMessage.value
|
|
762
955
|
);
|
|
763
|
-
|
|
764
|
-
'trace',
|
|
765
|
-
() => `Calling ${callbacks.length} triggered row callbacks`
|
|
766
|
-
);
|
|
767
|
-
for (const callback of callbacks) {
|
|
768
|
-
callback.cb();
|
|
769
|
-
}
|
|
956
|
+
this.#dispatchPendingCallbacks(callbacks);
|
|
770
957
|
break;
|
|
771
958
|
}
|
|
772
959
|
case 'ReducerResult': {
|
|
@@ -798,13 +985,7 @@ export class DbConnectionImpl<RemoteModule extends UntypedRemoteModule>
|
|
|
798
985
|
eventContext,
|
|
799
986
|
result.value.transactionUpdate
|
|
800
987
|
);
|
|
801
|
-
|
|
802
|
-
'trace',
|
|
803
|
-
() => `Calling ${callbacks.length} triggered row callbacks`
|
|
804
|
-
);
|
|
805
|
-
for (const callback of callbacks) {
|
|
806
|
-
callback.cb();
|
|
807
|
-
}
|
|
988
|
+
this.#dispatchPendingCallbacks(callbacks);
|
|
808
989
|
}
|
|
809
990
|
this.#reducerCallInfo.delete(requestId);
|
|
810
991
|
const cb = this.#reducerCallbacks.get(requestId);
|
|
@@ -833,18 +1014,65 @@ export class DbConnectionImpl<RemoteModule extends UntypedRemoteModule>
|
|
|
833
1014
|
}
|
|
834
1015
|
}
|
|
835
1016
|
|
|
1017
|
+
#processV2Message(data: Uint8Array): void {
|
|
1018
|
+
const reader = this.#messageReader;
|
|
1019
|
+
reader.reset(data);
|
|
1020
|
+
this.#processServerMessage(ServerMessage.deserialize(reader));
|
|
1021
|
+
}
|
|
1022
|
+
|
|
1023
|
+
#processMessage(data: Uint8Array): void {
|
|
1024
|
+
if (this.#negotiatedWsProtocol !== V3_WS_PROTOCOL) {
|
|
1025
|
+
this.#processV2Message(data);
|
|
1026
|
+
return;
|
|
1027
|
+
}
|
|
1028
|
+
|
|
1029
|
+
const messageCount = forEachServerMessageV3(
|
|
1030
|
+
this.#messageReader,
|
|
1031
|
+
data,
|
|
1032
|
+
serverMessage => {
|
|
1033
|
+
this.#processServerMessage(serverMessage);
|
|
1034
|
+
}
|
|
1035
|
+
);
|
|
1036
|
+
stdbLogger(
|
|
1037
|
+
'trace',
|
|
1038
|
+
() => `Processing server v3 payload with ${messageCount} message(s)`
|
|
1039
|
+
);
|
|
1040
|
+
}
|
|
1041
|
+
|
|
836
1042
|
/**
|
|
837
1043
|
* Handles WebSocket onMessage event.
|
|
838
1044
|
* @param wsMessage MessageEvent object.
|
|
839
1045
|
*/
|
|
840
1046
|
#handleOnMessage(wsMessage: { data: Uint8Array }): void {
|
|
841
|
-
//
|
|
842
|
-
//
|
|
843
|
-
//
|
|
844
|
-
// current message.
|
|
845
|
-
this.#
|
|
846
|
-
|
|
847
|
-
|
|
1047
|
+
// Queue inbound messages so they are processed strictly in arrival order.
|
|
1048
|
+
// We deliberately drain synchronously instead of promise-chaining each
|
|
1049
|
+
// message, but this still guarantees that we do not begin processing the
|
|
1050
|
+
// next message until the current message has been fully handled.
|
|
1051
|
+
this.#inboundQueue.push(wsMessage.data);
|
|
1052
|
+
if (this.#isDrainingInboundQueue) {
|
|
1053
|
+
return;
|
|
1054
|
+
}
|
|
1055
|
+
|
|
1056
|
+
this.#isDrainingInboundQueue = true;
|
|
1057
|
+
try {
|
|
1058
|
+
// TODO: If this loop starts monopolizing the event loop under sustained
|
|
1059
|
+
// inbound traffic, switch to a chunked drain that periodically yields.
|
|
1060
|
+
while (this.#inboundQueueOffset < this.#inboundQueue.length) {
|
|
1061
|
+
const data = this.#inboundQueue[this.#inboundQueueOffset];
|
|
1062
|
+
this.#inboundQueueOffset += 1;
|
|
1063
|
+
if (data) {
|
|
1064
|
+
this.#processMessage(data);
|
|
1065
|
+
}
|
|
1066
|
+
}
|
|
1067
|
+
} finally {
|
|
1068
|
+
if (this.#inboundQueueOffset >= this.#inboundQueue.length) {
|
|
1069
|
+
this.#inboundQueue.length = 0;
|
|
1070
|
+
} else if (this.#inboundQueueOffset > 0) {
|
|
1071
|
+
this.#inboundQueue = this.#inboundQueue.slice(this.#inboundQueueOffset);
|
|
1072
|
+
}
|
|
1073
|
+
this.#inboundQueueOffset = 0;
|
|
1074
|
+
this.#isDrainingInboundQueue = false;
|
|
1075
|
+
}
|
|
848
1076
|
}
|
|
849
1077
|
|
|
850
1078
|
/**
|
|
@@ -857,6 +1085,59 @@ export class DbConnectionImpl<RemoteModule extends UntypedRemoteModule>
|
|
|
857
1085
|
reducerName: string,
|
|
858
1086
|
argsBuffer: Uint8Array,
|
|
859
1087
|
reducerArgs?: object
|
|
1088
|
+
): Promise<void> {
|
|
1089
|
+
const encodedReducerName = this.#reducerNameBytes[reducerName];
|
|
1090
|
+
if (encodedReducerName) {
|
|
1091
|
+
return this.#callReducerWithEncodedName(
|
|
1092
|
+
reducerName,
|
|
1093
|
+
encodedReducerName,
|
|
1094
|
+
argsBuffer,
|
|
1095
|
+
reducerArgs
|
|
1096
|
+
);
|
|
1097
|
+
}
|
|
1098
|
+
return this.#callReducerGeneric(reducerName, argsBuffer, reducerArgs);
|
|
1099
|
+
}
|
|
1100
|
+
|
|
1101
|
+
#callReducerWithEncodedName(
|
|
1102
|
+
reducerName: string,
|
|
1103
|
+
encodedReducerName: Uint8Array,
|
|
1104
|
+
argsBuffer: Uint8Array,
|
|
1105
|
+
reducerArgs?: object
|
|
1106
|
+
): Promise<void> {
|
|
1107
|
+
const { promise, resolve, reject } = Promise.withResolvers<void>();
|
|
1108
|
+
const requestId = this.#getNextRequestId();
|
|
1109
|
+
this.#sendCallReducerMessage(requestId, encodedReducerName, argsBuffer);
|
|
1110
|
+
if (reducerArgs) {
|
|
1111
|
+
this.#reducerCallInfo.set(requestId, {
|
|
1112
|
+
name: reducerName,
|
|
1113
|
+
args: reducerArgs,
|
|
1114
|
+
});
|
|
1115
|
+
}
|
|
1116
|
+
this.#reducerCallbacks.set(requestId, result => {
|
|
1117
|
+
if (result.tag === 'Ok' || result.tag === 'OkEmpty') {
|
|
1118
|
+
resolve();
|
|
1119
|
+
} else {
|
|
1120
|
+
if (result.tag === 'Err') {
|
|
1121
|
+
/// Interpret the user-returned error as a string.
|
|
1122
|
+
const reader = new BinaryReader(result.value);
|
|
1123
|
+
const errorString = reader.readString();
|
|
1124
|
+
reject(new SenderError(errorString));
|
|
1125
|
+
} else if (result.tag === 'InternalError') {
|
|
1126
|
+
reject(new InternalError(result.value));
|
|
1127
|
+
} else {
|
|
1128
|
+
const unreachable: never = result;
|
|
1129
|
+
reject(new Error('Unexpected reducer result'));
|
|
1130
|
+
void unreachable;
|
|
1131
|
+
}
|
|
1132
|
+
}
|
|
1133
|
+
});
|
|
1134
|
+
return promise;
|
|
1135
|
+
}
|
|
1136
|
+
|
|
1137
|
+
#callReducerGeneric(
|
|
1138
|
+
reducerName: string,
|
|
1139
|
+
argsBuffer: Uint8Array,
|
|
1140
|
+
reducerArgs?: object
|
|
860
1141
|
): Promise<void> {
|
|
861
1142
|
const { promise, resolve, reject } = Promise.withResolvers<void>();
|
|
862
1143
|
const requestId = this.#getNextRequestId();
|
|
@@ -906,7 +1187,8 @@ export class DbConnectionImpl<RemoteModule extends UntypedRemoteModule>
|
|
|
906
1187
|
_paramsType: ProductType,
|
|
907
1188
|
params: object
|
|
908
1189
|
): Promise<void> {
|
|
909
|
-
const writer =
|
|
1190
|
+
const writer = this.#reducerArgsEncoder;
|
|
1191
|
+
writer.clear();
|
|
910
1192
|
this.#reducerArgsSerializers[reducerName].serialize(writer, params);
|
|
911
1193
|
const argsBuffer = writer.getBuffer();
|
|
912
1194
|
return this.callReducer(reducerName, argsBuffer, params);
|
|
@@ -921,6 +1203,39 @@ export class DbConnectionImpl<RemoteModule extends UntypedRemoteModule>
|
|
|
921
1203
|
callProcedure(
|
|
922
1204
|
procedureName: string,
|
|
923
1205
|
argsBuffer: Uint8Array
|
|
1206
|
+
): Promise<Uint8Array> {
|
|
1207
|
+
const encodedProcedureName = this.#procedureNameBytes[procedureName];
|
|
1208
|
+
if (encodedProcedureName) {
|
|
1209
|
+
return this.#callProcedureWithEncodedName(
|
|
1210
|
+
procedureName,
|
|
1211
|
+
encodedProcedureName,
|
|
1212
|
+
argsBuffer
|
|
1213
|
+
);
|
|
1214
|
+
}
|
|
1215
|
+
return this.#callProcedureGeneric(procedureName, argsBuffer);
|
|
1216
|
+
}
|
|
1217
|
+
|
|
1218
|
+
#callProcedureWithEncodedName(
|
|
1219
|
+
procedureName: string,
|
|
1220
|
+
encodedProcedureName: Uint8Array,
|
|
1221
|
+
argsBuffer: Uint8Array
|
|
1222
|
+
): Promise<Uint8Array> {
|
|
1223
|
+
const { promise, resolve, reject } = Promise.withResolvers<Uint8Array>();
|
|
1224
|
+
const requestId = this.#getNextRequestId();
|
|
1225
|
+
this.#sendCallProcedureMessage(requestId, encodedProcedureName, argsBuffer);
|
|
1226
|
+
this.#procedureCallbacks.set(requestId, result => {
|
|
1227
|
+
if (result.tag === 'Ok') {
|
|
1228
|
+
resolve(result.value);
|
|
1229
|
+
} else {
|
|
1230
|
+
reject(result.value);
|
|
1231
|
+
}
|
|
1232
|
+
});
|
|
1233
|
+
return promise;
|
|
1234
|
+
}
|
|
1235
|
+
|
|
1236
|
+
#callProcedureGeneric(
|
|
1237
|
+
procedureName: string,
|
|
1238
|
+
argsBuffer: Uint8Array
|
|
924
1239
|
): Promise<Uint8Array> {
|
|
925
1240
|
const { promise, resolve, reject } = Promise.withResolvers<Uint8Array>();
|
|
926
1241
|
const requestId = this.#getNextRequestId();
|