@mysten/sui 2.28.0 → 2.29.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/CHANGELOG.md +18 -0
- package/dist/bcs/bcs.d.mts +6 -6
- package/dist/bcs/index.d.mts +36 -36
- package/dist/cryptography/signature.d.mts +6 -6
- package/dist/grpc/client.d.mts +1 -1
- package/dist/grpc/client.d.mts.map +1 -1
- package/dist/grpc/client.mjs +3 -5
- package/dist/grpc/client.mjs.map +1 -1
- package/dist/grpc/core.d.mts.map +1 -1
- package/dist/grpc/core.mjs +10 -10
- package/dist/grpc/core.mjs.map +1 -1
- package/dist/grpc/index.d.mts +4 -3
- package/dist/grpc/index.mjs +4 -2
- package/dist/grpc/proto/sui/forking/v1alpha/forking_service.client.d.mts +4 -4
- package/dist/grpc/proto/sui/rpc/v2/move_package_service.client.d.mts +4 -4
- package/dist/grpc/proto/sui/rpc/v2/name_service.client.d.mts +4 -4
- package/dist/grpc/proto/sui/rpc/v2/signature_verification_service.client.d.mts +4 -4
- package/dist/grpc/proto/sui/rpc/v2/subscription_service.client.d.mts +4 -4
- package/dist/grpc/proto/sui/rpc/v2/transaction_execution_service.client.d.mts +4 -4
- package/dist/grpc/transport.d.mts +18 -0
- package/dist/grpc/transport.d.mts.map +1 -0
- package/dist/grpc/transport.mjs +97 -0
- package/dist/grpc/transport.mjs.map +1 -0
- package/dist/transactions/Transaction.d.mts +9 -9
- package/dist/transactions/data/internal.d.mts +127 -127
- package/dist/transactions/data/v1.d.mts +239 -239
- package/dist/transactions/data/v1.d.mts.map +1 -1
- package/dist/transactions/data/v2.d.mts +16 -16
- package/dist/transactions/data/v2.d.mts.map +1 -1
- package/dist/version.mjs +1 -1
- package/dist/version.mjs.map +1 -1
- package/dist/zklogin/bcs.d.mts +14 -14
- package/docs/clients/grpc.md +31 -7
- package/package.json +1 -1
- package/src/grpc/client.ts +12 -4
- package/src/grpc/core.ts +18 -14
- package/src/grpc/index.ts +7 -3
- package/src/grpc/transport.ts +160 -0
- package/src/version.ts +1 -1
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { EnumInputShape } from "@mysten/bcs";
|
|
2
|
-
import * as
|
|
2
|
+
import * as valibot0 from "valibot";
|
|
3
3
|
import { GenericSchema, InferOutput } from "valibot";
|
|
4
4
|
|
|
5
5
|
//#region src/transactions/data/v2.d.ts
|
|
6
|
-
declare const SerializedTransactionDataV2Schema:
|
|
7
|
-
readonly version:
|
|
8
|
-
readonly sender:
|
|
9
|
-
readonly expiration:
|
|
6
|
+
declare const SerializedTransactionDataV2Schema: valibot0.ObjectSchema<{
|
|
7
|
+
readonly version: valibot0.LiteralSchema<2, undefined>;
|
|
8
|
+
readonly sender: valibot0.NullishSchema<valibot0.SchemaWithPipe<readonly [valibot0.StringSchema<undefined>, valibot0.TransformAction<string, string>, valibot0.CheckAction<string, undefined>]>, undefined>;
|
|
9
|
+
readonly expiration: valibot0.NullishSchema<GenericSchema<EnumInputShape<{
|
|
10
10
|
None: true;
|
|
11
11
|
Epoch: string | number;
|
|
12
12
|
ValidDuring: {
|
|
@@ -30,17 +30,17 @@ declare const SerializedTransactionDataV2Schema: valibot1337.ObjectSchema<{
|
|
|
30
30
|
nonce: number;
|
|
31
31
|
};
|
|
32
32
|
}>>, undefined>;
|
|
33
|
-
readonly gasData:
|
|
34
|
-
readonly budget:
|
|
35
|
-
readonly price:
|
|
36
|
-
readonly owner:
|
|
37
|
-
readonly payment:
|
|
38
|
-
readonly objectId:
|
|
39
|
-
readonly version:
|
|
40
|
-
readonly digest:
|
|
33
|
+
readonly gasData: valibot0.ObjectSchema<{
|
|
34
|
+
readonly budget: valibot0.NullableSchema<valibot0.SchemaWithPipe<readonly [valibot0.UnionSchema<[valibot0.StringSchema<undefined>, valibot0.SchemaWithPipe<readonly [valibot0.NumberSchema<undefined>, valibot0.IntegerAction<number, undefined>]>], undefined>, valibot0.CheckAction<string | number, "Invalid u64">]>, undefined>;
|
|
35
|
+
readonly price: valibot0.NullableSchema<valibot0.SchemaWithPipe<readonly [valibot0.UnionSchema<[valibot0.StringSchema<undefined>, valibot0.SchemaWithPipe<readonly [valibot0.NumberSchema<undefined>, valibot0.IntegerAction<number, undefined>]>], undefined>, valibot0.CheckAction<string | number, "Invalid u64">]>, undefined>;
|
|
36
|
+
readonly owner: valibot0.NullableSchema<valibot0.SchemaWithPipe<readonly [valibot0.StringSchema<undefined>, valibot0.TransformAction<string, string>, valibot0.CheckAction<string, undefined>]>, undefined>;
|
|
37
|
+
readonly payment: valibot0.NullableSchema<valibot0.ArraySchema<valibot0.ObjectSchema<{
|
|
38
|
+
readonly objectId: valibot0.SchemaWithPipe<readonly [valibot0.StringSchema<undefined>, valibot0.TransformAction<string, string>, valibot0.CheckAction<string, undefined>]>;
|
|
39
|
+
readonly version: valibot0.SchemaWithPipe<readonly [valibot0.UnionSchema<[valibot0.StringSchema<undefined>, valibot0.SchemaWithPipe<readonly [valibot0.NumberSchema<undefined>, valibot0.IntegerAction<number, undefined>]>], undefined>, valibot0.CheckAction<string | number, "Invalid u64">]>;
|
|
40
|
+
readonly digest: valibot0.StringSchema<undefined>;
|
|
41
41
|
}, undefined>, undefined>, undefined>;
|
|
42
42
|
}, undefined>;
|
|
43
|
-
readonly inputs:
|
|
43
|
+
readonly inputs: valibot0.ArraySchema<GenericSchema<EnumInputShape<{
|
|
44
44
|
Object: EnumInputShape<{
|
|
45
45
|
ImmOrOwnedObject: {
|
|
46
46
|
objectId: string;
|
|
@@ -85,7 +85,7 @@ declare const SerializedTransactionDataV2Schema: valibot1337.ObjectSchema<{
|
|
|
85
85
|
};
|
|
86
86
|
};
|
|
87
87
|
}>>, undefined>;
|
|
88
|
-
readonly commands:
|
|
88
|
+
readonly commands: valibot0.ArraySchema<GenericSchema<EnumInputShape<{
|
|
89
89
|
MoveCall: {
|
|
90
90
|
package: string;
|
|
91
91
|
module: string;
|
|
@@ -184,7 +184,7 @@ declare const SerializedTransactionDataV2Schema: valibot1337.ObjectSchema<{
|
|
|
184
184
|
};
|
|
185
185
|
};
|
|
186
186
|
}>>, undefined>;
|
|
187
|
-
readonly digest:
|
|
187
|
+
readonly digest: valibot0.OptionalSchema<valibot0.NullableSchema<valibot0.StringSchema<undefined>, undefined>, undefined>;
|
|
188
188
|
}, undefined>;
|
|
189
189
|
type SerializedTransactionDataV2 = InferOutput<typeof SerializedTransactionDataV2Schema>;
|
|
190
190
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"v2.d.mts","names":[],"sources":["../../../src/transactions/data/v2.ts"],"mappings":";;;;;cAqJa,iCAAA,
|
|
1
|
+
{"version":3,"file":"v2.d.mts","names":[],"sources":["../../../src/transactions/data/v2.ts"],"mappings":";;;;;cAqJa,iCAAA,WAAiC,YAAA;EAAA,kBAQ5C,QAAA,CAAA,aAAA;EAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KAEU,2BAAA,GAA8B,WAAA,QAAmB,iCAAA"}
|
package/dist/version.mjs
CHANGED
package/dist/version.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"version.mjs","names":[],"sources":["../src/version.ts"],"sourcesContent":["// Copyright (c) Mysten Labs, Inc.\n// SPDX-License-Identifier: Apache-2.0\n\n// This file is generated by genversion.mjs. Do not edit it directly.\n\nexport const PACKAGE_VERSION = '2.
|
|
1
|
+
{"version":3,"file":"version.mjs","names":[],"sources":["../src/version.ts"],"sourcesContent":["// Copyright (c) Mysten Labs, Inc.\n// SPDX-License-Identifier: Apache-2.0\n\n// This file is generated by genversion.mjs. Do not edit it directly.\n\nexport const PACKAGE_VERSION = '2.29.0';\n"],"mappings":";AAKA,MAAa,kBAAkB"}
|
package/dist/zklogin/bcs.d.mts
CHANGED
|
@@ -1,31 +1,31 @@
|
|
|
1
|
-
import * as
|
|
1
|
+
import * as _mysten_bcs1378 from "@mysten/bcs";
|
|
2
2
|
import { InferBcsInput } from "@mysten/bcs";
|
|
3
3
|
|
|
4
4
|
//#region src/zklogin/bcs.d.ts
|
|
5
|
-
declare const zkLoginSignature:
|
|
6
|
-
inputs:
|
|
7
|
-
proofPoints:
|
|
8
|
-
a:
|
|
5
|
+
declare const zkLoginSignature: _mysten_bcs1378.BcsStruct<{
|
|
6
|
+
inputs: _mysten_bcs1378.BcsStruct<{
|
|
7
|
+
proofPoints: _mysten_bcs1378.BcsStruct<{
|
|
8
|
+
a: _mysten_bcs1378.BcsType<string[], Iterable<string> & {
|
|
9
9
|
length: number;
|
|
10
10
|
}, string>;
|
|
11
|
-
b:
|
|
11
|
+
b: _mysten_bcs1378.BcsType<string[][], Iterable<Iterable<string> & {
|
|
12
12
|
length: number;
|
|
13
13
|
}> & {
|
|
14
14
|
length: number;
|
|
15
15
|
}, string>;
|
|
16
|
-
c:
|
|
16
|
+
c: _mysten_bcs1378.BcsType<string[], Iterable<string> & {
|
|
17
17
|
length: number;
|
|
18
18
|
}, string>;
|
|
19
19
|
}, string>;
|
|
20
|
-
issBase64Details:
|
|
21
|
-
value:
|
|
22
|
-
indexMod4:
|
|
20
|
+
issBase64Details: _mysten_bcs1378.BcsStruct<{
|
|
21
|
+
value: _mysten_bcs1378.BcsType<string, string, "string">;
|
|
22
|
+
indexMod4: _mysten_bcs1378.BcsType<number, number, "u8">;
|
|
23
23
|
}, string>;
|
|
24
|
-
headerBase64:
|
|
25
|
-
addressSeed:
|
|
24
|
+
headerBase64: _mysten_bcs1378.BcsType<string, string, "string">;
|
|
25
|
+
addressSeed: _mysten_bcs1378.BcsType<string, string, "string">;
|
|
26
26
|
}, string>;
|
|
27
|
-
maxEpoch:
|
|
28
|
-
userSignature:
|
|
27
|
+
maxEpoch: _mysten_bcs1378.BcsType<string, string | number | bigint, "u64">;
|
|
28
|
+
userSignature: _mysten_bcs1378.BcsType<Uint8Array<ArrayBufferLike>, Iterable<number>, "vector<u8>">;
|
|
29
29
|
}, string>;
|
|
30
30
|
type ZkLoginSignature = InferBcsInput<typeof zkLoginSignature>;
|
|
31
31
|
type ZkLoginSignatureInputs = ZkLoginSignature['inputs'];
|
package/docs/clients/grpc.md
CHANGED
|
@@ -55,16 +55,39 @@ console.log(transaction.digest, result.protoJson);
|
|
|
55
55
|
|
|
56
56
|
## Transport options
|
|
57
57
|
|
|
58
|
-
By default, `SuiGrpcClient` uses `GrpcWebFetchTransport
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
58
|
+
By default, `SuiGrpcClient` uses `GrpcWebFetchTransport`, which works in browsers and Node.js
|
|
59
|
+
through the Fetch API. `@mysten/sui/grpc` exports it alongside the `GrpcWebOptions` and
|
|
60
|
+
`RpcTransport` types, and the `RpcError` class and `GrpcStatusCode` enum that gRPC failures use, so
|
|
61
|
+
you can configure a transport and handle its errors without adding `@protobuf-ts/*` as a direct
|
|
62
|
+
dependency.
|
|
63
|
+
|
|
64
|
+
That class extends the transport of the same name from
|
|
65
|
+
[protobuf-ts](https://github.com/timostamm/protobuf-ts) and fixes two defects in it:
|
|
66
|
+
|
|
67
|
+
- It decodes status messages, rather than delivering them as `Object%20not%20found:%200x1`.
|
|
68
|
+
- It codes a call that an abort ended from the reason the abort carried: `DEADLINE_EXCEEDED` for an
|
|
69
|
+
`AbortSignal.timeout`, the status on a reason carrying one, and `CANCELLED` otherwise. Upstream
|
|
70
|
+
reports all but a standard `AbortError` cancellation as `INTERNAL`.
|
|
71
|
+
|
|
72
|
+
Build custom transports from this class. `SuiGrpcClient` uses a transport you pass it as given.
|
|
63
73
|
|
|
64
74
|
### gRPC-web transport (default)
|
|
65
75
|
|
|
66
|
-
The default transport uses the gRPC-web protocol over HTTP/1.1 or HTTP/2.
|
|
67
|
-
`
|
|
76
|
+
The default transport uses the gRPC-web protocol over HTTP/1.1 or HTTP/2. `SuiGrpcClient` passes the
|
|
77
|
+
`GrpcWebOptions` it is given to the transport it builds, so most configuration needs no transport of
|
|
78
|
+
your own. The one exception is `abort`: Core API calls each carry their own signal, so a client-wide
|
|
79
|
+
one is not forwarded.
|
|
80
|
+
|
|
81
|
+
```typescript
|
|
82
|
+
|
|
83
|
+
const client = new SuiGrpcClient({
|
|
84
|
+
network: 'testnet',
|
|
85
|
+
baseUrl: 'https://your-custom-grpc-endpoint.com',
|
|
86
|
+
format: 'binary', // default is 'text' (base64-encoded)
|
|
87
|
+
});
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Build the transport yourself to share one between clients, or to subclass it:
|
|
68
91
|
|
|
69
92
|
```typescript
|
|
70
93
|
|
|
@@ -228,6 +251,7 @@ const result = parseGrpcTransactionResponse(response.transaction!, {
|
|
|
228
251
|
| `parseGrpcTransactionResponse` | Raw `ExecutedTransaction` to the unified result shape |
|
|
229
252
|
| `parseGrpcSimulateTransactionResponse` | Raw simulation response to the unified result shape |
|
|
230
253
|
| `GrpcWebFetchTransport`, `GrpcWebOptions`, `RpcTransport` | Configuring a [transport](#transport-options) |
|
|
254
|
+
| `RpcError`, `GrpcStatusCode` | Narrowing and coding gRPC failures |
|
|
231
255
|
| `SuiGrpcClientOptions`, `GrpcTransactionInclude`, … | Typing your own wrappers around the client |
|
|
232
256
|
| `isSuiGrpcClient` | Type guard for narrowing an unknown client |
|
|
233
257
|
|
package/package.json
CHANGED
package/src/grpc/client.ts
CHANGED
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
// SPDX-License-Identifier: Apache-2.0
|
|
3
3
|
|
|
4
4
|
import type { GrpcWebOptions } from '@protobuf-ts/grpcweb-transport';
|
|
5
|
-
import { GrpcWebFetchTransport } from '@protobuf-ts/grpcweb-transport';
|
|
6
5
|
import { TransactionExecutionServiceClient } from './proto/sui/rpc/v2/transaction_execution_service.client.js';
|
|
7
6
|
import { LedgerServiceClient } from './proto/sui/rpc/v2/ledger_service.client.js';
|
|
8
7
|
import { MovePackageServiceClient } from './proto/sui/rpc/v2/move_package_service.client.js';
|
|
@@ -19,6 +18,7 @@ import { fromBase64, toBase64 } from '@mysten/utils';
|
|
|
19
18
|
import { NameServiceClient } from './proto/sui/rpc/v2/name_service.client.js';
|
|
20
19
|
import { ForkingServiceClient } from './proto/sui/forking/v1alpha/forking_service.client.js';
|
|
21
20
|
import type { TransactionPlugin } from '../transactions/index.js';
|
|
21
|
+
import { GrpcWebFetchTransport } from './transport.js';
|
|
22
22
|
|
|
23
23
|
interface SuiGrpcTransportOptions extends GrpcWebOptions {
|
|
24
24
|
transport?: never;
|
|
@@ -157,9 +157,17 @@ export class SuiGrpcClient extends BaseClient implements SuiClientTypes.Transpor
|
|
|
157
157
|
|
|
158
158
|
constructor(options: SuiGrpcClientOptions) {
|
|
159
159
|
super({ network: options.network });
|
|
160
|
-
const
|
|
161
|
-
|
|
162
|
-
|
|
160
|
+
const {
|
|
161
|
+
network: _network,
|
|
162
|
+
mvr: _mvr,
|
|
163
|
+
// Not forwarded: every Core API call passes its own `signal`, which would overwrite it.
|
|
164
|
+
abort: _abort,
|
|
165
|
+
transport: providedTransport,
|
|
166
|
+
...transportOptions
|
|
167
|
+
} = options as SuiGrpcClientOptions & SuiGrpcTransportOptions & { transport?: RpcTransport };
|
|
168
|
+
|
|
169
|
+
// A caller-supplied transport is used as given. See ./transport.ts for the default.
|
|
170
|
+
const transport = providedTransport ?? new GrpcWebFetchTransport(transportOptions);
|
|
163
171
|
this.transactionExecutionService = new TransactionExecutionServiceClient(transport);
|
|
164
172
|
this.ledgerService = new LedgerServiceClient(transport);
|
|
165
173
|
this.stateService = new StateServiceClient(transport);
|
package/src/grpc/core.ts
CHANGED
|
@@ -69,24 +69,28 @@ import {
|
|
|
69
69
|
validateTransactionQuery,
|
|
70
70
|
} from '../client/query-filters.js';
|
|
71
71
|
import { toGrpcEventFilter, toGrpcTransactionFilter } from './filters.js';
|
|
72
|
+
import { hasDecodedStatusMessage } from './transport.js';
|
|
72
73
|
|
|
73
74
|
export interface GrpcCoreClientOptions extends CoreClientOptions {
|
|
74
75
|
client: SuiGrpcClient;
|
|
75
76
|
}
|
|
76
77
|
|
|
77
78
|
function isNameServiceResolutionMiss(error: unknown): boolean {
|
|
78
|
-
|
|
79
|
-
if (error
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
79
|
+
// Read by shape, so an error from another copy of runtime-rpc is still recognised.
|
|
80
|
+
if (typeof error !== 'object' || error === null) return false;
|
|
81
|
+
|
|
82
|
+
const { code, message } = error as { code?: unknown; message?: unknown };
|
|
83
|
+
if (typeof code !== 'string' || typeof message !== 'string') return false;
|
|
84
|
+
|
|
85
|
+
if (code === GrpcStatusCode[GrpcStatusCode.NOT_FOUND]) return true;
|
|
86
|
+
if (code !== GrpcStatusCode[GrpcStatusCode.RESOURCE_EXHAUSTED]) return false;
|
|
87
|
+
|
|
88
|
+
// The service reports an expired name as RESOURCE_EXHAUSTED with no structured reason, so match
|
|
89
|
+
// the status text and leave other RESOURCE_EXHAUSTED failures alone. A transport this package did
|
|
90
|
+
// not build leaves `grpc-message` encoded, hence the second form.
|
|
91
|
+
if (message === 'name has expired') return true;
|
|
92
|
+
|
|
93
|
+
return !hasDecodedStatusMessage(error) && message === 'name%20has%20expired';
|
|
90
94
|
}
|
|
91
95
|
|
|
92
96
|
export class GrpcCoreClient extends CoreClient {
|
|
@@ -987,9 +991,9 @@ export class GrpcCoreClient extends CoreClient {
|
|
|
987
991
|
});
|
|
988
992
|
response = result.response;
|
|
989
993
|
} catch (error) {
|
|
990
|
-
//
|
|
994
|
+
// The transport owns the status text. See ./transport.ts.
|
|
991
995
|
if (error instanceof Error && error.message) {
|
|
992
|
-
throw new SimulationError(
|
|
996
|
+
throw new SimulationError(error.message, { cause: error });
|
|
993
997
|
}
|
|
994
998
|
throw error;
|
|
995
999
|
}
|
package/src/grpc/index.ts
CHANGED
|
@@ -23,10 +23,14 @@ export type {
|
|
|
23
23
|
} from './client.js';
|
|
24
24
|
export type { GrpcCoreClientOptions } from './core.js';
|
|
25
25
|
|
|
26
|
-
//
|
|
27
|
-
|
|
28
|
-
|
|
26
|
+
// Subclasses `@protobuf-ts/grpcweb-transport`'s transport to fix its error handling.
|
|
27
|
+
export { GrpcWebFetchTransport } from './transport.js';
|
|
28
|
+
|
|
29
|
+
// Re-exported so users can configure a transport, and narrow and code the errors it produces,
|
|
30
|
+
// without adding @protobuf-ts/* as a dependency.
|
|
31
|
+
export { GrpcStatusCode } from '@protobuf-ts/grpcweb-transport';
|
|
29
32
|
export type { GrpcWebOptions } from '@protobuf-ts/grpcweb-transport';
|
|
33
|
+
export { RpcError } from '@protobuf-ts/runtime-rpc';
|
|
30
34
|
export type { RpcTransport } from '@protobuf-ts/runtime-rpc';
|
|
31
35
|
|
|
32
36
|
// Export all gRPC proto types as a namespace
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
// Copyright (c) Mysten Labs, Inc.
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
|
|
4
|
+
import { GrpcStatusCode } from '@protobuf-ts/grpcweb-transport';
|
|
5
|
+
import { GrpcWebFetchTransport as UpstreamGrpcWebFetchTransport } from '@protobuf-ts/grpcweb-transport';
|
|
6
|
+
import type {
|
|
7
|
+
MethodInfo,
|
|
8
|
+
RpcOptions,
|
|
9
|
+
ServerStreamingCall,
|
|
10
|
+
UnaryCall,
|
|
11
|
+
} from '@protobuf-ts/runtime-rpc';
|
|
12
|
+
import { RpcError } from '@protobuf-ts/runtime-rpc';
|
|
13
|
+
|
|
14
|
+
// A failed call rejects four promises with the same error, so it is only decoded once. Registered
|
|
15
|
+
// globally so another installed copy of this package sees the same marker.
|
|
16
|
+
const MESSAGE_DECODED = Symbol.for('@mysten/sui/grpc/decoded-status-message');
|
|
17
|
+
|
|
18
|
+
// `grpc-message` is percent-encoded on the wire and the upstream transport does not decode it:
|
|
19
|
+
// https://github.com/timostamm/protobuf-ts/pull/739
|
|
20
|
+
function decodeGrpcStatusMessage(message: string): string {
|
|
21
|
+
if (!message.includes('%')) return message;
|
|
22
|
+
|
|
23
|
+
try {
|
|
24
|
+
return decodeURIComponent(message);
|
|
25
|
+
} catch {
|
|
26
|
+
return message;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function markStatusMessageRead(error: RpcError): boolean {
|
|
31
|
+
if (MESSAGE_DECODED in error) return false;
|
|
32
|
+
Object.defineProperty(error, MESSAGE_DECODED, { value: true, configurable: true });
|
|
33
|
+
|
|
34
|
+
return true;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function decodeStatusMessageOnce(error: RpcError): void {
|
|
38
|
+
if (markStatusMessageRead(error)) error.message = decodeGrpcStatusMessage(error.message);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** Whether this package's transport decoded the error's status text. */
|
|
42
|
+
export function hasDecodedStatusMessage(error: object): boolean {
|
|
43
|
+
return MESSAGE_DECODED in error;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// An abort reason is arbitrary, and reading a property on it runs code the caller wrote. A read
|
|
47
|
+
// that throws counts as absent, so one bad accessor does not decide the status.
|
|
48
|
+
function readProperty(value: unknown, key: 'code' | 'name' | 'message'): unknown {
|
|
49
|
+
if (typeof value !== 'object' || value === null) return undefined;
|
|
50
|
+
|
|
51
|
+
try {
|
|
52
|
+
return (value as Record<string, unknown>)[key];
|
|
53
|
+
} catch {
|
|
54
|
+
return undefined;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// The reason is read by shape, since another copy of runtime-rpc or another realm fails
|
|
59
|
+
// `instanceof`. `AbortSignal.timeout` aborts with a `TimeoutError`.
|
|
60
|
+
function abortStatus(reason: unknown): string {
|
|
61
|
+
const code = readProperty(reason, 'code');
|
|
62
|
+
|
|
63
|
+
// A status name maps to a number; `in` would also match the enum's reverse mapping.
|
|
64
|
+
if (
|
|
65
|
+
typeof code === 'string' &&
|
|
66
|
+
typeof GrpcStatusCode[code as keyof typeof GrpcStatusCode] === 'number'
|
|
67
|
+
) {
|
|
68
|
+
return code;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return readProperty(reason, 'name') === 'TimeoutError'
|
|
72
|
+
? GrpcStatusCode[GrpcStatusCode.DEADLINE_EXCEEDED]
|
|
73
|
+
: GrpcStatusCode[GrpcStatusCode.CANCELLED];
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// The transport reuses the abort reason's message, so matching it says the text is local. A fetch
|
|
77
|
+
// that substitutes its own error (node-fetch) gives text of its own, which holds nothing to decode.
|
|
78
|
+
// The reason is never coerced: a symbol or a throwing hook would throw here.
|
|
79
|
+
function isAbortReasonText(message: string, reason: unknown): boolean {
|
|
80
|
+
if (typeof reason === 'string') return message === reason;
|
|
81
|
+
|
|
82
|
+
const reasonMessage = readProperty(reason, 'message');
|
|
83
|
+
|
|
84
|
+
return typeof reasonMessage === 'string' && message === reasonMessage;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Rewritten in place, so every promise a call rejects surfaces the same error. Only the two codes
|
|
88
|
+
// upstream uses for an abort are re-coded; a failure that raced the abort is relabelled with it.
|
|
89
|
+
function normalizeGrpcError(error: unknown, signal: AbortSignal | undefined): void {
|
|
90
|
+
try {
|
|
91
|
+
normalize(error, signal);
|
|
92
|
+
} catch {
|
|
93
|
+
// Never an unhandled rejection from a discarded handler: the call reports what it was going
|
|
94
|
+
// to report.
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function normalize(error: unknown, signal: AbortSignal | undefined): void {
|
|
99
|
+
if (!(error instanceof RpcError)) return;
|
|
100
|
+
|
|
101
|
+
if (
|
|
102
|
+
signal?.aborted &&
|
|
103
|
+
(error.code === GrpcStatusCode[GrpcStatusCode.INTERNAL] ||
|
|
104
|
+
error.code === GrpcStatusCode[GrpcStatusCode.CANCELLED])
|
|
105
|
+
) {
|
|
106
|
+
error.code = abortStatus(signal.reason);
|
|
107
|
+
|
|
108
|
+
// Text the abort reason supplied is local and holds nothing to decode; anything else came off
|
|
109
|
+
// the wire, even though the abort is what the caller sees. Settled here for the other promises
|
|
110
|
+
// the call rejects, which no longer match the codes above.
|
|
111
|
+
if (isAbortReasonText(error.message, signal.reason)) markStatusMessageRead(error);
|
|
112
|
+
else decodeStatusMessageOnce(error);
|
|
113
|
+
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
decodeStatusMessageOnce(error);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function normalizeRejections(promises: Promise<unknown>[], signal: AbortSignal | undefined) {
|
|
121
|
+
for (const promise of promises) {
|
|
122
|
+
// Registered before the call is returned, so it runs before the consumer's own await.
|
|
123
|
+
promise.then(undefined, (error: unknown) => normalizeGrpcError(error, signal));
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* `GrpcWebFetchTransport` from `@protobuf-ts/grpcweb-transport`, subclassed to decode status
|
|
129
|
+
* messages and to code an aborted call from its reason rather than as `INTERNAL`.
|
|
130
|
+
*
|
|
131
|
+
* `SuiGrpcClient` builds one by default. A transport imported from `@protobuf-ts/grpcweb-transport`
|
|
132
|
+
* keeps that package's behaviour.
|
|
133
|
+
*/
|
|
134
|
+
export class GrpcWebFetchTransport extends UpstreamGrpcWebFetchTransport {
|
|
135
|
+
override unary<I extends object, O extends object>(
|
|
136
|
+
method: MethodInfo<I, O>,
|
|
137
|
+
input: I,
|
|
138
|
+
options: RpcOptions,
|
|
139
|
+
): UnaryCall<I, O> {
|
|
140
|
+
const call = super.unary(method, input, options);
|
|
141
|
+
|
|
142
|
+
normalizeRejections([call.headers, call.response, call.status, call.trailers], options.abort);
|
|
143
|
+
|
|
144
|
+
return call;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
override serverStreaming<I extends object, O extends object>(
|
|
148
|
+
method: MethodInfo<I, O>,
|
|
149
|
+
input: I,
|
|
150
|
+
options: RpcOptions,
|
|
151
|
+
): ServerStreamingCall<I, O> {
|
|
152
|
+
const call = super.serverStreaming(method, input, options);
|
|
153
|
+
|
|
154
|
+
// A finite stream is often read through `responses` without awaiting `status`.
|
|
155
|
+
call.responses.onError((error) => normalizeGrpcError(error, options.abort));
|
|
156
|
+
normalizeRejections([call.headers, call.status, call.trailers], options.abort);
|
|
157
|
+
|
|
158
|
+
return call;
|
|
159
|
+
}
|
|
160
|
+
}
|
package/src/version.ts
CHANGED