@ic-reactor/candid 3.0.12-beta.0 → 3.0.14-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.
- package/README.md +28 -0
- package/dist/metadata-display-reactor.d.ts.map +1 -1
- package/dist/metadata-display-reactor.js +2 -2
- package/dist/metadata-display-reactor.js.map +1 -1
- package/dist/visitor/arguments/helpers.d.ts +40 -0
- package/dist/visitor/arguments/helpers.d.ts.map +1 -0
- package/dist/visitor/arguments/helpers.js +81 -0
- package/dist/visitor/arguments/helpers.js.map +1 -0
- package/dist/visitor/arguments/index.d.ts +19 -6
- package/dist/visitor/arguments/index.d.ts.map +1 -1
- package/dist/visitor/arguments/index.js +343 -24
- package/dist/visitor/arguments/index.js.map +1 -1
- package/dist/visitor/arguments/types.d.ts +183 -178
- package/dist/visitor/arguments/types.d.ts.map +1 -1
- package/dist/visitor/arguments/types.js +1 -40
- package/dist/visitor/arguments/types.js.map +1 -1
- package/dist/visitor/returns/types.d.ts +3 -4
- package/dist/visitor/returns/types.d.ts.map +1 -1
- package/dist/visitor/types.d.ts +14 -0
- package/dist/visitor/types.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/metadata-display-reactor.ts +2 -2
- package/src/visitor/arguments/helpers.ts +104 -0
- package/src/visitor/arguments/index.test.ts +443 -23
- package/src/visitor/arguments/index.ts +410 -41
- package/src/visitor/arguments/schema.test.ts +117 -7
- package/src/visitor/arguments/types.ts +284 -284
- package/src/visitor/returns/types.ts +4 -27
- package/src/visitor/types.ts +45 -0
- package/src/visitor/arguments/README.md +0 -230
|
@@ -1,246 +1,311 @@
|
|
|
1
1
|
import type { BaseActor, FunctionName, FunctionType } from "@ic-reactor/core"
|
|
2
2
|
import * as z from "zod"
|
|
3
|
+
import type { VisitorDataType, TextFormat, NumberFormat } from "../types"
|
|
4
|
+
|
|
5
|
+
export type { TextFormat, NumberFormat }
|
|
3
6
|
|
|
4
7
|
// ════════════════════════════════════════════════════════════════════════════
|
|
5
8
|
// Field Type Union
|
|
6
9
|
// ════════════════════════════════════════════════════════════════════════════
|
|
7
10
|
|
|
8
|
-
export type ArgumentFieldType =
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
11
|
+
export type ArgumentFieldType = VisitorDataType
|
|
12
|
+
|
|
13
|
+
// ════════════════════════════════════════════════════════════════════════════
|
|
14
|
+
// Component Type Hints
|
|
15
|
+
// ════════════════════════════════════════════════════════════════════════════
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Suggested component type for rendering the field.
|
|
19
|
+
* This eliminates the need for switch statements in the frontend.
|
|
20
|
+
*/
|
|
21
|
+
export type FieldComponentType =
|
|
22
|
+
| "record-container"
|
|
23
|
+
| "tuple-container"
|
|
24
|
+
| "variant-select"
|
|
25
|
+
| "optional-toggle"
|
|
26
|
+
| "vector-list"
|
|
27
|
+
| "blob-upload"
|
|
28
|
+
| "principal-input"
|
|
29
|
+
| "text-input"
|
|
30
|
+
| "number-input"
|
|
31
|
+
| "boolean-checkbox"
|
|
32
|
+
| "null-hidden"
|
|
33
|
+
| "recursive-lazy"
|
|
34
|
+
| "unknown-fallback"
|
|
35
|
+
|
|
36
|
+
// ════════════════════════════════════════════════════════════════════════════
|
|
37
|
+
// Render Hints for UI Rendering Strategy
|
|
38
|
+
// ════════════════════════════════════════════════════════════════════════════
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Input type hints for HTML input elements.
|
|
42
|
+
* Used by primitive fields to suggest the appropriate input type.
|
|
43
|
+
*/
|
|
44
|
+
export type InputType =
|
|
18
45
|
| "text"
|
|
19
|
-
| "
|
|
20
|
-
| "
|
|
21
|
-
| "
|
|
46
|
+
| "number"
|
|
47
|
+
| "checkbox"
|
|
48
|
+
| "select"
|
|
49
|
+
| "file"
|
|
50
|
+
| "textarea"
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Rendering hints for the UI.
|
|
54
|
+
* Eliminates the need for frontend to maintain COMPLEX_TYPES arrays.
|
|
55
|
+
*/
|
|
56
|
+
export interface RenderHint {
|
|
57
|
+
/** Whether this field has its own container/card styling (compound types) */
|
|
58
|
+
isCompound: boolean
|
|
59
|
+
/** Whether this is a leaf input (primitive types) */
|
|
60
|
+
isPrimitive: boolean
|
|
61
|
+
/** Suggested input type for HTML input elements */
|
|
62
|
+
inputType?: InputType
|
|
63
|
+
/** Description or help text for the field (derived from Candid) */
|
|
64
|
+
description?: string
|
|
65
|
+
}
|
|
22
66
|
|
|
23
67
|
// ════════════════════════════════════════════════════════════════════════════
|
|
24
|
-
//
|
|
68
|
+
// Primitive Input Props
|
|
25
69
|
// ════════════════════════════════════════════════════════════════════════════
|
|
26
70
|
|
|
27
|
-
|
|
28
|
-
|
|
71
|
+
/**
|
|
72
|
+
* Pre-computed HTML input props for primitive fields.
|
|
73
|
+
* Can be spread directly onto an input element.
|
|
74
|
+
*/
|
|
75
|
+
export interface PrimitiveInputProps {
|
|
76
|
+
/** HTML input type - includes format-specific types */
|
|
77
|
+
type?: "text" | "number" | "checkbox" | "email" | "url" | "tel"
|
|
78
|
+
/** Placeholder text */
|
|
29
79
|
placeholder?: string
|
|
30
|
-
/**
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
|
|
80
|
+
/** Minimum value for number inputs */
|
|
81
|
+
min?: string | number
|
|
82
|
+
/** Maximum value for number inputs */
|
|
83
|
+
max?: string | number
|
|
84
|
+
/** Step value for number inputs */
|
|
85
|
+
step?: string | number
|
|
86
|
+
/** Pattern for text inputs (e.g., phone numbers) */
|
|
87
|
+
pattern?: string
|
|
88
|
+
/** Input mode for virtual keyboards */
|
|
89
|
+
inputMode?: "text" | "numeric" | "decimal" | "email" | "tel" | "url"
|
|
90
|
+
/** Autocomplete hint */
|
|
91
|
+
autoComplete?: string
|
|
92
|
+
/** Whether to check spelling */
|
|
93
|
+
spellCheck?: boolean
|
|
94
|
+
/** Minimum length for text inputs */
|
|
95
|
+
minLength?: number
|
|
96
|
+
/** Maximum length for text inputs */
|
|
97
|
+
maxLength?: number
|
|
38
98
|
}
|
|
39
99
|
|
|
40
100
|
// ════════════════════════════════════════════════════════════════════════════
|
|
41
101
|
// Base Field Interface
|
|
42
102
|
// ════════════════════════════════════════════════════════════════════════════
|
|
43
103
|
|
|
44
|
-
|
|
104
|
+
interface FieldBase<T extends VisitorDataType = VisitorDataType> {
|
|
45
105
|
/** The field type */
|
|
46
|
-
type:
|
|
47
|
-
/**
|
|
106
|
+
type: T
|
|
107
|
+
/** Raw label from Candid: "__arg0", "_0_" */
|
|
48
108
|
label: string
|
|
49
|
-
/**
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
* Compatible with TanStack Form's `form.Field` name prop.
|
|
53
|
-
*
|
|
54
|
-
* @example
|
|
55
|
-
* ```tsx
|
|
56
|
-
* <form.Field name={field.name}>
|
|
57
|
-
* {(fieldApi) => <input {...} />}
|
|
58
|
-
* </form.Field>
|
|
59
|
-
* ```
|
|
60
|
-
*/
|
|
109
|
+
/** Pre-formatted display label for UI rendering */
|
|
110
|
+
displayLabel: string
|
|
111
|
+
/** Form field name path for binding */
|
|
61
112
|
name: string
|
|
113
|
+
/** Suggested component type for rendering this field */
|
|
114
|
+
component: FieldComponentType
|
|
115
|
+
/** Rendering hints for UI strategy */
|
|
116
|
+
renderHint: RenderHint
|
|
62
117
|
/** Zod schema for field validation */
|
|
63
118
|
schema: z.ZodTypeAny
|
|
64
|
-
/** Default value for the field */
|
|
65
|
-
defaultValue: TValue
|
|
66
119
|
/** Original Candid type name for reference */
|
|
67
|
-
candidType
|
|
68
|
-
/** UI rendering hints */
|
|
69
|
-
ui?: FieldUIHints
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
// ════════════════════════════════════════════════════════════════════════════
|
|
73
|
-
// Compound Types
|
|
74
|
-
// ════════════════════════════════════════════════════════════════════════════
|
|
75
|
-
|
|
76
|
-
export interface RecordField extends FieldBase<Record<string, unknown>> {
|
|
77
|
-
type: "record"
|
|
78
|
-
/** Child fields in the record */
|
|
79
|
-
fields: Field[]
|
|
80
|
-
/** Map of field label to its metadata for quick lookup */
|
|
81
|
-
fieldMap: Map<string, Field>
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
export interface VariantField extends FieldBase<Record<string, unknown>> {
|
|
85
|
-
type: "variant"
|
|
86
|
-
/** All variant option fields */
|
|
87
|
-
fields: Field[]
|
|
88
|
-
/** List of variant option names */
|
|
89
|
-
options: string[]
|
|
90
|
-
/** Default selected option */
|
|
91
|
-
defaultOption: string
|
|
92
|
-
/** Map of option name to its field metadata */
|
|
93
|
-
optionMap: Map<string, Field>
|
|
94
|
-
/**
|
|
95
|
-
* Get default value for a specific option.
|
|
96
|
-
* Useful when switching between variant options.
|
|
97
|
-
*
|
|
98
|
-
* @example
|
|
99
|
-
* ```tsx
|
|
100
|
-
* const handleOptionChange = (newOption: string) => {
|
|
101
|
-
* const newDefault = field.getOptionDefault(newOption)
|
|
102
|
-
* fieldApi.handleChange(newDefault)
|
|
103
|
-
* }
|
|
104
|
-
* ```
|
|
105
|
-
*/
|
|
106
|
-
getOptionDefault: (option: string) => Record<string, unknown>
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
export interface TupleField extends FieldBase<unknown[]> {
|
|
110
|
-
type: "tuple"
|
|
111
|
-
/** Tuple element fields in order */
|
|
112
|
-
fields: Field[]
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
export interface OptionalField extends FieldBase<null> {
|
|
116
|
-
type: "optional"
|
|
117
|
-
/** The inner field when value is present */
|
|
118
|
-
innerField: Field
|
|
119
|
-
/**
|
|
120
|
-
* Get default value when enabling the optional.
|
|
121
|
-
* Returns the inner field's default value.
|
|
122
|
-
*
|
|
123
|
-
* @example
|
|
124
|
-
* ```tsx
|
|
125
|
-
* const handleToggle = (enabled: boolean) => {
|
|
126
|
-
* if (enabled) {
|
|
127
|
-
* fieldApi.handleChange(field.getInnerDefault())
|
|
128
|
-
* } else {
|
|
129
|
-
* fieldApi.handleChange(null)
|
|
130
|
-
* }
|
|
131
|
-
* }
|
|
132
|
-
* ```
|
|
133
|
-
*/
|
|
134
|
-
getInnerDefault: () => unknown
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
export interface VectorField extends FieldBase<unknown[]> {
|
|
138
|
-
type: "vector"
|
|
139
|
-
/** Template field for vector items */
|
|
140
|
-
itemField: Field
|
|
141
|
-
/**
|
|
142
|
-
* Get a new item with default values.
|
|
143
|
-
* Used when adding items to the vector.
|
|
144
|
-
*
|
|
145
|
-
* @example
|
|
146
|
-
* ```tsx
|
|
147
|
-
* <button onClick={() => fieldApi.pushValue(field.getItemDefault())}>
|
|
148
|
-
* Add Item
|
|
149
|
-
* </button>
|
|
150
|
-
* ```
|
|
151
|
-
*/
|
|
152
|
-
getItemDefault: () => unknown
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
export interface BlobField extends FieldBase<string> {
|
|
156
|
-
type: "blob"
|
|
157
|
-
/** Item field for individual bytes (nat8) */
|
|
158
|
-
itemField: Field
|
|
159
|
-
/** Accepted input formats */
|
|
160
|
-
acceptedFormats: ("hex" | "base64" | "file")[]
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
export interface RecursiveField extends FieldBase<undefined> {
|
|
164
|
-
type: "recursive"
|
|
165
|
-
/** Type name for the recursive type */
|
|
166
|
-
typeName: string
|
|
167
|
-
/** Lazily extract the inner field to prevent infinite loops */
|
|
168
|
-
extract: () => Field
|
|
169
|
-
/**
|
|
170
|
-
* Get default value for the recursive type.
|
|
171
|
-
* Evaluates the inner type on demand.
|
|
172
|
-
*/
|
|
173
|
-
getInnerDefault: () => unknown
|
|
120
|
+
candidType: string
|
|
174
121
|
}
|
|
175
122
|
|
|
176
123
|
// ════════════════════════════════════════════════════════════════════════════
|
|
177
|
-
//
|
|
124
|
+
// Type-Specific Extras
|
|
178
125
|
// ════════════════════════════════════════════════════════════════════════════
|
|
179
126
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
/**
|
|
189
|
-
|
|
190
|
-
*/
|
|
191
|
-
candidType: string
|
|
192
|
-
/** Whether this is an unsigned type */
|
|
193
|
-
unsigned: boolean
|
|
194
|
-
/** Whether this is a floating point type */
|
|
195
|
-
isFloat: boolean
|
|
196
|
-
/** Bit width if applicable (8, 16, 32, 64, or undefined for unbounded) */
|
|
197
|
-
bits?: number
|
|
198
|
-
/** Minimum value constraint (for bounded types) */
|
|
199
|
-
min?: string
|
|
200
|
-
/** Maximum value constraint (for bounded types) */
|
|
201
|
-
max?: string
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
export interface TextField extends FieldBase<string> {
|
|
205
|
-
type: "text"
|
|
206
|
-
/** Minimum length constraint */
|
|
207
|
-
minLength?: number
|
|
208
|
-
/** Maximum length constraint */
|
|
209
|
-
maxLength?: number
|
|
210
|
-
/** Whether to render as multiline textarea */
|
|
211
|
-
multiline?: boolean
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
export interface BooleanField extends FieldBase<boolean> {
|
|
215
|
-
type: "boolean"
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
export interface NullField extends FieldBase<null> {
|
|
219
|
-
type: "null"
|
|
127
|
+
/**
|
|
128
|
+
* Blob field size limits.
|
|
129
|
+
*/
|
|
130
|
+
export interface BlobLimits {
|
|
131
|
+
/** Maximum bytes when entering as hex (e.g., 512 bytes) */
|
|
132
|
+
maxHexBytes: number
|
|
133
|
+
/** Maximum file size in bytes (e.g., 2MB ICP limit) */
|
|
134
|
+
maxFileBytes: number
|
|
135
|
+
/** Maximum hex display length before truncation */
|
|
136
|
+
maxHexDisplayLength: number
|
|
220
137
|
}
|
|
221
138
|
|
|
222
|
-
|
|
223
|
-
|
|
139
|
+
/**
|
|
140
|
+
* Validation result for blob input.
|
|
141
|
+
*/
|
|
142
|
+
export interface BlobValidationResult {
|
|
143
|
+
/** Whether the input is valid */
|
|
144
|
+
valid: boolean
|
|
145
|
+
/** Error message if invalid */
|
|
146
|
+
error?: string
|
|
224
147
|
}
|
|
225
148
|
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
149
|
+
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
|
+
}
|
|
157
|
+
: 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
|
+
}
|
|
177
|
+
: T extends "tuple"
|
|
178
|
+
? {
|
|
179
|
+
/** Tuple element fields in order */
|
|
180
|
+
fields: FieldNode[]
|
|
181
|
+
defaultValue: unknown[]
|
|
182
|
+
}
|
|
183
|
+
: 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
|
+
}
|
|
193
|
+
: 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
|
+
}
|
|
206
|
+
: 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
|
+
}
|
|
222
|
+
: 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
|
+
}
|
|
232
|
+
: 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
|
+
}
|
|
242
|
+
: 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
|
+
}
|
|
260
|
+
: 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
|
+
}
|
|
274
|
+
: T extends "boolean"
|
|
275
|
+
? {
|
|
276
|
+
/** Pre-computed HTML input props */
|
|
277
|
+
inputProps: PrimitiveInputProps
|
|
278
|
+
defaultValue: boolean
|
|
279
|
+
}
|
|
280
|
+
: T extends "null"
|
|
281
|
+
? {
|
|
282
|
+
defaultValue: null
|
|
283
|
+
}
|
|
284
|
+
: T extends "unknown"
|
|
285
|
+
? {
|
|
286
|
+
defaultValue: undefined
|
|
287
|
+
}
|
|
288
|
+
: {}
|
|
229
289
|
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
290
|
+
/**
|
|
291
|
+
* A unified field node that contains all metadata needed for rendering.
|
|
292
|
+
*/
|
|
293
|
+
export type FieldNode<T extends VisitorDataType = VisitorDataType> =
|
|
294
|
+
FieldBase<T> & FieldExtras<T>
|
|
295
|
+
|
|
296
|
+
export type RecordField = FieldNode<"record">
|
|
297
|
+
export type VariantField = FieldNode<"variant">
|
|
298
|
+
export type TupleField = FieldNode<"tuple">
|
|
299
|
+
export type OptionalField = FieldNode<"optional">
|
|
300
|
+
export type VectorField = FieldNode<"vector">
|
|
301
|
+
export type BlobField = FieldNode<"blob">
|
|
302
|
+
export type RecursiveField = FieldNode<"recursive">
|
|
303
|
+
export type PrincipalField = FieldNode<"principal">
|
|
304
|
+
export type NumberField = FieldNode<"number">
|
|
305
|
+
export type TextField = FieldNode<"text">
|
|
306
|
+
export type BooleanField = FieldNode<"boolean">
|
|
307
|
+
export type NullField = FieldNode<"null">
|
|
308
|
+
export type UnknownField = FieldNode<"unknown">
|
|
244
309
|
|
|
245
310
|
// ════════════════════════════════════════════════════════════════════════════
|
|
246
311
|
// Form Metadata - TanStack Form Integration
|
|
@@ -249,31 +314,6 @@ export type Field =
|
|
|
249
314
|
/**
|
|
250
315
|
* Form metadata for a Candid method.
|
|
251
316
|
* Contains all information needed to create a TanStack Form instance.
|
|
252
|
-
*
|
|
253
|
-
* @example
|
|
254
|
-
* ```tsx
|
|
255
|
-
* import { useForm } from '@tanstack/react-form'
|
|
256
|
-
*
|
|
257
|
-
* function MethodForm({ meta }: { meta: FormMeta }) {
|
|
258
|
-
* const form = useForm({
|
|
259
|
-
* ...meta.formOptions,
|
|
260
|
-
* onSubmit: async ({ value }) => {
|
|
261
|
-
* await actor[meta.functionName](...value)
|
|
262
|
-
* }
|
|
263
|
-
* })
|
|
264
|
-
*
|
|
265
|
-
* return (
|
|
266
|
-
* <form onSubmit={(e) => { e.preventDefault(); form.handleSubmit() }}>
|
|
267
|
-
* {meta.fields.map(field => (
|
|
268
|
-
* <form.Field key={field.name} name={field.name}>
|
|
269
|
-
* {(fieldApi) => <DynamicInput field={field} fieldApi={fieldApi} />}
|
|
270
|
-
* </form.Field>
|
|
271
|
-
* ))}
|
|
272
|
-
* <button type="submit">Submit</button>
|
|
273
|
-
* </form>
|
|
274
|
-
* )
|
|
275
|
-
* }
|
|
276
|
-
* ```
|
|
277
317
|
*/
|
|
278
318
|
export interface ArgumentsMeta<
|
|
279
319
|
A = BaseActor,
|
|
@@ -284,7 +324,7 @@ export interface ArgumentsMeta<
|
|
|
284
324
|
/** The function name */
|
|
285
325
|
functionName: Name
|
|
286
326
|
/** Argument field definitions for rendering */
|
|
287
|
-
fields:
|
|
327
|
+
fields: FieldNode[]
|
|
288
328
|
/** Default values for all arguments (as a tuple) */
|
|
289
329
|
defaultValues: unknown[]
|
|
290
330
|
/** Combined Zod schema for all arguments */
|
|
@@ -323,10 +363,19 @@ export type ArgumentsServiceMeta<A = BaseActor> = {
|
|
|
323
363
|
|
|
324
364
|
/** Extract a specific field type */
|
|
325
365
|
export type FieldByType<T extends ArgumentFieldType> = Extract<
|
|
326
|
-
|
|
366
|
+
FieldNode,
|
|
327
367
|
{ type: T }
|
|
328
368
|
>
|
|
329
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
|
+
|
|
330
379
|
/** Compound field types that contain other fields */
|
|
331
380
|
export type CompoundField =
|
|
332
381
|
| RecordField
|
|
@@ -343,52 +392,3 @@ export type PrimitiveField =
|
|
|
343
392
|
| TextField
|
|
344
393
|
| BooleanField
|
|
345
394
|
| NullField
|
|
346
|
-
|
|
347
|
-
/**
|
|
348
|
-
* Type guard for checking specific field types.
|
|
349
|
-
*
|
|
350
|
-
* @example
|
|
351
|
-
* ```tsx
|
|
352
|
-
* function FieldInput({ field }: { field: Field }) {
|
|
353
|
-
* if (isFieldType(field, 'record')) {
|
|
354
|
-
* // field is now typed as RecordField
|
|
355
|
-
* return <RecordInput field={field} />
|
|
356
|
-
* }
|
|
357
|
-
* if (isFieldType(field, 'text')) {
|
|
358
|
-
* // field is now typed as TextField
|
|
359
|
-
* return <TextInput field={field} />
|
|
360
|
-
* }
|
|
361
|
-
* // ...
|
|
362
|
-
* }
|
|
363
|
-
* ```
|
|
364
|
-
*/
|
|
365
|
-
export function isFieldType<T extends ArgumentFieldType>(
|
|
366
|
-
field: Field,
|
|
367
|
-
type: T
|
|
368
|
-
): field is FieldByType<T> {
|
|
369
|
-
return field.type === type
|
|
370
|
-
}
|
|
371
|
-
|
|
372
|
-
/** Check if a field is a compound type (contains other fields) */
|
|
373
|
-
export function isCompoundField(field: Field): field is CompoundField {
|
|
374
|
-
return [
|
|
375
|
-
"record",
|
|
376
|
-
"variant",
|
|
377
|
-
"tuple",
|
|
378
|
-
"optional",
|
|
379
|
-
"vector",
|
|
380
|
-
"recursive",
|
|
381
|
-
].includes(field.type)
|
|
382
|
-
}
|
|
383
|
-
|
|
384
|
-
/** Check if a field is a primitive type */
|
|
385
|
-
export function isPrimitiveField(field: Field): field is PrimitiveField {
|
|
386
|
-
return ["principal", "number", "text", "boolean", "null"].includes(field.type)
|
|
387
|
-
}
|
|
388
|
-
|
|
389
|
-
/** Check if a field has children (for iteration) */
|
|
390
|
-
export function hasChildFields(
|
|
391
|
-
field: Field
|
|
392
|
-
): field is RecordField | VariantField | TupleField {
|
|
393
|
-
return "fields" in field && Array.isArray((field as RecordField).fields)
|
|
394
|
-
}
|
|
@@ -4,25 +4,15 @@ import type {
|
|
|
4
4
|
FunctionType,
|
|
5
5
|
ActorMethodReturnType,
|
|
6
6
|
} from "@ic-reactor/core"
|
|
7
|
+
import type { VisitorDataType, TextFormat, NumberFormat } from "../types"
|
|
8
|
+
|
|
9
|
+
export type { TextFormat, NumberFormat }
|
|
7
10
|
|
|
8
11
|
// ════════════════════════════════════════════════════════════════════════════
|
|
9
12
|
// Core Types & Formats
|
|
10
13
|
// ════════════════════════════════════════════════════════════════════════════
|
|
11
14
|
|
|
12
|
-
export type NodeType =
|
|
13
|
-
| "record"
|
|
14
|
-
| "variant"
|
|
15
|
-
| "tuple"
|
|
16
|
-
| "optional"
|
|
17
|
-
| "vector"
|
|
18
|
-
| "blob"
|
|
19
|
-
| "recursive"
|
|
20
|
-
| "principal"
|
|
21
|
-
| "number"
|
|
22
|
-
| "text"
|
|
23
|
-
| "boolean"
|
|
24
|
-
| "null"
|
|
25
|
-
| "unknown"
|
|
15
|
+
export type NodeType = VisitorDataType
|
|
26
16
|
|
|
27
17
|
export type DisplayType =
|
|
28
18
|
| "string"
|
|
@@ -38,19 +28,6 @@ export type DisplayType =
|
|
|
38
28
|
| "blob"
|
|
39
29
|
| "unknown"
|
|
40
30
|
|
|
41
|
-
export type NumberFormat = "timestamp" | "cycle" | "value" | "token" | "normal"
|
|
42
|
-
export type TextFormat =
|
|
43
|
-
| "plain"
|
|
44
|
-
| "timestamp"
|
|
45
|
-
| "uuid"
|
|
46
|
-
| "url"
|
|
47
|
-
| "email"
|
|
48
|
-
| "phone"
|
|
49
|
-
| "btc"
|
|
50
|
-
| "eth"
|
|
51
|
-
| "account-id"
|
|
52
|
-
| "principal"
|
|
53
|
-
|
|
54
31
|
// ════════════════════════════════════════════════════════════════════════════
|
|
55
32
|
// Unified Result Node - Single Structure for Schema & Resolved Data
|
|
56
33
|
// ════════════════════════════════════════════════════════════════════════════
|