@elizaos/plugin-form 2.0.3-beta.5 → 2.0.3-beta.7
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/actions/form.d.ts +31 -0
- package/dist/actions/form.d.ts.map +1 -0
- package/dist/actions/form.js +187 -0
- package/dist/actions/form.js.map +1 -0
- package/dist/builder.d.ts +320 -0
- package/dist/builder.d.ts.map +1 -0
- package/dist/builder.js +458 -0
- package/dist/builder.js.map +1 -0
- package/dist/builtins.d.ts +128 -0
- package/dist/builtins.d.ts.map +1 -0
- package/dist/builtins.js +233 -0
- package/dist/builtins.js.map +1 -0
- package/dist/defaults.d.ts +95 -0
- package/dist/defaults.d.ts.map +1 -0
- package/dist/defaults.js +79 -0
- package/dist/defaults.js.map +1 -0
- package/dist/evaluators/extractor.d.ts +28 -0
- package/dist/evaluators/extractor.d.ts.map +1 -0
- package/dist/evaluators/extractor.js +251 -0
- package/dist/evaluators/extractor.js.map +1 -0
- package/dist/extraction.d.ts +55 -0
- package/dist/extraction.d.ts.map +1 -0
- package/dist/extraction.js +347 -0
- package/dist/extraction.js.map +1 -0
- package/dist/index.d.ts +31 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +149 -0
- package/dist/index.js.map +1 -0
- package/dist/providers/context.d.ts +56 -0
- package/dist/providers/context.d.ts.map +1 -0
- package/dist/providers/context.js +204 -0
- package/dist/providers/context.js.map +1 -0
- package/dist/service.d.ts +402 -0
- package/dist/service.d.ts.map +1 -0
- package/dist/service.js +1199 -0
- package/dist/service.js.map +1 -0
- package/dist/storage.d.ts +228 -0
- package/dist/storage.d.ts.map +1 -0
- package/dist/storage.js +255 -0
- package/dist/storage.js.map +1 -0
- package/dist/template.d.ts +10 -0
- package/dist/template.d.ts.map +1 -0
- package/dist/template.js +60 -0
- package/dist/template.js.map +1 -0
- package/dist/ttl.d.ts +144 -0
- package/dist/ttl.d.ts.map +1 -0
- package/dist/ttl.js +85 -0
- package/dist/ttl.js.map +1 -0
- package/dist/types.d.ts +1213 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +39 -0
- package/dist/types.js.map +1 -0
- package/dist/validation.d.ts +156 -0
- package/dist/validation.d.ts.map +1 -0
- package/dist/validation.js +289 -0
- package/dist/validation.js.map +1 -0
- package/package.json +3 -3
package/dist/builder.js
ADDED
|
@@ -0,0 +1,458 @@
|
|
|
1
|
+
class ControlBuilder {
|
|
2
|
+
/** Partial control being built */
|
|
3
|
+
control;
|
|
4
|
+
/**
|
|
5
|
+
* Create a new ControlBuilder.
|
|
6
|
+
*
|
|
7
|
+
* @param key - The unique key for this control
|
|
8
|
+
*/
|
|
9
|
+
constructor(key) {
|
|
10
|
+
this.control = { key };
|
|
11
|
+
}
|
|
12
|
+
// ═══ STATIC FACTORIES ═══
|
|
13
|
+
// WHY static factories: Cleaner than `new ControlBuilder(key).type('text')`
|
|
14
|
+
/** Create a generic field builder */
|
|
15
|
+
static field(key) {
|
|
16
|
+
return new ControlBuilder(key);
|
|
17
|
+
}
|
|
18
|
+
/** Create a text field */
|
|
19
|
+
static text(key) {
|
|
20
|
+
return new ControlBuilder(key).type("text");
|
|
21
|
+
}
|
|
22
|
+
/** Create an email field */
|
|
23
|
+
static email(key) {
|
|
24
|
+
return new ControlBuilder(key).type("email");
|
|
25
|
+
}
|
|
26
|
+
/** Create a number field */
|
|
27
|
+
static number(key) {
|
|
28
|
+
return new ControlBuilder(key).type("number");
|
|
29
|
+
}
|
|
30
|
+
/** Create a boolean (yes/no) field */
|
|
31
|
+
static boolean(key) {
|
|
32
|
+
return new ControlBuilder(key).type("boolean");
|
|
33
|
+
}
|
|
34
|
+
/** Create a select field with options */
|
|
35
|
+
static select(key, options) {
|
|
36
|
+
return new ControlBuilder(key).type("select").options(options);
|
|
37
|
+
}
|
|
38
|
+
/** Create a date field */
|
|
39
|
+
static date(key) {
|
|
40
|
+
return new ControlBuilder(key).type("date");
|
|
41
|
+
}
|
|
42
|
+
/** Create a file upload field */
|
|
43
|
+
static file(key) {
|
|
44
|
+
return new ControlBuilder(key).type("file");
|
|
45
|
+
}
|
|
46
|
+
// ═══ TYPE ═══
|
|
47
|
+
/** Set the field type */
|
|
48
|
+
type(type) {
|
|
49
|
+
this.control.type = type;
|
|
50
|
+
return this;
|
|
51
|
+
}
|
|
52
|
+
// ═══ BEHAVIOR ═══
|
|
53
|
+
/** Mark field as required */
|
|
54
|
+
required() {
|
|
55
|
+
this.control.required = true;
|
|
56
|
+
return this;
|
|
57
|
+
}
|
|
58
|
+
/** Mark field as optional (default) */
|
|
59
|
+
optional() {
|
|
60
|
+
this.control.required = false;
|
|
61
|
+
return this;
|
|
62
|
+
}
|
|
63
|
+
/** Mark field as hidden (extract silently, never ask) */
|
|
64
|
+
hidden() {
|
|
65
|
+
this.control.hidden = true;
|
|
66
|
+
return this;
|
|
67
|
+
}
|
|
68
|
+
/** Mark field as sensitive (don't echo value back) */
|
|
69
|
+
sensitive() {
|
|
70
|
+
this.control.sensitive = true;
|
|
71
|
+
return this;
|
|
72
|
+
}
|
|
73
|
+
/** Mark field as readonly (can't change after set) */
|
|
74
|
+
readonly() {
|
|
75
|
+
this.control.readonly = true;
|
|
76
|
+
return this;
|
|
77
|
+
}
|
|
78
|
+
/** Mark field as accepting multiple values */
|
|
79
|
+
multiple() {
|
|
80
|
+
this.control.multiple = true;
|
|
81
|
+
return this;
|
|
82
|
+
}
|
|
83
|
+
// ═══ VALIDATION ═══
|
|
84
|
+
/** Set regex pattern for validation */
|
|
85
|
+
pattern(regex) {
|
|
86
|
+
this.control.pattern = regex;
|
|
87
|
+
return this;
|
|
88
|
+
}
|
|
89
|
+
/** Set minimum value (for numbers) or minimum length (via minLength) */
|
|
90
|
+
min(n) {
|
|
91
|
+
this.control.min = n;
|
|
92
|
+
return this;
|
|
93
|
+
}
|
|
94
|
+
/** Set maximum value (for numbers) or maximum length (via maxLength) */
|
|
95
|
+
max(n) {
|
|
96
|
+
this.control.max = n;
|
|
97
|
+
return this;
|
|
98
|
+
}
|
|
99
|
+
/** Set minimum string length */
|
|
100
|
+
minLength(n) {
|
|
101
|
+
this.control.minLength = n;
|
|
102
|
+
return this;
|
|
103
|
+
}
|
|
104
|
+
/** Set maximum string length */
|
|
105
|
+
maxLength(n) {
|
|
106
|
+
this.control.maxLength = n;
|
|
107
|
+
return this;
|
|
108
|
+
}
|
|
109
|
+
/** Set allowed values (enum) */
|
|
110
|
+
enum(values) {
|
|
111
|
+
this.control.enum = values;
|
|
112
|
+
return this;
|
|
113
|
+
}
|
|
114
|
+
/** Set select options */
|
|
115
|
+
options(opts) {
|
|
116
|
+
this.control.options = opts;
|
|
117
|
+
return this;
|
|
118
|
+
}
|
|
119
|
+
// ═══ AGENT HINTS ═══
|
|
120
|
+
// WHY agent hints: Help LLM extract values correctly
|
|
121
|
+
/** Set human-readable label */
|
|
122
|
+
label(label) {
|
|
123
|
+
this.control.label = label;
|
|
124
|
+
return this;
|
|
125
|
+
}
|
|
126
|
+
/** Set custom prompt for asking this field */
|
|
127
|
+
ask(prompt) {
|
|
128
|
+
this.control.askPrompt = prompt;
|
|
129
|
+
return this;
|
|
130
|
+
}
|
|
131
|
+
/** Set description for LLM context */
|
|
132
|
+
description(desc) {
|
|
133
|
+
this.control.description = desc;
|
|
134
|
+
return this;
|
|
135
|
+
}
|
|
136
|
+
/** Add extraction hints (keywords to look for) */
|
|
137
|
+
hint(...hints) {
|
|
138
|
+
this.control.extractHints = hints;
|
|
139
|
+
return this;
|
|
140
|
+
}
|
|
141
|
+
/** Set example value for "give me an example" */
|
|
142
|
+
example(value) {
|
|
143
|
+
this.control.example = value;
|
|
144
|
+
return this;
|
|
145
|
+
}
|
|
146
|
+
/** Set confidence threshold for auto-acceptance */
|
|
147
|
+
confirmThreshold(n) {
|
|
148
|
+
this.control.confirmThreshold = n;
|
|
149
|
+
return this;
|
|
150
|
+
}
|
|
151
|
+
// ═══ FILE OPTIONS ═══
|
|
152
|
+
/** Set accepted MIME types for file upload */
|
|
153
|
+
accept(mimeTypes) {
|
|
154
|
+
this.control.file = { ...this.control.file, accept: mimeTypes };
|
|
155
|
+
return this;
|
|
156
|
+
}
|
|
157
|
+
/** Set maximum file size in bytes */
|
|
158
|
+
maxSize(bytes) {
|
|
159
|
+
this.control.file = { ...this.control.file, maxSize: bytes };
|
|
160
|
+
return this;
|
|
161
|
+
}
|
|
162
|
+
/** Set maximum number of files */
|
|
163
|
+
maxFiles(n) {
|
|
164
|
+
this.control.file = { ...this.control.file, maxFiles: n };
|
|
165
|
+
return this;
|
|
166
|
+
}
|
|
167
|
+
// ═══ ACCESS ═══
|
|
168
|
+
/** Set roles that can see/fill this field */
|
|
169
|
+
roles(...roles) {
|
|
170
|
+
this.control.roles = roles;
|
|
171
|
+
return this;
|
|
172
|
+
}
|
|
173
|
+
// ═══ DEFAULTS & CONDITIONS ═══
|
|
174
|
+
/** Set default value */
|
|
175
|
+
default(value) {
|
|
176
|
+
this.control.defaultValue = value;
|
|
177
|
+
return this;
|
|
178
|
+
}
|
|
179
|
+
/** Set dependency on another field */
|
|
180
|
+
dependsOn(field, condition = "exists", value) {
|
|
181
|
+
this.control.dependsOn = { field, condition, value };
|
|
182
|
+
return this;
|
|
183
|
+
}
|
|
184
|
+
// ═══ DATABASE ═══
|
|
185
|
+
/** Set database column name (defaults to key) */
|
|
186
|
+
dbbind(columnName) {
|
|
187
|
+
this.control.dbbind = columnName;
|
|
188
|
+
return this;
|
|
189
|
+
}
|
|
190
|
+
// ═══ UI ═══
|
|
191
|
+
/** Set section name for grouping */
|
|
192
|
+
section(name) {
|
|
193
|
+
this.control.ui = { ...this.control.ui, section: name };
|
|
194
|
+
return this;
|
|
195
|
+
}
|
|
196
|
+
/** Set display order within section */
|
|
197
|
+
order(n) {
|
|
198
|
+
this.control.ui = { ...this.control.ui, order: n };
|
|
199
|
+
return this;
|
|
200
|
+
}
|
|
201
|
+
/** Set placeholder text */
|
|
202
|
+
placeholder(text) {
|
|
203
|
+
this.control.ui = { ...this.control.ui, placeholder: text };
|
|
204
|
+
return this;
|
|
205
|
+
}
|
|
206
|
+
/** Set help text */
|
|
207
|
+
helpText(text) {
|
|
208
|
+
this.control.ui = { ...this.control.ui, helpText: text };
|
|
209
|
+
return this;
|
|
210
|
+
}
|
|
211
|
+
/** Set custom widget type */
|
|
212
|
+
widget(type) {
|
|
213
|
+
this.control.ui = { ...this.control.ui, widget: type };
|
|
214
|
+
return this;
|
|
215
|
+
}
|
|
216
|
+
// ═══ I18N ═══
|
|
217
|
+
/** Add localized text for a locale */
|
|
218
|
+
i18n(locale, translations) {
|
|
219
|
+
this.control.i18n = { ...this.control.i18n, [locale]: translations };
|
|
220
|
+
return this;
|
|
221
|
+
}
|
|
222
|
+
// ═══ META ═══
|
|
223
|
+
/** Add custom metadata */
|
|
224
|
+
meta(key, value) {
|
|
225
|
+
this.control.meta = { ...this.control.meta, [key]: value };
|
|
226
|
+
return this;
|
|
227
|
+
}
|
|
228
|
+
// ═══ BUILD ═══
|
|
229
|
+
/**
|
|
230
|
+
* Build the final FormControl.
|
|
231
|
+
*
|
|
232
|
+
* Applies defaults and validates the control.
|
|
233
|
+
*
|
|
234
|
+
* @returns Complete FormControl object
|
|
235
|
+
*/
|
|
236
|
+
build() {
|
|
237
|
+
const key = this.control.key;
|
|
238
|
+
if (!key) {
|
|
239
|
+
throw new Error("Control key is required");
|
|
240
|
+
}
|
|
241
|
+
const control = {
|
|
242
|
+
key,
|
|
243
|
+
label: this.control.label || prettify(key),
|
|
244
|
+
type: this.control.type || "text",
|
|
245
|
+
...this.control
|
|
246
|
+
};
|
|
247
|
+
return control;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
class FormBuilder {
|
|
251
|
+
/** Partial form being built */
|
|
252
|
+
form;
|
|
253
|
+
/**
|
|
254
|
+
* Create a new FormBuilder.
|
|
255
|
+
*
|
|
256
|
+
* @param id - Unique form identifier
|
|
257
|
+
*/
|
|
258
|
+
constructor(id) {
|
|
259
|
+
this.form = { id, controls: [] };
|
|
260
|
+
}
|
|
261
|
+
// ═══ STATIC FACTORY ═══
|
|
262
|
+
/** Create a new form builder */
|
|
263
|
+
static create(id) {
|
|
264
|
+
return new FormBuilder(id);
|
|
265
|
+
}
|
|
266
|
+
// ═══ METADATA ═══
|
|
267
|
+
/** Set form name */
|
|
268
|
+
name(name) {
|
|
269
|
+
this.form.name = name;
|
|
270
|
+
return this;
|
|
271
|
+
}
|
|
272
|
+
/** Set form description */
|
|
273
|
+
description(desc) {
|
|
274
|
+
this.form.description = desc;
|
|
275
|
+
return this;
|
|
276
|
+
}
|
|
277
|
+
/** Set form version */
|
|
278
|
+
version(v) {
|
|
279
|
+
this.form.version = v;
|
|
280
|
+
return this;
|
|
281
|
+
}
|
|
282
|
+
// ═══ CONTROLS ═══
|
|
283
|
+
/**
|
|
284
|
+
* Add a control to the form.
|
|
285
|
+
*
|
|
286
|
+
* Accepts either a ControlBuilder (calls .build()) or a FormControl.
|
|
287
|
+
*/
|
|
288
|
+
control(builder) {
|
|
289
|
+
const ctrl = builder instanceof ControlBuilder ? builder.build() : builder;
|
|
290
|
+
this.form.controls?.push(ctrl);
|
|
291
|
+
return this;
|
|
292
|
+
}
|
|
293
|
+
/** Add multiple controls */
|
|
294
|
+
controls(...builders) {
|
|
295
|
+
for (const builder of builders) {
|
|
296
|
+
this.control(builder);
|
|
297
|
+
}
|
|
298
|
+
return this;
|
|
299
|
+
}
|
|
300
|
+
// ═══ SHORTHAND CONTROLS ═══
|
|
301
|
+
// WHY shorthands: Quick form prototyping
|
|
302
|
+
/** Add required text fields */
|
|
303
|
+
required(...keys) {
|
|
304
|
+
for (const key of keys) {
|
|
305
|
+
this.control(ControlBuilder.field(key).required());
|
|
306
|
+
}
|
|
307
|
+
return this;
|
|
308
|
+
}
|
|
309
|
+
/** Add optional text fields */
|
|
310
|
+
optional(...keys) {
|
|
311
|
+
for (const key of keys) {
|
|
312
|
+
this.control(ControlBuilder.field(key));
|
|
313
|
+
}
|
|
314
|
+
return this;
|
|
315
|
+
}
|
|
316
|
+
// ═══ PERMISSIONS ═══
|
|
317
|
+
/** Set roles that can start this form */
|
|
318
|
+
roles(...roles) {
|
|
319
|
+
this.form.roles = roles;
|
|
320
|
+
return this;
|
|
321
|
+
}
|
|
322
|
+
/** Allow multiple submissions per user */
|
|
323
|
+
allowMultiple() {
|
|
324
|
+
this.form.allowMultiple = true;
|
|
325
|
+
return this;
|
|
326
|
+
}
|
|
327
|
+
// ═══ UX ═══
|
|
328
|
+
/** Disable undo functionality */
|
|
329
|
+
noUndo() {
|
|
330
|
+
this.form.ux = { ...this.form.ux, allowUndo: false };
|
|
331
|
+
return this;
|
|
332
|
+
}
|
|
333
|
+
/** Disable skip functionality */
|
|
334
|
+
noSkip() {
|
|
335
|
+
this.form.ux = { ...this.form.ux, allowSkip: false };
|
|
336
|
+
return this;
|
|
337
|
+
}
|
|
338
|
+
/** Disable autofill */
|
|
339
|
+
noAutofill() {
|
|
340
|
+
this.form.ux = { ...this.form.ux, allowAutofill: false };
|
|
341
|
+
return this;
|
|
342
|
+
}
|
|
343
|
+
/** Set maximum undo steps */
|
|
344
|
+
maxUndoSteps(n) {
|
|
345
|
+
this.form.ux = { ...this.form.ux, maxUndoSteps: n };
|
|
346
|
+
return this;
|
|
347
|
+
}
|
|
348
|
+
// ═══ TTL ═══
|
|
349
|
+
/** Configure TTL (time-to-live) settings */
|
|
350
|
+
ttl(config) {
|
|
351
|
+
this.form.ttl = { ...this.form.ttl, ...config };
|
|
352
|
+
return this;
|
|
353
|
+
}
|
|
354
|
+
// ═══ NUDGE ═══
|
|
355
|
+
/** Disable nudge messages */
|
|
356
|
+
noNudge() {
|
|
357
|
+
this.form.nudge = { ...this.form.nudge, enabled: false };
|
|
358
|
+
return this;
|
|
359
|
+
}
|
|
360
|
+
/** Set inactivity hours before nudge */
|
|
361
|
+
nudgeAfter(hours) {
|
|
362
|
+
this.form.nudge = { ...this.form.nudge, afterInactiveHours: hours };
|
|
363
|
+
return this;
|
|
364
|
+
}
|
|
365
|
+
/** Set custom nudge message */
|
|
366
|
+
nudgeMessage(message) {
|
|
367
|
+
this.form.nudge = { ...this.form.nudge, message };
|
|
368
|
+
return this;
|
|
369
|
+
}
|
|
370
|
+
// ═══ HOOKS ═══
|
|
371
|
+
// WHY hooks: Allow consuming plugins to handle form events
|
|
372
|
+
/** Set task worker to call on session start */
|
|
373
|
+
onStart(workerName) {
|
|
374
|
+
this.form.hooks = { ...this.form.hooks, onStart: workerName };
|
|
375
|
+
return this;
|
|
376
|
+
}
|
|
377
|
+
/** Set task worker to call on field change */
|
|
378
|
+
onFieldChange(workerName) {
|
|
379
|
+
this.form.hooks = { ...this.form.hooks, onFieldChange: workerName };
|
|
380
|
+
return this;
|
|
381
|
+
}
|
|
382
|
+
/** Set task worker to call when form is ready to submit */
|
|
383
|
+
onReady(workerName) {
|
|
384
|
+
this.form.hooks = { ...this.form.hooks, onReady: workerName };
|
|
385
|
+
return this;
|
|
386
|
+
}
|
|
387
|
+
/** Set task worker to call on submission */
|
|
388
|
+
onSubmit(workerName) {
|
|
389
|
+
this.form.hooks = { ...this.form.hooks, onSubmit: workerName };
|
|
390
|
+
return this;
|
|
391
|
+
}
|
|
392
|
+
/** Set task worker to call on cancellation */
|
|
393
|
+
onCancel(workerName) {
|
|
394
|
+
this.form.hooks = { ...this.form.hooks, onCancel: workerName };
|
|
395
|
+
return this;
|
|
396
|
+
}
|
|
397
|
+
/** Set task worker to call on expiration */
|
|
398
|
+
onExpire(workerName) {
|
|
399
|
+
this.form.hooks = { ...this.form.hooks, onExpire: workerName };
|
|
400
|
+
return this;
|
|
401
|
+
}
|
|
402
|
+
/** Set multiple hooks at once */
|
|
403
|
+
hooks(hooks) {
|
|
404
|
+
this.form.hooks = { ...this.form.hooks, ...hooks };
|
|
405
|
+
return this;
|
|
406
|
+
}
|
|
407
|
+
// ═══ DEBUG ═══
|
|
408
|
+
/** Enable debug mode (logs extraction reasoning) */
|
|
409
|
+
debug() {
|
|
410
|
+
this.form.debug = true;
|
|
411
|
+
return this;
|
|
412
|
+
}
|
|
413
|
+
// ═══ I18N ═══
|
|
414
|
+
/** Add localized form text */
|
|
415
|
+
i18n(locale, translations) {
|
|
416
|
+
this.form.i18n = { ...this.form.i18n, [locale]: translations };
|
|
417
|
+
return this;
|
|
418
|
+
}
|
|
419
|
+
// ═══ META ═══
|
|
420
|
+
/** Add custom metadata */
|
|
421
|
+
meta(key, value) {
|
|
422
|
+
this.form.meta = { ...this.form.meta, [key]: value };
|
|
423
|
+
return this;
|
|
424
|
+
}
|
|
425
|
+
// ═══ BUILD ═══
|
|
426
|
+
/**
|
|
427
|
+
* Build the final FormDefinition.
|
|
428
|
+
*
|
|
429
|
+
* Applies defaults and validates the form.
|
|
430
|
+
*
|
|
431
|
+
* @returns Complete FormDefinition object
|
|
432
|
+
*/
|
|
433
|
+
build() {
|
|
434
|
+
const id = this.form.id;
|
|
435
|
+
if (!id) {
|
|
436
|
+
throw new Error("Form id is required");
|
|
437
|
+
}
|
|
438
|
+
const form = {
|
|
439
|
+
id,
|
|
440
|
+
name: this.form.name || prettify(id),
|
|
441
|
+
controls: this.form.controls || [],
|
|
442
|
+
...this.form
|
|
443
|
+
};
|
|
444
|
+
return form;
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
const Form = FormBuilder;
|
|
448
|
+
const C = ControlBuilder;
|
|
449
|
+
function prettify(key) {
|
|
450
|
+
return key.replace(/[-_]/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
|
|
451
|
+
}
|
|
452
|
+
export {
|
|
453
|
+
C,
|
|
454
|
+
ControlBuilder,
|
|
455
|
+
Form,
|
|
456
|
+
FormBuilder
|
|
457
|
+
};
|
|
458
|
+
//# sourceMappingURL=builder.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/builder.ts"],"sourcesContent":["/**\n * @module builder\n * @description Fluent builder API for defining forms and controls\n *\n * ## Why a Builder API\n *\n * Form definitions can be verbose with many optional fields. The builder\n * API provides:\n *\n * 1. **Type Safety**: Method chaining with TypeScript gives autocomplete\n * 2. **Readability**: Intent is clear from method names\n * 3. **Defaults**: Builder applies sensible defaults\n * 4. **Validation**: Build-time checks for common mistakes\n *\n * ## Usage Examples\n *\n * ### Simple Form\n *\n * ```typescript\n * const form = Form.create('contact')\n * .name('Contact Form')\n * .control(C.email('email').required())\n * .control(C.text('message').required())\n * .build();\n * ```\n *\n * ### Complex Form\n *\n * ```typescript\n * const registrationForm = Form.create('registration')\n * .name('User Registration')\n * .description('Create your account')\n * .control(\n * C.email('email')\n * .required()\n * .ask('What email should we use for your account?')\n * .example('user@example.com')\n * )\n * .control(\n * C.text('username')\n * .required()\n * .minLength(3)\n * .maxLength(20)\n * .pattern('^[a-z0-9_]+$')\n * .ask('Choose a username (letters, numbers, underscore)')\n * )\n * .control(\n * C.number('age')\n * .min(13)\n * .ask('How old are you?')\n * )\n * .onSubmit('handle_registration')\n * .ttl({ minDays: 7, maxDays: 30 })\n * .build();\n * ```\n *\n * ### Custom Type\n *\n * ```typescript\n * // Register custom type first\n * FormService.registerType('solana_address', {\n * validate: (v) => ({\n * valid: /^[1-9A-HJ-NP-Za-km-z]{32,44}$/.test(String(v)),\n * error: 'Invalid Solana address'\n * }),\n * extractionPrompt: 'a Solana wallet address (Base58 encoded)'\n * });\n *\n * // Use in form\n * const form = Form.create('wallet')\n * .control(\n * C.field('walletAddress')\n * .type('solana_address')\n * .required()\n * .label('Wallet Address')\n * .ask('What is your Solana wallet address?')\n * )\n * .build();\n * ```\n *\n * ## Shorthand Exports\n *\n * For convenience:\n * - `Form` is an alias for `FormBuilder`\n * - `C` is an alias for `ControlBuilder`\n *\n * This enables the concise syntax shown in examples.\n */\n\nimport type { JsonValue } from \"@elizaos/core\";\nimport type {\n FormControl,\n FormControlDependency,\n FormControlOption,\n FormDefinition,\n FormDefinitionHooks,\n} from \"./types.js\";\n\n// ============================================================================\n// CONTROL BUILDER\n// ============================================================================\n\n/**\n * Fluent builder for FormControl.\n *\n * Create controls with readable, chainable syntax:\n *\n * ```typescript\n * ControlBuilder.email('email').required().ask('What is your email?')\n * ```\n *\n * All methods return `this` for chaining except `build()` which returns\n * the final FormControl.\n */\nexport class ControlBuilder {\n /** Partial control being built */\n private control: Partial<FormControl>;\n\n /**\n * Create a new ControlBuilder.\n *\n * @param key - The unique key for this control\n */\n constructor(key: string) {\n this.control = { key };\n }\n\n // ═══ STATIC FACTORIES ═══\n // WHY static factories: Cleaner than `new ControlBuilder(key).type('text')`\n\n /** Create a generic field builder */\n static field(key: string): ControlBuilder {\n return new ControlBuilder(key);\n }\n\n /** Create a text field */\n static text(key: string): ControlBuilder {\n return new ControlBuilder(key).type(\"text\");\n }\n\n /** Create an email field */\n static email(key: string): ControlBuilder {\n return new ControlBuilder(key).type(\"email\");\n }\n\n /** Create a number field */\n static number(key: string): ControlBuilder {\n return new ControlBuilder(key).type(\"number\");\n }\n\n /** Create a boolean (yes/no) field */\n static boolean(key: string): ControlBuilder {\n return new ControlBuilder(key).type(\"boolean\");\n }\n\n /** Create a select field with options */\n static select(key: string, options: FormControlOption[]): ControlBuilder {\n return new ControlBuilder(key).type(\"select\").options(options);\n }\n\n /** Create a date field */\n static date(key: string): ControlBuilder {\n return new ControlBuilder(key).type(\"date\");\n }\n\n /** Create a file upload field */\n static file(key: string): ControlBuilder {\n return new ControlBuilder(key).type(\"file\");\n }\n\n // ═══ TYPE ═══\n\n /** Set the field type */\n type(type: string): this {\n this.control.type = type;\n return this;\n }\n\n // ═══ BEHAVIOR ═══\n\n /** Mark field as required */\n required(): this {\n this.control.required = true;\n return this;\n }\n\n /** Mark field as optional (default) */\n optional(): this {\n this.control.required = false;\n return this;\n }\n\n /** Mark field as hidden (extract silently, never ask) */\n hidden(): this {\n this.control.hidden = true;\n return this;\n }\n\n /** Mark field as sensitive (don't echo value back) */\n sensitive(): this {\n this.control.sensitive = true;\n return this;\n }\n\n /** Mark field as readonly (can't change after set) */\n readonly(): this {\n this.control.readonly = true;\n return this;\n }\n\n /** Mark field as accepting multiple values */\n multiple(): this {\n this.control.multiple = true;\n return this;\n }\n\n // ═══ VALIDATION ═══\n\n /** Set regex pattern for validation */\n pattern(regex: string): this {\n this.control.pattern = regex;\n return this;\n }\n\n /** Set minimum value (for numbers) or minimum length (via minLength) */\n min(n: number): this {\n this.control.min = n;\n return this;\n }\n\n /** Set maximum value (for numbers) or maximum length (via maxLength) */\n max(n: number): this {\n this.control.max = n;\n return this;\n }\n\n /** Set minimum string length */\n minLength(n: number): this {\n this.control.minLength = n;\n return this;\n }\n\n /** Set maximum string length */\n maxLength(n: number): this {\n this.control.maxLength = n;\n return this;\n }\n\n /** Set allowed values (enum) */\n enum(values: string[]): this {\n this.control.enum = values;\n return this;\n }\n\n /** Set select options */\n options(opts: FormControlOption[]): this {\n this.control.options = opts;\n return this;\n }\n\n // ═══ AGENT HINTS ═══\n // WHY agent hints: Help LLM extract values correctly\n\n /** Set human-readable label */\n label(label: string): this {\n this.control.label = label;\n return this;\n }\n\n /** Set custom prompt for asking this field */\n ask(prompt: string): this {\n this.control.askPrompt = prompt;\n return this;\n }\n\n /** Set description for LLM context */\n description(desc: string): this {\n this.control.description = desc;\n return this;\n }\n\n /** Add extraction hints (keywords to look for) */\n hint(...hints: string[]): this {\n this.control.extractHints = hints;\n return this;\n }\n\n /** Set example value for \"give me an example\" */\n example(value: string): this {\n this.control.example = value;\n return this;\n }\n\n /** Set confidence threshold for auto-acceptance */\n confirmThreshold(n: number): this {\n this.control.confirmThreshold = n;\n return this;\n }\n\n // ═══ FILE OPTIONS ═══\n\n /** Set accepted MIME types for file upload */\n accept(mimeTypes: string[]): this {\n this.control.file = { ...this.control.file, accept: mimeTypes };\n return this;\n }\n\n /** Set maximum file size in bytes */\n maxSize(bytes: number): this {\n this.control.file = { ...this.control.file, maxSize: bytes };\n return this;\n }\n\n /** Set maximum number of files */\n maxFiles(n: number): this {\n this.control.file = { ...this.control.file, maxFiles: n };\n return this;\n }\n\n // ═══ ACCESS ═══\n\n /** Set roles that can see/fill this field */\n roles(...roles: string[]): this {\n this.control.roles = roles;\n return this;\n }\n\n // ═══ DEFAULTS & CONDITIONS ═══\n\n /** Set default value */\n default(value: JsonValue): this {\n this.control.defaultValue = value;\n return this;\n }\n\n /** Set dependency on another field */\n dependsOn(\n field: string,\n condition: FormControlDependency[\"condition\"] = \"exists\",\n value?: JsonValue,\n ): this {\n this.control.dependsOn = { field, condition, value };\n return this;\n }\n\n // ═══ DATABASE ═══\n\n /** Set database column name (defaults to key) */\n dbbind(columnName: string): this {\n this.control.dbbind = columnName;\n return this;\n }\n\n // ═══ UI ═══\n\n /** Set section name for grouping */\n section(name: string): this {\n this.control.ui = { ...this.control.ui, section: name };\n return this;\n }\n\n /** Set display order within section */\n order(n: number): this {\n this.control.ui = { ...this.control.ui, order: n };\n return this;\n }\n\n /** Set placeholder text */\n placeholder(text: string): this {\n this.control.ui = { ...this.control.ui, placeholder: text };\n return this;\n }\n\n /** Set help text */\n helpText(text: string): this {\n this.control.ui = { ...this.control.ui, helpText: text };\n return this;\n }\n\n /** Set custom widget type */\n widget(type: string): this {\n this.control.ui = { ...this.control.ui, widget: type };\n return this;\n }\n\n // ═══ I18N ═══\n\n /** Add localized text for a locale */\n i18n(\n locale: string,\n translations: {\n label?: string;\n description?: string;\n askPrompt?: string;\n helpText?: string;\n },\n ): this {\n this.control.i18n = { ...this.control.i18n, [locale]: translations };\n return this;\n }\n\n // ═══ META ═══\n\n /** Add custom metadata */\n meta(key: string, value: JsonValue): this {\n this.control.meta = { ...this.control.meta, [key]: value };\n return this;\n }\n\n // ═══ BUILD ═══\n\n /**\n * Build the final FormControl.\n *\n * Applies defaults and validates the control.\n *\n * @returns Complete FormControl object\n */\n build(): FormControl {\n const key = this.control.key;\n if (!key) {\n throw new Error(\"Control key is required\");\n }\n\n // Apply defaults\n const control: FormControl = {\n key,\n label: this.control.label || prettify(key),\n type: this.control.type || \"text\",\n ...this.control,\n };\n\n return control;\n }\n}\n\n// ============================================================================\n// FORM BUILDER\n// ============================================================================\n\n/**\n * Fluent builder for FormDefinition.\n *\n * Create forms with readable, chainable syntax:\n *\n * ```typescript\n * Form.create('contact')\n * .name('Contact Form')\n * .control(C.email('email').required())\n * .onSubmit('handle_contact')\n * .build();\n * ```\n */\nexport class FormBuilder {\n /** Partial form being built */\n private form: Partial<FormDefinition>;\n\n /**\n * Create a new FormBuilder.\n *\n * @param id - Unique form identifier\n */\n constructor(id: string) {\n this.form = { id, controls: [] };\n }\n\n // ═══ STATIC FACTORY ═══\n\n /** Create a new form builder */\n static create(id: string): FormBuilder {\n return new FormBuilder(id);\n }\n\n // ═══ METADATA ═══\n\n /** Set form name */\n name(name: string): this {\n this.form.name = name;\n return this;\n }\n\n /** Set form description */\n description(desc: string): this {\n this.form.description = desc;\n return this;\n }\n\n /** Set form version */\n version(v: number): this {\n this.form.version = v;\n return this;\n }\n\n // ═══ CONTROLS ═══\n\n /**\n * Add a control to the form.\n *\n * Accepts either a ControlBuilder (calls .build()) or a FormControl.\n */\n control(builder: ControlBuilder | FormControl): this {\n const ctrl = builder instanceof ControlBuilder ? builder.build() : builder;\n this.form.controls?.push(ctrl);\n return this;\n }\n\n /** Add multiple controls */\n controls(...builders: (ControlBuilder | FormControl)[]): this {\n for (const builder of builders) {\n this.control(builder);\n }\n return this;\n }\n\n // ═══ SHORTHAND CONTROLS ═══\n // WHY shorthands: Quick form prototyping\n\n /** Add required text fields */\n required(...keys: string[]): this {\n for (const key of keys) {\n this.control(ControlBuilder.field(key).required());\n }\n return this;\n }\n\n /** Add optional text fields */\n optional(...keys: string[]): this {\n for (const key of keys) {\n this.control(ControlBuilder.field(key));\n }\n return this;\n }\n\n // ═══ PERMISSIONS ═══\n\n /** Set roles that can start this form */\n roles(...roles: string[]): this {\n this.form.roles = roles;\n return this;\n }\n\n /** Allow multiple submissions per user */\n allowMultiple(): this {\n this.form.allowMultiple = true;\n return this;\n }\n\n // ═══ UX ═══\n\n /** Disable undo functionality */\n noUndo(): this {\n this.form.ux = { ...this.form.ux, allowUndo: false };\n return this;\n }\n\n /** Disable skip functionality */\n noSkip(): this {\n this.form.ux = { ...this.form.ux, allowSkip: false };\n return this;\n }\n\n /** Disable autofill */\n noAutofill(): this {\n this.form.ux = { ...this.form.ux, allowAutofill: false };\n return this;\n }\n\n /** Set maximum undo steps */\n maxUndoSteps(n: number): this {\n this.form.ux = { ...this.form.ux, maxUndoSteps: n };\n return this;\n }\n\n // ═══ TTL ═══\n\n /** Configure TTL (time-to-live) settings */\n ttl(config: {\n minDays?: number;\n maxDays?: number;\n effortMultiplier?: number;\n }): this {\n this.form.ttl = { ...this.form.ttl, ...config };\n return this;\n }\n\n // ═══ NUDGE ═══\n\n /** Disable nudge messages */\n noNudge(): this {\n this.form.nudge = { ...this.form.nudge, enabled: false };\n return this;\n }\n\n /** Set inactivity hours before nudge */\n nudgeAfter(hours: number): this {\n this.form.nudge = { ...this.form.nudge, afterInactiveHours: hours };\n return this;\n }\n\n /** Set custom nudge message */\n nudgeMessage(message: string): this {\n this.form.nudge = { ...this.form.nudge, message };\n return this;\n }\n\n // ═══ HOOKS ═══\n // WHY hooks: Allow consuming plugins to handle form events\n\n /** Set task worker to call on session start */\n onStart(workerName: string): this {\n this.form.hooks = { ...this.form.hooks, onStart: workerName };\n return this;\n }\n\n /** Set task worker to call on field change */\n onFieldChange(workerName: string): this {\n this.form.hooks = { ...this.form.hooks, onFieldChange: workerName };\n return this;\n }\n\n /** Set task worker to call when form is ready to submit */\n onReady(workerName: string): this {\n this.form.hooks = { ...this.form.hooks, onReady: workerName };\n return this;\n }\n\n /** Set task worker to call on submission */\n onSubmit(workerName: string): this {\n this.form.hooks = { ...this.form.hooks, onSubmit: workerName };\n return this;\n }\n\n /** Set task worker to call on cancellation */\n onCancel(workerName: string): this {\n this.form.hooks = { ...this.form.hooks, onCancel: workerName };\n return this;\n }\n\n /** Set task worker to call on expiration */\n onExpire(workerName: string): this {\n this.form.hooks = { ...this.form.hooks, onExpire: workerName };\n return this;\n }\n\n /** Set multiple hooks at once */\n hooks(hooks: FormDefinitionHooks): this {\n this.form.hooks = { ...this.form.hooks, ...hooks };\n return this;\n }\n\n // ═══ DEBUG ═══\n\n /** Enable debug mode (logs extraction reasoning) */\n debug(): this {\n this.form.debug = true;\n return this;\n }\n\n // ═══ I18N ═══\n\n /** Add localized form text */\n i18n(\n locale: string,\n translations: { name?: string; description?: string },\n ): this {\n this.form.i18n = { ...this.form.i18n, [locale]: translations };\n return this;\n }\n\n // ═══ META ═══\n\n /** Add custom metadata */\n meta(key: string, value: JsonValue): this {\n this.form.meta = { ...this.form.meta, [key]: value };\n return this;\n }\n\n // ═══ BUILD ═══\n\n /**\n * Build the final FormDefinition.\n *\n * Applies defaults and validates the form.\n *\n * @returns Complete FormDefinition object\n */\n build(): FormDefinition {\n const id = this.form.id;\n if (!id) {\n throw new Error(\"Form id is required\");\n }\n\n const form: FormDefinition = {\n id,\n name: this.form.name || prettify(id),\n controls: this.form.controls || [],\n ...this.form,\n };\n\n return form;\n }\n}\n\n// ============================================================================\n// SHORTHAND EXPORTS\n// ============================================================================\n\n/**\n * Shorthand for FormBuilder.create\n *\n * Usage: `Form.create('myForm')...`\n */\nexport const Form = FormBuilder;\n\n/**\n * Shorthand for ControlBuilder factories\n *\n * Usage: `C.email('email').required()`\n */\nexport const C = ControlBuilder;\n\n// ============================================================================\n// UTILITY\n// ============================================================================\n\n/**\n * Convert snake_case or kebab-case to Title Case.\n *\n * Used to generate human-readable labels from field keys.\n *\n * @example prettify('first_name') // \"First Name\"\n * @example prettify('email-address') // \"Email Address\"\n */\nfunction prettify(key: string): string {\n return key.replace(/[-_]/g, \" \").replace(/\\b\\w/g, (c) => c.toUpperCase());\n}\n"],"mappings":"AAkHO,MAAM,eAAe;AAAA;AAAA,EAElB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOR,YAAY,KAAa;AACvB,SAAK,UAAU,EAAE,IAAI;AAAA,EACvB;AAAA;AAAA;AAAA;AAAA,EAMA,OAAO,MAAM,KAA6B;AACxC,WAAO,IAAI,eAAe,GAAG;AAAA,EAC/B;AAAA;AAAA,EAGA,OAAO,KAAK,KAA6B;AACvC,WAAO,IAAI,eAAe,GAAG,EAAE,KAAK,MAAM;AAAA,EAC5C;AAAA;AAAA,EAGA,OAAO,MAAM,KAA6B;AACxC,WAAO,IAAI,eAAe,GAAG,EAAE,KAAK,OAAO;AAAA,EAC7C;AAAA;AAAA,EAGA,OAAO,OAAO,KAA6B;AACzC,WAAO,IAAI,eAAe,GAAG,EAAE,KAAK,QAAQ;AAAA,EAC9C;AAAA;AAAA,EAGA,OAAO,QAAQ,KAA6B;AAC1C,WAAO,IAAI,eAAe,GAAG,EAAE,KAAK,SAAS;AAAA,EAC/C;AAAA;AAAA,EAGA,OAAO,OAAO,KAAa,SAA8C;AACvE,WAAO,IAAI,eAAe,GAAG,EAAE,KAAK,QAAQ,EAAE,QAAQ,OAAO;AAAA,EAC/D;AAAA;AAAA,EAGA,OAAO,KAAK,KAA6B;AACvC,WAAO,IAAI,eAAe,GAAG,EAAE,KAAK,MAAM;AAAA,EAC5C;AAAA;AAAA,EAGA,OAAO,KAAK,KAA6B;AACvC,WAAO,IAAI,eAAe,GAAG,EAAE,KAAK,MAAM;AAAA,EAC5C;AAAA;AAAA;AAAA,EAKA,KAAK,MAAoB;AACvB,SAAK,QAAQ,OAAO;AACpB,WAAO;AAAA,EACT;AAAA;AAAA;AAAA,EAKA,WAAiB;AACf,SAAK,QAAQ,WAAW;AACxB,WAAO;AAAA,EACT;AAAA;AAAA,EAGA,WAAiB;AACf,SAAK,QAAQ,WAAW;AACxB,WAAO;AAAA,EACT;AAAA;AAAA,EAGA,SAAe;AACb,SAAK,QAAQ,SAAS;AACtB,WAAO;AAAA,EACT;AAAA;AAAA,EAGA,YAAkB;AAChB,SAAK,QAAQ,YAAY;AACzB,WAAO;AAAA,EACT;AAAA;AAAA,EAGA,WAAiB;AACf,SAAK,QAAQ,WAAW;AACxB,WAAO;AAAA,EACT;AAAA;AAAA,EAGA,WAAiB;AACf,SAAK,QAAQ,WAAW;AACxB,WAAO;AAAA,EACT;AAAA;AAAA;AAAA,EAKA,QAAQ,OAAqB;AAC3B,SAAK,QAAQ,UAAU;AACvB,WAAO;AAAA,EACT;AAAA;AAAA,EAGA,IAAI,GAAiB;AACnB,SAAK,QAAQ,MAAM;AACnB,WAAO;AAAA,EACT;AAAA;AAAA,EAGA,IAAI,GAAiB;AACnB,SAAK,QAAQ,MAAM;AACnB,WAAO;AAAA,EACT;AAAA;AAAA,EAGA,UAAU,GAAiB;AACzB,SAAK,QAAQ,YAAY;AACzB,WAAO;AAAA,EACT;AAAA;AAAA,EAGA,UAAU,GAAiB;AACzB,SAAK,QAAQ,YAAY;AACzB,WAAO;AAAA,EACT;AAAA;AAAA,EAGA,KAAK,QAAwB;AAC3B,SAAK,QAAQ,OAAO;AACpB,WAAO;AAAA,EACT;AAAA;AAAA,EAGA,QAAQ,MAAiC;AACvC,SAAK,QAAQ,UAAU;AACvB,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,OAAqB;AACzB,SAAK,QAAQ,QAAQ;AACrB,WAAO;AAAA,EACT;AAAA;AAAA,EAGA,IAAI,QAAsB;AACxB,SAAK,QAAQ,YAAY;AACzB,WAAO;AAAA,EACT;AAAA;AAAA,EAGA,YAAY,MAAoB;AAC9B,SAAK,QAAQ,cAAc;AAC3B,WAAO;AAAA,EACT;AAAA;AAAA,EAGA,QAAQ,OAAuB;AAC7B,SAAK,QAAQ,eAAe;AAC5B,WAAO;AAAA,EACT;AAAA;AAAA,EAGA,QAAQ,OAAqB;AAC3B,SAAK,QAAQ,UAAU;AACvB,WAAO;AAAA,EACT;AAAA;AAAA,EAGA,iBAAiB,GAAiB;AAChC,SAAK,QAAQ,mBAAmB;AAChC,WAAO;AAAA,EACT;AAAA;AAAA;AAAA,EAKA,OAAO,WAA2B;AAChC,SAAK,QAAQ,OAAO,EAAE,GAAG,KAAK,QAAQ,MAAM,QAAQ,UAAU;AAC9D,WAAO;AAAA,EACT;AAAA;AAAA,EAGA,QAAQ,OAAqB;AAC3B,SAAK,QAAQ,OAAO,EAAE,GAAG,KAAK,QAAQ,MAAM,SAAS,MAAM;AAC3D,WAAO;AAAA,EACT;AAAA;AAAA,EAGA,SAAS,GAAiB;AACxB,SAAK,QAAQ,OAAO,EAAE,GAAG,KAAK,QAAQ,MAAM,UAAU,EAAE;AACxD,WAAO;AAAA,EACT;AAAA;AAAA;AAAA,EAKA,SAAS,OAAuB;AAC9B,SAAK,QAAQ,QAAQ;AACrB,WAAO;AAAA,EACT;AAAA;AAAA;AAAA,EAKA,QAAQ,OAAwB;AAC9B,SAAK,QAAQ,eAAe;AAC5B,WAAO;AAAA,EACT;AAAA;AAAA,EAGA,UACE,OACA,YAAgD,UAChD,OACM;AACN,SAAK,QAAQ,YAAY,EAAE,OAAO,WAAW,MAAM;AACnD,WAAO;AAAA,EACT;AAAA;AAAA;AAAA,EAKA,OAAO,YAA0B;AAC/B,SAAK,QAAQ,SAAS;AACtB,WAAO;AAAA,EACT;AAAA;AAAA;AAAA,EAKA,QAAQ,MAAoB;AAC1B,SAAK,QAAQ,KAAK,EAAE,GAAG,KAAK,QAAQ,IAAI,SAAS,KAAK;AACtD,WAAO;AAAA,EACT;AAAA;AAAA,EAGA,MAAM,GAAiB;AACrB,SAAK,QAAQ,KAAK,EAAE,GAAG,KAAK,QAAQ,IAAI,OAAO,EAAE;AACjD,WAAO;AAAA,EACT;AAAA;AAAA,EAGA,YAAY,MAAoB;AAC9B,SAAK,QAAQ,KAAK,EAAE,GAAG,KAAK,QAAQ,IAAI,aAAa,KAAK;AAC1D,WAAO;AAAA,EACT;AAAA;AAAA,EAGA,SAAS,MAAoB;AAC3B,SAAK,QAAQ,KAAK,EAAE,GAAG,KAAK,QAAQ,IAAI,UAAU,KAAK;AACvD,WAAO;AAAA,EACT;AAAA;AAAA,EAGA,OAAO,MAAoB;AACzB,SAAK,QAAQ,KAAK,EAAE,GAAG,KAAK,QAAQ,IAAI,QAAQ,KAAK;AACrD,WAAO;AAAA,EACT;AAAA;AAAA;AAAA,EAKA,KACE,QACA,cAMM;AACN,SAAK,QAAQ,OAAO,EAAE,GAAG,KAAK,QAAQ,MAAM,CAAC,MAAM,GAAG,aAAa;AACnE,WAAO;AAAA,EACT;AAAA;AAAA;AAAA,EAKA,KAAK,KAAa,OAAwB;AACxC,SAAK,QAAQ,OAAO,EAAE,GAAG,KAAK,QAAQ,MAAM,CAAC,GAAG,GAAG,MAAM;AACzD,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWA,QAAqB;AACnB,UAAM,MAAM,KAAK,QAAQ;AACzB,QAAI,CAAC,KAAK;AACR,YAAM,IAAI,MAAM,yBAAyB;AAAA,IAC3C;AAGA,UAAM,UAAuB;AAAA,MAC3B;AAAA,MACA,OAAO,KAAK,QAAQ,SAAS,SAAS,GAAG;AAAA,MACzC,MAAM,KAAK,QAAQ,QAAQ;AAAA,MAC3B,GAAG,KAAK;AAAA,IACV;AAEA,WAAO;AAAA,EACT;AACF;AAmBO,MAAM,YAAY;AAAA;AAAA,EAEf;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOR,YAAY,IAAY;AACtB,SAAK,OAAO,EAAE,IAAI,UAAU,CAAC,EAAE;AAAA,EACjC;AAAA;AAAA;AAAA,EAKA,OAAO,OAAO,IAAyB;AACrC,WAAO,IAAI,YAAY,EAAE;AAAA,EAC3B;AAAA;AAAA;AAAA,EAKA,KAAK,MAAoB;AACvB,SAAK,KAAK,OAAO;AACjB,WAAO;AAAA,EACT;AAAA;AAAA,EAGA,YAAY,MAAoB;AAC9B,SAAK,KAAK,cAAc;AACxB,WAAO;AAAA,EACT;AAAA;AAAA,EAGA,QAAQ,GAAiB;AACvB,SAAK,KAAK,UAAU;AACpB,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,QAAQ,SAA6C;AACnD,UAAM,OAAO,mBAAmB,iBAAiB,QAAQ,MAAM,IAAI;AACnE,SAAK,KAAK,UAAU,KAAK,IAAI;AAC7B,WAAO;AAAA,EACT;AAAA;AAAA,EAGA,YAAY,UAAkD;AAC5D,eAAW,WAAW,UAAU;AAC9B,WAAK,QAAQ,OAAO;AAAA,IACtB;AACA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAMA,YAAY,MAAsB;AAChC,eAAW,OAAO,MAAM;AACtB,WAAK,QAAQ,eAAe,MAAM,GAAG,EAAE,SAAS,CAAC;AAAA,IACnD;AACA,WAAO;AAAA,EACT;AAAA;AAAA,EAGA,YAAY,MAAsB;AAChC,eAAW,OAAO,MAAM;AACtB,WAAK,QAAQ,eAAe,MAAM,GAAG,CAAC;AAAA,IACxC;AACA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA,EAKA,SAAS,OAAuB;AAC9B,SAAK,KAAK,QAAQ;AAClB,WAAO;AAAA,EACT;AAAA;AAAA,EAGA,gBAAsB;AACpB,SAAK,KAAK,gBAAgB;AAC1B,WAAO;AAAA,EACT;AAAA;AAAA;AAAA,EAKA,SAAe;AACb,SAAK,KAAK,KAAK,EAAE,GAAG,KAAK,KAAK,IAAI,WAAW,MAAM;AACnD,WAAO;AAAA,EACT;AAAA;AAAA,EAGA,SAAe;AACb,SAAK,KAAK,KAAK,EAAE,GAAG,KAAK,KAAK,IAAI,WAAW,MAAM;AACnD,WAAO;AAAA,EACT;AAAA;AAAA,EAGA,aAAmB;AACjB,SAAK,KAAK,KAAK,EAAE,GAAG,KAAK,KAAK,IAAI,eAAe,MAAM;AACvD,WAAO;AAAA,EACT;AAAA;AAAA,EAGA,aAAa,GAAiB;AAC5B,SAAK,KAAK,KAAK,EAAE,GAAG,KAAK,KAAK,IAAI,cAAc,EAAE;AAClD,WAAO;AAAA,EACT;AAAA;AAAA;AAAA,EAKA,IAAI,QAIK;AACP,SAAK,KAAK,MAAM,EAAE,GAAG,KAAK,KAAK,KAAK,GAAG,OAAO;AAC9C,WAAO;AAAA,EACT;AAAA;AAAA;AAAA,EAKA,UAAgB;AACd,SAAK,KAAK,QAAQ,EAAE,GAAG,KAAK,KAAK,OAAO,SAAS,MAAM;AACvD,WAAO;AAAA,EACT;AAAA;AAAA,EAGA,WAAW,OAAqB;AAC9B,SAAK,KAAK,QAAQ,EAAE,GAAG,KAAK,KAAK,OAAO,oBAAoB,MAAM;AAClE,WAAO;AAAA,EACT;AAAA;AAAA,EAGA,aAAa,SAAuB;AAClC,SAAK,KAAK,QAAQ,EAAE,GAAG,KAAK,KAAK,OAAO,QAAQ;AAChD,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAMA,QAAQ,YAA0B;AAChC,SAAK,KAAK,QAAQ,EAAE,GAAG,KAAK,KAAK,OAAO,SAAS,WAAW;AAC5D,WAAO;AAAA,EACT;AAAA;AAAA,EAGA,cAAc,YAA0B;AACtC,SAAK,KAAK,QAAQ,EAAE,GAAG,KAAK,KAAK,OAAO,eAAe,WAAW;AAClE,WAAO;AAAA,EACT;AAAA;AAAA,EAGA,QAAQ,YAA0B;AAChC,SAAK,KAAK,QAAQ,EAAE,GAAG,KAAK,KAAK,OAAO,SAAS,WAAW;AAC5D,WAAO;AAAA,EACT;AAAA;AAAA,EAGA,SAAS,YAA0B;AACjC,SAAK,KAAK,QAAQ,EAAE,GAAG,KAAK,KAAK,OAAO,UAAU,WAAW;AAC7D,WAAO;AAAA,EACT;AAAA;AAAA,EAGA,SAAS,YAA0B;AACjC,SAAK,KAAK,QAAQ,EAAE,GAAG,KAAK,KAAK,OAAO,UAAU,WAAW;AAC7D,WAAO;AAAA,EACT;AAAA;AAAA,EAGA,SAAS,YAA0B;AACjC,SAAK,KAAK,QAAQ,EAAE,GAAG,KAAK,KAAK,OAAO,UAAU,WAAW;AAC7D,WAAO;AAAA,EACT;AAAA;AAAA,EAGA,MAAM,OAAkC;AACtC,SAAK,KAAK,QAAQ,EAAE,GAAG,KAAK,KAAK,OAAO,GAAG,MAAM;AACjD,WAAO;AAAA,EACT;AAAA;AAAA;AAAA,EAKA,QAAc;AACZ,SAAK,KAAK,QAAQ;AAClB,WAAO;AAAA,EACT;AAAA;AAAA;AAAA,EAKA,KACE,QACA,cACM;AACN,SAAK,KAAK,OAAO,EAAE,GAAG,KAAK,KAAK,MAAM,CAAC,MAAM,GAAG,aAAa;AAC7D,WAAO;AAAA,EACT;AAAA;AAAA;AAAA,EAKA,KAAK,KAAa,OAAwB;AACxC,SAAK,KAAK,OAAO,EAAE,GAAG,KAAK,KAAK,MAAM,CAAC,GAAG,GAAG,MAAM;AACnD,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWA,QAAwB;AACtB,UAAM,KAAK,KAAK,KAAK;AACrB,QAAI,CAAC,IAAI;AACP,YAAM,IAAI,MAAM,qBAAqB;AAAA,IACvC;AAEA,UAAM,OAAuB;AAAA,MAC3B;AAAA,MACA,MAAM,KAAK,KAAK,QAAQ,SAAS,EAAE;AAAA,MACnC,UAAU,KAAK,KAAK,YAAY,CAAC;AAAA,MACjC,GAAG,KAAK;AAAA,IACV;AAEA,WAAO;AAAA,EACT;AACF;AAWO,MAAM,OAAO;AAOb,MAAM,IAAI;AAcjB,SAAS,SAAS,KAAqB;AACrC,SAAO,IAAI,QAAQ,SAAS,GAAG,EAAE,QAAQ,SAAS,CAAC,MAAM,EAAE,YAAY,CAAC;AAC1E;","names":[]}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module builtins
|
|
3
|
+
* @description Built-in control types for the Form Plugin
|
|
4
|
+
*
|
|
5
|
+
* ## Overview
|
|
6
|
+
*
|
|
7
|
+
* This module defines the standard control types that are available out of the box:
|
|
8
|
+
* - text: Plain text strings
|
|
9
|
+
* - number: Numeric values (integers or decimals)
|
|
10
|
+
* - email: Email addresses with format validation
|
|
11
|
+
* - boolean: Yes/no, true/false values
|
|
12
|
+
* - select: Choice from predefined options
|
|
13
|
+
* - date: Date values in various formats
|
|
14
|
+
* - file: File uploads (handled specially)
|
|
15
|
+
*
|
|
16
|
+
* ## Why Built-in Types
|
|
17
|
+
*
|
|
18
|
+
* Built-in types provide:
|
|
19
|
+
* 1. **Consistent validation** across all forms - same rules everywhere
|
|
20
|
+
* 2. **Sensible defaults** for common field types - less configuration
|
|
21
|
+
* 3. **LLM extraction hints** optimized for each type - better extraction
|
|
22
|
+
* 4. **Override protection** to prevent accidental shadowing - safety first
|
|
23
|
+
*
|
|
24
|
+
* ## Architecture Decision: ControlType vs TypeHandler
|
|
25
|
+
*
|
|
26
|
+
* We use ControlType (not the legacy TypeHandler) because:
|
|
27
|
+
* - ControlType is the shared interface for all type categories
|
|
28
|
+
* - ControlType supports composite types (subcontrols)
|
|
29
|
+
* - ControlType supports external types (activate/confirm)
|
|
30
|
+
* - TypeHandler is legacy and maintained only for backwards compatibility
|
|
31
|
+
*
|
|
32
|
+
* ## Why These Specific Types
|
|
33
|
+
*
|
|
34
|
+
* | Type | Why Built-in |
|
|
35
|
+
* |------|--------------|
|
|
36
|
+
* | text | Most common field type, catch-all for strings |
|
|
37
|
+
* | number | Second most common, needs special parsing (commas, $) |
|
|
38
|
+
* | email | Critical for communication, has clear format rules |
|
|
39
|
+
* | boolean | Binary choice, many natural language forms (yes/no/true/false) |
|
|
40
|
+
* | select | Constrained choice, validation against options |
|
|
41
|
+
* | date | Complex parsing (many formats), needs normalization |
|
|
42
|
+
* | file | Special handling needed (size, type, storage) |
|
|
43
|
+
*
|
|
44
|
+
* ## Extending
|
|
45
|
+
*
|
|
46
|
+
* Plugins can register custom types via FormService.registerControlType().
|
|
47
|
+
* Built-in types can be overridden with { allowOverride: true } option,
|
|
48
|
+
* but this will log a warning.
|
|
49
|
+
*
|
|
50
|
+
* ## Usage
|
|
51
|
+
*
|
|
52
|
+
* Built-in types are automatically registered when FormService starts.
|
|
53
|
+
* You don't need to call registerBuiltinTypes() manually.
|
|
54
|
+
*
|
|
55
|
+
* @example
|
|
56
|
+
* ```typescript
|
|
57
|
+
* // Check if a type is built-in before overriding
|
|
58
|
+
* if (isBuiltinType('email')) {
|
|
59
|
+
* console.log('Warning: overriding built-in type');
|
|
60
|
+
* formService.registerControlType(myEmailType, { allowOverride: true });
|
|
61
|
+
* }
|
|
62
|
+
* ```
|
|
63
|
+
*/
|
|
64
|
+
import type { ControlType } from "./types";
|
|
65
|
+
/**
|
|
66
|
+
* Array of all built-in control types.
|
|
67
|
+
*
|
|
68
|
+
* These are registered automatically when FormService starts.
|
|
69
|
+
* The order doesn't matter; they're looked up by id.
|
|
70
|
+
*
|
|
71
|
+
* WHY array + map:
|
|
72
|
+
* - Array allows easy iteration for registration
|
|
73
|
+
* - Map provides O(1) lookup for override checks
|
|
74
|
+
* - Both derived from same source ensures consistency
|
|
75
|
+
*/
|
|
76
|
+
export declare const BUILTIN_TYPES: ControlType[];
|
|
77
|
+
/**
|
|
78
|
+
* Map of built-in types by id for quick lookup.
|
|
79
|
+
*
|
|
80
|
+
* WHY Map (not object):
|
|
81
|
+
* - Type-safe keys
|
|
82
|
+
* - O(1) lookup performance
|
|
83
|
+
* - Clear has/get semantics
|
|
84
|
+
*/
|
|
85
|
+
export declare const BUILTIN_TYPE_MAP: Map<string, ControlType>;
|
|
86
|
+
/**
|
|
87
|
+
* Register all built-in types with a FormService instance.
|
|
88
|
+
*
|
|
89
|
+
* This is called automatically during FormService.start().
|
|
90
|
+
* You typically don't need to call this directly.
|
|
91
|
+
*
|
|
92
|
+
* WHY take a function (not FormService):
|
|
93
|
+
* - Avoids circular dependency (builtins.ts ↔ service.ts)
|
|
94
|
+
* - Service passes its registerControlType method
|
|
95
|
+
* - Clean separation of concerns
|
|
96
|
+
*
|
|
97
|
+
* @param registerFn - The FormService.registerControlType method
|
|
98
|
+
*/
|
|
99
|
+
export declare function registerBuiltinTypes(registerFn: (type: ControlType, options?: {
|
|
100
|
+
allowOverride?: boolean;
|
|
101
|
+
}) => void): void;
|
|
102
|
+
/**
|
|
103
|
+
* Get a built-in type by id.
|
|
104
|
+
*
|
|
105
|
+
* This is a convenience for checking if a type is built-in
|
|
106
|
+
* before attempting to override it.
|
|
107
|
+
*
|
|
108
|
+
* WHY this exists:
|
|
109
|
+
* - Plugins may want to extend a built-in type
|
|
110
|
+
* - Checking before override allows informed decisions
|
|
111
|
+
* - Avoids accidental shadowing
|
|
112
|
+
*
|
|
113
|
+
* @param id - The type id to look up
|
|
114
|
+
* @returns The ControlType if found, undefined otherwise
|
|
115
|
+
*/
|
|
116
|
+
export declare function getBuiltinType(id: string): ControlType | undefined;
|
|
117
|
+
/**
|
|
118
|
+
* Check if a type id is a built-in type.
|
|
119
|
+
*
|
|
120
|
+
* WHY boolean convenience:
|
|
121
|
+
* - Most callers just need yes/no answer
|
|
122
|
+
* - Cleaner than checking getBuiltinType() !== undefined
|
|
123
|
+
*
|
|
124
|
+
* @param id - The type id to check
|
|
125
|
+
* @returns true if the type is built-in
|
|
126
|
+
*/
|
|
127
|
+
export declare function isBuiltinType(id: string): boolean;
|
|
128
|
+
//# sourceMappingURL=builtins.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"builtins.d.ts","sourceRoot":"","sources":["../src/builtins.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8DG;AAGH,OAAO,KAAK,EAAE,WAAW,EAAiC,MAAM,SAAS,CAAC;AA+a1E;;;;;;;;;;GAUG;AACH,eAAO,MAAM,aAAa,EAAE,WAAW,EAQtC,CAAC;AAEF;;;;;;;GAOG;AACH,eAAO,MAAM,gBAAgB,EAAE,GAAG,CAAC,MAAM,EAAE,WAAW,CAErD,CAAC;AAEF;;;;;;;;;;;;GAYG;AACH,wBAAgB,oBAAoB,CAClC,UAAU,EAAE,CACV,IAAI,EAAE,WAAW,EACjB,OAAO,CAAC,EAAE;IAAE,aAAa,CAAC,EAAE,OAAO,CAAA;CAAE,KAClC,IAAI,GACR,IAAI,CAIN;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,cAAc,CAAC,EAAE,EAAE,MAAM,GAAG,WAAW,GAAG,SAAS,CAElE;AAED;;;;;;;;;GASG;AACH,wBAAgB,aAAa,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAEjD"}
|