@ic-reactor/candid 3.0.14-beta.0 → 3.0.14-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/metadata-display-reactor.d.ts +66 -11
- package/dist/metadata-display-reactor.d.ts.map +1 -1
- package/dist/metadata-display-reactor.js +48 -12
- package/dist/metadata-display-reactor.js.map +1 -1
- package/dist/visitor/arguments/helpers.d.ts +22 -7
- package/dist/visitor/arguments/helpers.d.ts.map +1 -1
- package/dist/visitor/arguments/helpers.js +39 -5
- package/dist/visitor/arguments/helpers.js.map +1 -1
- package/dist/visitor/arguments/index.d.ts.map +1 -1
- package/dist/visitor/arguments/index.js +26 -34
- package/dist/visitor/arguments/index.js.map +1 -1
- package/dist/visitor/arguments/types.d.ts +120 -132
- package/dist/visitor/arguments/types.d.ts.map +1 -1
- package/dist/visitor/arguments/types.js +25 -1
- package/dist/visitor/arguments/types.js.map +1 -1
- package/dist/visitor/constants.d.ts.map +1 -1
- package/dist/visitor/constants.js +19 -17
- package/dist/visitor/constants.js.map +1 -1
- package/dist/visitor/returns/index.d.ts.map +1 -1
- package/dist/visitor/returns/index.js +24 -13
- package/dist/visitor/returns/index.js.map +1 -1
- package/dist/visitor/returns/types.d.ts +48 -10
- package/dist/visitor/returns/types.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/metadata-display-reactor.ts +86 -14
- package/src/visitor/arguments/helpers.ts +50 -8
- package/src/visitor/arguments/index.test.ts +105 -61
- package/src/visitor/arguments/index.ts +38 -36
- package/src/visitor/arguments/types.ts +211 -220
- package/src/visitor/constants.ts +24 -15
- package/src/visitor/returns/index.test.ts +30 -30
- package/src/visitor/returns/index.ts +52 -21
- package/src/visitor/returns/types.ts +60 -17
|
@@ -2,21 +2,34 @@ import type { BaseActor, FunctionName, FunctionType } from "@ic-reactor/core"
|
|
|
2
2
|
import * as z from "zod"
|
|
3
3
|
import type { VisitorDataType, TextFormat, NumberFormat } from "../types"
|
|
4
4
|
|
|
5
|
-
export type { TextFormat, NumberFormat }
|
|
5
|
+
export type { VisitorDataType, TextFormat, NumberFormat }
|
|
6
6
|
|
|
7
7
|
// ════════════════════════════════════════════════════════════════════════════
|
|
8
|
-
//
|
|
8
|
+
// Custom Error Class
|
|
9
9
|
// ════════════════════════════════════════════════════════════════════════════
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
/**
|
|
12
|
+
* Custom error class for metadata-related errors.
|
|
13
|
+
* Provides additional context about the field path and Candid type.
|
|
14
|
+
*/
|
|
15
|
+
export class MetadataError extends Error {
|
|
16
|
+
constructor(
|
|
17
|
+
message: string,
|
|
18
|
+
public readonly fieldPath?: string,
|
|
19
|
+
public readonly candidType?: string
|
|
20
|
+
) {
|
|
21
|
+
super(message)
|
|
22
|
+
this.name = "MetadataError"
|
|
23
|
+
}
|
|
24
|
+
}
|
|
12
25
|
|
|
13
26
|
// ════════════════════════════════════════════════════════════════════════════
|
|
14
|
-
// Component
|
|
27
|
+
// Component & UI Types
|
|
15
28
|
// ════════════════════════════════════════════════════════════════════════════
|
|
16
29
|
|
|
17
30
|
/**
|
|
18
31
|
* Suggested component type for rendering the field.
|
|
19
|
-
*
|
|
32
|
+
* Use this to map field types to your UI components.
|
|
20
33
|
*/
|
|
21
34
|
export type FieldComponentType =
|
|
22
35
|
| "record-container"
|
|
@@ -33,13 +46,8 @@ export type FieldComponentType =
|
|
|
33
46
|
| "recursive-lazy"
|
|
34
47
|
| "unknown-fallback"
|
|
35
48
|
|
|
36
|
-
// ════════════════════════════════════════════════════════════════════════════
|
|
37
|
-
// Render Hints for UI Rendering Strategy
|
|
38
|
-
// ════════════════════════════════════════════════════════════════════════════
|
|
39
|
-
|
|
40
49
|
/**
|
|
41
50
|
* Input type hints for HTML input elements.
|
|
42
|
-
* Used by primitive fields to suggest the appropriate input type.
|
|
43
51
|
*/
|
|
44
52
|
export type InputType =
|
|
45
53
|
| "text"
|
|
@@ -51,49 +59,28 @@ export type InputType =
|
|
|
51
59
|
|
|
52
60
|
/**
|
|
53
61
|
* Rendering hints for the UI.
|
|
54
|
-
* Eliminates the need for frontend to maintain COMPLEX_TYPES arrays.
|
|
55
62
|
*/
|
|
56
63
|
export interface RenderHint {
|
|
57
|
-
/** Whether this field has its own container/card styling (compound types) */
|
|
58
64
|
isCompound: boolean
|
|
59
|
-
/** Whether this is a leaf input (primitive types) */
|
|
60
65
|
isPrimitive: boolean
|
|
61
|
-
/** Suggested input type for HTML input elements */
|
|
62
66
|
inputType?: InputType
|
|
63
|
-
/** Description or help text for the field (derived from Candid) */
|
|
64
67
|
description?: string
|
|
65
68
|
}
|
|
66
69
|
|
|
67
|
-
// ════════════════════════════════════════════════════════════════════════════
|
|
68
|
-
// Primitive Input Props
|
|
69
|
-
// ════════════════════════════════════════════════════════════════════════════
|
|
70
|
-
|
|
71
70
|
/**
|
|
72
71
|
* Pre-computed HTML input props for primitive fields.
|
|
73
|
-
* Can be spread directly onto an input element.
|
|
74
72
|
*/
|
|
75
73
|
export interface PrimitiveInputProps {
|
|
76
|
-
/** HTML input type - includes format-specific types */
|
|
77
74
|
type?: "text" | "number" | "checkbox" | "email" | "url" | "tel"
|
|
78
|
-
/** Placeholder text */
|
|
79
75
|
placeholder?: string
|
|
80
|
-
/** Minimum value for number inputs */
|
|
81
76
|
min?: string | number
|
|
82
|
-
/** Maximum value for number inputs */
|
|
83
77
|
max?: string | number
|
|
84
|
-
/** Step value for number inputs */
|
|
85
78
|
step?: string | number
|
|
86
|
-
/** Pattern for text inputs (e.g., phone numbers) */
|
|
87
79
|
pattern?: string
|
|
88
|
-
/** Input mode for virtual keyboards */
|
|
89
80
|
inputMode?: "text" | "numeric" | "decimal" | "email" | "tel" | "url"
|
|
90
|
-
/** Autocomplete hint */
|
|
91
81
|
autoComplete?: string
|
|
92
|
-
/** Whether to check spelling */
|
|
93
82
|
spellCheck?: boolean
|
|
94
|
-
/** Minimum length for text inputs */
|
|
95
83
|
minLength?: number
|
|
96
|
-
/** Maximum length for text inputs */
|
|
97
84
|
maxLength?: number
|
|
98
85
|
}
|
|
99
86
|
|
|
@@ -101,197 +88,223 @@ export interface PrimitiveInputProps {
|
|
|
101
88
|
// Base Field Interface
|
|
102
89
|
// ════════════════════════════════════════════════════════════════════════════
|
|
103
90
|
|
|
91
|
+
/**
|
|
92
|
+
* Base properties shared by all field nodes.
|
|
93
|
+
*/
|
|
104
94
|
interface FieldBase<T extends VisitorDataType = VisitorDataType> {
|
|
105
|
-
/** The
|
|
95
|
+
/** The Candid type category (record, variant, text, number, etc.) */
|
|
106
96
|
type: T
|
|
107
|
-
/** Raw label from Candid
|
|
97
|
+
/** Raw label from Candid definition */
|
|
108
98
|
label: string
|
|
109
|
-
/**
|
|
99
|
+
/** Human-readable formatted label for display */
|
|
110
100
|
displayLabel: string
|
|
111
|
-
/** Form
|
|
101
|
+
/** Form path compatible with TanStack Form (e.g., "[0]", "[0].owner") */
|
|
112
102
|
name: string
|
|
113
|
-
/** Suggested component type for rendering
|
|
103
|
+
/** Suggested component type for rendering */
|
|
114
104
|
component: FieldComponentType
|
|
115
|
-
/**
|
|
105
|
+
/** UI rendering hints */
|
|
116
106
|
renderHint: RenderHint
|
|
117
|
-
/** Zod schema
|
|
107
|
+
/** Zod validation schema */
|
|
118
108
|
schema: z.ZodTypeAny
|
|
119
|
-
/** Original Candid type name
|
|
109
|
+
/** Original Candid type name */
|
|
120
110
|
candidType: string
|
|
111
|
+
/** Default value for form initialization */
|
|
112
|
+
defaultValue: unknown
|
|
121
113
|
}
|
|
122
114
|
|
|
123
115
|
// ════════════════════════════════════════════════════════════════════════════
|
|
124
|
-
// Type-Specific
|
|
116
|
+
// Field Extras - Type-Specific Properties
|
|
125
117
|
// ════════════════════════════════════════════════════════════════════════════
|
|
126
118
|
|
|
127
|
-
/**
|
|
128
|
-
* Blob field size limits.
|
|
129
|
-
*/
|
|
119
|
+
/** Blob upload limits configuration */
|
|
130
120
|
export interface BlobLimits {
|
|
131
|
-
/** Maximum bytes when entering as hex (e.g., 512 bytes) */
|
|
132
121
|
maxHexBytes: number
|
|
133
|
-
/** Maximum file size in bytes (e.g., 2MB ICP limit) */
|
|
134
122
|
maxFileBytes: number
|
|
135
|
-
/** Maximum hex display length before truncation */
|
|
136
123
|
maxHexDisplayLength: number
|
|
137
124
|
}
|
|
138
125
|
|
|
139
|
-
/**
|
|
140
|
-
* Validation result for blob input.
|
|
141
|
-
*/
|
|
126
|
+
/** Blob validation result */
|
|
142
127
|
export interface BlobValidationResult {
|
|
143
|
-
/** Whether the input is valid */
|
|
144
128
|
valid: boolean
|
|
145
|
-
/** Error message if invalid */
|
|
146
129
|
error?: string
|
|
147
130
|
}
|
|
148
131
|
|
|
132
|
+
interface RecordExtras {
|
|
133
|
+
/** Child fields of the record */
|
|
134
|
+
fields: FieldNode[]
|
|
135
|
+
/** Default value as object with all field defaults */
|
|
136
|
+
defaultValue: Record<string, unknown>
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
interface VariantExtras {
|
|
140
|
+
/** All variant options as fields */
|
|
141
|
+
options: FieldNode[]
|
|
142
|
+
/** The default selected option key */
|
|
143
|
+
defaultOption: string
|
|
144
|
+
/** Default value with the first option selected */
|
|
145
|
+
defaultValue: Record<string, unknown>
|
|
146
|
+
/** Get default value for a specific option */
|
|
147
|
+
getOptionDefault: (option: string) => Record<string, unknown>
|
|
148
|
+
/** Get field descriptor for a specific option */
|
|
149
|
+
getOption: (option: string) => FieldNode
|
|
150
|
+
/** Get currently selected option key from a value */
|
|
151
|
+
getSelectedKey: (value: Record<string, unknown>) => string
|
|
152
|
+
/** Get the field for the currently selected option */
|
|
153
|
+
getSelectedOption: (value: Record<string, unknown>) => FieldNode
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
interface TupleExtras {
|
|
157
|
+
/** Tuple element fields */
|
|
158
|
+
fields: FieldNode[]
|
|
159
|
+
/** Default value as array of element defaults */
|
|
160
|
+
defaultValue: unknown[]
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
interface OptionalExtras {
|
|
164
|
+
/** The inner type field */
|
|
165
|
+
innerField: FieldNode
|
|
166
|
+
/** Default value is always null */
|
|
167
|
+
defaultValue: null
|
|
168
|
+
/** Get default value of the inner type when enabling */
|
|
169
|
+
getInnerDefault: () => unknown
|
|
170
|
+
/** Check if a value represents an enabled optional */
|
|
171
|
+
isEnabled: (value: unknown) => boolean
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
interface VectorExtras {
|
|
175
|
+
/** Template field for vector items */
|
|
176
|
+
itemField: FieldNode
|
|
177
|
+
/** Default value is empty array */
|
|
178
|
+
defaultValue: unknown[]
|
|
179
|
+
/** Get default value for a new item */
|
|
180
|
+
getItemDefault: () => unknown
|
|
181
|
+
/** Create a field node for an item at a specific index */
|
|
182
|
+
createItemField: (index: number, overrides?: { label?: string }) => FieldNode
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
interface BlobExtras {
|
|
186
|
+
/** Template field for blob bytes */
|
|
187
|
+
itemField: FieldNode
|
|
188
|
+
/** Accepted input formats */
|
|
189
|
+
acceptedFormats: ("hex" | "base64" | "file")[]
|
|
190
|
+
/** Upload limits */
|
|
191
|
+
limits: BlobLimits
|
|
192
|
+
/** Normalize hex input string */
|
|
193
|
+
normalizeHex: (input: string) => string
|
|
194
|
+
/** Validate blob input */
|
|
195
|
+
validateInput: (value: string | Uint8Array) => BlobValidationResult
|
|
196
|
+
/** Default value is empty string */
|
|
197
|
+
defaultValue: string
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
interface RecursiveExtras {
|
|
201
|
+
/** The recursive type name */
|
|
202
|
+
typeName: string
|
|
203
|
+
/** Lazily extract the inner type */
|
|
204
|
+
extract: () => FieldNode
|
|
205
|
+
/** Get default value of the inner type */
|
|
206
|
+
getInnerDefault: () => unknown
|
|
207
|
+
/** Default value is undefined */
|
|
208
|
+
defaultValue: undefined
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
interface PrincipalExtras {
|
|
212
|
+
/** Maximum Principal string length */
|
|
213
|
+
maxLength: number
|
|
214
|
+
/** Minimum Principal string length */
|
|
215
|
+
minLength: number
|
|
216
|
+
/** Detected text format */
|
|
217
|
+
format: TextFormat
|
|
218
|
+
/** Pre-computed HTML input props */
|
|
219
|
+
inputProps: PrimitiveInputProps
|
|
220
|
+
/** Default value is empty string */
|
|
221
|
+
defaultValue: string
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
interface NumberExtras {
|
|
225
|
+
/** Whether the number is unsigned (nat vs int) */
|
|
226
|
+
unsigned: boolean
|
|
227
|
+
/** Whether the number is a float */
|
|
228
|
+
isFloat: boolean
|
|
229
|
+
/** Bit width (8, 16, 32, 64) */
|
|
230
|
+
bits?: number
|
|
231
|
+
/** Minimum value as string */
|
|
232
|
+
min?: string
|
|
233
|
+
/** Maximum value as string */
|
|
234
|
+
max?: string
|
|
235
|
+
/** Detected number format */
|
|
236
|
+
format: NumberFormat
|
|
237
|
+
/** Pre-computed HTML input props */
|
|
238
|
+
inputProps: PrimitiveInputProps
|
|
239
|
+
/** Default value is empty string */
|
|
240
|
+
defaultValue: string
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
interface TextExtras {
|
|
244
|
+
/** Minimum text length */
|
|
245
|
+
minLength?: number
|
|
246
|
+
/** Maximum text length */
|
|
247
|
+
maxLength?: number
|
|
248
|
+
/** Whether to use multiline input */
|
|
249
|
+
multiline?: boolean
|
|
250
|
+
/** Detected text format */
|
|
251
|
+
format: TextFormat
|
|
252
|
+
/** Pre-computed HTML input props */
|
|
253
|
+
inputProps: PrimitiveInputProps
|
|
254
|
+
/** Default value is empty string */
|
|
255
|
+
defaultValue: string
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
interface BooleanExtras {
|
|
259
|
+
/** Pre-computed HTML input props */
|
|
260
|
+
inputProps: PrimitiveInputProps
|
|
261
|
+
/** Default value is false */
|
|
262
|
+
defaultValue: boolean
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
interface NullExtras {
|
|
266
|
+
/** Default value is null */
|
|
267
|
+
defaultValue: null
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
interface UnknownExtras {
|
|
271
|
+
/** Default value is undefined */
|
|
272
|
+
defaultValue: undefined
|
|
273
|
+
}
|
|
274
|
+
|
|
149
275
|
type FieldExtras<T extends VisitorDataType> = T extends "record"
|
|
150
|
-
?
|
|
151
|
-
/** Child fields in the record */
|
|
152
|
-
fields: FieldNode[]
|
|
153
|
-
/** Map of field label to its metadata for quick lookup */
|
|
154
|
-
fieldMap: Map<string, FieldNode>
|
|
155
|
-
defaultValue: Record<string, unknown>
|
|
156
|
-
}
|
|
276
|
+
? RecordExtras
|
|
157
277
|
: T extends "variant"
|
|
158
|
-
?
|
|
159
|
-
/** All variant option fields */
|
|
160
|
-
fields: FieldNode[]
|
|
161
|
-
/** List of variant option names */
|
|
162
|
-
options: string[]
|
|
163
|
-
/** Default selected option */
|
|
164
|
-
defaultOption: string
|
|
165
|
-
/** Map of option name to its field metadata */
|
|
166
|
-
optionMap: Map<string, FieldNode>
|
|
167
|
-
defaultValue: Record<string, unknown>
|
|
168
|
-
/** Get default value for a specific option */
|
|
169
|
-
getOptionDefault: (option: string) => Record<string, unknown>
|
|
170
|
-
/** Get the field for a specific option */
|
|
171
|
-
getField: (option: string) => FieldNode
|
|
172
|
-
/** Get the currently selected option from a value */
|
|
173
|
-
getSelectedOption: (value: Record<string, unknown>) => string
|
|
174
|
-
/** Get the selected field from a value */
|
|
175
|
-
getSelectedField: (value: Record<string, unknown>) => FieldNode
|
|
176
|
-
}
|
|
278
|
+
? VariantExtras
|
|
177
279
|
: T extends "tuple"
|
|
178
|
-
?
|
|
179
|
-
/** Tuple element fields in order */
|
|
180
|
-
fields: FieldNode[]
|
|
181
|
-
defaultValue: unknown[]
|
|
182
|
-
}
|
|
280
|
+
? TupleExtras
|
|
183
281
|
: T extends "optional"
|
|
184
|
-
?
|
|
185
|
-
/** The inner field when value is present */
|
|
186
|
-
innerField: FieldNode
|
|
187
|
-
defaultValue: null
|
|
188
|
-
/** Get default value when enabling the optional */
|
|
189
|
-
getInnerDefault: () => unknown
|
|
190
|
-
/** Check if a value represents an enabled optional */
|
|
191
|
-
isEnabled: (value: unknown) => boolean
|
|
192
|
-
}
|
|
282
|
+
? OptionalExtras
|
|
193
283
|
: T extends "vector"
|
|
194
|
-
?
|
|
195
|
-
/** Template field for vector items */
|
|
196
|
-
itemField: FieldNode
|
|
197
|
-
defaultValue: unknown[]
|
|
198
|
-
/** Get a new item with default values */
|
|
199
|
-
getItemDefault: () => unknown
|
|
200
|
-
/** Create a properly configured item field for a specific index */
|
|
201
|
-
createItemField: (
|
|
202
|
-
index: number,
|
|
203
|
-
overrides?: { label?: string }
|
|
204
|
-
) => FieldNode
|
|
205
|
-
}
|
|
284
|
+
? VectorExtras
|
|
206
285
|
: T extends "blob"
|
|
207
|
-
?
|
|
208
|
-
/** Item field for individual bytes (nat8) */
|
|
209
|
-
itemField: FieldNode
|
|
210
|
-
/** Accepted input formats */
|
|
211
|
-
acceptedFormats: ("hex" | "base64" | "file")[]
|
|
212
|
-
/** Size limits for blob input */
|
|
213
|
-
limits: BlobLimits
|
|
214
|
-
/** Normalize hex input */
|
|
215
|
-
normalizeHex: (input: string) => string
|
|
216
|
-
/** Validate blob input value */
|
|
217
|
-
validateInput: (
|
|
218
|
-
value: string | Uint8Array
|
|
219
|
-
) => BlobValidationResult
|
|
220
|
-
defaultValue: string
|
|
221
|
-
}
|
|
286
|
+
? BlobExtras
|
|
222
287
|
: T extends "recursive"
|
|
223
|
-
?
|
|
224
|
-
/** Type name for the recursive type */
|
|
225
|
-
typeName: string
|
|
226
|
-
/** Lazily extract the inner field to prevent infinite loops */
|
|
227
|
-
extract: () => FieldNode
|
|
228
|
-
/** Get default value for the recursive type (evaluates lazily) */
|
|
229
|
-
getInnerDefault: () => unknown
|
|
230
|
-
defaultValue: undefined
|
|
231
|
-
}
|
|
288
|
+
? RecursiveExtras
|
|
232
289
|
: T extends "principal"
|
|
233
|
-
?
|
|
234
|
-
maxLength: number
|
|
235
|
-
minLength: number
|
|
236
|
-
/** Detected format based on label heuristics */
|
|
237
|
-
format: TextFormat
|
|
238
|
-
/** Pre-computed HTML input props */
|
|
239
|
-
inputProps: PrimitiveInputProps
|
|
240
|
-
defaultValue: string
|
|
241
|
-
}
|
|
290
|
+
? PrincipalExtras
|
|
242
291
|
: T extends "number"
|
|
243
|
-
?
|
|
244
|
-
/** Whether this is an unsigned type */
|
|
245
|
-
unsigned: boolean
|
|
246
|
-
/** Whether this is a floating point type */
|
|
247
|
-
isFloat: boolean
|
|
248
|
-
/** Bit width if applicable (8, 16, 32, 64, or undefined for unbounded) */
|
|
249
|
-
bits?: number
|
|
250
|
-
/** Minimum value constraint (for bounded types) */
|
|
251
|
-
min?: string
|
|
252
|
-
/** Maximum value constraint (for bounded types) */
|
|
253
|
-
max?: string
|
|
254
|
-
/** Detected format based on label heuristics */
|
|
255
|
-
format: NumberFormat
|
|
256
|
-
/** Pre-computed HTML input props */
|
|
257
|
-
inputProps: PrimitiveInputProps
|
|
258
|
-
defaultValue: string
|
|
259
|
-
}
|
|
292
|
+
? NumberExtras
|
|
260
293
|
: T extends "text"
|
|
261
|
-
?
|
|
262
|
-
/** Minimum length constraint */
|
|
263
|
-
minLength?: number
|
|
264
|
-
/** Maximum length constraint */
|
|
265
|
-
maxLength?: number
|
|
266
|
-
/** Whether to render as multiline textarea */
|
|
267
|
-
multiline?: boolean
|
|
268
|
-
/** Detected format based on label heuristics */
|
|
269
|
-
format: TextFormat
|
|
270
|
-
/** Pre-computed HTML input props */
|
|
271
|
-
inputProps: PrimitiveInputProps
|
|
272
|
-
defaultValue: string
|
|
273
|
-
}
|
|
294
|
+
? TextExtras
|
|
274
295
|
: T extends "boolean"
|
|
275
|
-
?
|
|
276
|
-
/** Pre-computed HTML input props */
|
|
277
|
-
inputProps: PrimitiveInputProps
|
|
278
|
-
defaultValue: boolean
|
|
279
|
-
}
|
|
296
|
+
? BooleanExtras
|
|
280
297
|
: T extends "null"
|
|
281
|
-
?
|
|
282
|
-
defaultValue: null
|
|
283
|
-
}
|
|
298
|
+
? NullExtras
|
|
284
299
|
: T extends "unknown"
|
|
285
|
-
?
|
|
286
|
-
defaultValue: undefined
|
|
287
|
-
}
|
|
300
|
+
? UnknownExtras
|
|
288
301
|
: {}
|
|
289
302
|
|
|
290
303
|
/**
|
|
291
304
|
* A unified field node that contains all metadata needed for rendering.
|
|
292
305
|
*/
|
|
293
306
|
export type FieldNode<T extends VisitorDataType = VisitorDataType> =
|
|
294
|
-
FieldBase<T> & FieldExtras<T>
|
|
307
|
+
T extends any ? FieldBase<T> & FieldExtras<T> : never
|
|
295
308
|
|
|
296
309
|
export type RecordField = FieldNode<"record">
|
|
297
310
|
export type VariantField = FieldNode<"variant">
|
|
@@ -308,74 +321,52 @@ export type NullField = FieldNode<"null">
|
|
|
308
321
|
export type UnknownField = FieldNode<"unknown">
|
|
309
322
|
|
|
310
323
|
// ════════════════════════════════════════════════════════════════════════════
|
|
311
|
-
// Form Metadata
|
|
324
|
+
// Form Metadata
|
|
312
325
|
// ════════════════════════════════════════════════════════════════════════════
|
|
313
326
|
|
|
314
327
|
/**
|
|
315
|
-
*
|
|
316
|
-
*
|
|
328
|
+
* Metadata for a single method's input arguments.
|
|
329
|
+
* Use this to generate dynamic forms for calling canister methods.
|
|
317
330
|
*/
|
|
318
331
|
export interface ArgumentsMeta<
|
|
319
332
|
A = BaseActor,
|
|
320
333
|
Name extends FunctionName<A> = FunctionName<A>,
|
|
321
334
|
> {
|
|
322
|
-
/** Whether this is a "query" or "update"
|
|
335
|
+
/** Whether this is a "query" or "update" call */
|
|
323
336
|
functionType: FunctionType
|
|
324
|
-
/** The
|
|
337
|
+
/** The method name as defined in the Candid interface */
|
|
325
338
|
functionName: Name
|
|
326
|
-
/**
|
|
327
|
-
|
|
328
|
-
/** Default values for
|
|
329
|
-
|
|
330
|
-
/**
|
|
339
|
+
/** Array of field descriptors, one per argument */
|
|
340
|
+
args: FieldNode[]
|
|
341
|
+
/** Default values suitable for form initialization */
|
|
342
|
+
defaults: unknown[]
|
|
343
|
+
/** Zod schema for validating all arguments together */
|
|
331
344
|
schema: z.ZodTuple<[z.ZodTypeAny, ...z.ZodTypeAny[]]>
|
|
332
|
-
/** Number of arguments */
|
|
345
|
+
/** Number of arguments (0 for no-arg methods) */
|
|
333
346
|
argCount: number
|
|
334
|
-
/**
|
|
335
|
-
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
/**
|
|
339
|
-
* Options that can be spread into useForm().
|
|
340
|
-
* Pre-configured with defaultValues and validators.
|
|
341
|
-
*/
|
|
342
|
-
export interface FormOptions {
|
|
343
|
-
/** Initial form values */
|
|
344
|
-
defaultValues: unknown[]
|
|
345
|
-
/** Validators using the Zod schema */
|
|
346
|
-
validators: {
|
|
347
|
-
onChange: z.ZodTypeAny
|
|
348
|
-
onBlur: z.ZodTypeAny
|
|
349
|
-
}
|
|
347
|
+
/** True if this method takes no arguments */
|
|
348
|
+
isEmpty: boolean
|
|
350
349
|
}
|
|
351
350
|
|
|
352
351
|
/**
|
|
353
|
-
* Service-level
|
|
354
|
-
* Maps each method name to its FormMeta.
|
|
352
|
+
* Service-level metadata mapping method names to their argument metadata.
|
|
355
353
|
*/
|
|
356
354
|
export type ArgumentsServiceMeta<A = BaseActor> = {
|
|
357
355
|
[K in FunctionName<A>]: ArgumentsMeta<A, K>
|
|
358
356
|
}
|
|
359
357
|
|
|
360
358
|
// ════════════════════════════════════════════════════════════════════════════
|
|
361
|
-
// Type Utilities
|
|
359
|
+
// Type Utilities
|
|
362
360
|
// ════════════════════════════════════════════════════════════════════════════
|
|
363
361
|
|
|
364
|
-
/**
|
|
365
|
-
|
|
362
|
+
/**
|
|
363
|
+
* Extract field type by VisitorDataType.
|
|
364
|
+
*/
|
|
365
|
+
export type FieldByType<T extends VisitorDataType> = Extract<
|
|
366
366
|
FieldNode,
|
|
367
367
|
{ type: T }
|
|
368
368
|
>
|
|
369
369
|
|
|
370
|
-
/**
|
|
371
|
-
* Props type helper for field components.
|
|
372
|
-
* Use this to type your field components for better DX.
|
|
373
|
-
*/
|
|
374
|
-
export type FieldProps<T extends ArgumentFieldType> = {
|
|
375
|
-
field: FieldByType<T>
|
|
376
|
-
renderField?: (child: FieldNode) => React.ReactNode
|
|
377
|
-
}
|
|
378
|
-
|
|
379
370
|
/** Compound field types that contain other fields */
|
|
380
371
|
export type CompoundField =
|
|
381
372
|
| RecordField
|
|
@@ -385,7 +376,7 @@ export type CompoundField =
|
|
|
385
376
|
| VectorField
|
|
386
377
|
| RecursiveField
|
|
387
378
|
|
|
388
|
-
/** Primitive field types */
|
|
379
|
+
/** Primitive field types with direct values */
|
|
389
380
|
export type PrimitiveField =
|
|
390
381
|
| PrincipalField
|
|
391
382
|
| NumberField
|
package/src/visitor/constants.ts
CHANGED
|
@@ -30,27 +30,36 @@ const CYCLE_KEYS_REGEX = new RegExp(
|
|
|
30
30
|
"i"
|
|
31
31
|
)
|
|
32
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
33
|
const ACCOUNT_ID_KEYS_REGEX =
|
|
40
|
-
/
|
|
41
|
-
|
|
34
|
+
/account_identifier|ledger_account|block_hash|transaction_hash|tx_hash/i
|
|
35
|
+
|
|
36
|
+
const tokenize = (label: string): Set<string> => {
|
|
37
|
+
const parts = label
|
|
38
|
+
.replace(/_/g, " ")
|
|
39
|
+
.replace(/([a-z])([A-Z])/g, "$1 $2")
|
|
40
|
+
.toLowerCase()
|
|
41
|
+
.split(/[\s-]+/)
|
|
42
|
+
return new Set(parts)
|
|
43
|
+
}
|
|
42
44
|
|
|
43
45
|
export const checkTextFormat = (label?: string): TextFormat => {
|
|
44
46
|
if (!label) return "plain"
|
|
47
|
+
|
|
45
48
|
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
49
|
if (ACCOUNT_ID_KEYS_REGEX.test(label)) return "account-id"
|
|
53
|
-
|
|
50
|
+
|
|
51
|
+
const tokens = tokenize(label)
|
|
52
|
+
|
|
53
|
+
if (tokens.has("email") || tokens.has("mail")) return "email"
|
|
54
|
+
if (tokens.has("phone") || tokens.has("tel") || tokens.has("mobile"))
|
|
55
|
+
return "phone"
|
|
56
|
+
if (tokens.has("url") || tokens.has("link") || tokens.has("website"))
|
|
57
|
+
return "url"
|
|
58
|
+
if (tokens.has("uuid") || tokens.has("guid")) return "uuid"
|
|
59
|
+
if (tokens.has("btc") || tokens.has("bitcoin")) return "btc"
|
|
60
|
+
if (tokens.has("eth") || tokens.has("ethereum")) return "eth"
|
|
61
|
+
if (tokens.has("principal") || tokens.has("canister")) return "principal"
|
|
62
|
+
|
|
54
63
|
return "plain"
|
|
55
64
|
}
|
|
56
65
|
|