@ic-reactor/candid 3.0.9-beta.0 → 3.0.10-beta.1

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.
@@ -1,22 +1,23 @@
1
1
  import { describe, it, expect } from "vitest"
2
- import { IDL } from "@icp-sdk/core/candid"
2
+ import { IDL } from "../types"
3
3
  import { ResultFieldVisitor } from "./index"
4
4
  import type {
5
- ResultField,
6
- RecordResultField,
7
- VariantResultField,
8
- TupleResultField,
9
- OptionalResultField,
10
- VectorResultField,
11
- BlobResultField,
12
- RecursiveResultField,
13
- NumberResultField,
14
- TextResultField,
15
- PrincipalResultField,
16
- BooleanResultField,
17
- NullResultField,
18
- MethodResultMeta,
19
- ServiceResultMeta,
5
+ ResultNode,
6
+ ResolvedNode,
7
+ RecordNode,
8
+ VariantNode,
9
+ TupleNode,
10
+ OptionalNode,
11
+ VectorNode,
12
+ BlobNode,
13
+ RecursiveNode,
14
+ NumberNode,
15
+ TextNode,
16
+ PrincipalNode,
17
+ BooleanNode,
18
+ NullNode,
19
+ MethodMeta,
20
+ ServiceMeta,
20
21
  } from "./types"
21
22
 
22
23
  describe("ResultFieldVisitor", () => {
@@ -35,33 +36,33 @@ describe("ResultFieldVisitor", () => {
35
36
  expect(field.label).toBe("message")
36
37
  expect(field.candidType).toBe("text")
37
38
  expect(field.displayType).toBe("string")
38
- expect(field.textFormat).toBe("plain")
39
+ expect(field.format).toBe("plain")
39
40
  })
40
41
 
41
42
  it("should handle text with special label detection", () => {
42
43
  const emailField = visitor.visitText(IDL.Text, "email")
43
- expect(emailField.textFormat).toBe("email")
44
+ expect(emailField.format).toBe("email")
44
45
 
45
46
  const urlField = visitor.visitText(IDL.Text, "website_url")
46
- expect(urlField.textFormat).toBe("url")
47
+ expect(urlField.format).toBe("url")
47
48
 
48
49
  const phoneField = visitor.visitText(IDL.Text, "phone_number")
49
- expect(phoneField.textFormat).toBe("phone")
50
+ expect(phoneField.format).toBe("phone")
50
51
 
51
52
  const uuidField = visitor.visitText(IDL.Text, "transaction_uuid")
52
- expect(uuidField.textFormat).toBe("uuid")
53
+ expect(uuidField.format).toBe("uuid")
53
54
 
54
55
  const btcField = visitor.visitText(IDL.Text, "btc_address")
55
- expect(btcField.textFormat).toBe("btc")
56
+ expect(btcField.format).toBe("btc")
56
57
 
57
58
  const ethField = visitor.visitText(IDL.Text, "ethereum_address")
58
- expect(ethField.textFormat).toBe("eth")
59
+ expect(ethField.format).toBe("eth")
59
60
 
60
61
  const accountIdField = visitor.visitText(IDL.Text, "account_id")
61
- expect(accountIdField.textFormat).toBe("account-id")
62
+ expect(accountIdField.format).toBe("account-id")
62
63
 
63
64
  const principalField = visitor.visitText(IDL.Text, "canister_id")
64
- expect(principalField.textFormat).toBe("principal")
65
+ expect(principalField.format).toBe("principal")
65
66
  })
66
67
 
67
68
  it("should handle bool type", () => {
@@ -106,7 +107,7 @@ describe("ResultFieldVisitor", () => {
106
107
  expect(field.type).toBe("number")
107
108
  expect(field.candidType).toBe("nat")
108
109
  expect(field.displayType).toBe("string") // BigInt → string
109
- expect(field.numberFormat).toBe("normal")
110
+ expect(field.format).toBe("normal")
110
111
  })
111
112
 
112
113
  it("should map int to string display type", () => {
@@ -191,30 +192,30 @@ describe("ResultFieldVisitor", () => {
191
192
  describe("Number format detection", () => {
192
193
  it("should detect timestamp format from label", () => {
193
194
  const timestampField = visitor.visitFixedNat(IDL.Nat64, "created_at")
194
- expect(timestampField.numberFormat).toBe("timestamp")
195
+ expect(timestampField.format).toBe("timestamp")
195
196
 
196
197
  const dateField = visitor.visitNat(IDL.Nat, "timestamp_nanos")
197
- expect(dateField.numberFormat).toBe("timestamp")
198
+ expect(dateField.format).toBe("timestamp")
198
199
 
199
200
  const deadlineField = visitor.visitNat(IDL.Nat, "deadline")
200
- expect(deadlineField.numberFormat).toBe("timestamp")
201
+ expect(deadlineField.format).toBe("timestamp")
201
202
  })
202
203
 
203
204
  it("should detect cycle format from label", () => {
204
205
  const cycleField = visitor.visitNat(IDL.Nat, "cycles")
205
- expect(cycleField.numberFormat).toBe("cycle")
206
+ expect(cycleField.format).toBe("cycle")
206
207
 
207
208
  // "cycle" as standalone word
208
209
  const cycleSingleField = visitor.visitNat(IDL.Nat, "cycle")
209
- expect(cycleSingleField.numberFormat).toBe("cycle")
210
+ expect(cycleSingleField.format).toBe("cycle")
210
211
  })
211
212
 
212
213
  it("should default to normal format", () => {
213
214
  const amountField = visitor.visitNat(IDL.Nat, "amount")
214
- expect(amountField.numberFormat).toBe("normal")
215
+ expect(amountField.format).toBe("normal")
215
216
 
216
217
  const countField = visitor.visitNat(IDL.Nat, "count")
217
- expect(countField.numberFormat).toBe("normal")
218
+ expect(countField.format).toBe("normal")
218
219
  })
219
220
  })
220
221
  })
@@ -242,15 +243,15 @@ describe("ResultFieldVisitor", () => {
242
243
  expect(field.label).toBe("person")
243
244
  expect(field.candidType).toBe("record")
244
245
  expect(field.displayType).toBe("object")
245
- expect(field.fields).toHaveLength(2)
246
+ expect(Object.keys(field.fields)).toHaveLength(2)
246
247
 
247
- const nameField = field.fields.find((f) => f.label === "name")
248
+ const nameField = field.fields["name"]
248
249
  if (!nameField || nameField.type !== "text") {
249
250
  throw new Error("Name field not found or not text")
250
251
  }
251
252
  expect(nameField.displayType).toBe("string")
252
253
 
253
- const ageField = field.fields.find((f) => f.label === "age")
254
+ const ageField = field.fields["age"]
254
255
  if (!ageField || ageField.type !== "number") {
255
256
  throw new Error("Age field not found or not number")
256
257
  }
@@ -277,14 +278,14 @@ describe("ResultFieldVisitor", () => {
277
278
 
278
279
  expect(field.type).toBe("record")
279
280
 
280
- const addressField = field.fields.find((f) => f.label === "address")
281
+ const addressField = field.fields["address"] as RecordNode
281
282
  if (!addressField || addressField.type !== "record") {
282
283
  throw new Error("Address field not found or not a record")
283
284
  }
284
285
 
285
286
  expect(addressField.type).toBe("record")
286
287
  expect(addressField.displayType).toBe("object")
287
- expect(addressField.fields).toHaveLength(2)
288
+ expect(Object.keys(addressField.fields)).toHaveLength(2)
288
289
  })
289
290
 
290
291
  it("should handle ICRC-1 account record", () => {
@@ -303,22 +304,23 @@ describe("ResultFieldVisitor", () => {
303
304
 
304
305
  expect(field.type).toBe("record")
305
306
 
306
- const ownerField = field.fields.find((f) => f.label === "owner")
307
+ const ownerField = field.fields["owner"]
307
308
  if (!ownerField || ownerField.type !== "principal") {
308
309
  throw new Error("Owner field not found or not principal")
309
310
  }
310
311
  expect(ownerField.type).toBe("principal")
311
312
  expect(ownerField.displayType).toBe("string")
312
313
 
313
- const subaccountField = field.fields.find((f) => f.label === "subaccount")
314
+ const subaccountField = field.fields["subaccount"] as OptionalNode
314
315
  if (!subaccountField || subaccountField.type !== "optional") {
315
316
  throw new Error("Subaccount field not found or not optional")
316
317
  }
317
318
  expect(subaccountField.type).toBe("optional")
318
- if (subaccountField.innerField.type !== "blob") {
319
- throw new Error("Subaccount inner field is not blob")
320
- }
321
- expect(subaccountField.innerField.type).toBe("blob")
319
+ // Resolve an example value to validate inner blob type
320
+ const subaccountResolved = subaccountField.resolve([
321
+ new Uint8Array([1, 2, 3]),
322
+ ])
323
+ expect((subaccountResolved.value as ResolvedNode).type).toBe("blob")
322
324
  })
323
325
  })
324
326
 
@@ -342,10 +344,14 @@ describe("ResultFieldVisitor", () => {
342
344
  expect(field.type).toBe("variant")
343
345
  expect(field.candidType).toBe("variant")
344
346
  expect(field.displayType).toBe("variant")
345
- expect(field.options).toContain("Active")
346
- expect(field.options).toContain("Inactive")
347
- expect(field.options).toContain("Pending")
348
- expect(field.options).toHaveLength(3)
347
+ // Validate options by resolving each option
348
+ const resolvedActive = field.resolve({ Active: null })
349
+ expect(resolvedActive.selected).toBe("Active")
350
+ expect(resolvedActive.selectedOption.type).toBe("null")
351
+ const resolvedInactive = field.resolve({ Inactive: null })
352
+ expect(resolvedInactive.selected).toBe("Inactive")
353
+ const resolvedPending = field.resolve({ Pending: null })
354
+ expect(resolvedPending.selected).toBe("Pending")
349
355
  })
350
356
 
351
357
  it("should handle variant with payloads", () => {
@@ -385,26 +391,19 @@ describe("ResultFieldVisitor", () => {
385
391
  )
386
392
 
387
393
  expect(field.type).toBe("variant")
388
- expect(field.options).toContain("Transfer")
389
- expect(field.options).toContain("Approve")
390
- expect(field.options).toContain("Mint")
391
- expect(field.options).toHaveLength(3)
392
-
393
- const transferField = field.optionFields.find(
394
- (f) => f.label === "Transfer"
395
- )
396
-
397
- if (!transferField || transferField.type !== "record") {
398
- throw new Error("Transfer field not found or not record")
399
- }
400
- expect(transferField.type).toBe("record")
401
- expect(transferField.fields).toHaveLength(3)
394
+ // Validate Transfer payload
395
+ const transferResolved = field.resolve({
396
+ Transfer: { from: "aaaaa-aa", to: "bbbbb-bb", amount: BigInt(1) },
397
+ })
398
+ expect(transferResolved.selected).toBe("Transfer")
399
+ expect(transferResolved.selectedOption.type).toBe("record")
400
+ expect(
401
+ Object.keys((transferResolved.selectedOption as RecordNode).fields)
402
+ ).toHaveLength(3)
402
403
 
403
- const approveField = field.optionFields.find((f) => f.label === "Approve")
404
- if (!approveField || approveField.type !== "number") {
405
- throw new Error("Approve field not found or not number")
406
- }
407
- expect(approveField.type).toBe("number")
404
+ const approveResolved = field.resolve({ Approve: BigInt(5) })
405
+ expect(approveResolved.selected).toBe("Approve")
406
+ expect(approveResolved.selectedOption.type).toBe("number")
408
407
  })
409
408
 
410
409
  it("should detect Result variant (Ok/Err)", () => {
@@ -423,20 +422,16 @@ describe("ResultFieldVisitor", () => {
423
422
 
424
423
  expect(field.type).toBe("variant")
425
424
  expect(field.displayType).toBe("result") // Special result type
426
- expect(field.options).toContain("Ok")
427
- expect(field.options).toContain("Err")
428
425
 
429
- const okField = field.optionFields.find((f) => f.label === "Ok")
430
- if (!okField || okField.type !== "number") {
431
- throw new Error("Ok field not found or not number")
432
- }
433
- expect(okField.displayType).toBe("string")
426
+ const okResolved = field.resolve({ Ok: BigInt(1) })
427
+ expect(okResolved.selected).toBe("Ok")
428
+ expect(okResolved.selectedOption.type).toBe("number")
429
+ expect(okResolved.selectedOption.displayType).toBe("string")
434
430
 
435
- const errField = field.optionFields.find((f) => f.label === "Err")
436
- if (!errField || errField.type !== "text") {
437
- throw new Error("Err field not found or not text")
438
- }
439
- expect(errField.displayType).toBe("string")
431
+ const errResolved = field.resolve({ Err: "error" })
432
+ expect(errResolved.selected).toBe("Err")
433
+ expect(errResolved.selectedOption.type).toBe("text")
434
+ expect(errResolved.selectedOption.displayType).toBe("string")
440
435
  })
441
436
 
442
437
  it("should detect complex Result variant", () => {
@@ -475,17 +470,15 @@ describe("ResultFieldVisitor", () => {
475
470
 
476
471
  expect(field.displayType).toBe("result")
477
472
 
478
- const okField = field.optionFields.find((f) => f.label === "Ok")
479
- if (!okField || okField.type !== "record") {
480
- throw new Error("Ok field not found or not record")
481
- }
482
- expect(okField.type).toBe("record")
473
+ const okResolved = field.resolve({
474
+ Ok: { id: BigInt(1), data: new Uint8Array([1, 2, 3]) },
475
+ })
476
+ expect(okResolved.selected).toBe("Ok")
477
+ expect(okResolved.selectedOption.type).toBe("record")
483
478
 
484
- const errField = field.optionFields.find((f) => f.label === "Err")
485
- if (!errField || errField.type !== "variant") {
486
- throw new Error("Err field not found or not variant")
487
- }
488
- expect(errField.type).toBe("variant")
479
+ const errResolved = field.resolve({ Err: { NotFound: null } })
480
+ expect(errResolved.selected).toBe("Err")
481
+ expect(errResolved.selectedOption.type).toBe("variant")
489
482
  })
490
483
 
491
484
  it("should not detect non-Result variant with Ok and other options", () => {
@@ -516,9 +509,9 @@ describe("ResultFieldVisitor", () => {
516
509
  expect(field.type).toBe("tuple")
517
510
  expect(field.candidType).toBe("tuple")
518
511
  expect(field.displayType).toBe("array")
519
- expect(field.fields).toHaveLength(2)
520
- expect(field.fields[0].type).toBe("text")
521
- expect(field.fields[1].type).toBe("number")
512
+ expect(field.items).toHaveLength(2)
513
+ expect(field.items[0].type).toBe("text")
514
+ expect(field.items[1].type).toBe("number")
522
515
  })
523
516
 
524
517
  it("should handle tuple with mixed types", () => {
@@ -534,17 +527,17 @@ describe("ResultFieldVisitor", () => {
534
527
  "data"
535
528
  )
536
529
 
537
- expect(field.fields).toHaveLength(4)
538
- expect(field.fields[0].type).toBe("principal")
539
- expect(field.fields[1].type).toBe("number")
540
- const numField = field.fields[1]
530
+ expect(field.items).toHaveLength(4)
531
+ expect(field.items[0].type).toBe("principal")
532
+ expect(field.items[1].type).toBe("number")
533
+ const numField = field.items[1]
541
534
  if (numField.type === "number") {
542
535
  expect(numField.displayType).toBe("string") // nat64 → string
543
536
  } else {
544
537
  throw new Error("Expected number field")
545
538
  }
546
- expect(field.fields[2].type).toBe("boolean")
547
- expect(field.fields[3].type).toBe("blob")
539
+ expect(field.items[2].type).toBe("boolean")
540
+ expect(field.items[3].type).toBe("blob")
548
541
  })
549
542
  })
550
543
 
@@ -556,7 +549,9 @@ describe("ResultFieldVisitor", () => {
556
549
  expect(field.type).toBe("optional")
557
550
  expect(field.candidType).toBe("opt")
558
551
  expect(field.displayType).toBe("nullable") // opt T → T | null
559
- expect(field.innerField.type).toBe("text")
552
+ // Validate inner by resolving a value
553
+ const optResolved = field.resolve(["Bob"])
554
+ expect((optResolved.value as ResolvedNode).type).toBe("text")
560
555
  })
561
556
 
562
557
  it("should handle optional record", () => {
@@ -568,7 +563,9 @@ describe("ResultFieldVisitor", () => {
568
563
  const field = visitor.visitOpt(optType, recordInOpt, "metadata")
569
564
 
570
565
  expect(field.type).toBe("optional")
571
- expect(field.innerField.type).toBe("record")
566
+ // Validate inner by resolving a record
567
+ const metaResolved = field.resolve([{ name: "x", value: BigInt(1) }])
568
+ expect((metaResolved.value as ResolvedNode).type).toBe("record")
572
569
  })
573
570
 
574
571
  it("should handle nested optional", () => {
@@ -577,13 +574,11 @@ describe("ResultFieldVisitor", () => {
577
574
  const field = visitor.visitOpt(optType, innerOpt, "maybeNumber")
578
575
 
579
576
  expect(field.type).toBe("optional")
580
- expect(field.innerField.type).toBe("optional")
581
- const inner = field.innerField
582
- if (inner.type === "optional") {
583
- expect(inner.innerField.type).toBe("number")
584
- } else {
585
- throw new Error("Inner field is not optional")
586
- }
577
+ // Validate nested optional by resolving a nested array
578
+ const nestedResolved = field.resolve([[BigInt(1)]])
579
+ expect((nestedResolved.value as ResolvedNode).type).toBe("optional")
580
+ const innerResolved = nestedResolved.value as OptionalNode
581
+ expect((innerResolved.value as ResolvedNode).type).toBe("number")
587
582
  })
588
583
  })
589
584
 
@@ -595,11 +590,10 @@ describe("ResultFieldVisitor", () => {
595
590
  expect(field.type).toBe("vector")
596
591
  expect(field.candidType).toBe("vec")
597
592
  expect(field.displayType).toBe("array")
598
- if (field.type === "vector") {
599
- expect(field.itemField.type).toBe("text")
600
- } else {
601
- throw new Error("Field is not a vector")
602
- }
593
+ // Validate items by resolving
594
+ const vecResolved = field.resolve(["a", "b", "c"]) as VectorNode
595
+ expect(vecResolved.items).toHaveLength(3)
596
+ expect(vecResolved.items[0].value).toBe("a")
603
597
  })
604
598
 
605
599
  it("should handle vector of records", () => {
@@ -611,11 +605,11 @@ describe("ResultFieldVisitor", () => {
611
605
  const field = visitor.visitVec(vecType, recType, "items")
612
606
 
613
607
  expect(field.type).toBe("vector")
614
- if (field.type === "vector") {
615
- expect(field.itemField.type).toBe("record")
616
- } else {
617
- throw new Error("Field is not a vector")
618
- }
608
+ // Validate by resolving
609
+ const itemsResolved = field.resolve([
610
+ { id: BigInt(1), name: "x" },
611
+ ]) as VectorNode
612
+ expect(itemsResolved.items[0].type).toBe("record")
619
613
  })
620
614
 
621
615
  it("should handle blob (vec nat8)", () => {
@@ -625,11 +619,11 @@ describe("ResultFieldVisitor", () => {
625
619
  expect(field.type).toBe("blob")
626
620
  expect(field.candidType).toBe("blob")
627
621
  expect(field.displayType).toBe("string") // Blob → hex string
628
- if (field.type === "blob") {
629
- expect(field.displayHint).toBe("hex")
630
- } else {
631
- throw new Error("Field is not a blob")
632
- }
622
+ // Validate by resolving a Uint8Array
623
+ const blobResolved = field.resolve(
624
+ new Uint8Array([0x12, 0x34, 0xab, 0xcd])
625
+ )
626
+ expect(blobResolved.value).toBe("1234abcd")
633
627
  })
634
628
 
635
629
  it("should handle nested vectors", () => {
@@ -638,11 +632,9 @@ describe("ResultFieldVisitor", () => {
638
632
  const field = visitor.visitVec(nestedVecType, innerVec, "matrix")
639
633
 
640
634
  expect(field.type).toBe("vector")
641
- if (field.type === "vector") {
642
- expect(field.itemField.type).toBe("vector")
643
- } else {
644
- throw new Error("Field is not a vector")
645
- }
635
+ // Validate nested items via resolve
636
+ const nestedResolved = field.resolve([[BigInt(1)]]) as VectorNode
637
+ expect(nestedResolved.items[0].type).toBe("vector")
646
638
  })
647
639
 
648
640
  it("should handle vec of tuples (Map-like)", () => {
@@ -652,13 +644,15 @@ describe("ResultFieldVisitor", () => {
652
644
  mapType,
653
645
  tupleType,
654
646
  "metadata"
655
- ) as VectorResultField
647
+ ) as VectorNode
656
648
 
657
649
  expect(field.type).toBe("vector")
658
- expect(field.itemField.type).toBe("tuple")
659
- const itemField = field.itemField
660
- if (itemField.type === "tuple") {
661
- expect(itemField.fields).toHaveLength(2)
650
+ // Validate by resolving a tuple item
651
+ const vecTupleResolved = field.resolve([["a", BigInt(1)]])
652
+ expect(vecTupleResolved.items[0].type).toBe("tuple")
653
+ const item = vecTupleResolved.items[0] as TupleNode
654
+ if (item.type === "tuple") {
655
+ expect(item.items).toHaveLength(2)
662
656
  } else {
663
657
  throw new Error("Item field is not tuple")
664
658
  }
@@ -693,17 +687,10 @@ describe("ResultFieldVisitor", () => {
693
687
  expect(field.type).toBe("recursive")
694
688
  expect(field.candidType).toBe("rec")
695
689
  expect(field.displayType).toBe("recursive")
696
- expect(field.typeName).toBeDefined()
697
- expect(typeof field.extract).toBe("function")
698
-
699
- // Extract should return the variant
700
- const extracted = field.extract()
701
- if (extracted.type !== "variant") {
702
- throw new Error("Extracted field is not variant")
703
- }
704
- expect(extracted.type).toBe("variant")
705
- expect(extracted.options).toContain("Leaf")
706
- expect(extracted.options).toContain("Node")
690
+ // Resolve to inspect inner schema
691
+ const treeResolved = field.resolve({ Leaf: BigInt(1) }) as RecursiveNode
692
+ expect(treeResolved.inner.type).toBe("variant")
693
+ expect((treeResolved.inner as VariantNode).selected).toBe("Leaf")
707
694
  })
708
695
 
709
696
  it("should handle recursive linked list", () => {
@@ -732,11 +719,9 @@ describe("ResultFieldVisitor", () => {
732
719
 
733
720
  expect(field.type).toBe("recursive")
734
721
 
735
- const extracted = field.extract()
736
- if (extracted.type !== "variant") {
737
- throw new Error("Extracted field is not variant")
738
- }
739
- expect(extracted.options).toEqual(["Nil", "Cons"])
722
+ const listResolved = field.resolve({ Nil: null }) as RecursiveNode
723
+ expect(listResolved.inner.type).toBe("variant")
724
+ expect((listResolved.inner as VariantNode).selected).toBe("Nil")
740
725
  })
741
726
  })
742
727
 
@@ -752,11 +737,11 @@ describe("ResultFieldVisitor", () => {
752
737
  expect(meta.functionType).toBe("query")
753
738
  expect(meta.functionName).toBe("get_balance")
754
739
  expect(meta.returnCount).toBe(1)
755
- expect(meta.resultFields).toHaveLength(1)
740
+ expect(meta.returns).toHaveLength(1)
756
741
 
757
- expect(meta.resultFields).toHaveLength(1)
742
+ expect(meta.returns).toHaveLength(1)
758
743
 
759
- const returnField = meta.resultFields[0]
744
+ const returnField = meta.returns[0]
760
745
  if (returnField.type === "number") {
761
746
  expect(returnField.candidType).toBe("nat")
762
747
  expect(returnField.displayType).toBe("string")
@@ -786,7 +771,7 @@ describe("ResultFieldVisitor", () => {
786
771
  expect(meta.functionType).toBe("update")
787
772
  expect(meta.returnCount).toBe(1)
788
773
 
789
- const resultField = meta.resultFields[0]
774
+ const resultField = meta.returns[0]
790
775
  if (resultField.type === "variant") {
791
776
  expect(resultField.displayType).toBe("result")
792
777
  } else {
@@ -799,10 +784,10 @@ describe("ResultFieldVisitor", () => {
799
784
  const meta = visitor.visitFunc(funcType, "get_info")
800
785
 
801
786
  expect(meta.returnCount).toBe(3)
802
- expect(meta.resultFields).toHaveLength(3)
803
- expect(meta.resultFields[0].type).toBe("text")
804
- expect(meta.resultFields[1].type).toBe("number")
805
- expect(meta.resultFields[2].type).toBe("boolean")
787
+ expect(meta.returns).toHaveLength(3)
788
+ expect(meta.returns[0].type).toBe("text")
789
+ expect(meta.returns[1].type).toBe("number")
790
+ expect(meta.returns[2].type).toBe("boolean")
806
791
  })
807
792
 
808
793
  it("should handle function with no returns", () => {
@@ -810,7 +795,7 @@ describe("ResultFieldVisitor", () => {
810
795
  const meta = visitor.visitFunc(funcType, "log")
811
796
 
812
797
  expect(meta.returnCount).toBe(0)
813
- expect(meta.resultFields).toHaveLength(0)
798
+ expect(meta.returns).toHaveLength(0)
814
799
  })
815
800
  })
816
801
 
@@ -852,8 +837,8 @@ describe("ResultFieldVisitor", () => {
852
837
  const getBalanceMeta = serviceMeta["get_balance"]
853
838
  expect(getBalanceMeta.functionType).toBe("query")
854
839
  expect(getBalanceMeta.returnCount).toBe(1)
855
- expect(getBalanceMeta.resultFields[0].type).toBe("number")
856
- const balanceField = getBalanceMeta.resultFields[0]
840
+ expect(getBalanceMeta.returns[0].type).toBe("number")
841
+ const balanceField = getBalanceMeta.returns[0]
857
842
  if (balanceField.type === "number") {
858
843
  expect(balanceField.displayType).toBe("string")
859
844
  } else {
@@ -867,7 +852,7 @@ describe("ResultFieldVisitor", () => {
867
852
  // Check get_metadata
868
853
  const getMetadataMeta = serviceMeta["get_metadata"]
869
854
  expect(getMetadataMeta.returnCount).toBe(1)
870
- expect(getMetadataMeta.resultFields[0].type).toBe("vector")
855
+ expect(getMetadataMeta.returns[0].type).toBe("vector")
871
856
  })
872
857
  })
873
858
 
@@ -892,7 +877,7 @@ describe("ResultFieldVisitor", () => {
892
877
  expect(meta.functionType).toBe("query")
893
878
  expect(meta.returnCount).toBe(1)
894
879
 
895
- const balanceField = meta.resultFields[0]
880
+ const balanceField = meta.returns[0]
896
881
  if (balanceField.type === "number") {
897
882
  expect(balanceField.candidType).toBe("nat")
898
883
  expect(balanceField.displayType).toBe("string")
@@ -942,23 +927,28 @@ describe("ResultFieldVisitor", () => {
942
927
 
943
928
  expect(field.displayType).toBe("result")
944
929
 
945
- const okField = field.optionFields.find((f) => f.label === "Ok")
946
-
947
- if (!okField || okField.type !== "number") {
930
+ const okResolved = field.resolve({ Ok: BigInt(123) })
931
+ if ((okResolved.selectedOption as ResolvedNode).type !== "number") {
948
932
  throw new Error("Ok field is not number")
949
933
  }
934
+ expect((okResolved.selectedOption as ResolvedNode).candidType).toBe("nat")
935
+ expect((okResolved.selectedOption as ResolvedNode).displayType).toBe(
936
+ "string"
937
+ )
950
938
 
951
- expect(okField.candidType).toBe("nat")
952
- expect(okField.displayType).toBe("string")
953
-
954
- const errField = field.optionFields.find((f) => f.label === "Err")
955
- if (!errField || errField.type !== "variant") {
956
- throw new Error("Err field is not variant")
957
- }
958
-
959
- expect(errField.type).toBe("variant")
960
- expect(errField.options).toContain("InsufficientFunds")
961
- expect(errField.options).toContain("GenericError")
939
+ const errResolved = field.resolve({
940
+ Err: { InsufficientFunds: { balance: BigInt(0) } },
941
+ })
942
+ const innerErr = errResolved.selectedOption as ResolvedNode
943
+ expect(innerErr.type).toBe("variant")
944
+ const insufficient = (innerErr as any).resolve({
945
+ InsufficientFunds: { balance: BigInt(0) },
946
+ })
947
+ expect(insufficient.selected).toBe("InsufficientFunds")
948
+ const generic = (innerErr as any).resolve({
949
+ GenericError: { error_code: BigInt(1), message: "err" },
950
+ })
951
+ expect(generic.selected).toBe("GenericError")
962
952
  })
963
953
 
964
954
  it("should handle SNS canister status return", () => {
@@ -1011,45 +1001,46 @@ describe("ResultFieldVisitor", () => {
1011
1001
  expect(field.type).toBe("record")
1012
1002
 
1013
1003
  // Check status variant
1014
- const statusField = field.fields.find((f) => f.label === "status")
1004
+ const statusField = field.fields["status"] as VariantNode
1015
1005
  if (!statusField || statusField.type !== "variant") {
1016
- throw new Error("Status field is not variant")
1006
+ throw new Error("Status field not found or not variant")
1017
1007
  }
1018
1008
  expect(statusField.type).toBe("variant")
1019
- expect(statusField.options).toContain("running")
1020
- expect(statusField.options).toContain("stopping")
1021
- expect(statusField.options).toContain("stopped")
1022
- expect(statusField.options).toHaveLength(3)
1009
+ expect(statusField.displayType).toBe("variant")
1010
+ // Validate via resolving options
1011
+ const statusResolved = statusField.resolve({ running: null })
1012
+ expect(statusResolved.selected).toBe("running")
1023
1013
 
1024
1014
  // Check settings record
1025
- const settingsField = field.fields.find((f) => f.label === "settings")
1015
+ const settingsField = field.fields["settings"] as RecordNode
1026
1016
  if (!settingsField || settingsField.type !== "record") {
1027
- throw new Error("Settings field is not record")
1017
+ throw new Error("Settings field not found or not record")
1028
1018
  }
1029
1019
  expect(settingsField.type).toBe("record")
1020
+ expect(settingsField.displayType).toBe("object")
1030
1021
 
1031
- const controllersField = settingsField.fields.find(
1032
- (f) => f.label === "controllers"
1033
- )
1022
+ const controllersField = settingsField.fields["controllers"] as VectorNode
1034
1023
  if (!controllersField || controllersField.type !== "vector") {
1035
- throw new Error("Controllers field is not vector")
1024
+ throw new Error("Controllers field not found or not vector")
1036
1025
  }
1037
1026
  expect(controllersField.type).toBe("vector")
1038
- expect(controllersField.itemField.type).toBe("principal")
1027
+ // Validate item type via resolve
1028
+ const controllersResolved = controllersField.resolve(["aaaaa-aa"])
1029
+ expect(controllersResolved.items[0].type).toBe("principal")
1039
1030
 
1040
1031
  // Check cycles - should detect special format
1041
- const cyclesField = field.fields.find((f) => f.label === "cycles")
1032
+ const cyclesField = field.fields["cycles"] as NumberNode
1042
1033
  if (!cyclesField || cyclesField.type !== "number") {
1043
- throw new Error("Cycles field is not number")
1034
+ throw new Error("Cycles field not found or not number")
1044
1035
  }
1045
- expect(cyclesField.numberFormat).toBe("cycle")
1036
+ expect(cyclesField.format).toBe("cycle")
1046
1037
 
1047
1038
  // Check module_hash - optional blob
1048
- const moduleHashField = field.fields.find(
1049
- (f) => f.label === "module_hash"
1050
- ) as OptionalResultField
1039
+ const moduleHashField = field.fields["module_hash"] as OptionalNode
1051
1040
  expect(moduleHashField.type).toBe("optional")
1052
- expect(moduleHashField.innerField.type).toBe("blob")
1041
+ // Validate via resolving a sample blob
1042
+ const moduleHashResolved = moduleHashField.resolve([new Uint8Array([1])])
1043
+ expect((moduleHashResolved.value as ResolvedNode).type).toBe("blob")
1053
1044
  })
1054
1045
 
1055
1046
  it("should handle complex governance proposal types", () => {
@@ -1114,12 +1105,10 @@ describe("ResultFieldVisitor", () => {
1114
1105
  )
1115
1106
 
1116
1107
  expect(field.type).toBe("record")
1117
- expect(field.fields.length).toBeGreaterThan(8)
1108
+ expect(Object.keys(field.fields)).toHaveLength(12)
1118
1109
 
1119
1110
  // Check timestamp field (note: the label pattern matching may not match "proposal_timestamp_seconds")
1120
- const timestampField = field.fields.find(
1121
- (f) => f.label === "proposal_timestamp_seconds"
1122
- )
1111
+ const timestampField = field.fields["proposal_timestamp_seconds"]
1123
1112
  if (!timestampField || timestampField.type !== "number") {
1124
1113
  throw new Error("Timestamp field not found or not number")
1125
1114
  }
@@ -1127,20 +1116,23 @@ describe("ResultFieldVisitor", () => {
1127
1116
  expect(timestampField.displayType).toBe("string") // nat64 → string
1128
1117
 
1129
1118
  // Check ballots - vec of tuples
1130
- const ballotsField = field.fields.find((f) => f.label === "ballots")
1119
+ const ballotsField = field.fields["ballots"]
1131
1120
  if (!ballotsField || ballotsField.type !== "vector") {
1132
1121
  throw new Error("Ballots field not found or not vector")
1133
1122
  }
1134
1123
  expect(ballotsField.type).toBe("vector")
1135
- expect(ballotsField.itemField.type).toBe("tuple")
1136
-
1137
- const ballotTuple = ballotsField.itemField
1124
+ // Validate via resolve
1125
+ const ballotsResolved = ballotsField.resolve([
1126
+ [BigInt(1), { vote: 1, voting_power: BigInt(2) }],
1127
+ ]) as TupleNode
1128
+ expect(ballotsResolved.items).toHaveLength(1)
1129
+ const ballotTuple = ballotsResolved.items[0] as TupleNode
1138
1130
  if (ballotTuple.type !== "tuple") {
1139
1131
  throw new Error("Ballot item is not tuple")
1140
1132
  }
1141
- expect(ballotTuple.fields).toHaveLength(2)
1142
- expect(ballotTuple.fields[0].type).toBe("number") // nat64
1143
- expect(ballotTuple.fields[1].type).toBe("record") // ballot record
1133
+ expect(ballotTuple.items).toHaveLength(2)
1134
+ expect(ballotTuple.items[0].type).toBe("number") // nat64
1135
+ expect(ballotTuple.items[1].type).toBe("record") // ballot record
1144
1136
  })
1145
1137
  })
1146
1138
 
@@ -1179,7 +1171,7 @@ describe("ResultFieldVisitor", () => {
1179
1171
  if (typeof field === "object" && "displayType" in field) {
1180
1172
  expect(field.displayType).toBe(expectedDisplay)
1181
1173
  } else {
1182
- throw new Error("Expected a ResultField")
1174
+ throw new Error("Expected as ResultNode")
1183
1175
  }
1184
1176
  })
1185
1177
  })
@@ -1194,40 +1186,30 @@ describe("ResultFieldVisitor", () => {
1194
1186
  it("should resolve text field with value", () => {
1195
1187
  const field = visitor.visitText(IDL.Text, "message")
1196
1188
  const resolved = field.resolve("Hello World")
1197
-
1198
- expect(resolved.field).toBe(field)
1199
1189
  expect(resolved.value).toBe("Hello World")
1200
1190
  })
1201
1191
 
1202
1192
  it("should resolve number field with value", () => {
1203
1193
  const field = visitor.visitNat(IDL.Nat, "amount")
1204
1194
  const resolved = field.resolve(BigInt(1000000))
1205
-
1206
- expect(resolved.field).toBe(field)
1207
1195
  expect(resolved.value).toBe("1000000")
1208
1196
  })
1209
1197
 
1210
1198
  it("should resolve boolean field with value", () => {
1211
1199
  const field = visitor.visitBool(IDL.Bool, "active")
1212
1200
  const resolved = field.resolve(true)
1213
-
1214
- expect(resolved.field).toBe(field)
1215
1201
  expect(resolved.value).toBe(true)
1216
1202
  })
1217
1203
 
1218
1204
  it("should resolve null field", () => {
1219
1205
  const field = visitor.visitNull(IDL.Null, "empty")
1220
1206
  const resolved = field.resolve(null)
1221
-
1222
- expect(resolved.field).toBe(field)
1223
1207
  expect(resolved.value).toBe(null)
1224
1208
  })
1225
1209
 
1226
1210
  it("should resolve principal field with string value", () => {
1227
1211
  const field = visitor.visitPrincipal(IDL.Principal, "owner")
1228
1212
  const resolved = field.resolve("aaaaa-aa")
1229
-
1230
- expect(resolved.field).toBe(field)
1231
1213
  expect(resolved.value).toBe("aaaaa-aa")
1232
1214
  })
1233
1215
  })
@@ -1255,14 +1237,11 @@ describe("ResultFieldVisitor", () => {
1255
1237
  active: true,
1256
1238
  })
1257
1239
 
1258
- expect(resolved.field).toBe(field)
1259
- const value = resolved.value as Record<
1260
- string,
1261
- { field: ResultField; value: unknown }
1262
- >
1263
- expect(value.name.value).toBe("Alice")
1264
- expect(value.age.value).toBe(30)
1265
- expect(value.active.value).toBe(true)
1240
+ expect(resolved.type).toBe(field.type)
1241
+ const value = resolved.fields as Record<string, ResolvedNode>
1242
+ expect(value["name"].value).toBe("Alice")
1243
+ expect(value["age"].value).toBe(30)
1244
+ expect(value["active"].value).toBe(true)
1266
1245
  })
1267
1246
 
1268
1247
  it("should handle null record value", () => {
@@ -1295,13 +1274,10 @@ describe("ResultFieldVisitor", () => {
1295
1274
  )
1296
1275
 
1297
1276
  const resolved = field.resolve({ Ok: "Success" })
1298
-
1299
- const value = resolved.value as {
1300
- option: string
1301
- value: { field: ResultField; value: unknown }
1302
- }
1303
- expect(value.option).toBe("Ok")
1304
- expect(value.value.value).toBe("Success")
1277
+ expect(resolved.type).toBe(field.type)
1278
+ expect(resolved.selected).toBe("Ok")
1279
+ const data = resolved.selectedOption as ResolvedNode
1280
+ expect(data.value).toBe("Success")
1305
1281
  })
1306
1282
 
1307
1283
  it("should resolve variant with Err option", () => {
@@ -1320,12 +1296,9 @@ describe("ResultFieldVisitor", () => {
1320
1296
 
1321
1297
  const resolved = field.resolve({ Err: "Something went wrong" })
1322
1298
 
1323
- const value = resolved.value as {
1324
- option: string
1325
- value: { field: ResultField; value: unknown }
1326
- }
1327
- expect(value.option).toBe("Err")
1328
- expect(value.value.value).toBe("Something went wrong")
1299
+ expect(resolved.selected).toBe("Err")
1300
+ const data = resolved.selectedOption as ResolvedNode
1301
+ expect(data.value).toBe("Something went wrong")
1329
1302
  })
1330
1303
 
1331
1304
  it("should handle null variant value", () => {
@@ -1356,11 +1329,8 @@ describe("ResultFieldVisitor", () => {
1356
1329
 
1357
1330
  const resolved = field.resolve(["hello", 123n, true])
1358
1331
 
1359
- expect(resolved.field).toBe(field)
1360
- const value = resolved.value as Array<{
1361
- field: ResultField
1362
- value: unknown
1363
- }>
1332
+ expect(resolved.type).toBe(field.type)
1333
+ const value = resolved.items as ResolvedNode[]
1364
1334
  expect(value).toHaveLength(3)
1365
1335
  expect(value[0].value).toBe("hello")
1366
1336
  expect(value[1].value).toBe("123")
@@ -1384,8 +1354,8 @@ describe("ResultFieldVisitor", () => {
1384
1354
 
1385
1355
  const resolved = field.resolve(["Bob"])
1386
1356
 
1387
- expect(resolved.field).toBe(field)
1388
- const inner = resolved.value as { field: ResultField; value: unknown }
1357
+ expect(resolved.type).toBe(field.type)
1358
+ const inner = resolved.value as ResolvedNode
1389
1359
  expect(inner.value).toBe("Bob")
1390
1360
  })
1391
1361
 
@@ -1394,8 +1364,6 @@ describe("ResultFieldVisitor", () => {
1394
1364
  const field = visitor.visitOpt(optType, IDL.Text, "nickname")
1395
1365
 
1396
1366
  const resolved = field.resolve(null)
1397
-
1398
- expect(resolved.field).toBe(field)
1399
1367
  expect(resolved.value).toBe(null)
1400
1368
  })
1401
1369
  })
@@ -1405,13 +1373,10 @@ describe("ResultFieldVisitor", () => {
1405
1373
  const vecType = IDL.Vec(IDL.Text)
1406
1374
  const field = visitor.visitVec(vecType, IDL.Text, "tags")
1407
1375
 
1408
- const resolved = field.resolve(["a", "b", "c"])
1376
+ const resolved = field.resolve(["a", "b", "c"]) as VectorNode
1409
1377
 
1410
- expect(resolved.field).toBe(field)
1411
- const value = resolved.value as Array<{
1412
- field: ResultField
1413
- value: unknown
1414
- }>
1378
+ expect(resolved.type).toBe(field.type)
1379
+ const value = resolved.items as ResolvedNode[]
1415
1380
  expect(value).toHaveLength(3)
1416
1381
  expect(value[0].value).toBe("a")
1417
1382
  expect(value[1].value).toBe("b")
@@ -1422,12 +1387,11 @@ describe("ResultFieldVisitor", () => {
1422
1387
  const vecType = IDL.Vec(IDL.Nat)
1423
1388
  const field = visitor.visitVec(vecType, IDL.Nat, "numbers")
1424
1389
 
1425
- const resolved = field.resolve([])
1390
+ const resolved = field.resolve([]) as VectorNode
1426
1391
 
1427
- const value = resolved.value as Array<{
1428
- field: ResultField
1429
- value: unknown
1430
- }>
1392
+ expect(resolved.type).toBe(field.type)
1393
+
1394
+ const value = resolved.items as ResolvedNode[]
1431
1395
  expect(value).toHaveLength(0)
1432
1396
  })
1433
1397
 
@@ -1448,7 +1412,7 @@ describe("ResultFieldVisitor", () => {
1448
1412
 
1449
1413
  const resolved = field.resolve(new Uint8Array([0x12, 0x34, 0xab, 0xcd]))
1450
1414
 
1451
- expect(resolved.field).toBe(field)
1415
+ expect(resolved.type).toBe(field.type)
1452
1416
  expect(resolved.value).toBe("1234abcd")
1453
1417
  })
1454
1418
  })
@@ -1479,7 +1443,7 @@ describe("ResultFieldVisitor", () => {
1479
1443
  })
1480
1444
 
1481
1445
  // The recursive type should delegate to its inner record type
1482
- expect(resolved.field.type).toBe("record")
1446
+ expect(resolved.type).toBe("recursive")
1483
1447
  })
1484
1448
  })
1485
1449
 
@@ -1519,29 +1483,24 @@ describe("ResultFieldVisitor", () => {
1519
1483
  },
1520
1484
  })
1521
1485
 
1522
- const value = resolved.value as Record<
1523
- string,
1524
- { field: ResultField; value: unknown }
1525
- >
1526
- const userValue = value.user.value as Record<
1527
- string,
1528
- { field: ResultField; value: unknown }
1529
- >
1530
- const profileValue = userValue.profile.value as Record<
1531
- string,
1532
- { field: ResultField; value: unknown }
1533
- >
1534
- expect(profileValue.name.value).toBe("Alice")
1535
- expect(profileValue.verified.value).toBe(true)
1486
+ const value = resolved.fields as Record<string, ResolvedNode>
1487
+ const userNode = value.user as RecordNode
1488
+ if (userNode.type !== "record") {
1489
+ throw new Error("User node is not a record")
1490
+ }
1491
+ const profileNode = userNode.fields["profile"] as RecordNode
1492
+ const profileFields = profileNode.fields as Record<string, ResolvedNode>
1493
+ expect(profileFields["name"].value).toBe("Alice")
1494
+ expect(profileFields["verified"].value).toBe(true)
1536
1495
  })
1537
1496
  })
1538
1497
  })
1539
1498
 
1540
1499
  // ════════════════════════════════════════════════════════════════════════
1541
- // generateMetadata() Method Tests
1500
+ // resolve() Method Tests
1542
1501
  // ════════════════════════════════════════════════════════════════════════
1543
1502
 
1544
- describe("generateMetadata() Method", () => {
1503
+ describe("resolve() Method", () => {
1545
1504
  it("should generate metadata for single return value", () => {
1546
1505
  const service = IDL.Service({
1547
1506
  getName: IDL.Func([], [IDL.Text], ["query"]),
@@ -1552,13 +1511,13 @@ describe("ResultFieldVisitor", () => {
1552
1511
  )
1553
1512
  const methodMeta = serviceMeta["getName"]
1554
1513
 
1555
- const result = methodMeta.generateMetadata("Alice")
1514
+ const result = methodMeta.resolve("Alice")
1556
1515
 
1557
1516
  expect(result.functionName).toBe("getName")
1558
1517
  expect(result.functionType).toBe("query")
1559
1518
  expect(result.results).toHaveLength(1)
1560
1519
  expect(result.results[0].value).toBe("Alice")
1561
- expect(result.results[0].field.type).toBe("text")
1520
+ expect(result.results[0].type).toBe("text")
1562
1521
  })
1563
1522
 
1564
1523
  it("should generate metadata for multiple return values", () => {
@@ -1571,18 +1530,14 @@ describe("ResultFieldVisitor", () => {
1571
1530
  )
1572
1531
  const methodMeta = serviceMeta["getStats"]
1573
1532
 
1574
- const result = methodMeta.generateMetadata([
1575
- BigInt(100),
1576
- BigInt(200),
1577
- "active",
1578
- ])
1533
+ const result = methodMeta.resolve([BigInt(100), BigInt(200), "active"])
1579
1534
 
1580
1535
  expect(result.results).toHaveLength(3)
1581
1536
  expect(result.results[0].value).toBe("100")
1582
- expect(result.results[0].field.type).toBe("number")
1537
+ expect(result.results[0].type).toBe("number")
1583
1538
  expect(result.results[1].value).toBe("200")
1584
1539
  expect(result.results[2].value).toBe("active")
1585
- expect(result.results[2].field.type).toBe("text")
1540
+ expect(result.results[2].type).toBe("text")
1586
1541
  })
1587
1542
 
1588
1543
  it("should generate metadata for record return value", () => {
@@ -1599,22 +1554,19 @@ describe("ResultFieldVisitor", () => {
1599
1554
  )
1600
1555
  const methodMeta = serviceMeta["getUser"]
1601
1556
 
1602
- const result = methodMeta.generateMetadata({
1557
+ const result = methodMeta.resolve({
1603
1558
  name: "Bob",
1604
1559
  balance: BigInt(1000),
1605
1560
  })
1606
1561
 
1607
1562
  expect(result.results).toHaveLength(1)
1608
- expect(result.results[0].field.type).toBe("record")
1563
+ expect(result.results[0].type).toBe("record")
1609
1564
 
1610
- const recordValue = result.results[0].value
1611
- if (typeof recordValue !== "object" || recordValue === null) {
1612
- throw new Error("Expected record value object")
1565
+ const recordNode = result.results[0] as RecordNode
1566
+ if (recordNode.type !== "record") {
1567
+ throw new Error("Expected record node")
1613
1568
  }
1614
- const val = recordValue as Record<
1615
- string,
1616
- { field: ResultField; value: unknown }
1617
- >
1569
+ const val = recordNode.fields as Record<string, ResolvedNode>
1618
1570
  expect(val.name.value).toBe("Bob")
1619
1571
  expect(val.balance.value).toBe("1000")
1620
1572
  })
@@ -1634,31 +1586,29 @@ describe("ResultFieldVisitor", () => {
1634
1586
  const methodMeta = serviceMeta["transfer"]
1635
1587
 
1636
1588
  // Test Ok case
1637
- const okResult = methodMeta.generateMetadata({ Ok: BigInt(12345) })
1638
- expect(okResult.results[0].field.type).toBe("variant")
1639
- if (okResult.results[0].field.type === "variant") {
1640
- expect(okResult.results[0].field.displayType).toBe("result")
1589
+ const okResult = methodMeta.resolve({ Ok: BigInt(12345) })
1590
+ expect(okResult.results[0].type).toBe("variant")
1591
+ if (okResult.results[0].type === "variant") {
1592
+ expect(okResult.results[0].displayType).toBe("result")
1641
1593
  } else {
1642
1594
  throw new Error("Expected variant field")
1643
1595
  }
1644
1596
 
1645
- const okValue = okResult.results[0].value as {
1646
- option: string
1647
- value: { field: ResultField; value: unknown }
1648
- }
1649
- expect(okValue.option).toBe("Ok")
1650
- expect(okValue.value.value).toBe("12345")
1597
+ const okValue = okResult.results[0] as ResolvedNode
1598
+ expect((okValue as any).selected).toBe("Ok")
1599
+ expect(((okValue as any).selectedOption as ResolvedNode).value).toBe(
1600
+ "12345"
1601
+ )
1651
1602
 
1652
1603
  // Test Err case
1653
- const errResult = methodMeta.generateMetadata({
1604
+ const errResult = methodMeta.resolve({
1654
1605
  Err: "Insufficient funds",
1655
1606
  })
1656
- const errValue = errResult.results[0].value as {
1657
- option: string
1658
- value: { field: ResultField; value: unknown }
1659
- }
1660
- expect(errValue.option).toBe("Err")
1661
- expect(errValue.value.value).toBe("Insufficient funds")
1607
+ const errValue = errResult.results[0] as ResolvedNode
1608
+ expect((errValue as any).selected).toBe("Err")
1609
+ expect(((errValue as any).selectedOption as ResolvedNode).value).toBe(
1610
+ "Insufficient funds"
1611
+ )
1662
1612
  })
1663
1613
 
1664
1614
  it("should generate metadata for optional return value", () => {
@@ -1672,16 +1622,13 @@ describe("ResultFieldVisitor", () => {
1672
1622
  const methodMeta = serviceMeta["findUser"]
1673
1623
 
1674
1624
  // Test with value - Optional is [value]
1675
- const foundResult = methodMeta.generateMetadata(["Alice"])
1676
- expect(foundResult.results[0].field.type).toBe("optional")
1677
- const foundInner = foundResult.results[0].value as {
1678
- field: ResultField
1679
- value: unknown
1680
- }
1681
- expect(foundInner.value).toBe("Alice")
1625
+ const foundResult = methodMeta.resolve(["Alice"])
1626
+ expect(foundResult.results[0].type).toBe("optional")
1627
+ const foundInner = foundResult.results[0] as OptionalNode
1628
+ expect(foundInner.value?.value).toBe("Alice")
1682
1629
 
1683
1630
  // Test with null - optional is []
1684
- const notFoundResult = methodMeta.generateMetadata([])
1631
+ const notFoundResult = methodMeta.resolve([])
1685
1632
  expect(notFoundResult.results[0].value).toBe(null)
1686
1633
  })
1687
1634
 
@@ -1695,17 +1642,15 @@ describe("ResultFieldVisitor", () => {
1695
1642
  )
1696
1643
  const methodMeta = serviceMeta["getItems"]
1697
1644
 
1698
- const result = methodMeta.generateMetadata(["item1", "item2", "item3"])
1645
+ const result = methodMeta.resolve(["item1", "item2", "item3"])
1699
1646
 
1700
- expect(result.results[0].field.type).toBe("vector")
1701
- const vecValue = result.results[0].value as Array<{
1702
- field: ResultField
1703
- value: unknown
1704
- }>
1705
- expect(vecValue).toHaveLength(3)
1706
- expect(vecValue[0].value).toBe("item1")
1707
- expect(vecValue[1].value).toBe("item2")
1708
- expect(vecValue[2].value).toBe("item3")
1647
+ expect(result.results[0].type).toBe("vector")
1648
+ const vecNode = result.results[0] as ResolvedNode
1649
+ const vecItems = (vecNode as any).items as ResolvedNode[]
1650
+ expect(vecItems).toHaveLength(3)
1651
+ expect(vecItems[0].value).toBe("item1")
1652
+ expect(vecItems[1].value).toBe("item2")
1653
+ expect(vecItems[2].value).toBe("item3")
1709
1654
  })
1710
1655
 
1711
1656
  it("should generate metadata for update function", () => {
@@ -1721,7 +1666,7 @@ describe("ResultFieldVisitor", () => {
1721
1666
  expect(methodMeta.functionType).toBe("update")
1722
1667
 
1723
1668
  const rawData = [true]
1724
- const result = methodMeta.generateMetadata(rawData[0])
1669
+ const result = methodMeta.resolve(rawData[0])
1725
1670
 
1726
1671
  expect(result.functionType).toBe("update")
1727
1672
  expect(result.functionName).toBe("setName")
@@ -1754,7 +1699,7 @@ describe("ResultFieldVisitor", () => {
1754
1699
  const methodMeta = serviceMeta["icrc1_transfer"]
1755
1700
 
1756
1701
  // Test successful transfer
1757
- const successResult = methodMeta.generateMetadata({ Ok: BigInt(1000) })
1702
+ const successResult = methodMeta.resolve({ Ok: BigInt(1000) })
1758
1703
  console.log(
1759
1704
  "🚀 ~ result:",
1760
1705
  JSON.stringify(
@@ -1764,28 +1709,24 @@ describe("ResultFieldVisitor", () => {
1764
1709
  )
1765
1710
  )
1766
1711
 
1767
- const successValue = successResult.results[0].value as {
1768
- option: string
1769
- value: { field: ResultField; value: unknown }
1770
- }
1771
- expect(successValue.option).toBe("Ok")
1772
- expect(successValue.value.value).toBe("1000")
1712
+ const successValue = successResult.results[0] as ResolvedNode
1713
+ expect((successValue as any).selected).toBe("Ok")
1714
+ expect(((successValue as any).selectedOption as ResolvedNode).value).toBe(
1715
+ "1000"
1716
+ )
1773
1717
 
1774
1718
  // Test error case
1775
- const errorResult = methodMeta.generateMetadata({
1719
+ const errorResult = methodMeta.resolve({
1776
1720
  Err: { InsufficientFunds: { balance: BigInt(50) } },
1777
1721
  })
1778
- const errorValue = errorResult.results[0].value as {
1779
- option: string
1780
- value: { field: ResultField; value: unknown }
1781
- }
1782
- expect(errorValue.option).toBe("Err")
1722
+ const errorValue = errorResult.results[0] as ResolvedNode
1723
+ expect((errorValue as any).selected).toBe("Err")
1783
1724
 
1784
- const val = errorValue.value.value
1785
- if (typeof val !== "object" || val === null || !("option" in val)) {
1725
+ const val = (errorValue as any).selectedOption as ResolvedNode
1726
+ if (typeof val !== "object" || val === null || !("selected" in val)) {
1786
1727
  throw new Error("Expected variant value object")
1787
1728
  }
1788
- expect(val.option).toBe("InsufficientFunds")
1729
+ expect((val as any).selected).toBe("InsufficientFunds")
1789
1730
  })
1790
1731
 
1791
1732
  it("should handle empty return", () => {
@@ -1800,16 +1741,16 @@ describe("ResultFieldVisitor", () => {
1800
1741
 
1801
1742
  expect(methodMeta.returnCount).toBe(0)
1802
1743
 
1803
- const result = methodMeta.generateMetadata([])
1744
+ const result = methodMeta.resolve([])
1804
1745
  expect(result.results).toHaveLength(0)
1805
1746
  })
1806
1747
  })
1807
1748
 
1808
1749
  // ════════════════════════════════════════════════════════════════════════════
1809
- // generateMetadataWithRaw() Method Tests
1750
+ // resolve() Method Tests
1810
1751
  // ════════════════════════════════════════════════════════════════════════════
1811
1752
 
1812
- describe("generateMetadataWithRaw() Method", () => {
1753
+ describe("resolve() Method", () => {
1813
1754
  it("should include both raw and display values for single return", () => {
1814
1755
  const service = IDL.Service({
1815
1756
  getBalance: IDL.Func([], [IDL.Nat], ["query"]),
@@ -1823,7 +1764,7 @@ describe("ResultFieldVisitor", () => {
1823
1764
  // Simulate raw BigInt and display string
1824
1765
  const rawData = [BigInt(1000000)]
1825
1766
 
1826
- const result = methodMeta.generateMetadata(rawData[0])
1767
+ const result = methodMeta.resolve(rawData[0])
1827
1768
 
1828
1769
  expect(result.functionName).toBe("getBalance")
1829
1770
  expect(result.functionType).toBe("query")
@@ -1831,7 +1772,7 @@ describe("ResultFieldVisitor", () => {
1831
1772
  expect(result.results[0].raw).toBe(BigInt(1000000))
1832
1773
  expect(result.results[0].value).toBe("1000000")
1833
1774
  expect(result.results[0].raw).toBe(BigInt(1000000))
1834
- expect(result.results[0].field.type).toBe("number")
1775
+ expect(result.results[0].type).toBe("number")
1835
1776
  })
1836
1777
 
1837
1778
  it("should include both raw and display values for multiple returns", () => {
@@ -1847,14 +1788,14 @@ describe("ResultFieldVisitor", () => {
1847
1788
  // Use BigInt with string to safe safe integer
1848
1789
  const rawData = [BigInt("9007199254740993"), "active", true]
1849
1790
 
1850
- const result = methodMeta.generateMetadata(rawData)
1791
+ const result = methodMeta.resolve(rawData)
1851
1792
 
1852
1793
  expect(result.results).toHaveLength(3)
1853
1794
 
1854
1795
  // nat64 → string display, BigInt raw
1855
1796
  expect(result.results[0].value).toBe("9007199254740993")
1856
1797
  expect(result.results[0].raw).toBe(BigInt("9007199254740993"))
1857
- expect(result.results[0].field.candidType).toBe("nat64")
1798
+ expect(result.results[0].candidType).toBe("nat64")
1858
1799
 
1859
1800
  // text → same for both
1860
1801
  expect(result.results[1].value).toBe("active")
@@ -1880,21 +1821,18 @@ describe("ResultFieldVisitor", () => {
1880
1821
  const methodMeta = serviceMeta["getUser"]
1881
1822
 
1882
1823
  const rawData = [{ name: "Alice", balance: BigInt(500) }]
1883
- const result = methodMeta.generateMetadata(rawData[0])
1824
+ const result = methodMeta.resolve(rawData[0])
1884
1825
 
1885
1826
  expect(result.results[0].raw).toEqual({
1886
1827
  name: "Alice",
1887
1828
  balance: BigInt(500),
1888
1829
  })
1889
1830
 
1890
- const recordValue = result.results[0].value
1891
- if (typeof recordValue !== "object" || recordValue === null) {
1892
- throw new Error("Expected record value object")
1831
+ const recordNode = result.results[0] as RecordNode
1832
+ if (recordNode.type !== "record") {
1833
+ throw new Error("Expected record node")
1893
1834
  }
1894
- const val = recordValue as Record<
1895
- string,
1896
- { field: ResultField; value: unknown }
1897
- >
1835
+ const val = recordNode.fields as Record<string, ResolvedNode>
1898
1836
  expect(val.name.value).toBe("Alice")
1899
1837
  expect(val.balance.value).toBe("500")
1900
1838
  })
@@ -1916,20 +1854,13 @@ describe("ResultFieldVisitor", () => {
1916
1854
  // Test Ok case with raw BigInt
1917
1855
  const rawData = [{ Ok: BigInt(12345) }]
1918
1856
 
1919
- const result = methodMeta.generateMetadata(rawData[0])
1857
+ const result = methodMeta.resolve(rawData[0])
1920
1858
 
1921
1859
  expect(result.results[0].raw).toEqual({ Ok: BigInt(12345) })
1922
1860
 
1923
- const variantValue = result.results[0].value as any
1924
- if (
1925
- typeof variantValue !== "object" ||
1926
- variantValue === null ||
1927
- !("option" in variantValue)
1928
- ) {
1929
- throw new Error("Expected variant value object")
1930
- }
1931
- expect(variantValue.option).toBe("Ok")
1932
- const innerVal = variantValue.value
1861
+ const variantValue = result.results[0] as ResolvedNode
1862
+ expect((variantValue as any).selected).toBe("Ok")
1863
+ const innerVal = (variantValue as any).selectedOption as ResolvedNode
1933
1864
  expect(innerVal.value).toBe("12345")
1934
1865
  })
1935
1866
 
@@ -1948,11 +1879,11 @@ describe("ResultFieldVisitor", () => {
1948
1879
  const principal = Principal.fromText("aaaaa-aa")
1949
1880
  const rawData = [principal]
1950
1881
 
1951
- const result = methodMeta.generateMetadata(rawData[0])
1882
+ const result = methodMeta.resolve(rawData[0])
1952
1883
 
1953
1884
  expect(result.results[0].value).toBe("aaaaa-aa")
1954
1885
  expect(result.results[0].raw).toBe(principal)
1955
- expect(result.results[0].field.type).toBe("principal")
1886
+ expect(result.results[0].type).toBe("principal")
1956
1887
  })
1957
1888
 
1958
1889
  it("should handle vector with raw and display values", () => {
@@ -1967,12 +1898,10 @@ describe("ResultFieldVisitor", () => {
1967
1898
 
1968
1899
  const rawData = [[BigInt(100), BigInt(200), BigInt(300)]]
1969
1900
 
1970
- const result = methodMeta.generateMetadata(rawData[0])
1901
+ const result = methodMeta.resolve(rawData[0])
1971
1902
 
1972
- const vecValue = result.results[0].value
1973
- if (!Array.isArray(vecValue)) {
1974
- throw new Error("Expected vector value array")
1975
- }
1903
+ const vecNode = result.results[0] as ResolvedNode
1904
+ const vecValue = (vecNode as any).items as ResolvedNode[]
1976
1905
  expect(vecValue).toHaveLength(3)
1977
1906
  expect(vecValue[0].value).toBe("100")
1978
1907
  expect(vecValue[1].value).toBe("200")
@@ -1992,7 +1921,7 @@ describe("ResultFieldVisitor", () => {
1992
1921
  // Test with value - Optional is [value]
1993
1922
  const rawDataWithValue = [[BigInt(999)]]
1994
1923
 
1995
- const resultWithValue = methodMeta.generateMetadata(rawDataWithValue[0])
1924
+ const resultWithValue = methodMeta.resolve(rawDataWithValue[0])
1996
1925
 
1997
1926
  expect(resultWithValue.results[0].raw).toEqual([BigInt(999)])
1998
1927
  const innerValue = resultWithValue.results[0].value
@@ -2008,7 +1937,7 @@ describe("ResultFieldVisitor", () => {
2008
1937
  // Test with null - Optional is []
2009
1938
  const rawDataNull = [[]]
2010
1939
 
2011
- const resultNull = methodMeta.generateMetadata(rawDataNull[0])
1940
+ const resultNull = methodMeta.resolve(rawDataNull[0])
2012
1941
  expect(resultNull.results[0].raw).toEqual([])
2013
1942
  expect(resultNull.results[0].value).toBe(null)
2014
1943
  })
@@ -2023,7 +1952,7 @@ describe("ResultFieldVisitor", () => {
2023
1952
  )
2024
1953
  const methodMeta = serviceMeta["doNothing"]
2025
1954
 
2026
- const result = methodMeta.generateMetadata([])
1955
+ const result = methodMeta.resolve([])
2027
1956
 
2028
1957
  expect(result.results).toHaveLength(0)
2029
1958
  })