@ic-reactor/candid 3.0.7-beta.2 → 3.0.8-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.
- package/dist/display-reactor.d.ts +3 -2
- package/dist/display-reactor.d.ts.map +1 -1
- package/dist/display-reactor.js +6 -0
- package/dist/display-reactor.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/metadata-display-reactor.d.ts +73 -0
- package/dist/metadata-display-reactor.d.ts.map +1 -0
- package/dist/metadata-display-reactor.js +128 -0
- package/dist/metadata-display-reactor.js.map +1 -0
- package/dist/visitor/arguments/index.d.ts +69 -0
- package/dist/visitor/arguments/index.d.ts.map +1 -0
- package/dist/visitor/arguments/index.js +277 -0
- package/dist/visitor/arguments/index.js.map +1 -0
- package/dist/visitor/arguments/types.d.ts +92 -0
- package/dist/visitor/arguments/types.d.ts.map +1 -0
- package/dist/visitor/arguments/types.js +2 -0
- package/dist/visitor/arguments/types.js.map +1 -0
- package/dist/visitor/constants.d.ts +4 -0
- package/dist/visitor/constants.d.ts.map +1 -0
- package/dist/visitor/constants.js +61 -0
- package/dist/visitor/constants.js.map +1 -0
- package/dist/visitor/helpers.d.ts +30 -0
- package/dist/visitor/helpers.d.ts.map +1 -0
- package/dist/visitor/helpers.js +200 -0
- package/dist/visitor/helpers.js.map +1 -0
- package/dist/visitor/returns/index.d.ts +76 -0
- package/dist/visitor/returns/index.d.ts.map +1 -0
- package/dist/visitor/returns/index.js +426 -0
- package/dist/visitor/returns/index.js.map +1 -0
- package/dist/visitor/returns/types.d.ts +143 -0
- package/dist/visitor/returns/types.d.ts.map +1 -0
- package/dist/visitor/returns/types.js +2 -0
- package/dist/visitor/returns/types.js.map +1 -0
- package/dist/visitor/types.d.ts +6 -0
- package/dist/visitor/types.d.ts.map +1 -0
- package/dist/visitor/types.js +3 -0
- package/dist/visitor/types.js.map +1 -0
- package/package.json +3 -2
- package/src/display-reactor.ts +10 -2
- package/src/index.ts +1 -0
- package/src/metadata-display-reactor.ts +184 -0
- package/src/visitor/arguments/index.test.ts +882 -0
- package/src/visitor/arguments/index.ts +405 -0
- package/src/visitor/arguments/types.ts +168 -0
- package/src/visitor/constants.ts +62 -0
- package/src/visitor/helpers.ts +221 -0
- package/src/visitor/returns/index.test.ts +2027 -0
- package/src/visitor/returns/index.ts +553 -0
- package/src/visitor/returns/types.ts +272 -0
- 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
|
+
}
|