@ic-reactor/candid 3.0.14-beta.0 → 3.0.14-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/metadata-display-reactor.d.ts +66 -11
- package/dist/metadata-display-reactor.d.ts.map +1 -1
- package/dist/metadata-display-reactor.js +48 -12
- package/dist/metadata-display-reactor.js.map +1 -1
- package/dist/visitor/arguments/helpers.d.ts +22 -7
- package/dist/visitor/arguments/helpers.d.ts.map +1 -1
- package/dist/visitor/arguments/helpers.js +39 -5
- package/dist/visitor/arguments/helpers.js.map +1 -1
- package/dist/visitor/arguments/index.d.ts.map +1 -1
- package/dist/visitor/arguments/index.js +26 -34
- package/dist/visitor/arguments/index.js.map +1 -1
- package/dist/visitor/arguments/types.d.ts +120 -132
- package/dist/visitor/arguments/types.d.ts.map +1 -1
- package/dist/visitor/arguments/types.js +25 -1
- package/dist/visitor/arguments/types.js.map +1 -1
- package/dist/visitor/constants.d.ts.map +1 -1
- package/dist/visitor/constants.js +19 -17
- package/dist/visitor/constants.js.map +1 -1
- package/dist/visitor/returns/index.d.ts.map +1 -1
- package/dist/visitor/returns/index.js +24 -13
- package/dist/visitor/returns/index.js.map +1 -1
- package/dist/visitor/returns/types.d.ts +48 -10
- package/dist/visitor/returns/types.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/metadata-display-reactor.ts +86 -14
- package/src/visitor/arguments/helpers.ts +50 -8
- package/src/visitor/arguments/index.test.ts +105 -61
- package/src/visitor/arguments/index.ts +38 -36
- package/src/visitor/arguments/types.ts +211 -220
- package/src/visitor/constants.ts +24 -15
- package/src/visitor/returns/index.test.ts +30 -30
- package/src/visitor/returns/index.ts +52 -21
- package/src/visitor/returns/types.ts +60 -17
|
@@ -58,7 +58,7 @@ describe("ResultFieldVisitor", () => {
|
|
|
58
58
|
const ethField = visitor.visitText(IDL.Text, "ethereum_address")
|
|
59
59
|
expect(ethField.format).toBe("eth")
|
|
60
60
|
|
|
61
|
-
const accountIdField = visitor.visitText(IDL.Text, "
|
|
61
|
+
const accountIdField = visitor.visitText(IDL.Text, "account_identifier")
|
|
62
62
|
expect(accountIdField.format).toBe("account-id")
|
|
63
63
|
|
|
64
64
|
const principalField = visitor.visitText(IDL.Text, "canister_id")
|
|
@@ -347,7 +347,7 @@ describe("ResultFieldVisitor", () => {
|
|
|
347
347
|
// Validate options by resolving each option
|
|
348
348
|
const resolvedActive = field.resolve({ Active: null })
|
|
349
349
|
expect(resolvedActive.selected).toBe("Active")
|
|
350
|
-
expect(resolvedActive.
|
|
350
|
+
expect(resolvedActive.selectedValue.type).toBe("null")
|
|
351
351
|
const resolvedInactive = field.resolve({ Inactive: null })
|
|
352
352
|
expect(resolvedInactive.selected).toBe("Inactive")
|
|
353
353
|
const resolvedPending = field.resolve({ Pending: null })
|
|
@@ -396,14 +396,14 @@ describe("ResultFieldVisitor", () => {
|
|
|
396
396
|
Transfer: { from: "aaaaa-aa", to: "bbbbb-bb", amount: BigInt(1) },
|
|
397
397
|
})
|
|
398
398
|
expect(transferResolved.selected).toBe("Transfer")
|
|
399
|
-
expect(transferResolved.
|
|
399
|
+
expect(transferResolved.selectedValue.type).toBe("record")
|
|
400
400
|
expect(
|
|
401
|
-
Object.keys((transferResolved.
|
|
401
|
+
Object.keys((transferResolved.selectedValue as RecordNode).fields)
|
|
402
402
|
).toHaveLength(3)
|
|
403
403
|
|
|
404
404
|
const approveResolved = field.resolve({ Approve: BigInt(5) })
|
|
405
405
|
expect(approveResolved.selected).toBe("Approve")
|
|
406
|
-
expect(approveResolved.
|
|
406
|
+
expect(approveResolved.selectedValue.type).toBe("number")
|
|
407
407
|
})
|
|
408
408
|
|
|
409
409
|
it("should detect Result variant (Ok/Err)", () => {
|
|
@@ -425,13 +425,13 @@ describe("ResultFieldVisitor", () => {
|
|
|
425
425
|
|
|
426
426
|
const okResolved = field.resolve({ Ok: BigInt(1) })
|
|
427
427
|
expect(okResolved.selected).toBe("Ok")
|
|
428
|
-
expect(okResolved.
|
|
429
|
-
expect(okResolved.
|
|
428
|
+
expect(okResolved.selectedValue.type).toBe("number")
|
|
429
|
+
expect(okResolved.selectedValue.displayType).toBe("string")
|
|
430
430
|
|
|
431
431
|
const errResolved = field.resolve({ Err: "error" })
|
|
432
432
|
expect(errResolved.selected).toBe("Err")
|
|
433
|
-
expect(errResolved.
|
|
434
|
-
expect(errResolved.
|
|
433
|
+
expect(errResolved.selectedValue.type).toBe("text")
|
|
434
|
+
expect(errResolved.selectedValue.displayType).toBe("string")
|
|
435
435
|
})
|
|
436
436
|
|
|
437
437
|
it("should detect complex Result variant", () => {
|
|
@@ -474,11 +474,11 @@ describe("ResultFieldVisitor", () => {
|
|
|
474
474
|
Ok: { id: BigInt(1), data: new Uint8Array([1, 2, 3]) },
|
|
475
475
|
})
|
|
476
476
|
expect(okResolved.selected).toBe("Ok")
|
|
477
|
-
expect(okResolved.
|
|
477
|
+
expect(okResolved.selectedValue.type).toBe("record")
|
|
478
478
|
|
|
479
479
|
const errResolved = field.resolve({ Err: { NotFound: null } })
|
|
480
480
|
expect(errResolved.selected).toBe("Err")
|
|
481
|
-
expect(errResolved.
|
|
481
|
+
expect(errResolved.selectedValue.type).toBe("variant")
|
|
482
482
|
})
|
|
483
483
|
|
|
484
484
|
it("should not detect non-Result variant with Ok and other options", () => {
|
|
@@ -944,18 +944,18 @@ describe("ResultFieldVisitor", () => {
|
|
|
944
944
|
expect(field.displayType).toBe("result")
|
|
945
945
|
|
|
946
946
|
const okResolved = field.resolve({ Ok: BigInt(123) })
|
|
947
|
-
if ((okResolved.
|
|
947
|
+
if ((okResolved.selectedValue as ResolvedNode).type !== "number") {
|
|
948
948
|
throw new Error("Ok field is not number")
|
|
949
949
|
}
|
|
950
|
-
expect((okResolved.
|
|
951
|
-
expect((okResolved.
|
|
950
|
+
expect((okResolved.selectedValue as ResolvedNode).candidType).toBe("nat")
|
|
951
|
+
expect((okResolved.selectedValue as ResolvedNode).displayType).toBe(
|
|
952
952
|
"string"
|
|
953
953
|
)
|
|
954
954
|
|
|
955
955
|
const errResolved = field.resolve({
|
|
956
956
|
Err: { InsufficientFunds: { balance: BigInt(0) } },
|
|
957
957
|
})
|
|
958
|
-
const innerErr = errResolved.
|
|
958
|
+
const innerErr = errResolved.selectedValue as ResolvedNode
|
|
959
959
|
expect(innerErr.type).toBe("variant")
|
|
960
960
|
const insufficient = (innerErr as any).resolve({
|
|
961
961
|
InsufficientFunds: { balance: BigInt(0) },
|
|
@@ -1269,7 +1269,7 @@ describe("ResultFieldVisitor", () => {
|
|
|
1269
1269
|
)
|
|
1270
1270
|
|
|
1271
1271
|
expect(() => field.resolve(null)).toThrow(
|
|
1272
|
-
"Expected record
|
|
1272
|
+
"Expected record, but got null"
|
|
1273
1273
|
)
|
|
1274
1274
|
})
|
|
1275
1275
|
})
|
|
@@ -1292,7 +1292,7 @@ describe("ResultFieldVisitor", () => {
|
|
|
1292
1292
|
const resolved = field.resolve({ Ok: "Success" })
|
|
1293
1293
|
expect(resolved.type).toBe(field.type)
|
|
1294
1294
|
expect(resolved.selected).toBe("Ok")
|
|
1295
|
-
const data = resolved.
|
|
1295
|
+
const data = resolved.selectedValue as ResolvedNode
|
|
1296
1296
|
expect(data.value).toBe("Success")
|
|
1297
1297
|
})
|
|
1298
1298
|
|
|
@@ -1313,7 +1313,7 @@ describe("ResultFieldVisitor", () => {
|
|
|
1313
1313
|
const resolved = field.resolve({ Err: "Something went wrong" })
|
|
1314
1314
|
|
|
1315
1315
|
expect(resolved.selected).toBe("Err")
|
|
1316
|
-
const data = resolved.
|
|
1316
|
+
const data = resolved.selectedValue as ResolvedNode
|
|
1317
1317
|
expect(data.value).toBe("Something went wrong")
|
|
1318
1318
|
})
|
|
1319
1319
|
|
|
@@ -1329,7 +1329,7 @@ describe("ResultFieldVisitor", () => {
|
|
|
1329
1329
|
)
|
|
1330
1330
|
|
|
1331
1331
|
expect(() => field.resolve(null)).toThrow(
|
|
1332
|
-
"Expected variant
|
|
1332
|
+
"Expected variant, but got null"
|
|
1333
1333
|
)
|
|
1334
1334
|
})
|
|
1335
1335
|
})
|
|
@@ -1358,7 +1358,7 @@ describe("ResultFieldVisitor", () => {
|
|
|
1358
1358
|
const field = visitor.visitTuple(tupleType, [IDL.Text, IDL.Nat], "pair")
|
|
1359
1359
|
|
|
1360
1360
|
expect(() => field.resolve(null)).toThrow(
|
|
1361
|
-
"Expected tuple
|
|
1361
|
+
"Expected tuple, but got null"
|
|
1362
1362
|
)
|
|
1363
1363
|
})
|
|
1364
1364
|
})
|
|
@@ -1416,7 +1416,7 @@ describe("ResultFieldVisitor", () => {
|
|
|
1416
1416
|
const field = visitor.visitVec(vecType, IDL.Text, "items")
|
|
1417
1417
|
|
|
1418
1418
|
expect(() => field.resolve(null)).toThrow(
|
|
1419
|
-
"Expected vector
|
|
1419
|
+
"Expected vector, but got null"
|
|
1420
1420
|
)
|
|
1421
1421
|
})
|
|
1422
1422
|
})
|
|
@@ -1612,7 +1612,7 @@ describe("ResultFieldVisitor", () => {
|
|
|
1612
1612
|
|
|
1613
1613
|
const okValue = okResult.results[0] as ResolvedNode
|
|
1614
1614
|
expect((okValue as any).selected).toBe("Ok")
|
|
1615
|
-
expect(((okValue as any).
|
|
1615
|
+
expect(((okValue as any).selectedValue as ResolvedNode).value).toBe(
|
|
1616
1616
|
"12345"
|
|
1617
1617
|
)
|
|
1618
1618
|
|
|
@@ -1622,7 +1622,7 @@ describe("ResultFieldVisitor", () => {
|
|
|
1622
1622
|
})
|
|
1623
1623
|
const errValue = errResult.results[0] as ResolvedNode
|
|
1624
1624
|
expect((errValue as any).selected).toBe("Err")
|
|
1625
|
-
expect(((errValue as any).
|
|
1625
|
+
expect(((errValue as any).selectedValue as ResolvedNode).value).toBe(
|
|
1626
1626
|
"Insufficient funds"
|
|
1627
1627
|
)
|
|
1628
1628
|
})
|
|
@@ -1727,7 +1727,7 @@ describe("ResultFieldVisitor", () => {
|
|
|
1727
1727
|
|
|
1728
1728
|
const successValue = successResult.results[0] as ResolvedNode
|
|
1729
1729
|
expect((successValue as any).selected).toBe("Ok")
|
|
1730
|
-
expect(((successValue as any).
|
|
1730
|
+
expect(((successValue as any).selectedValue as ResolvedNode).value).toBe(
|
|
1731
1731
|
"1000"
|
|
1732
1732
|
)
|
|
1733
1733
|
|
|
@@ -1738,7 +1738,7 @@ describe("ResultFieldVisitor", () => {
|
|
|
1738
1738
|
const errorValue = errorResult.results[0] as ResolvedNode
|
|
1739
1739
|
expect((errorValue as any).selected).toBe("Err")
|
|
1740
1740
|
|
|
1741
|
-
const val = (errorValue as any).
|
|
1741
|
+
const val = (errorValue as any).selectedValue as ResolvedNode
|
|
1742
1742
|
if (typeof val !== "object" || val === null || !("selected" in val)) {
|
|
1743
1743
|
throw new Error("Expected variant value object")
|
|
1744
1744
|
}
|
|
@@ -1876,7 +1876,7 @@ describe("ResultFieldVisitor", () => {
|
|
|
1876
1876
|
|
|
1877
1877
|
const variantValue = result.results[0] as ResolvedNode
|
|
1878
1878
|
expect((variantValue as any).selected).toBe("Ok")
|
|
1879
|
-
const innerVal = (variantValue as any).
|
|
1879
|
+
const innerVal = (variantValue as any).selectedValue as ResolvedNode
|
|
1880
1880
|
expect(innerVal.value).toBe("12345")
|
|
1881
1881
|
})
|
|
1882
1882
|
|
|
@@ -2042,7 +2042,7 @@ describe("ResultFieldVisitor Reproduction - User reported issue", () => {
|
|
|
2042
2042
|
const resolved = field.resolve(transformedData)
|
|
2043
2043
|
|
|
2044
2044
|
expect(resolved.selected).toBe("Quorum")
|
|
2045
|
-
expect(resolved.
|
|
2045
|
+
expect(resolved.selectedValue.value).toBe("some text")
|
|
2046
2046
|
})
|
|
2047
2047
|
|
|
2048
2048
|
it("should handle already transformed optional data (unwrapped)", () => {
|
|
@@ -2071,7 +2071,7 @@ describe("ResultFieldVisitor Reproduction - User reported issue", () => {
|
|
|
2071
2071
|
)
|
|
2072
2072
|
|
|
2073
2073
|
expect(() => field.resolve({ C: null })).toThrow(
|
|
2074
|
-
/Option C not found
|
|
2074
|
+
/Option "C" not found. Available: A, B/
|
|
2075
2075
|
)
|
|
2076
2076
|
})
|
|
2077
2077
|
|
|
@@ -2101,7 +2101,7 @@ describe("ResultFieldVisitor Reproduction - User reported issue", () => {
|
|
|
2101
2101
|
const variantNode = resolved.inner as VariantNode
|
|
2102
2102
|
|
|
2103
2103
|
expect(variantNode.selected).toBe("Quorum")
|
|
2104
|
-
expect(variantNode.
|
|
2104
|
+
expect(variantNode.selectedValue.type).toBe("record")
|
|
2105
2105
|
})
|
|
2106
2106
|
|
|
2107
2107
|
it("should handle deeply nested recursive variant with transformed data", () => {
|
|
@@ -2130,7 +2130,7 @@ describe("ResultFieldVisitor Reproduction - User reported issue", () => {
|
|
|
2130
2130
|
const variantNode = resolved.inner as VariantNode
|
|
2131
2131
|
expect(variantNode.selected).toBe("Nested")
|
|
2132
2132
|
|
|
2133
|
-
const nestedResolved = variantNode.
|
|
2133
|
+
const nestedResolved = variantNode.selectedValue as RecursiveNode
|
|
2134
2134
|
const innerVariant = nestedResolved.inner as VariantNode
|
|
2135
2135
|
expect(innerVariant.selected).toBe("Quorum")
|
|
2136
2136
|
})
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import { isQuery } from "../helpers"
|
|
2
2
|
import { checkTextFormat, checkNumberFormat } from "../constants"
|
|
3
|
+
import { formatLabel } from "../arguments/helpers"
|
|
4
|
+
import { MetadataError } from "../arguments/types"
|
|
3
5
|
import type {
|
|
4
6
|
ResultNode,
|
|
5
7
|
ResolvedNode,
|
|
6
|
-
|
|
8
|
+
VisitorDataType,
|
|
7
9
|
MethodMeta,
|
|
8
10
|
ServiceMeta,
|
|
9
|
-
|
|
11
|
+
MethodResult,
|
|
10
12
|
NumberFormat,
|
|
11
13
|
TextFormat,
|
|
12
14
|
} from "./types"
|
|
@@ -32,7 +34,7 @@ type Codec = { decode: (v: unknown) => unknown }
|
|
|
32
34
|
/**
|
|
33
35
|
* Creates a primitive node with automatic resolve implementation.
|
|
34
36
|
*/
|
|
35
|
-
function primitiveNode<T extends
|
|
37
|
+
function primitiveNode<T extends VisitorDataType>(
|
|
36
38
|
type: T,
|
|
37
39
|
label: string,
|
|
38
40
|
candidType: string,
|
|
@@ -43,6 +45,7 @@ function primitiveNode<T extends NodeType>(
|
|
|
43
45
|
const node: ResultNode<T> = {
|
|
44
46
|
type,
|
|
45
47
|
label,
|
|
48
|
+
displayLabel: formatLabel(label),
|
|
46
49
|
candidType,
|
|
47
50
|
displayType,
|
|
48
51
|
...extras,
|
|
@@ -54,8 +57,10 @@ function primitiveNode<T extends NodeType>(
|
|
|
54
57
|
raw: data,
|
|
55
58
|
} as unknown as ResolvedNode<T>
|
|
56
59
|
} catch (e) {
|
|
57
|
-
throw new
|
|
58
|
-
`Failed to decode
|
|
60
|
+
throw new MetadataError(
|
|
61
|
+
`Failed to decode: ${e instanceof Error ? e.message : String(e)}`,
|
|
62
|
+
label,
|
|
63
|
+
candidType
|
|
59
64
|
)
|
|
60
65
|
}
|
|
61
66
|
},
|
|
@@ -116,7 +121,7 @@ export class ResultFieldVisitor<A = BaseActor> extends IDL.Visitor<
|
|
|
116
121
|
returnCount: t.retTypes.length,
|
|
117
122
|
resolve: (
|
|
118
123
|
data: ActorMethodReturnType<A[FunctionName<A>]>
|
|
119
|
-
):
|
|
124
|
+
): MethodResult<A> => {
|
|
120
125
|
const dataArray = returns.length <= 1 ? [data] : (data as unknown[])
|
|
121
126
|
return {
|
|
122
127
|
functionType,
|
|
@@ -145,12 +150,17 @@ export class ResultFieldVisitor<A = BaseActor> extends IDL.Visitor<
|
|
|
145
150
|
const node: ResultNode<"record"> = {
|
|
146
151
|
type: "record",
|
|
147
152
|
label,
|
|
153
|
+
displayLabel: formatLabel(label),
|
|
148
154
|
candidType: "record",
|
|
149
155
|
displayType: "object",
|
|
150
156
|
fields,
|
|
151
157
|
resolve(data: unknown): ResolvedNode<"record"> {
|
|
152
158
|
if (data === null || data === undefined) {
|
|
153
|
-
throw new
|
|
159
|
+
throw new MetadataError(
|
|
160
|
+
`Expected record, but got ${data === null ? "null" : "undefined"}`,
|
|
161
|
+
label,
|
|
162
|
+
"record"
|
|
163
|
+
)
|
|
154
164
|
}
|
|
155
165
|
const recordData = data as Record<string, unknown>
|
|
156
166
|
const resolvedFields: Record<string, ResolvedNode> = {}
|
|
@@ -161,8 +171,10 @@ export class ResultFieldVisitor<A = BaseActor> extends IDL.Visitor<
|
|
|
161
171
|
recordData[key] !== undefined ? recordData[key] : recordData[index]
|
|
162
172
|
|
|
163
173
|
if (!field || typeof field.resolve !== "function") {
|
|
164
|
-
throw new
|
|
165
|
-
`Field "${key}"
|
|
174
|
+
throw new MetadataError(
|
|
175
|
+
`Field "${key}" is not a valid ResultNode`,
|
|
176
|
+
`${label}.${key}`,
|
|
177
|
+
"record"
|
|
166
178
|
)
|
|
167
179
|
}
|
|
168
180
|
|
|
@@ -180,38 +192,44 @@ export class ResultFieldVisitor<A = BaseActor> extends IDL.Visitor<
|
|
|
180
192
|
fields_: Array<[string, IDL.Type]>,
|
|
181
193
|
label: string
|
|
182
194
|
): ResultNode<"variant"> {
|
|
183
|
-
const
|
|
195
|
+
const options: Record<string, ResultNode> = {}
|
|
184
196
|
for (const [key, type] of fields_) {
|
|
185
|
-
|
|
197
|
+
options[key] = type.accept(this, key) as ResultNode
|
|
186
198
|
}
|
|
187
|
-
const isResult = "Ok" in
|
|
199
|
+
const isResult = "Ok" in options && "Err" in options
|
|
188
200
|
const node: ResultNode<"variant"> = {
|
|
189
201
|
type: "variant",
|
|
190
202
|
label,
|
|
203
|
+
displayLabel: formatLabel(label),
|
|
191
204
|
candidType: "variant",
|
|
192
205
|
displayType: isResult ? "result" : "variant",
|
|
193
|
-
|
|
206
|
+
options,
|
|
207
|
+
selectedValue: {} as ResultNode, // placeholder, populated on resolve
|
|
194
208
|
resolve(data: unknown): ResolvedNode<"variant"> {
|
|
195
209
|
if (data === null || data === undefined) {
|
|
196
|
-
throw new
|
|
197
|
-
`Expected variant
|
|
210
|
+
throw new MetadataError(
|
|
211
|
+
`Expected variant, but got ${data === null ? "null" : "undefined"}`,
|
|
212
|
+
label,
|
|
213
|
+
"variant"
|
|
198
214
|
)
|
|
199
215
|
}
|
|
200
216
|
const variantData = data as Record<string, unknown>
|
|
201
217
|
// Support both raw { Selected: value } and transformed { _type: 'Selected', Selected: value }
|
|
202
218
|
const selected =
|
|
203
219
|
(variantData._type as string) || Object.keys(variantData)[0]
|
|
204
|
-
const optionNode =
|
|
220
|
+
const optionNode = options[selected]
|
|
205
221
|
|
|
206
222
|
if (!optionNode) {
|
|
207
|
-
throw new
|
|
208
|
-
`Option ${selected} not found
|
|
223
|
+
throw new MetadataError(
|
|
224
|
+
`Option "${selected}" not found. Available: ${Object.keys(options).join(", ")}`,
|
|
225
|
+
label,
|
|
226
|
+
"variant"
|
|
209
227
|
)
|
|
210
228
|
}
|
|
211
229
|
return {
|
|
212
230
|
...node,
|
|
213
231
|
selected,
|
|
214
|
-
|
|
232
|
+
selectedValue: optionNode.resolve(variantData[selected]),
|
|
215
233
|
raw: data,
|
|
216
234
|
}
|
|
217
235
|
},
|
|
@@ -231,12 +249,17 @@ export class ResultFieldVisitor<A = BaseActor> extends IDL.Visitor<
|
|
|
231
249
|
const node: ResultNode<"tuple"> = {
|
|
232
250
|
type: "tuple",
|
|
233
251
|
label,
|
|
252
|
+
displayLabel: formatLabel(label),
|
|
234
253
|
candidType: "tuple",
|
|
235
254
|
displayType: "array",
|
|
236
255
|
items,
|
|
237
256
|
resolve(data: unknown): ResolvedNode<"tuple"> {
|
|
238
257
|
if (data === null || data === undefined) {
|
|
239
|
-
throw new
|
|
258
|
+
throw new MetadataError(
|
|
259
|
+
`Expected tuple, but got ${data === null ? "null" : "undefined"}`,
|
|
260
|
+
label,
|
|
261
|
+
"tuple"
|
|
262
|
+
)
|
|
240
263
|
}
|
|
241
264
|
const tupleData = data as unknown[]
|
|
242
265
|
return {
|
|
@@ -259,6 +282,7 @@ export class ResultFieldVisitor<A = BaseActor> extends IDL.Visitor<
|
|
|
259
282
|
const node: ResultNode<"optional"> = {
|
|
260
283
|
type: "optional",
|
|
261
284
|
label,
|
|
285
|
+
displayLabel: formatLabel(label),
|
|
262
286
|
candidType: "opt",
|
|
263
287
|
displayType: "nullable",
|
|
264
288
|
value: null, // null until resolved
|
|
@@ -290,6 +314,7 @@ export class ResultFieldVisitor<A = BaseActor> extends IDL.Visitor<
|
|
|
290
314
|
const node: ResultNode<"blob"> = {
|
|
291
315
|
type: "blob",
|
|
292
316
|
label,
|
|
317
|
+
displayLabel: formatLabel(label),
|
|
293
318
|
candidType: "blob",
|
|
294
319
|
displayType: "string",
|
|
295
320
|
length: 0,
|
|
@@ -321,12 +346,17 @@ export class ResultFieldVisitor<A = BaseActor> extends IDL.Visitor<
|
|
|
321
346
|
const node: ResultNode<"vector"> = {
|
|
322
347
|
type: "vector",
|
|
323
348
|
label,
|
|
349
|
+
displayLabel: formatLabel(label),
|
|
324
350
|
candidType: "vec",
|
|
325
351
|
displayType: "array",
|
|
326
352
|
items: [], // empty schema placeholder, populated on resolve
|
|
327
353
|
resolve(data: unknown): ResolvedNode<"vector"> {
|
|
328
354
|
if (data === null || data === undefined) {
|
|
329
|
-
throw new
|
|
355
|
+
throw new MetadataError(
|
|
356
|
+
`Expected vector, but got ${data === null ? "null" : "undefined"}`,
|
|
357
|
+
label,
|
|
358
|
+
"vec"
|
|
359
|
+
)
|
|
330
360
|
}
|
|
331
361
|
const vectorData = data as unknown[]
|
|
332
362
|
return {
|
|
@@ -357,6 +387,7 @@ export class ResultFieldVisitor<A = BaseActor> extends IDL.Visitor<
|
|
|
357
387
|
const node: ResultNode<"recursive"> = {
|
|
358
388
|
type: "recursive",
|
|
359
389
|
label,
|
|
390
|
+
displayLabel: formatLabel(label),
|
|
360
391
|
candidType: "rec",
|
|
361
392
|
displayType: "recursive",
|
|
362
393
|
inner: {} as ResultNode, // placeholder, populated on resolve
|
|
@@ -6,14 +6,16 @@ import type {
|
|
|
6
6
|
} from "@ic-reactor/core"
|
|
7
7
|
import type { VisitorDataType, TextFormat, NumberFormat } from "../types"
|
|
8
8
|
|
|
9
|
-
export type { TextFormat, NumberFormat }
|
|
9
|
+
export type { VisitorDataType, TextFormat, NumberFormat }
|
|
10
10
|
|
|
11
11
|
// ════════════════════════════════════════════════════════════════════════════
|
|
12
12
|
// Core Types & Formats
|
|
13
13
|
// ════════════════════════════════════════════════════════════════════════════
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
|
|
15
|
+
/**
|
|
16
|
+
* The display type category after transformation.
|
|
17
|
+
* Maps Candid types to JavaScript-friendly display types.
|
|
18
|
+
*/
|
|
17
19
|
export type DisplayType =
|
|
18
20
|
| "string"
|
|
19
21
|
| "number"
|
|
@@ -35,11 +37,13 @@ export type DisplayType =
|
|
|
35
37
|
/**
|
|
36
38
|
* Base properties shared by all result nodes.
|
|
37
39
|
*/
|
|
38
|
-
interface ResultNodeBase<T extends
|
|
40
|
+
interface ResultNodeBase<T extends VisitorDataType = VisitorDataType> {
|
|
39
41
|
/** The Candid type category */
|
|
40
42
|
type: T
|
|
41
|
-
/**
|
|
43
|
+
/** Raw label from Candid definition */
|
|
42
44
|
label: string
|
|
45
|
+
/** Human-readable formatted label for display */
|
|
46
|
+
displayLabel: string
|
|
43
47
|
/** Original Candid type name */
|
|
44
48
|
candidType: string
|
|
45
49
|
/** What it becomes after display transformation */
|
|
@@ -55,10 +59,17 @@ interface ResultNodeBase<T extends NodeType = NodeType> {
|
|
|
55
59
|
// For compound types, children are stored directly in their respective fields
|
|
56
60
|
// ════════════════════════════════════════════════════════════════════════════
|
|
57
61
|
|
|
58
|
-
type NodeTypeExtras<T extends
|
|
62
|
+
type NodeTypeExtras<T extends VisitorDataType> = T extends "record"
|
|
59
63
|
? { fields: Record<string, ResultNode> }
|
|
60
64
|
: T extends "variant"
|
|
61
|
-
? {
|
|
65
|
+
? {
|
|
66
|
+
/** All variant options as schema */
|
|
67
|
+
options: Record<string, ResultNode>
|
|
68
|
+
/** The resolved selected option value */
|
|
69
|
+
selectedValue: ResultNode
|
|
70
|
+
/** The selected option key (populated after resolution) */
|
|
71
|
+
selected?: string
|
|
72
|
+
}
|
|
62
73
|
: T extends "tuple" | "vector"
|
|
63
74
|
? { items: ResultNode[] }
|
|
64
75
|
: T extends "optional"
|
|
@@ -86,18 +97,20 @@ type NodeTypeExtras<T extends NodeType> = T extends "record"
|
|
|
86
97
|
* resolved children directly in their structure fields.
|
|
87
98
|
* Primitive types store the display value in `value`.
|
|
88
99
|
*/
|
|
89
|
-
export type ResultNode<T extends
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
100
|
+
export type ResultNode<T extends VisitorDataType = VisitorDataType> =
|
|
101
|
+
ResultNodeBase<T> &
|
|
102
|
+
NodeTypeExtras<T> & {
|
|
103
|
+
/** Resolve this node with a value, returning a new resolved node */
|
|
104
|
+
resolve(data: unknown): ResolvedNode<T>
|
|
105
|
+
}
|
|
94
106
|
|
|
95
107
|
/**
|
|
96
108
|
* A resolved node has `raw` populated and children resolved.
|
|
97
109
|
*/
|
|
98
|
-
export type ResolvedNode<T extends
|
|
99
|
-
|
|
100
|
-
|
|
110
|
+
export type ResolvedNode<T extends VisitorDataType = VisitorDataType> =
|
|
111
|
+
ResultNode<T> & {
|
|
112
|
+
raw: unknown
|
|
113
|
+
}
|
|
101
114
|
|
|
102
115
|
// ════════════════════════════════════════════════════════════════════════════
|
|
103
116
|
// Convenience Type Aliases
|
|
@@ -121,27 +134,57 @@ export type UnknownNode = ResultNode<"unknown">
|
|
|
121
134
|
// Method & Service Level
|
|
122
135
|
// ════════════════════════════════════════════════════════════════════════════
|
|
123
136
|
|
|
137
|
+
/**
|
|
138
|
+
* Metadata for a single method's return values.
|
|
139
|
+
* Use this to render method results.
|
|
140
|
+
*/
|
|
124
141
|
export interface MethodMeta<
|
|
125
142
|
A = BaseActor,
|
|
126
143
|
Name extends FunctionName<A> = FunctionName<A>,
|
|
127
144
|
> {
|
|
145
|
+
/** Whether this is a "query" or "update" call */
|
|
128
146
|
functionType: FunctionType
|
|
147
|
+
/** The method name as defined in the Candid interface */
|
|
129
148
|
functionName: Name
|
|
149
|
+
/** Array of result node descriptors, one per return value */
|
|
130
150
|
returns: ResultNode[]
|
|
151
|
+
/** Number of return values */
|
|
131
152
|
returnCount: number
|
|
132
153
|
/**
|
|
133
154
|
* Resolve the method result schema with actual return data.
|
|
155
|
+
* @param data The raw return data from the canister
|
|
156
|
+
* @returns A resolved result with display-friendly values
|
|
134
157
|
*/
|
|
135
|
-
resolve(data: ActorMethodReturnType<A[Name]>):
|
|
158
|
+
resolve(data: ActorMethodReturnType<A[Name]>): MethodResult<A>
|
|
136
159
|
}
|
|
137
160
|
|
|
138
|
-
|
|
161
|
+
/**
|
|
162
|
+
* A resolved method result with display-friendly values.
|
|
163
|
+
*/
|
|
164
|
+
export interface MethodResult<A = BaseActor> {
|
|
165
|
+
/** Whether this is a "query" or "update" call */
|
|
139
166
|
functionType: FunctionType
|
|
167
|
+
/** The method name */
|
|
140
168
|
functionName: FunctionName<A>
|
|
169
|
+
/** Resolved return values */
|
|
141
170
|
results: ResolvedNode[]
|
|
171
|
+
/** Original raw data from the canister */
|
|
142
172
|
raw: ActorMethodReturnType<A[FunctionName<A>]>
|
|
143
173
|
}
|
|
144
174
|
|
|
175
|
+
/**
|
|
176
|
+
* Service-level metadata mapping method names to their return metadata.
|
|
177
|
+
*/
|
|
145
178
|
export type ServiceMeta<A = BaseActor> = {
|
|
146
179
|
[K in FunctionName<A>]: MethodMeta<A, K>
|
|
147
180
|
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Props type for result display components.
|
|
184
|
+
*/
|
|
185
|
+
export type ResultDisplayProps<T extends VisitorDataType = VisitorDataType> = {
|
|
186
|
+
/** The resolved result node */
|
|
187
|
+
node: ResolvedNode<T>
|
|
188
|
+
/** Nesting depth for indentation */
|
|
189
|
+
depth?: number
|
|
190
|
+
}
|