@ic-reactor/candid 3.0.2-beta.0 → 3.0.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.
Files changed (83) hide show
  1. package/README.md +33 -1
  2. package/dist/adapter.js +2 -1
  3. package/dist/adapter.js.map +1 -1
  4. package/dist/display-reactor.d.ts +4 -13
  5. package/dist/display-reactor.d.ts.map +1 -1
  6. package/dist/display-reactor.js +22 -8
  7. package/dist/display-reactor.js.map +1 -1
  8. package/dist/index.d.ts +3 -1
  9. package/dist/index.d.ts.map +1 -1
  10. package/dist/index.js +3 -1
  11. package/dist/index.js.map +1 -1
  12. package/dist/metadata-display-reactor.d.ts +108 -0
  13. package/dist/metadata-display-reactor.d.ts.map +1 -0
  14. package/dist/metadata-display-reactor.js +141 -0
  15. package/dist/metadata-display-reactor.js.map +1 -0
  16. package/dist/reactor.d.ts +1 -1
  17. package/dist/reactor.d.ts.map +1 -1
  18. package/dist/reactor.js +10 -6
  19. package/dist/reactor.js.map +1 -1
  20. package/dist/types.d.ts +38 -7
  21. package/dist/types.d.ts.map +1 -1
  22. package/dist/utils.d.ts +4 -4
  23. package/dist/utils.d.ts.map +1 -1
  24. package/dist/utils.js +33 -10
  25. package/dist/utils.js.map +1 -1
  26. package/dist/visitor/arguments/helpers.d.ts +55 -0
  27. package/dist/visitor/arguments/helpers.d.ts.map +1 -0
  28. package/dist/visitor/arguments/helpers.js +123 -0
  29. package/dist/visitor/arguments/helpers.js.map +1 -0
  30. package/dist/visitor/arguments/index.d.ts +101 -0
  31. package/dist/visitor/arguments/index.d.ts.map +1 -0
  32. package/dist/visitor/arguments/index.js +780 -0
  33. package/dist/visitor/arguments/index.js.map +1 -0
  34. package/dist/visitor/arguments/types.d.ts +270 -0
  35. package/dist/visitor/arguments/types.d.ts.map +1 -0
  36. package/dist/visitor/arguments/types.js +26 -0
  37. package/dist/visitor/arguments/types.js.map +1 -0
  38. package/dist/visitor/constants.d.ts +4 -0
  39. package/dist/visitor/constants.d.ts.map +1 -0
  40. package/dist/visitor/constants.js +73 -0
  41. package/dist/visitor/constants.js.map +1 -0
  42. package/dist/visitor/helpers.d.ts +30 -0
  43. package/dist/visitor/helpers.d.ts.map +1 -0
  44. package/dist/visitor/helpers.js +204 -0
  45. package/dist/visitor/helpers.js.map +1 -0
  46. package/dist/visitor/index.d.ts +5 -0
  47. package/dist/visitor/index.d.ts.map +1 -0
  48. package/dist/visitor/index.js +5 -0
  49. package/dist/visitor/index.js.map +1 -0
  50. package/dist/visitor/returns/index.d.ts +38 -0
  51. package/dist/visitor/returns/index.d.ts.map +1 -0
  52. package/dist/visitor/returns/index.js +460 -0
  53. package/dist/visitor/returns/index.js.map +1 -0
  54. package/dist/visitor/returns/types.d.ts +202 -0
  55. package/dist/visitor/returns/types.d.ts.map +1 -0
  56. package/dist/visitor/returns/types.js +2 -0
  57. package/dist/visitor/returns/types.js.map +1 -0
  58. package/dist/visitor/types.d.ts +19 -0
  59. package/dist/visitor/types.d.ts.map +1 -0
  60. package/dist/visitor/types.js +2 -0
  61. package/dist/visitor/types.js.map +1 -0
  62. package/package.json +16 -7
  63. package/src/adapter.ts +446 -0
  64. package/src/constants.ts +11 -0
  65. package/src/display-reactor.ts +337 -0
  66. package/src/index.ts +8 -0
  67. package/src/metadata-display-reactor.ts +230 -0
  68. package/src/reactor.ts +199 -0
  69. package/src/types.ts +127 -0
  70. package/src/utils.ts +60 -0
  71. package/src/visitor/arguments/helpers.ts +153 -0
  72. package/src/visitor/arguments/index.test.ts +1439 -0
  73. package/src/visitor/arguments/index.ts +981 -0
  74. package/src/visitor/arguments/schema.test.ts +324 -0
  75. package/src/visitor/arguments/types.ts +387 -0
  76. package/src/visitor/constants.ts +76 -0
  77. package/src/visitor/helpers.test.ts +274 -0
  78. package/src/visitor/helpers.ts +223 -0
  79. package/src/visitor/index.ts +4 -0
  80. package/src/visitor/returns/index.test.ts +2377 -0
  81. package/src/visitor/returns/index.ts +658 -0
  82. package/src/visitor/returns/types.ts +302 -0
  83. package/src/visitor/types.ts +75 -0
@@ -0,0 +1,658 @@
1
+ import { isQuery } from "../helpers"
2
+ import { checkTextFormat, checkNumberFormat } from "../constants"
3
+ import { formatLabel } from "../arguments/helpers"
4
+ import { MetadataError } from "../arguments/types"
5
+ import type {
6
+ ResultNode,
7
+ ResolvedNode,
8
+ VisitorDataType,
9
+ MethodMeta,
10
+ ServiceMeta,
11
+ MethodResult,
12
+ NumberFormat,
13
+ TextFormat,
14
+ } from "./types"
15
+
16
+ import { sha256 } from "@noble/hashes/sha2.js"
17
+ import { IDL } from "@icp-sdk/core/candid"
18
+ import { DisplayCodecVisitor, uint8ArrayToHex } from "@ic-reactor/core"
19
+ import type {
20
+ ActorMethodReturnType,
21
+ BaseActor,
22
+ FunctionName,
23
+ FunctionType,
24
+ } from "@ic-reactor/core"
25
+
26
+ export * from "./types"
27
+
28
+ // ════════════════════════════════════════════════════════════════════════════
29
+ // Node Factory - Eliminates Boilerplate
30
+ // ════════════════════════════════════════════════════════════════════════════
31
+
32
+ type Codec = { decode: (v: unknown) => unknown }
33
+
34
+ /**
35
+ * Creates a primitive node with automatic resolve implementation.
36
+ */
37
+ function primitiveNode<T extends VisitorDataType>(
38
+ type: T,
39
+ label: string,
40
+ candidType: string,
41
+ displayType: ResultNode["displayType"],
42
+ codec: Codec,
43
+ extras: object = {}
44
+ ): ResultNode<T> {
45
+ const node: ResultNode<T> = {
46
+ type,
47
+ label,
48
+ displayLabel: formatLabel(label),
49
+ candidType,
50
+ displayType,
51
+ ...extras,
52
+ resolve(data: unknown): ResolvedNode<T> {
53
+ try {
54
+ return {
55
+ ...node,
56
+ value: codec.decode(data),
57
+ raw: data,
58
+ } as unknown as ResolvedNode<T>
59
+ } catch (e) {
60
+ throw new MetadataError(
61
+ `Failed to decode: ${e instanceof Error ? e.message : String(e)}`,
62
+ label,
63
+ candidType
64
+ )
65
+ }
66
+ },
67
+ } as unknown as ResultNode<T>
68
+ return node
69
+ }
70
+
71
+ // ════════════════════════════════════════════════════════════════════════════
72
+ // Simplified Result Field Visitor
73
+ // ════════════════════════════════════════════════════════════════════════════
74
+
75
+ export class ResultFieldVisitor<A = BaseActor> extends IDL.Visitor<
76
+ string,
77
+ ResultNode | MethodMeta<A> | ServiceMeta<A>
78
+ > {
79
+ private codec = new DisplayCodecVisitor()
80
+
81
+ private recCache = new Map<IDL.RecClass<any>, ResultNode<"recursive">>()
82
+
83
+ private getCodec(t: IDL.Type): Codec {
84
+ const codec = t.accept(this.codec, null) as any
85
+ return {
86
+ decode: (v: unknown) => {
87
+ try {
88
+ return typeof codec?.decode === "function" ? codec.decode(v) : v
89
+ } catch {
90
+ return v
91
+ }
92
+ },
93
+ }
94
+ }
95
+
96
+ // ══════════════════════════════════════════════════════════════════════════
97
+ // Service & Function
98
+ // ══════════════════════════════════════════════════════════════════════════
99
+
100
+ public visitService(t: IDL.ServiceClass): ServiceMeta<A> {
101
+ const result = {} as ServiceMeta<A>
102
+ for (const [name, func] of t._fields) {
103
+ // Process each service method using dedicated method handler
104
+ result[name as FunctionName<A>] = this.visitFuncAsMethod(
105
+ func,
106
+ name as FunctionName<A>
107
+ )
108
+ }
109
+ return result
110
+ }
111
+
112
+ /**
113
+ * Handle func type when encountered as a service method definition.
114
+ * Returns MethodMeta with information about the method's inputs/outputs.
115
+ * This is public so callers can explicitly request method metadata.
116
+ */
117
+ public visitFuncAsMethod(
118
+ t: IDL.FuncClass,
119
+ functionName: FunctionName<A>
120
+ ): MethodMeta<A> {
121
+ const functionType: FunctionType = isQuery(t) ? "query" : "update"
122
+ const returns = t.retTypes.map((ret, i) =>
123
+ ret.accept(this, `__ret${i}`)
124
+ ) as ResultNode[]
125
+
126
+ return {
127
+ functionType,
128
+ functionName,
129
+ returns,
130
+ returnCount: t.retTypes.length,
131
+ resolve: (
132
+ data: ActorMethodReturnType<A[FunctionName<A>]>
133
+ ): MethodResult<A> => {
134
+ const dataArray = returns.length <= 1 ? [data] : (data as unknown[])
135
+ return {
136
+ functionType,
137
+ functionName,
138
+ results: returns.map((node, i) => node.resolve(dataArray[i])),
139
+ raw: data,
140
+ }
141
+ },
142
+ }
143
+ }
144
+
145
+ /**
146
+ * Handle func type when encountered as a data field (e.g., callback in a record).
147
+ * Returns ResultNode that can resolve [Principal, string] data to a func reference.
148
+ */
149
+ public visitFunc(_t: IDL.FuncClass, label: string): ResultNode<"func"> {
150
+ const node: ResultNode<"func"> = {
151
+ type: "func",
152
+ label,
153
+ displayLabel: formatLabel(label),
154
+ candidType: "func",
155
+ displayType: "func",
156
+ canisterId: "", // placeholder, populated on resolve
157
+ methodName: "", // placeholder, populated on resolve
158
+ resolve(data: unknown): ResolvedNode<"func"> {
159
+ // Func values are represented as [Principal, string] tuples
160
+ if (!Array.isArray(data) || data.length !== 2) {
161
+ throw new MetadataError(
162
+ `Expected func reference [Principal, string], but got ${typeof data}`,
163
+ label,
164
+ "func"
165
+ )
166
+ }
167
+ const [principal, methodName] = data
168
+ const canisterId =
169
+ typeof principal === "string"
170
+ ? principal
171
+ : (principal?.toText?.() ?? String(principal))
172
+
173
+ return {
174
+ ...node,
175
+ canisterId,
176
+ methodName: String(methodName),
177
+ raw: data,
178
+ }
179
+ },
180
+ }
181
+ return node
182
+ }
183
+
184
+ // ══════════════════════════════════════════════════════════════════════════
185
+ // Compound Types
186
+ // ══════════════════════════════════════════════════════════════════════════
187
+
188
+ public visitRecord(
189
+ _t: IDL.RecordClass,
190
+ fields_: Array<[string, IDL.Type]>,
191
+ label: string
192
+ ): ResultNode<"record"> | ResultNode<"funcRecord"> {
193
+ const fields: Record<string, ResultNode> = {}
194
+ // Track func fields for funcRecord detection
195
+ const funcEntries: Array<{
196
+ key: string
197
+ funcType: IDL.FuncClass
198
+ node: ResultNode<"func">
199
+ }> = []
200
+
201
+ for (const [key, type] of fields_) {
202
+ const fieldNode = type.accept(this, key) as ResultNode
203
+ fields[key] = fieldNode
204
+
205
+ if (type instanceof IDL.FuncClass) {
206
+ funcEntries.push({
207
+ key,
208
+ funcType: type,
209
+ node: fieldNode as ResultNode<"func">,
210
+ })
211
+ }
212
+ }
213
+
214
+ // ── funcRecord: exactly one func field + other argument fields ──
215
+ if (funcEntries.length === 1) {
216
+ const {
217
+ key: funcFieldKey,
218
+ funcType,
219
+ node: funcFieldNode,
220
+ } = funcEntries[0]
221
+ const funcCallType: "query" | "update" = isQuery(funcType)
222
+ ? "query"
223
+ : "update"
224
+
225
+ const argFields: Record<string, ResultNode> = {}
226
+ for (const [k, v] of Object.entries(fields)) {
227
+ if (k !== funcFieldKey) argFields[k] = v
228
+ }
229
+
230
+ const node: ResultNode<"funcRecord"> = {
231
+ type: "funcRecord",
232
+ label,
233
+ displayLabel: formatLabel(label),
234
+ candidType: "record",
235
+ displayType: "func-record",
236
+ canisterId: "",
237
+ methodName: "",
238
+ funcType: funcCallType,
239
+ funcClass: funcType,
240
+ funcFieldKey,
241
+ funcField: funcFieldNode,
242
+ argFields,
243
+ fields,
244
+ resolve(data: unknown): ResolvedNode<"funcRecord"> {
245
+ if (data === null || data === undefined) {
246
+ throw new MetadataError(
247
+ `Expected funcRecord, but got ${data === null ? "null" : "undefined"}`,
248
+ label,
249
+ "record"
250
+ )
251
+ }
252
+ const recordData = data as Record<string, unknown>
253
+ const resolvedFields: Record<string, ResolvedNode> = {}
254
+ let index = 0
255
+ for (const [key, field] of Object.entries(fields)) {
256
+ const value =
257
+ recordData[key] !== undefined
258
+ ? recordData[key]
259
+ : recordData[index]
260
+ resolvedFields[key] = field.resolve(value)
261
+ index++
262
+ }
263
+
264
+ const resolvedFuncField = resolvedFields[
265
+ funcFieldKey
266
+ ] as ResolvedNode<"func">
267
+
268
+ const resolvedArgFields: Record<string, ResolvedNode> = {}
269
+ for (const [k, v] of Object.entries(resolvedFields)) {
270
+ if (k !== funcFieldKey) resolvedArgFields[k] = v
271
+ }
272
+
273
+ // Build display-type default args ready for callMethod
274
+ const argRecord = Object.fromEntries(
275
+ Object.entries(resolvedArgFields).map(([k, v]) => [
276
+ k,
277
+ v.value ?? v.raw,
278
+ ])
279
+ )
280
+ const defaultArgs = funcType.argTypes.length > 0 ? [argRecord] : []
281
+
282
+ return {
283
+ ...node,
284
+ canisterId: resolvedFuncField.canisterId,
285
+ methodName: resolvedFuncField.methodName,
286
+ funcField: resolvedFuncField,
287
+ argFields: resolvedArgFields,
288
+ fields: resolvedFields,
289
+ defaultArgs,
290
+ raw: data,
291
+ }
292
+ },
293
+ }
294
+ return node
295
+ }
296
+
297
+ // ── Regular record ──
298
+ const node: ResultNode<"record"> = {
299
+ type: "record",
300
+ label,
301
+ displayLabel: formatLabel(label),
302
+ candidType: "record",
303
+ displayType: "object",
304
+ fields,
305
+ resolve(data: unknown): ResolvedNode<"record"> {
306
+ if (data === null || data === undefined) {
307
+ throw new MetadataError(
308
+ `Expected record, but got ${data === null ? "null" : "undefined"}`,
309
+ label,
310
+ "record"
311
+ )
312
+ }
313
+ const recordData = data as Record<string, unknown>
314
+ const resolvedFields: Record<string, ResolvedNode> = {}
315
+ let index = 0
316
+ for (const [key, field] of Object.entries(fields)) {
317
+ // Try named key first, then try numeric index (for tuples/indexed records)
318
+ const value =
319
+ recordData[key] !== undefined ? recordData[key] : recordData[index]
320
+
321
+ if (!field || typeof field.resolve !== "function") {
322
+ throw new MetadataError(
323
+ `Field "${key}" is not a valid ResultNode`,
324
+ `${label}.${key}`,
325
+ "record"
326
+ )
327
+ }
328
+
329
+ resolvedFields[key] = field.resolve(value)
330
+ index++
331
+ }
332
+ return { ...node, fields: resolvedFields, raw: data }
333
+ },
334
+ }
335
+ return node
336
+ }
337
+
338
+ public visitVariant(
339
+ _t: IDL.VariantClass,
340
+ fields_: Array<[string, IDL.Type]>,
341
+ label: string
342
+ ): ResultNode<"variant"> {
343
+ const options: Record<string, ResultNode> = {}
344
+ for (const [key, type] of fields_) {
345
+ options[key] = type.accept(this, key) as ResultNode
346
+ }
347
+ const isResult =
348
+ ("Ok" in options && "Err" in options) ||
349
+ ("ok" in options && "err" in options)
350
+ const isNullVariant =
351
+ !isResult &&
352
+ Object.values(options).every((option) => option.type === "null")
353
+ const node: ResultNode<"variant"> = {
354
+ type: "variant",
355
+ label,
356
+ displayLabel: formatLabel(label),
357
+ candidType: "variant",
358
+ displayType: isResult
359
+ ? "result"
360
+ : isNullVariant
361
+ ? "variant-null"
362
+ : "variant",
363
+ options,
364
+ selectedValue: {} as ResultNode, // placeholder, populated on resolve
365
+ resolve(data: unknown): ResolvedNode<"variant"> {
366
+ if (data === null || data === undefined) {
367
+ throw new MetadataError(
368
+ `Expected variant, but got ${data === null ? "null" : "undefined"}, raw: ${data}`,
369
+ label,
370
+ "variant"
371
+ )
372
+ }
373
+ const variantData = data as Record<string, unknown>
374
+ // Support both raw { Selected: value } and transformed { _type: 'Selected', Selected: value }
375
+ const selected =
376
+ (variantData._type as string) || Object.keys(variantData)[0]
377
+ const optionNode = options[selected]
378
+
379
+ if (!optionNode) {
380
+ throw new MetadataError(
381
+ `Option "${selected}" not found. Available: ${Object.keys(options).join(", ")}`,
382
+ label,
383
+ "variant"
384
+ )
385
+ }
386
+ return {
387
+ ...node,
388
+ selected,
389
+ selectedValue: optionNode.resolve(variantData[selected]),
390
+ raw: data,
391
+ }
392
+ },
393
+ }
394
+ return node
395
+ }
396
+
397
+ public visitTuple<T extends IDL.Type[]>(
398
+ _t: IDL.TupleClass<T>,
399
+ components: IDL.Type[],
400
+ label: string
401
+ ): ResultNode<"tuple"> {
402
+ const items = components.map(
403
+ (t, i) => t.accept(this, `_${i}`) as ResultNode
404
+ )
405
+
406
+ const node: ResultNode<"tuple"> = {
407
+ type: "tuple",
408
+ label,
409
+ displayLabel: formatLabel(label),
410
+ candidType: "tuple",
411
+ displayType: "array",
412
+ items,
413
+ resolve(data: unknown): ResolvedNode<"tuple"> {
414
+ if (data === null || data === undefined || !Array.isArray(data)) {
415
+ throw new MetadataError(
416
+ `Expected tuple, but got ${data === null ? "null" : typeof data}, raw: ${data}`,
417
+ label,
418
+ "tuple"
419
+ )
420
+ }
421
+ const tupleData = data as unknown[]
422
+ return {
423
+ ...node,
424
+ items: items.map((item, i) => item.resolve(tupleData[i])),
425
+ raw: data,
426
+ }
427
+ },
428
+ }
429
+ return node
430
+ }
431
+
432
+ public visitOpt<T>(
433
+ _t: IDL.OptClass<T>,
434
+ ty: IDL.Type<T>,
435
+ label: string
436
+ ): ResultNode<"optional"> {
437
+ const inner = ty.accept(this, label) as ResultNode
438
+
439
+ const node: ResultNode<"optional"> = {
440
+ type: "optional",
441
+ label,
442
+ displayLabel: formatLabel(label),
443
+ candidType: "opt",
444
+ displayType: "nullable",
445
+ value: null, // null until resolved
446
+ resolve(data: unknown): ResolvedNode<"optional"> {
447
+ // If data is an array (raw format [T] or []), unwrap it.
448
+ // Otherwise, use data directly (already transformed or null/undefined).
449
+ const resolved = Array.isArray(data)
450
+ ? data.length > 0
451
+ ? inner.resolve(data[0])
452
+ : null
453
+ : data !== null && data !== undefined
454
+ ? inner.resolve(data)
455
+ : null
456
+
457
+ return { ...node, value: resolved, raw: data }
458
+ },
459
+ }
460
+ return node
461
+ }
462
+
463
+ public visitVec<T>(
464
+ _t: IDL.VecClass<T>,
465
+ ty: IDL.Type<T>,
466
+ label: string
467
+ ): ResultNode<"vector"> | ResultNode<"blob"> {
468
+ // Blob detection (vec nat8)
469
+ if (ty instanceof IDL.FixedNatClass && ty._bits === 8) {
470
+ const codec = this.getCodec(_t)
471
+ const node: ResultNode<"blob"> = {
472
+ type: "blob",
473
+ label,
474
+ displayLabel: formatLabel(label),
475
+ candidType: "blob",
476
+ displayType: "string",
477
+ length: 0,
478
+ hash: "",
479
+ value: "", // empty schema placeholder, populated on resolve
480
+ resolve(data: unknown): ResolvedNode<"blob"> {
481
+ const value = codec.decode(data) as string | Uint8Array
482
+ return {
483
+ ...node,
484
+ value,
485
+ displayType: typeof value === "string" ? "string" : "blob",
486
+ hash: uint8ArrayToHex(
487
+ sha256(
488
+ data instanceof Uint8Array
489
+ ? data
490
+ : new Uint8Array(data as number[])
491
+ )
492
+ ),
493
+ length: value.length,
494
+ raw: data,
495
+ }
496
+ },
497
+ }
498
+ return node
499
+ }
500
+
501
+ const itemSchema = ty.accept(this, "item") as ResultNode
502
+
503
+ const node: ResultNode<"vector"> = {
504
+ type: "vector",
505
+ label,
506
+ displayLabel: formatLabel(label),
507
+ candidType: "vec",
508
+ displayType: "array",
509
+ items: [], // empty schema placeholder, populated on resolve
510
+ resolve(data: unknown): ResolvedNode<"vector"> {
511
+ if (data === null || data === undefined || !Array.isArray(data)) {
512
+ throw new MetadataError(
513
+ `Expected vector, but got ${data === null ? "null" : typeof data}, raw: ${data}`,
514
+ label,
515
+ "vec"
516
+ )
517
+ }
518
+ const vectorData = data as unknown[]
519
+ return {
520
+ ...node,
521
+ items: vectorData.map((v) => itemSchema.resolve(v)),
522
+ raw: data,
523
+ }
524
+ },
525
+ }
526
+ return node
527
+ }
528
+
529
+ public visitRec<T>(
530
+ t: IDL.RecClass<T>,
531
+ ty: IDL.ConstructType<T>,
532
+ label: string
533
+ ): ResultNode<"recursive"> {
534
+ if (this.recCache.has(t)) {
535
+ return this.recCache.get(t)! as ResultNode<"recursive">
536
+ }
537
+
538
+ const self = this
539
+ // Lazy extraction to prevent infinite loops
540
+ let innerSchema: ResultNode | null = null
541
+ const getInner = () =>
542
+ (innerSchema ??= ty.accept(self, label) as ResultNode)
543
+
544
+ const node: ResultNode<"recursive"> = {
545
+ type: "recursive",
546
+ label,
547
+ displayLabel: formatLabel(label),
548
+ candidType: "rec",
549
+ displayType: "recursive",
550
+ inner: {} as ResultNode, // placeholder, populated on resolve
551
+ resolve(data: unknown): ResolvedNode<"recursive"> {
552
+ return { ...node, inner: getInner().resolve(data), raw: data }
553
+ },
554
+ }
555
+
556
+ this.recCache.set(t, node)
557
+ return node
558
+ }
559
+
560
+ // ══════════════════════════════════════════════════════════════════════════
561
+ // Primitives - Using Factory
562
+ // ══════════════════════════════════════════════════════════════════════════
563
+
564
+ public visitPrincipal(
565
+ t: IDL.PrincipalClass,
566
+ label: string
567
+ ): ResultNode<"principal"> {
568
+ return primitiveNode(
569
+ "principal",
570
+ label,
571
+ "principal",
572
+ "string",
573
+ this.getCodec(t),
574
+ {
575
+ format: checkTextFormat(label) as TextFormat,
576
+ }
577
+ )
578
+ }
579
+
580
+ public visitText(t: IDL.TextClass, label: string): ResultNode<"text"> {
581
+ return primitiveNode("text", label, "text", "string", this.getCodec(t), {
582
+ format: checkTextFormat(label) as TextFormat,
583
+ })
584
+ }
585
+
586
+ public visitBool(t: IDL.BoolClass, label: string): ResultNode<"boolean"> {
587
+ return primitiveNode("boolean", label, "bool", "boolean", this.getCodec(t))
588
+ }
589
+
590
+ public visitNull(t: IDL.NullClass, label: string): ResultNode<"null"> {
591
+ return primitiveNode("null", label, "null", "null", this.getCodec(t))
592
+ }
593
+
594
+ public visitInt(t: IDL.IntClass, label: string): ResultNode<"number"> {
595
+ return primitiveNode("number", label, "int", "string", this.getCodec(t), {
596
+ format: checkNumberFormat(label) as NumberFormat,
597
+ })
598
+ }
599
+
600
+ public visitNat(t: IDL.NatClass, label: string): ResultNode<"number"> {
601
+ return primitiveNode("number", label, "nat", "string", this.getCodec(t), {
602
+ format: checkNumberFormat(label) as NumberFormat,
603
+ })
604
+ }
605
+
606
+ public visitFloat(t: IDL.FloatClass, label: string): ResultNode<"number"> {
607
+ return primitiveNode(
608
+ "number",
609
+ label,
610
+ `float${t._bits}`,
611
+ "number",
612
+ this.getCodec(t),
613
+ {
614
+ format: checkNumberFormat(label) as NumberFormat,
615
+ }
616
+ )
617
+ }
618
+
619
+ public visitFixedInt(
620
+ t: IDL.FixedIntClass,
621
+ label: string
622
+ ): ResultNode<"number"> {
623
+ const bits = t._bits
624
+ return primitiveNode(
625
+ "number",
626
+ label,
627
+ `int${bits}`,
628
+ bits <= 32 ? "number" : "string",
629
+ this.getCodec(t),
630
+ {
631
+ format: checkNumberFormat(label) as NumberFormat,
632
+ }
633
+ )
634
+ }
635
+
636
+ public visitFixedNat(
637
+ t: IDL.FixedNatClass,
638
+ label: string
639
+ ): ResultNode<"number"> {
640
+ const bits = t._bits
641
+ return primitiveNode(
642
+ "number",
643
+ label,
644
+ `nat${bits}`,
645
+ bits <= 32 ? "number" : "string",
646
+ this.getCodec(t),
647
+ {
648
+ format: checkNumberFormat(label) as NumberFormat,
649
+ }
650
+ )
651
+ }
652
+
653
+ public visitType<T>(_t: IDL.Type<T>, label: string): ResultNode<"unknown"> {
654
+ return primitiveNode("unknown", label, "unknown", "unknown", {
655
+ decode: (v) => v,
656
+ })
657
+ }
658
+ }