@ic-reactor/candid 3.0.7-beta.2 → 3.0.8-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. package/dist/display-reactor.d.ts +3 -2
  2. package/dist/display-reactor.d.ts.map +1 -1
  3. package/dist/display-reactor.js +6 -0
  4. package/dist/display-reactor.js.map +1 -1
  5. package/dist/index.d.ts +1 -0
  6. package/dist/index.d.ts.map +1 -1
  7. package/dist/index.js +1 -0
  8. package/dist/index.js.map +1 -1
  9. package/dist/metadata-display-reactor.d.ts +73 -0
  10. package/dist/metadata-display-reactor.d.ts.map +1 -0
  11. package/dist/metadata-display-reactor.js +128 -0
  12. package/dist/metadata-display-reactor.js.map +1 -0
  13. package/dist/visitor/arguments/index.d.ts +69 -0
  14. package/dist/visitor/arguments/index.d.ts.map +1 -0
  15. package/dist/visitor/arguments/index.js +277 -0
  16. package/dist/visitor/arguments/index.js.map +1 -0
  17. package/dist/visitor/arguments/types.d.ts +92 -0
  18. package/dist/visitor/arguments/types.d.ts.map +1 -0
  19. package/dist/visitor/arguments/types.js +2 -0
  20. package/dist/visitor/arguments/types.js.map +1 -0
  21. package/dist/visitor/constants.d.ts +4 -0
  22. package/dist/visitor/constants.d.ts.map +1 -0
  23. package/dist/visitor/constants.js +61 -0
  24. package/dist/visitor/constants.js.map +1 -0
  25. package/dist/visitor/helpers.d.ts +30 -0
  26. package/dist/visitor/helpers.d.ts.map +1 -0
  27. package/dist/visitor/helpers.js +200 -0
  28. package/dist/visitor/helpers.js.map +1 -0
  29. package/dist/visitor/returns/index.d.ts +76 -0
  30. package/dist/visitor/returns/index.d.ts.map +1 -0
  31. package/dist/visitor/returns/index.js +425 -0
  32. package/dist/visitor/returns/index.js.map +1 -0
  33. package/dist/visitor/returns/types.d.ts +142 -0
  34. package/dist/visitor/returns/types.d.ts.map +1 -0
  35. package/dist/visitor/returns/types.js +2 -0
  36. package/dist/visitor/returns/types.js.map +1 -0
  37. package/dist/visitor/types.d.ts +6 -0
  38. package/dist/visitor/types.d.ts.map +1 -0
  39. package/dist/visitor/types.js +3 -0
  40. package/dist/visitor/types.js.map +1 -0
  41. package/package.json +3 -2
  42. package/src/display-reactor.ts +10 -2
  43. package/src/index.ts +1 -0
  44. package/src/metadata-display-reactor.ts +184 -0
  45. package/src/visitor/arguments/index.test.ts +882 -0
  46. package/src/visitor/arguments/index.ts +405 -0
  47. package/src/visitor/arguments/types.ts +168 -0
  48. package/src/visitor/constants.ts +62 -0
  49. package/src/visitor/helpers.ts +221 -0
  50. package/src/visitor/returns/index.test.ts +2027 -0
  51. package/src/visitor/returns/index.ts +545 -0
  52. package/src/visitor/returns/types.ts +271 -0
  53. package/src/visitor/types.ts +29 -0
@@ -0,0 +1,405 @@
1
+ import { isQuery } from "../helpers"
2
+ import { IDL } from "../types"
3
+ import type {
4
+ ArgumentField,
5
+ RecordArgumentField,
6
+ VariantArgumentField,
7
+ TupleArgumentField,
8
+ OptionalArgumentField,
9
+ VectorArgumentField,
10
+ BlobArgumentField,
11
+ RecursiveArgumentField,
12
+ PrincipalArgumentField,
13
+ NumberArgumentField,
14
+ BooleanArgumentField,
15
+ NullArgumentField,
16
+ TextArgumentField,
17
+ UnknownArgumentField,
18
+ MethodArgumentsMeta,
19
+ ServiceArgumentsMeta,
20
+ } from "./types"
21
+ import { BaseActor, FunctionName } from "@ic-reactor/core"
22
+
23
+ export * from "./types"
24
+
25
+ /**
26
+ * ArgumentFieldVisitor generates metadata for form input fields from Candid IDL types.
27
+ *
28
+ * ## Design Principles
29
+ *
30
+ * 1. **Works with raw IDL types** - generates metadata at initialization time
31
+ * 2. **No value dependencies** - metadata is independent of actual values
32
+ * 3. **Form-framework agnostic** - output can be used with TanStack, React Hook Form, etc.
33
+ * 4. **Efficient** - single traversal, no runtime type checking
34
+ *
35
+ * ## Output Structure
36
+ *
37
+ * Each field has:
38
+ * - `type`: The field type (record, variant, text, number, etc.)
39
+ * - `label`: Human-readable label from Candid
40
+ * - `path`: Dot-notation path for form binding (e.g., "0.owner")
41
+ * - `defaultValue`: Initial value for the form
42
+ * - Type-specific properties (options for variant, fields for record, etc.)
43
+ *
44
+ * @example
45
+ * ```typescript
46
+ * const visitor = new ArgumentFieldVisitor()
47
+ * const serviceMeta = service.accept(visitor, null)
48
+ *
49
+ * // For a specific method
50
+ * const methodMeta = serviceMeta["icrc1_transfer"]
51
+ * // methodMeta.fields = [{ type: "record", fields: [...] }]
52
+ * // methodMeta.defaultValues = [{ to: "", amount: "" }]
53
+ * ```
54
+ */
55
+ export class ArgumentFieldVisitor<A = BaseActor> extends IDL.Visitor<
56
+ string,
57
+ ArgumentField | MethodArgumentsMeta<A> | ServiceArgumentsMeta<A>
58
+ > {
59
+ private pathStack: string[] = []
60
+
61
+ private withPath<T>(path: string, fn: () => T): T {
62
+ this.pathStack.push(path)
63
+ try {
64
+ return fn()
65
+ } finally {
66
+ this.pathStack.pop()
67
+ }
68
+ }
69
+
70
+ private currentPath(): string {
71
+ return this.pathStack[this.pathStack.length - 1] ?? ""
72
+ }
73
+
74
+ private childPath(key: string | number): string {
75
+ const parent = this.currentPath()
76
+ if (typeof key === "number") {
77
+ return parent ? `${parent}[${key}]` : String(key)
78
+ }
79
+ return parent ? `${parent}.${key}` : key
80
+ }
81
+
82
+ // ════════════════════════════════════════════════════════════════════════
83
+ // Service & Function Level
84
+ // ════════════════════════════════════════════════════════════════════════
85
+
86
+ public visitService(t: IDL.ServiceClass): ServiceArgumentsMeta<A> {
87
+ const result = {} as ServiceArgumentsMeta<A>
88
+
89
+ for (const [functionName, func] of t._fields) {
90
+ result[functionName as FunctionName<A>] = func.accept(
91
+ this,
92
+ functionName
93
+ ) as MethodArgumentsMeta<A>
94
+ }
95
+
96
+ return result
97
+ }
98
+
99
+ public visitFunc(
100
+ t: IDL.FuncClass,
101
+ functionName: FunctionName<A>
102
+ ): MethodArgumentsMeta<A> {
103
+ const functionType = isQuery(t) ? "query" : "update"
104
+
105
+ const fields = t.argTypes.map((arg, index) => {
106
+ return this.withPath(`[${index}]`, () =>
107
+ arg.accept(this, `__arg${index}`)
108
+ ) as ArgumentField
109
+ })
110
+
111
+ const defaultValues = fields.map((field) => this.extractDefaultValue(field))
112
+
113
+ return {
114
+ functionType,
115
+ functionName,
116
+ fields,
117
+ defaultValues,
118
+ }
119
+ }
120
+
121
+ private extractDefaultValue(field: ArgumentField): unknown {
122
+ if ("defaultValue" in field) {
123
+ return field.defaultValue
124
+ }
125
+ if ("defaultValues" in field) {
126
+ return field.defaultValues
127
+ }
128
+ return undefined
129
+ }
130
+
131
+ // ════════════════════════════════════════════════════════════════════════
132
+ // Compound Types
133
+ // ════════════════════════════════════════════════════════════════════════
134
+
135
+ public visitRecord(
136
+ _t: IDL.RecordClass,
137
+ fields_: Array<[string, IDL.Type]>,
138
+ label: string
139
+ ): RecordArgumentField {
140
+ const path = this.currentPath()
141
+ const fields: ArgumentField[] = []
142
+ const defaultValues: Record<string, unknown> = {}
143
+
144
+ for (const [key, type] of fields_) {
145
+ const field = this.withPath(this.childPath(key), () =>
146
+ type.accept(this, key)
147
+ ) as ArgumentField
148
+
149
+ fields.push(field)
150
+ defaultValues[key] = this.extractDefaultValue(field)
151
+ }
152
+
153
+ return {
154
+ type: "record",
155
+ label,
156
+ path,
157
+ fields,
158
+ defaultValues,
159
+ }
160
+ }
161
+
162
+ public visitVariant(
163
+ _t: IDL.VariantClass,
164
+ fields_: Array<[string, IDL.Type]>,
165
+ label: string
166
+ ): VariantArgumentField {
167
+ const path = this.currentPath()
168
+ const fields: ArgumentField[] = []
169
+ const options: string[] = []
170
+
171
+ for (const [key, type] of fields_) {
172
+ const field = this.withPath(this.childPath(key), () =>
173
+ type.accept(this, key)
174
+ ) as ArgumentField
175
+
176
+ fields.push(field)
177
+ options.push(key)
178
+ }
179
+
180
+ const defaultOption = options[0]
181
+ const defaultValues = {
182
+ [defaultOption]: this.extractDefaultValue(fields[0]),
183
+ }
184
+
185
+ return {
186
+ type: "variant",
187
+ label,
188
+ path,
189
+ fields,
190
+ options,
191
+ defaultOption,
192
+ defaultValues,
193
+ }
194
+ }
195
+
196
+ public visitTuple<T extends IDL.Type[]>(
197
+ _t: IDL.TupleClass<T>,
198
+ components: IDL.Type[],
199
+ label: string
200
+ ): TupleArgumentField {
201
+ const path = this.currentPath()
202
+ const fields: ArgumentField[] = []
203
+ const defaultValues: unknown[] = []
204
+
205
+ for (let index = 0; index < components.length; index++) {
206
+ const type = components[index]
207
+ const field = this.withPath(this.childPath(index), () =>
208
+ type.accept(this, `_${index}_`)
209
+ ) as ArgumentField
210
+
211
+ fields.push(field)
212
+ defaultValues.push(this.extractDefaultValue(field))
213
+ }
214
+
215
+ return {
216
+ type: "tuple",
217
+ label,
218
+ path,
219
+ fields,
220
+ defaultValues,
221
+ }
222
+ }
223
+
224
+ public visitOpt<T>(
225
+ _t: IDL.OptClass<T>,
226
+ ty: IDL.Type<T>,
227
+ label: string
228
+ ): OptionalArgumentField {
229
+ const path = this.currentPath()
230
+
231
+ const innerField = this.withPath(this.childPath(0), () =>
232
+ ty.accept(this, label)
233
+ ) as ArgumentField
234
+
235
+ return {
236
+ type: "optional",
237
+ label,
238
+ path,
239
+ innerField,
240
+ defaultValue: null,
241
+ }
242
+ }
243
+
244
+ public visitVec<T>(
245
+ _t: IDL.VecClass<T>,
246
+ ty: IDL.Type<T>,
247
+ label: string
248
+ ): VectorArgumentField | BlobArgumentField {
249
+ const path = this.currentPath()
250
+
251
+ // Check if it's blob (vec nat8)
252
+ const isBlob = ty instanceof IDL.FixedNatClass && ty._bits === 8
253
+
254
+ const itemField = this.withPath(this.childPath(0), () =>
255
+ ty.accept(this, label)
256
+ ) as ArgumentField
257
+
258
+ if (isBlob) {
259
+ return {
260
+ type: "blob",
261
+ label,
262
+ path,
263
+ itemField,
264
+ defaultValue: "",
265
+ }
266
+ }
267
+
268
+ return {
269
+ type: "vector",
270
+ label,
271
+ path,
272
+ itemField,
273
+ defaultValue: [],
274
+ }
275
+ }
276
+
277
+ public visitRec<T>(
278
+ _t: IDL.RecClass<T>,
279
+ ty: IDL.ConstructType<T>,
280
+ label: string
281
+ ): RecursiveArgumentField {
282
+ const path = this.currentPath()
283
+
284
+ return {
285
+ type: "recursive",
286
+ label,
287
+ path,
288
+ // Lazy extraction to prevent infinite loops
289
+ extract: () =>
290
+ this.withPath(path, () => ty.accept(this, label)) as ArgumentField,
291
+ }
292
+ }
293
+
294
+ // ════════════════════════════════════════════════════════════════════════
295
+ // Primitive Types
296
+ // ════════════════════════════════════════════════════════════════════════
297
+
298
+ public visitPrincipal(
299
+ _t: IDL.PrincipalClass,
300
+ label: string
301
+ ): PrincipalArgumentField {
302
+ return {
303
+ type: "principal",
304
+ label,
305
+ path: this.currentPath(),
306
+ defaultValue: "",
307
+ maxLength: 64,
308
+ minLength: 7,
309
+ }
310
+ }
311
+
312
+ public visitText(_t: IDL.TextClass, label: string): TextArgumentField {
313
+ return {
314
+ type: "text",
315
+ label,
316
+ path: this.currentPath(),
317
+ defaultValue: "",
318
+ }
319
+ }
320
+
321
+ public visitBool(_t: IDL.BoolClass, label: string): BooleanArgumentField {
322
+ return {
323
+ type: "boolean",
324
+ label,
325
+ path: this.currentPath(),
326
+ defaultValue: false,
327
+ }
328
+ }
329
+
330
+ public visitNull(_t: IDL.NullClass, label: string): NullArgumentField {
331
+ return {
332
+ type: "null",
333
+ label,
334
+ path: this.currentPath(),
335
+ defaultValue: null,
336
+ }
337
+ }
338
+
339
+ // Numbers - all use string for display format
340
+ private visitNumberType(
341
+ label: string,
342
+ candidType: string
343
+ ): NumberArgumentField {
344
+ return {
345
+ type: "number",
346
+ label,
347
+ path: this.currentPath(),
348
+ defaultValue: "",
349
+ candidType,
350
+ }
351
+ }
352
+
353
+ public visitInt(_t: IDL.IntClass, label: string): NumberArgumentField {
354
+ return this.visitNumberType(label, "int")
355
+ }
356
+
357
+ public visitNat(_t: IDL.NatClass, label: string): NumberArgumentField {
358
+ return this.visitNumberType(label, "nat")
359
+ }
360
+
361
+ public visitFloat(_t: IDL.FloatClass, label: string): NumberArgumentField {
362
+ return this.visitNumberType(label, "float")
363
+ }
364
+
365
+ public visitFixedInt(
366
+ t: IDL.FixedIntClass,
367
+ label: string
368
+ ): NumberArgumentField {
369
+ return this.visitNumberType(label, `int${t._bits}`)
370
+ }
371
+
372
+ public visitFixedNat(
373
+ t: IDL.FixedNatClass,
374
+ label: string
375
+ ): NumberArgumentField {
376
+ return this.visitNumberType(label, `nat${t._bits}`)
377
+ }
378
+
379
+ public visitType<T>(_t: IDL.Type<T>, label: string): UnknownArgumentField {
380
+ return {
381
+ type: "unknown",
382
+ label,
383
+ path: this.currentPath(),
384
+ defaultValue: undefined,
385
+ }
386
+ }
387
+ }
388
+
389
+ // ════════════════════════════════════════════════════════════════════════════
390
+ // Legacy Exports (for backward compatibility)
391
+ // ════════════════════════════════════════════════════════════════════════════
392
+
393
+ /**
394
+ * @deprecated Use ArgumentFieldVisitor instead
395
+ */
396
+ export { ArgumentFieldVisitor as VisitTanstackField }
397
+
398
+ /**
399
+ * @deprecated Use ArgumentField instead
400
+ */
401
+ export type {
402
+ ArgumentField as TanstackAllArgTypes,
403
+ MethodArgumentsMeta as TanstackMethodField,
404
+ ServiceArgumentsMeta as TanstackServiceField,
405
+ }
@@ -0,0 +1,168 @@
1
+ import type { BaseActor, FunctionName, FunctionType } from "@ic-reactor/core"
2
+
3
+ // ════════════════════════════════════════════════════════════════════════════
4
+ // Field Type Union
5
+ // ════════════════════════════════════════════════════════════════════════════
6
+
7
+ export type ArgumentFieldType =
8
+ | "record"
9
+ | "variant"
10
+ | "tuple"
11
+ | "optional"
12
+ | "vector"
13
+ | "blob"
14
+ | "recursive"
15
+ | "principal"
16
+ | "number"
17
+ | "text"
18
+ | "boolean"
19
+ | "null"
20
+ | "unknown"
21
+
22
+ // ════════════════════════════════════════════════════════════════════════════
23
+ // Base Field Interface
24
+ // ════════════════════════════════════════════════════════════════════════════
25
+
26
+ export interface ArgumentFieldBase {
27
+ /** The field type */
28
+ type: ArgumentFieldType
29
+ /** Human-readable label from Candid */
30
+ label: string
31
+ /** Dot-notation path for form binding (e.g., "0.owner", "[0].to") */
32
+ path: string
33
+ }
34
+
35
+ // ════════════════════════════════════════════════════════════════════════════
36
+ // Compound Types
37
+ // ════════════════════════════════════════════════════════════════════════════
38
+
39
+ export interface RecordArgumentField extends ArgumentFieldBase {
40
+ type: "record"
41
+ fields: ArgumentField[]
42
+ defaultValues: Record<string, unknown>
43
+ }
44
+
45
+ export interface VariantArgumentField extends ArgumentFieldBase {
46
+ type: "variant"
47
+ fields: ArgumentField[]
48
+ options: string[]
49
+ defaultOption: string
50
+ defaultValues: Record<string, unknown>
51
+ }
52
+
53
+ export interface TupleArgumentField extends ArgumentFieldBase {
54
+ type: "tuple"
55
+ fields: ArgumentField[]
56
+ defaultValues: unknown[]
57
+ }
58
+
59
+ export interface OptionalArgumentField extends ArgumentFieldBase {
60
+ type: "optional"
61
+ innerField: ArgumentField
62
+ defaultValue: null
63
+ }
64
+
65
+ export interface VectorArgumentField extends ArgumentFieldBase {
66
+ type: "vector"
67
+ itemField: ArgumentField
68
+ defaultValue: []
69
+ }
70
+
71
+ export interface BlobArgumentField extends ArgumentFieldBase {
72
+ type: "blob"
73
+ itemField: ArgumentField
74
+ /** Default is empty hex string */
75
+ defaultValue: string
76
+ }
77
+
78
+ export interface RecursiveArgumentField extends ArgumentFieldBase {
79
+ type: "recursive"
80
+ /** Lazily extract the inner field to prevent infinite loops */
81
+ extract: () => ArgumentField
82
+ }
83
+
84
+ // ════════════════════════════════════════════════════════════════════════════
85
+ // Primitive Types
86
+ // ════════════════════════════════════════════════════════════════════════════
87
+
88
+ export interface PrincipalArgumentField extends ArgumentFieldBase {
89
+ type: "principal"
90
+ /** Display format: string */
91
+ defaultValue: string
92
+ maxLength: number
93
+ minLength: number
94
+ }
95
+
96
+ export interface NumberArgumentField extends ArgumentFieldBase {
97
+ type: "number"
98
+ /** Display format: string (for bigint compatibility) */
99
+ defaultValue: string
100
+ /** Original Candid type: nat, int, nat64, etc. */
101
+ candidType: string
102
+ }
103
+
104
+ export interface TextArgumentField extends ArgumentFieldBase {
105
+ type: "text"
106
+ defaultValue: string
107
+ }
108
+
109
+ export interface BooleanArgumentField extends ArgumentFieldBase {
110
+ type: "boolean"
111
+ defaultValue: boolean
112
+ }
113
+
114
+ export interface NullArgumentField extends ArgumentFieldBase {
115
+ type: "null"
116
+ defaultValue: null
117
+ }
118
+
119
+ export interface UnknownArgumentField extends ArgumentFieldBase {
120
+ type: "unknown"
121
+ defaultValue: undefined
122
+ }
123
+
124
+ // ════════════════════════════════════════════════════════════════════════════
125
+ // Union Type
126
+ // ════════════════════════════════════════════════════════════════════════════
127
+
128
+ export type ArgumentField =
129
+ | RecordArgumentField
130
+ | VariantArgumentField
131
+ | TupleArgumentField
132
+ | OptionalArgumentField
133
+ | VectorArgumentField
134
+ | BlobArgumentField
135
+ | RecursiveArgumentField
136
+ | PrincipalArgumentField
137
+ | NumberArgumentField
138
+ | TextArgumentField
139
+ | BooleanArgumentField
140
+ | NullArgumentField
141
+ | UnknownArgumentField
142
+
143
+ // ════════════════════════════════════════════════════════════════════════════
144
+ // Method & Service Level
145
+ // ════════════════════════════════════════════════════════════════════════════
146
+
147
+ export interface MethodArgumentsMeta<
148
+ A = BaseActor,
149
+ Name extends FunctionName<A> = FunctionName<A>,
150
+ > {
151
+ functionType: FunctionType
152
+ functionName: Name
153
+ fields: ArgumentField[]
154
+ defaultValues: unknown[]
155
+ }
156
+
157
+ export type ServiceArgumentsMeta<A = BaseActor> = {
158
+ [K in FunctionName<A>]: MethodArgumentsMeta<A, K>
159
+ }
160
+
161
+ // ════════════════════════════════════════════════════════════════════════════
162
+ // Type Utilities
163
+ // ════════════════════════════════════════════════════════════════════════════
164
+
165
+ export type ArgumentFieldByType<T extends ArgumentFieldType> = Extract<
166
+ ArgumentField,
167
+ { type: T }
168
+ >
@@ -0,0 +1,62 @@
1
+ import type { TextFormat, NumberFormat } from "./returns/types"
2
+
3
+ const TAMESTAMP_KEYS = [
4
+ "time",
5
+ "date",
6
+ "deadline",
7
+ "timestamp",
8
+ "timestamp_nanos",
9
+ "statusAt",
10
+ "createdAt",
11
+ "updatedAt",
12
+ "deletedAt",
13
+ "validUntil",
14
+ "status_at",
15
+ "created_at",
16
+ "updated_at",
17
+ "deleted_at",
18
+ "valid_until",
19
+ ]
20
+
21
+ const TAMESTAMP_KEYS_REGEX = new RegExp(
22
+ TAMESTAMP_KEYS.map((key) => `^[\\w-]*${key}[\\w-]*$`).join("|"),
23
+ "i"
24
+ )
25
+
26
+ const CYCLE_KEYS = ["cycle", "cycles"]
27
+
28
+ const CYCLE_KEYS_REGEX = new RegExp(
29
+ CYCLE_KEYS.map((key) => `^[\\w-]*${key}[\\w-]*$`).join("|"),
30
+ "i"
31
+ )
32
+
33
+ const EMAIL_KEYS_REGEX = /email|mail/i
34
+ const PHONE_KEYS_REGEX = /phone|tel|mobile/i
35
+ const URL_KEYS_REGEX = /url|link|website/i
36
+ const UUID_KEYS_REGEX = /uuid|guid/i
37
+ const BITCOIN_KEYS_REGEX = /bitcoin|btc/i
38
+ const ETHEREUM_KEYS_REGEX = /ethereum|eth/i
39
+ const ACCOUNT_ID_KEYS_REGEX =
40
+ /account_id|account_identifier|ledger_account|block_hash|transaction_hash|tx_hash/i
41
+ const PRINCIPAL_KEYS_REGEX = /canister|principal/i
42
+
43
+ export const checkTextFormat = (label?: string): TextFormat => {
44
+ if (!label) return "plain"
45
+ if (TAMESTAMP_KEYS_REGEX.test(label)) return "timestamp"
46
+ if (EMAIL_KEYS_REGEX.test(label)) return "email"
47
+ if (PHONE_KEYS_REGEX.test(label)) return "phone"
48
+ if (URL_KEYS_REGEX.test(label)) return "url"
49
+ if (UUID_KEYS_REGEX.test(label)) return "uuid"
50
+ if (BITCOIN_KEYS_REGEX.test(label)) return "btc"
51
+ if (ETHEREUM_KEYS_REGEX.test(label)) return "eth"
52
+ if (ACCOUNT_ID_KEYS_REGEX.test(label)) return "account-id"
53
+ if (PRINCIPAL_KEYS_REGEX.test(label)) return "principal"
54
+ return "plain"
55
+ }
56
+
57
+ export const checkNumberFormat = (label?: string): NumberFormat => {
58
+ if (!label) return "normal"
59
+ if (TAMESTAMP_KEYS_REGEX.test(label)) return "timestamp"
60
+ if (CYCLE_KEYS_REGEX.test(label)) return "cycle"
61
+ return "normal"
62
+ }