@ic-reactor/core 3.0.3-beta.5 → 3.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -4
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +2 -3
- package/dist/client.js.map +1 -1
- package/dist/display/types.d.ts +4 -2
- package/dist/display/types.d.ts.map +1 -1
- package/dist/display/visitor.d.ts +2 -1
- package/dist/display/visitor.d.ts.map +1 -1
- package/dist/display/visitor.js +146 -121
- package/dist/display/visitor.js.map +1 -1
- package/dist/display-reactor.d.ts +9 -41
- package/dist/display-reactor.d.ts.map +1 -1
- package/dist/display-reactor.js +5 -41
- package/dist/display-reactor.js.map +1 -1
- package/dist/reactor.d.ts +17 -1
- package/dist/reactor.d.ts.map +1 -1
- package/dist/reactor.js +60 -44
- package/dist/reactor.js.map +1 -1
- package/dist/types/display-reactor.d.ts +2 -2
- package/dist/types/display-reactor.d.ts.map +1 -1
- package/dist/types/reactor.d.ts +7 -7
- package/dist/types/reactor.d.ts.map +1 -1
- package/dist/types/transform.d.ts +1 -1
- package/dist/types/transform.d.ts.map +1 -1
- package/dist/utils/helper.d.ts +20 -1
- package/dist/utils/helper.d.ts.map +1 -1
- package/dist/utils/helper.js +37 -6
- package/dist/utils/helper.js.map +1 -1
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +1 -0
- package/dist/utils/index.js.map +1 -1
- package/dist/utils/zod.d.ts +34 -0
- package/dist/utils/zod.d.ts.map +1 -0
- package/dist/utils/zod.js +39 -0
- package/dist/utils/zod.js.map +1 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.d.ts.map +1 -1
- package/dist/version.js +2 -1
- package/dist/version.js.map +1 -1
- package/package.json +7 -6
- package/src/client.ts +571 -0
- package/src/display/helper.ts +92 -0
- package/src/display/index.ts +3 -0
- package/src/display/types.ts +91 -0
- package/src/display/visitor.ts +415 -0
- package/src/display-reactor.ts +361 -0
- package/src/errors/index.ts +246 -0
- package/src/index.ts +8 -0
- package/src/reactor.ts +461 -0
- package/src/types/client.ts +110 -0
- package/src/types/display-reactor.ts +73 -0
- package/src/types/index.ts +6 -0
- package/src/types/reactor.ts +188 -0
- package/src/types/result.ts +50 -0
- package/src/types/transform.ts +29 -0
- package/src/types/variant.ts +39 -0
- package/src/utils/agent.ts +201 -0
- package/src/utils/candid.ts +112 -0
- package/src/utils/constants.ts +12 -0
- package/src/utils/helper.ts +155 -0
- package/src/utils/index.ts +5 -0
- package/src/utils/polling.ts +330 -0
- package/src/utils/zod.ts +56 -0
- package/src/version.ts +5 -0
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
ActorMethod,
|
|
3
|
+
ActorSubclass,
|
|
4
|
+
CallConfig,
|
|
5
|
+
PollingOptions,
|
|
6
|
+
} from "@icp-sdk/core/agent"
|
|
7
|
+
import type { Principal } from "@icp-sdk/core/principal"
|
|
8
|
+
import type { QueryKey } from "@tanstack/query-core"
|
|
9
|
+
import type { ClientManager } from "../client"
|
|
10
|
+
import type { CallError, CanisterError } from "../errors"
|
|
11
|
+
import type { OkResult, ErrResult } from "./result"
|
|
12
|
+
import type { DisplayOf, ActorDisplayCodec } from "../display"
|
|
13
|
+
|
|
14
|
+
export interface DefaultActorType {
|
|
15
|
+
[key: string]: ActorMethod<any, any>
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export type BaseActor<T = DefaultActorType> = ActorSubclass<T>
|
|
19
|
+
|
|
20
|
+
export type FunctionName<A = BaseActor> = Extract<keyof A, string>
|
|
21
|
+
|
|
22
|
+
export type FunctionType = "query" | "update"
|
|
23
|
+
|
|
24
|
+
export type CanisterId = string | Principal
|
|
25
|
+
|
|
26
|
+
// Extracts the argument types of an ActorMethod
|
|
27
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
28
|
+
export type ActorMethodParameters<T> =
|
|
29
|
+
T extends ActorMethod<infer Args, any> ? Args : never
|
|
30
|
+
|
|
31
|
+
// Extracts the return type of an ActorMethod
|
|
32
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
33
|
+
export type ActorMethodReturnType<T> =
|
|
34
|
+
T extends ActorMethod<any, infer Ret> ? Ret : never
|
|
35
|
+
|
|
36
|
+
export interface ReactorParameters {
|
|
37
|
+
clientManager: ClientManager
|
|
38
|
+
name: string
|
|
39
|
+
idlFactory: (IDL: any) => any
|
|
40
|
+
canisterId?: CanisterId
|
|
41
|
+
pollingOptions?: PollingOptions
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export type ActorMethodType<A, M extends keyof A> = {
|
|
45
|
+
(...args: ActorMethodParameters<A[M]>): Promise<ActorMethodReturnType<A[M]>>
|
|
46
|
+
withOptions: (
|
|
47
|
+
options?: CallConfig
|
|
48
|
+
) => (
|
|
49
|
+
...args: ActorMethodParameters<A[M]>
|
|
50
|
+
) => Promise<ActorMethodReturnType<A[M]>>
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Registry for argument transformations.
|
|
55
|
+
* Users can augment this interface to add custom transforms:
|
|
56
|
+
*
|
|
57
|
+
* @example
|
|
58
|
+
* ```typescript
|
|
59
|
+
* // In your code, augment the module
|
|
60
|
+
* declare module '@ic-reactor/core' {
|
|
61
|
+
* interface TransformArgsRegistry<T> {
|
|
62
|
+
* myCustom: MyCustomArgTransform<T>
|
|
63
|
+
* }
|
|
64
|
+
* }
|
|
65
|
+
* ```
|
|
66
|
+
*/
|
|
67
|
+
/**
|
|
68
|
+
* Helper to extract arguments type for codecs (unwraps single argument tuples).
|
|
69
|
+
*/
|
|
70
|
+
export type ArgsType<T> = T extends readonly [infer U]
|
|
71
|
+
? U
|
|
72
|
+
: T extends readonly []
|
|
73
|
+
? null
|
|
74
|
+
: T
|
|
75
|
+
|
|
76
|
+
export interface TransformArgsRegistry<T> {
|
|
77
|
+
candid: T
|
|
78
|
+
display: AsDisplayArgs<T>
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Registry for return type transformations.
|
|
83
|
+
* Users can augment this interface to add custom transforms:
|
|
84
|
+
*
|
|
85
|
+
* @example
|
|
86
|
+
* ```typescript
|
|
87
|
+
* declare module '@ic-reactor/core' {
|
|
88
|
+
* interface TransformReturnRegistry<T> {
|
|
89
|
+
* myCustom: MyCustomReturnTransform<T>
|
|
90
|
+
* }
|
|
91
|
+
* }
|
|
92
|
+
* ```
|
|
93
|
+
*/
|
|
94
|
+
// @ts-expect-error - A is used in module augmentation
|
|
95
|
+
export interface TransformReturnRegistry<T, A = BaseActor> {
|
|
96
|
+
candid: T
|
|
97
|
+
display: DisplayOf<T>
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Helper type to transform args array elements using ToDisplay
|
|
102
|
+
*/
|
|
103
|
+
export type AsDisplayArgs<T> = T extends readonly unknown[]
|
|
104
|
+
? { [K in keyof T]: DisplayOf<T[K]> }
|
|
105
|
+
: DisplayOf<T>
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Union of all available transform keys.
|
|
109
|
+
* Automatically includes any user-defined transforms via module augmentation.
|
|
110
|
+
*/
|
|
111
|
+
export type TransformKey = keyof TransformArgsRegistry<unknown>
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Apply argument transformation based on the transform key.
|
|
115
|
+
* Looks up the transform in TransformArgsRegistry.
|
|
116
|
+
*/
|
|
117
|
+
export type ReactorArgs<
|
|
118
|
+
A,
|
|
119
|
+
M extends FunctionName<A>,
|
|
120
|
+
Transform extends TransformKey = "candid",
|
|
121
|
+
> = TransformArgsRegistry<ActorMethodParameters<A[M]>>[Transform]
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Apply return type transformation based on the transform key.
|
|
125
|
+
* Looks up the transform in TransformReturnRegistry.
|
|
126
|
+
*/
|
|
127
|
+
export type ReactorReturnOk<
|
|
128
|
+
A,
|
|
129
|
+
M extends FunctionName<A>,
|
|
130
|
+
Transform extends TransformKey = "candid",
|
|
131
|
+
> = TransformReturnRegistry<OkResult<ActorMethodReturnType<A[M]>>, A>[Transform]
|
|
132
|
+
|
|
133
|
+
export type ReactorReturnErr<
|
|
134
|
+
A,
|
|
135
|
+
M extends FunctionName<A>,
|
|
136
|
+
Transform extends TransformKey = "candid",
|
|
137
|
+
> =
|
|
138
|
+
| CanisterError<
|
|
139
|
+
TransformReturnRegistry<
|
|
140
|
+
ErrResult<ActorMethodReturnType<A[M]>>,
|
|
141
|
+
A
|
|
142
|
+
>[Transform]
|
|
143
|
+
>
|
|
144
|
+
| CallError
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Helper type for actor method codecs returend by getCodec
|
|
148
|
+
*/
|
|
149
|
+
export interface ActorMethodCodecs<A, M extends FunctionName<A>> {
|
|
150
|
+
args: ActorDisplayCodec<
|
|
151
|
+
ArgsType<ActorMethodParameters<A[M]>>,
|
|
152
|
+
DisplayOf<ArgsType<ActorMethodParameters<A[M]>>>
|
|
153
|
+
>
|
|
154
|
+
result: ActorDisplayCodec<
|
|
155
|
+
ActorMethodReturnType<A[M]>,
|
|
156
|
+
DisplayOf<ActorMethodReturnType<A[M]>>
|
|
157
|
+
>
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// ══════════════════════════════════════════════════════════════════════════
|
|
161
|
+
// REACTOR QUERY PARAMS - Reusable parameter types for reactor methods
|
|
162
|
+
// ══════════════════════════════════════════════════════════════════════════
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Basic query parameters for reactor cache operations.
|
|
166
|
+
* Used by: generateQueryKey, getQueryData
|
|
167
|
+
*/
|
|
168
|
+
export interface ReactorQueryParams<
|
|
169
|
+
A,
|
|
170
|
+
M extends FunctionName<A>,
|
|
171
|
+
T extends TransformKey = "candid",
|
|
172
|
+
> {
|
|
173
|
+
functionName: M
|
|
174
|
+
args?: ReactorArgs<A, M, T>
|
|
175
|
+
queryKey?: QueryKey
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Query parameters with optional call configuration.
|
|
180
|
+
* Used by: getQueryOptions, fetchQuery, callMethod
|
|
181
|
+
*/
|
|
182
|
+
export interface ReactorCallParams<
|
|
183
|
+
A,
|
|
184
|
+
M extends FunctionName<A>,
|
|
185
|
+
T extends TransformKey = "candid",
|
|
186
|
+
> extends ReactorQueryParams<A, M, T> {
|
|
187
|
+
callConfig?: CallConfig
|
|
188
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
export type UnwrapOkErrResult<T> = T extends { Ok: infer U }
|
|
2
|
+
? U
|
|
3
|
+
: T extends { ok: infer U }
|
|
4
|
+
? U
|
|
5
|
+
: T extends { Err: infer E }
|
|
6
|
+
? E
|
|
7
|
+
: T extends { err: infer E }
|
|
8
|
+
? E
|
|
9
|
+
: T
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Extract the Ok value from a Result type.
|
|
13
|
+
* Supports both uppercase (Ok/Err - Rust) and lowercase (ok/err - Motoko).
|
|
14
|
+
* - If T is { Ok: U } or { ok: U }, returns U
|
|
15
|
+
* - If T is { Err: E } or { err: E }, returns never (filters it out from unions)
|
|
16
|
+
* - If T is { Ok: U } | { Err: E }, returns U (the Err variant is filtered out)
|
|
17
|
+
* - Otherwise, returns T as-is
|
|
18
|
+
*/
|
|
19
|
+
export type OkResult<T> = T extends { Err: unknown }
|
|
20
|
+
? never
|
|
21
|
+
: T extends { err: unknown }
|
|
22
|
+
? never
|
|
23
|
+
: T extends { Ok: infer U }
|
|
24
|
+
? U
|
|
25
|
+
: T extends { ok: infer U }
|
|
26
|
+
? U
|
|
27
|
+
: T
|
|
28
|
+
|
|
29
|
+
export type ErrResult<T> = T extends { Ok: unknown }
|
|
30
|
+
? never
|
|
31
|
+
: T extends { ok: unknown }
|
|
32
|
+
? never
|
|
33
|
+
: T extends { Err: infer E }
|
|
34
|
+
? E
|
|
35
|
+
: T extends { err: infer E }
|
|
36
|
+
? E
|
|
37
|
+
: never
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Check if T is a Result type ({ Ok: U } | { Err: E } or { ok: U } | { err: E })
|
|
41
|
+
*/
|
|
42
|
+
export type IsOkErrResultType<T> = T extends { Ok: unknown }
|
|
43
|
+
? true
|
|
44
|
+
: T extends { ok: unknown }
|
|
45
|
+
? true
|
|
46
|
+
: T extends { Err: unknown }
|
|
47
|
+
? true
|
|
48
|
+
: T extends { err: unknown }
|
|
49
|
+
? true
|
|
50
|
+
: false
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
type UnionToIntersection<U> = (U extends any ? (x: U) => void : never) extends (
|
|
2
|
+
x: infer I
|
|
3
|
+
) => void
|
|
4
|
+
? I
|
|
5
|
+
: never
|
|
6
|
+
|
|
7
|
+
// The "Last" type extracts the last member of a union type
|
|
8
|
+
type Last<T> =
|
|
9
|
+
UnionToIntersection<T extends any ? (x: T) => void : never> extends (
|
|
10
|
+
x: infer L
|
|
11
|
+
) => void
|
|
12
|
+
? L
|
|
13
|
+
: never
|
|
14
|
+
|
|
15
|
+
/// Convert a union type to a tuple type (order is not guaranteed)
|
|
16
|
+
/// (Note: there are several variants of this trick – choose one that suits your needs.)
|
|
17
|
+
export type UnionToTuple<T, L = Last<T>> = [T] extends [never]
|
|
18
|
+
? []
|
|
19
|
+
: [...UnionToTuple<Exclude<T, L>>, L]
|
|
20
|
+
|
|
21
|
+
export type IsBlobType<T> = T extends Uint8Array
|
|
22
|
+
? true
|
|
23
|
+
: T extends number[]
|
|
24
|
+
? number[] extends T
|
|
25
|
+
? true
|
|
26
|
+
: false
|
|
27
|
+
: false
|
|
28
|
+
|
|
29
|
+
export type IsOptionalType<T> = [T] extends [[] | [any]] ? true : false
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
export type IsCandidVariant<T> = [T] extends [CandidVariantToIntersection<T>]
|
|
2
|
+
? false
|
|
3
|
+
: true
|
|
4
|
+
|
|
5
|
+
export type CandidVariantToIntersection<U> = (
|
|
6
|
+
U extends object ? (k: U) => void : never
|
|
7
|
+
) extends (k: infer I) => void
|
|
8
|
+
? I
|
|
9
|
+
: never
|
|
10
|
+
|
|
11
|
+
export type CandidVariantKey<T> = T extends any ? keyof T : never
|
|
12
|
+
|
|
13
|
+
export type CandidVariantValue<T, K extends CandidVariantKey<T>> =
|
|
14
|
+
T extends Record<K, infer U> ? U : never
|
|
15
|
+
|
|
16
|
+
export type CandidVariant<T> =
|
|
17
|
+
IsCandidVariant<T> extends true
|
|
18
|
+
? {
|
|
19
|
+
_type: CandidVariantKey<T> & string
|
|
20
|
+
} & {
|
|
21
|
+
[K in CandidVariantKey<T> & string as CandidVariantValue<
|
|
22
|
+
T,
|
|
23
|
+
K
|
|
24
|
+
> extends null
|
|
25
|
+
? never
|
|
26
|
+
: K]: CandidVariantValue<T, K>
|
|
27
|
+
}
|
|
28
|
+
: T
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* A type that represents the extracted key and value from a variant
|
|
32
|
+
* Designed to be used with the extractVariant function
|
|
33
|
+
*/
|
|
34
|
+
export type CandidKeyValue<T> = T extends infer U
|
|
35
|
+
? [
|
|
36
|
+
CandidVariantKey<U> & string,
|
|
37
|
+
CandidVariantValue<U, CandidVariantKey<U> & string>,
|
|
38
|
+
]
|
|
39
|
+
: never
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
ApiQueryResponse,
|
|
3
|
+
HttpAgent,
|
|
4
|
+
HttpDetailsResponse,
|
|
5
|
+
PollingOptions,
|
|
6
|
+
SubmitResponse,
|
|
7
|
+
} from "@icp-sdk/core/agent"
|
|
8
|
+
import { Principal } from "@icp-sdk/core/principal"
|
|
9
|
+
|
|
10
|
+
import {
|
|
11
|
+
isV2ResponseBody,
|
|
12
|
+
isV4ResponseBody,
|
|
13
|
+
Certificate,
|
|
14
|
+
lookupResultToBuffer,
|
|
15
|
+
pollForResponse,
|
|
16
|
+
QueryResponseStatus,
|
|
17
|
+
UncertifiedRejectErrorCode,
|
|
18
|
+
RejectError,
|
|
19
|
+
UncertifiedRejectUpdateErrorCode,
|
|
20
|
+
CertifiedRejectErrorCode,
|
|
21
|
+
MissingRootKeyErrorCode,
|
|
22
|
+
ExternalError,
|
|
23
|
+
UnknownError,
|
|
24
|
+
UnexpectedErrorCode,
|
|
25
|
+
} from "@icp-sdk/core/agent"
|
|
26
|
+
|
|
27
|
+
// ══════════════════════════════════════════════════════════════════════
|
|
28
|
+
// QUERY CALL RESPONSE PROCESSING
|
|
29
|
+
// ══════════════════════════════════════════════════════════════════════
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Process a query call response following the exact logic from @icp-sdk/core/agent Actor.
|
|
33
|
+
*
|
|
34
|
+
* @param response - The query call response options
|
|
35
|
+
* @returns The raw reply bytes
|
|
36
|
+
* @throws CallError if the query was rejected
|
|
37
|
+
*/
|
|
38
|
+
export function processQueryCallResponse(
|
|
39
|
+
response: ApiQueryResponse,
|
|
40
|
+
canisterId: Principal,
|
|
41
|
+
methodName: string
|
|
42
|
+
): Uint8Array {
|
|
43
|
+
switch (response.status) {
|
|
44
|
+
case QueryResponseStatus.Rejected: {
|
|
45
|
+
const uncertifiedRejectErrorCode = new UncertifiedRejectErrorCode(
|
|
46
|
+
response.requestId,
|
|
47
|
+
response.reject_code,
|
|
48
|
+
response.reject_message,
|
|
49
|
+
response.error_code,
|
|
50
|
+
response.signatures
|
|
51
|
+
)
|
|
52
|
+
uncertifiedRejectErrorCode.callContext = {
|
|
53
|
+
canisterId,
|
|
54
|
+
methodName,
|
|
55
|
+
httpDetails: response.httpDetails,
|
|
56
|
+
}
|
|
57
|
+
throw RejectError.fromCode(uncertifiedRejectErrorCode)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
case QueryResponseStatus.Replied:
|
|
61
|
+
return response.reply.arg
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// ══════════════════════════════════════════════════════════════════════
|
|
66
|
+
// UPDATE CALL RESPONSE PROCESSING
|
|
67
|
+
// ══════════════════════════════════════════════════════════════════════
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Process an update call response following the exact logic from @icp-sdk/core/agent Actor.
|
|
71
|
+
*
|
|
72
|
+
* This handles:
|
|
73
|
+
* - V4 responses with embedded certificate (sync call response)
|
|
74
|
+
* - V2 responses with immediate rejection
|
|
75
|
+
* - 202 responses that require polling
|
|
76
|
+
*
|
|
77
|
+
* @param result - The submit response from agent.call()
|
|
78
|
+
* @param canisterId - The target canister ID
|
|
79
|
+
* @param methodName - The method name being called
|
|
80
|
+
* @param agent - The HTTP agent
|
|
81
|
+
* @param pollingOptions - Options for polling
|
|
82
|
+
* @param blsVerify - Optional BLS verification function
|
|
83
|
+
* @returns The raw reply bytes
|
|
84
|
+
* @throws RejectError if the call was rejected
|
|
85
|
+
* @throws UnknownError if the response format is unexpected
|
|
86
|
+
*/
|
|
87
|
+
export async function processUpdateCallResponse(
|
|
88
|
+
result: SubmitResponse,
|
|
89
|
+
canisterId: Principal,
|
|
90
|
+
methodName: string,
|
|
91
|
+
agent: HttpAgent,
|
|
92
|
+
pollingOptions: PollingOptions
|
|
93
|
+
): Promise<Uint8Array> {
|
|
94
|
+
let reply: Uint8Array | undefined
|
|
95
|
+
let certificate: Certificate | undefined
|
|
96
|
+
|
|
97
|
+
if (isV4ResponseBody(result.response.body)) {
|
|
98
|
+
if (agent.rootKey == null) {
|
|
99
|
+
throw ExternalError.fromCode(new MissingRootKeyErrorCode())
|
|
100
|
+
}
|
|
101
|
+
const cert = result.response.body.certificate
|
|
102
|
+
certificate = await Certificate.create({
|
|
103
|
+
certificate: cert,
|
|
104
|
+
rootKey: agent.rootKey,
|
|
105
|
+
principal: { canisterId },
|
|
106
|
+
agent,
|
|
107
|
+
})
|
|
108
|
+
|
|
109
|
+
const path = [new TextEncoder().encode("request_status"), result.requestId]
|
|
110
|
+
const status = new TextDecoder().decode(
|
|
111
|
+
lookupResultToBuffer(certificate.lookup_path([...path, "status"]))
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
switch (status) {
|
|
115
|
+
case "replied":
|
|
116
|
+
reply = lookupResultToBuffer(
|
|
117
|
+
certificate.lookup_path([...path, "reply"])
|
|
118
|
+
)
|
|
119
|
+
break
|
|
120
|
+
case "rejected": {
|
|
121
|
+
// Find rejection details in the certificate
|
|
122
|
+
const rejectCode = new Uint8Array(
|
|
123
|
+
lookupResultToBuffer(
|
|
124
|
+
certificate.lookup_path([...path, "reject_code"])
|
|
125
|
+
)!
|
|
126
|
+
)[0]
|
|
127
|
+
const rejectMessage = new TextDecoder().decode(
|
|
128
|
+
lookupResultToBuffer(
|
|
129
|
+
certificate.lookup_path([...path, "reject_message"])
|
|
130
|
+
)!
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
const error_code_buf = lookupResultToBuffer(
|
|
134
|
+
certificate.lookup_path([...path, "error_code"])
|
|
135
|
+
)
|
|
136
|
+
const error_code = error_code_buf
|
|
137
|
+
? new TextDecoder().decode(error_code_buf)
|
|
138
|
+
: undefined
|
|
139
|
+
|
|
140
|
+
const certifiedRejectErrorCode = new CertifiedRejectErrorCode(
|
|
141
|
+
result.requestId,
|
|
142
|
+
rejectCode,
|
|
143
|
+
rejectMessage,
|
|
144
|
+
error_code
|
|
145
|
+
)
|
|
146
|
+
certifiedRejectErrorCode.callContext = {
|
|
147
|
+
canisterId,
|
|
148
|
+
methodName,
|
|
149
|
+
httpDetails: result.response,
|
|
150
|
+
}
|
|
151
|
+
throw RejectError.fromCode(certifiedRejectErrorCode)
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
} else if (isV2ResponseBody(result.response.body)) {
|
|
155
|
+
const { reject_code, reject_message, error_code } = result.response.body
|
|
156
|
+
const errorCode = new UncertifiedRejectUpdateErrorCode(
|
|
157
|
+
result.requestId,
|
|
158
|
+
reject_code,
|
|
159
|
+
reject_message,
|
|
160
|
+
error_code
|
|
161
|
+
)
|
|
162
|
+
errorCode.callContext = {
|
|
163
|
+
canisterId,
|
|
164
|
+
methodName,
|
|
165
|
+
httpDetails: result.response,
|
|
166
|
+
}
|
|
167
|
+
throw RejectError.fromCode(errorCode)
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// Fall back to polling if we receive an Accepted response code
|
|
171
|
+
if (result.response.status === 202) {
|
|
172
|
+
// Contains the certificate and the reply from the boundary node
|
|
173
|
+
const response = await pollForResponse(
|
|
174
|
+
agent,
|
|
175
|
+
canisterId,
|
|
176
|
+
result.requestId,
|
|
177
|
+
pollingOptions
|
|
178
|
+
)
|
|
179
|
+
certificate = response.certificate
|
|
180
|
+
reply = response.reply
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
if (reply !== undefined) {
|
|
184
|
+
return reply
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// Unexpected response format
|
|
188
|
+
const httpDetails = {
|
|
189
|
+
...result.response,
|
|
190
|
+
requestDetails: result.requestDetails,
|
|
191
|
+
} as HttpDetailsResponse
|
|
192
|
+
const errorCode = new UnexpectedErrorCode(
|
|
193
|
+
`Call was returned undefined. We cannot determine if the call was successful or not.`
|
|
194
|
+
)
|
|
195
|
+
errorCode.callContext = {
|
|
196
|
+
canisterId,
|
|
197
|
+
methodName,
|
|
198
|
+
httpDetails,
|
|
199
|
+
}
|
|
200
|
+
throw UnknownError.fromCode(errorCode)
|
|
201
|
+
}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CandidVariant,
|
|
3
|
+
CandidVariantKey,
|
|
4
|
+
CandidVariantValue,
|
|
5
|
+
CandidKeyValue,
|
|
6
|
+
} from "../types"
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Creates a Candid variant from a string value.
|
|
10
|
+
* @param str - The string to convert into a variant
|
|
11
|
+
* @returns An object representing the Candid variant
|
|
12
|
+
*/
|
|
13
|
+
export function createNullVariant<T extends string>(str: T): Record<T, null> {
|
|
14
|
+
return { [str]: null } as Record<T, null>
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Creates a Candid variant from a record.
|
|
19
|
+
* @param variant - The record to convert into a variant
|
|
20
|
+
* @returns An object representing the Candid variant
|
|
21
|
+
*/
|
|
22
|
+
export function createVariant<T extends Record<string, any>>(
|
|
23
|
+
variant: T
|
|
24
|
+
): CandidVariant<T> {
|
|
25
|
+
const keys = Object.keys(variant)
|
|
26
|
+
if (keys.length !== 1) {
|
|
27
|
+
throw new Error(`
|
|
28
|
+
Invalid variant: must have exactly one key but found ${keys.length} keys: ${keys.map(
|
|
29
|
+
(key) => `${key}: ${variant[key]}`
|
|
30
|
+
)}
|
|
31
|
+
`)
|
|
32
|
+
}
|
|
33
|
+
const key = keys[0] as keyof T
|
|
34
|
+
const value = variant[key]
|
|
35
|
+
|
|
36
|
+
return {
|
|
37
|
+
_type: key,
|
|
38
|
+
...(value !== null ? { [key]: value } : {}),
|
|
39
|
+
} as CandidVariant<T>
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Extract variant key and value from a variant type
|
|
44
|
+
* Works with types like:
|
|
45
|
+
* type User = { 'Business': BusinessUser } | { 'Individual': IndividualUser }
|
|
46
|
+
*
|
|
47
|
+
* @template T - The variant type
|
|
48
|
+
* @returns A tuple containing the key and value of the variant
|
|
49
|
+
* @throws Error if the variant object does not have exactly one key
|
|
50
|
+
*/
|
|
51
|
+
export function getVariantKeyValue<T extends Record<string, any>>(
|
|
52
|
+
variant: T
|
|
53
|
+
): CandidKeyValue<T> {
|
|
54
|
+
const keys = Object.keys(variant)
|
|
55
|
+
if (keys.length !== 1) {
|
|
56
|
+
const msg = `Invalid variant: must have exactly one key but found ${keys.length} keys: ${keys}`
|
|
57
|
+
throw new Error(msg)
|
|
58
|
+
}
|
|
59
|
+
const key = keys[0] as CandidKeyValue<T>[0]
|
|
60
|
+
const value = variant[key] as CandidKeyValue<T>[1]
|
|
61
|
+
|
|
62
|
+
return [key, value] as CandidKeyValue<T>
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Extracts the key from a Candid variant type.
|
|
67
|
+
* Variants in Candid are represented as objects with a single key-value pair.
|
|
68
|
+
* @param variant - The variant object
|
|
69
|
+
* @returns The key of the variant
|
|
70
|
+
*/
|
|
71
|
+
export function getVariantKey<T extends Record<string, any>>(
|
|
72
|
+
variant: T
|
|
73
|
+
): CandidVariantKey<T> {
|
|
74
|
+
const keys = Object.keys(variant)
|
|
75
|
+
if (keys.length !== 1) {
|
|
76
|
+
throw new Error(
|
|
77
|
+
`Invalid variant: must have exactly one key but found ${keys}`
|
|
78
|
+
)
|
|
79
|
+
}
|
|
80
|
+
return keys[0] as CandidVariantKey<T>
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Extracts the value from a Candid variant type.
|
|
85
|
+
* @param variant - The variant object
|
|
86
|
+
* @returns The value associated with the variant's key
|
|
87
|
+
*/
|
|
88
|
+
export function getVariantValue<
|
|
89
|
+
T extends Record<string, any>,
|
|
90
|
+
K extends CandidVariantKey<T> = CandidVariantKey<T>,
|
|
91
|
+
>(variant: T): CandidVariantValue<T, K> {
|
|
92
|
+
return variant[getVariantKey(variant)]
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export function getVariantValueByKey<
|
|
96
|
+
T extends Record<string, any>,
|
|
97
|
+
K extends CandidVariantKey<T>,
|
|
98
|
+
>(variant: T, key: K): CandidVariantValue<T, K> {
|
|
99
|
+
if (getVariantKey(variant) !== key) {
|
|
100
|
+
throw new Error(
|
|
101
|
+
`Variant key mismatch: expected ${key}, got ${getVariantKey(variant)}`
|
|
102
|
+
)
|
|
103
|
+
}
|
|
104
|
+
return variant[key]
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export function isKeyMatchVariant<
|
|
108
|
+
T extends Record<string, any>,
|
|
109
|
+
K extends CandidVariantKey<T>,
|
|
110
|
+
>(variant: T, key: K): variant is T & Record<K, unknown> {
|
|
111
|
+
return getVariantKey(variant) === key
|
|
112
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export const REMOTE_HOSTS = [".github.dev", ".gitpod.io"]
|
|
2
|
+
|
|
3
|
+
export const LOCAL_HOSTS = ["localhost", "127.0.0.1"]
|
|
4
|
+
|
|
5
|
+
export const IC_HOST_NETWORK_URI = "https://ic0.app"
|
|
6
|
+
|
|
7
|
+
export const LOCAL_HOST_NETWORK_URI = "http://127.0.0.1:4943"
|
|
8
|
+
|
|
9
|
+
export const IC_INTERNET_IDENTITY_PROVIDER = "https://id.ai"
|
|
10
|
+
|
|
11
|
+
export const LOCAL_INTERNET_IDENTITY_PROVIDER =
|
|
12
|
+
"http://rdmx6-jaaaa-aaaaa-aaadq-cai.localhost:4943"
|