@tooluminati/forms 0.1.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/LICENSE +21 -0
- package/dist/create-form-tools.d.ts +6 -0
- package/dist/create-form-tools.d.ts.map +1 -0
- package/dist/create-form-tools.js +96 -0
- package/dist/create-form-tools.test.d.ts +2 -0
- package/dist/create-form-tools.test.d.ts.map +1 -0
- package/dist/create-form-tools.test.js +45 -0
- package/dist/errors.d.ts +7 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +9 -0
- package/dist/form-tool-definition.d.ts +11 -0
- package/dist/form-tool-definition.d.ts.map +1 -0
- package/dist/form-tool-definition.js +1 -0
- package/dist/formik.d.ts +16 -0
- package/dist/formik.d.ts.map +1 -0
- package/dist/formik.js +35 -0
- package/dist/index.cjs +413 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +375 -0
- package/dist/infer-schema.d.ts +3 -0
- package/dist/infer-schema.d.ts.map +1 -0
- package/dist/infer-schema.js +42 -0
- package/dist/infer-schema.test.d.ts +2 -0
- package/dist/infer-schema.test.d.ts.map +1 -0
- package/dist/infer-schema.test.js +27 -0
- package/dist/native-form.d.ts +8 -0
- package/dist/native-form.d.ts.map +1 -0
- package/dist/native-form.js +41 -0
- package/dist/react-hook-form.d.ts +21 -0
- package/dist/react-hook-form.d.ts.map +1 -0
- package/dist/react-hook-form.js +53 -0
- package/dist/react-hook-form.test.d.ts +2 -0
- package/dist/react-hook-form.test.d.ts.map +1 -0
- package/dist/react-hook-form.test.js +28 -0
- package/dist/tanstack-form.d.ts +17 -0
- package/dist/tanstack-form.d.ts.map +1 -0
- package/dist/tanstack-form.js +33 -0
- package/dist/types.d.ts +41 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +1 -0
- package/dist/useWebMcpFormTool.d.ts +5 -0
- package/dist/useWebMcpFormTool.d.ts.map +1 -0
- package/dist/useWebMcpFormTool.js +14 -0
- package/package.json +54 -0
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,413 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
FormValidationError: () => FormValidationError,
|
|
24
|
+
createFormSubmitTool: () => createFormSubmitTool,
|
|
25
|
+
createFormValidationSummaryTool: () => createFormValidationSummaryTool,
|
|
26
|
+
createWebMcpFormTools: () => createWebMcpFormTools,
|
|
27
|
+
flattenReactHookFormErrors: () => flattenReactHookFormErrors,
|
|
28
|
+
formDataToObject: () => formDataToObject,
|
|
29
|
+
inferSchemaFromValue: () => inferSchemaFromValue,
|
|
30
|
+
useFormikWebMcpTool: () => useFormikWebMcpTool,
|
|
31
|
+
useNativeFormWebMcpTool: () => useNativeFormWebMcpTool,
|
|
32
|
+
useReactHookFormWebMcpTool: () => useReactHookFormWebMcpTool,
|
|
33
|
+
useTanStackFormWebMcpTool: () => useTanStackFormWebMcpTool,
|
|
34
|
+
useWebMcpFormTool: () => useWebMcpFormTool
|
|
35
|
+
});
|
|
36
|
+
module.exports = __toCommonJS(index_exports);
|
|
37
|
+
|
|
38
|
+
// src/errors.ts
|
|
39
|
+
var FormValidationError = class extends Error {
|
|
40
|
+
errors;
|
|
41
|
+
constructor(message, errors) {
|
|
42
|
+
super(message);
|
|
43
|
+
this.name = "FormValidationError";
|
|
44
|
+
this.errors = errors;
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
// src/useWebMcpFormTool.ts
|
|
49
|
+
var import_react = require("react");
|
|
50
|
+
var import_react2 = require("@tooluminati/react");
|
|
51
|
+
|
|
52
|
+
// src/infer-schema.ts
|
|
53
|
+
function inferSchemaFromValue(value) {
|
|
54
|
+
if (typeof value === "string") {
|
|
55
|
+
return { type: "string" };
|
|
56
|
+
}
|
|
57
|
+
if (typeof value === "number") {
|
|
58
|
+
return { type: "number" };
|
|
59
|
+
}
|
|
60
|
+
if (typeof value === "boolean") {
|
|
61
|
+
return { type: "boolean" };
|
|
62
|
+
}
|
|
63
|
+
if (value === null || value === void 0) {
|
|
64
|
+
return void 0;
|
|
65
|
+
}
|
|
66
|
+
if (Array.isArray(value)) {
|
|
67
|
+
if (value.length === 0) {
|
|
68
|
+
return void 0;
|
|
69
|
+
}
|
|
70
|
+
const items = inferSchemaFromValue(value[0]);
|
|
71
|
+
return items ? { type: "array", items } : void 0;
|
|
72
|
+
}
|
|
73
|
+
if (typeof value === "object") {
|
|
74
|
+
const properties = {};
|
|
75
|
+
const required = [];
|
|
76
|
+
for (const [key, childValue] of Object.entries(
|
|
77
|
+
value
|
|
78
|
+
)) {
|
|
79
|
+
const childSchema = inferSchemaFromValue(childValue);
|
|
80
|
+
if (!childSchema) {
|
|
81
|
+
return void 0;
|
|
82
|
+
}
|
|
83
|
+
properties[key] = childSchema;
|
|
84
|
+
if (childValue !== void 0) {
|
|
85
|
+
required.push(key);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
return {
|
|
89
|
+
type: "object",
|
|
90
|
+
properties,
|
|
91
|
+
required,
|
|
92
|
+
additionalProperties: false
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
return void 0;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// src/create-form-tools.ts
|
|
99
|
+
function formatFormErrors(errors, message) {
|
|
100
|
+
const lines = errors.map(
|
|
101
|
+
(error) => `${error.path ? `${error.path}: ` : ""}${error.message || error.kind}`
|
|
102
|
+
);
|
|
103
|
+
return [message, ...lines].filter(Boolean).join("\n");
|
|
104
|
+
}
|
|
105
|
+
function applyRedactResult(result, redactResult) {
|
|
106
|
+
if (!redactResult) {
|
|
107
|
+
return result;
|
|
108
|
+
}
|
|
109
|
+
return redactResult(result);
|
|
110
|
+
}
|
|
111
|
+
function createFormValidationSummaryTool(options) {
|
|
112
|
+
const schema = options.schema ?? inferSchemaFromValue(options.getValues());
|
|
113
|
+
return {
|
|
114
|
+
name: `${options.name}_validation_summary`,
|
|
115
|
+
description: "Returns form schema, validation messages, and submitting/validating state without submitting.",
|
|
116
|
+
inputSchema: {
|
|
117
|
+
type: "object",
|
|
118
|
+
properties: {},
|
|
119
|
+
additionalProperties: false
|
|
120
|
+
},
|
|
121
|
+
annotations: { readOnlyHint: true },
|
|
122
|
+
execute: () => {
|
|
123
|
+
const summary = {
|
|
124
|
+
name: options.name,
|
|
125
|
+
errors: options.getErrors?.() ?? [],
|
|
126
|
+
schema,
|
|
127
|
+
...options.getSummary?.()
|
|
128
|
+
};
|
|
129
|
+
if (options.redactValues) {
|
|
130
|
+
return {
|
|
131
|
+
...summary,
|
|
132
|
+
values: options.redactValues(options.getValues())
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
return summary;
|
|
136
|
+
}
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
function createFormSubmitTool(options) {
|
|
140
|
+
const schema = options.schema ?? inferSchemaFromValue(options.getValues());
|
|
141
|
+
if (!schema) {
|
|
142
|
+
throw new Error(
|
|
143
|
+
`Could not infer WebMCP schema for form "${options.name}". Provide an explicit schema or use concrete non-null defaults.`
|
|
144
|
+
);
|
|
145
|
+
}
|
|
146
|
+
return {
|
|
147
|
+
name: options.name,
|
|
148
|
+
description: options.description,
|
|
149
|
+
inputSchema: schema,
|
|
150
|
+
annotations: {
|
|
151
|
+
readOnlyHint: false,
|
|
152
|
+
...options.annotations
|
|
153
|
+
},
|
|
154
|
+
validateArgs: options.validateArgs,
|
|
155
|
+
execute: async (rawArgs) => {
|
|
156
|
+
const values = options.validateArgs ? options.validateArgs(rawArgs) : rawArgs;
|
|
157
|
+
await options.setValues(values);
|
|
158
|
+
const result = await options.submit();
|
|
159
|
+
if (result.success) {
|
|
160
|
+
let output = result.message ?? "Form submitted successfully.";
|
|
161
|
+
if (result.data !== void 0) {
|
|
162
|
+
output = {
|
|
163
|
+
message: output,
|
|
164
|
+
data: options.redactValues ? options.redactValues(result.data) : result.data
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
return applyRedactResult(output, options.redactResult);
|
|
168
|
+
}
|
|
169
|
+
const errors = result.errors ?? options.getErrors?.() ?? [];
|
|
170
|
+
return applyRedactResult(
|
|
171
|
+
{
|
|
172
|
+
content: [
|
|
173
|
+
{
|
|
174
|
+
type: "text",
|
|
175
|
+
text: `Form submission failed:
|
|
176
|
+
${formatFormErrors(
|
|
177
|
+
errors,
|
|
178
|
+
result.message
|
|
179
|
+
)}`
|
|
180
|
+
}
|
|
181
|
+
],
|
|
182
|
+
isError: true
|
|
183
|
+
},
|
|
184
|
+
options.redactResult
|
|
185
|
+
);
|
|
186
|
+
}
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
function createWebMcpFormTools(options) {
|
|
190
|
+
const includeValidationSummary = options.includeValidationSummary ?? true;
|
|
191
|
+
const submitTool = createFormSubmitTool(options);
|
|
192
|
+
if (includeValidationSummary) {
|
|
193
|
+
return [
|
|
194
|
+
submitTool,
|
|
195
|
+
createFormValidationSummaryTool(options)
|
|
196
|
+
];
|
|
197
|
+
}
|
|
198
|
+
return [submitTool];
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// src/useWebMcpFormTool.ts
|
|
202
|
+
function useWebMcpFormTool(options, deps = []) {
|
|
203
|
+
const schema = (0, import_react.useMemo)(
|
|
204
|
+
() => options.schema ?? inferSchemaFromValue(options.getValues()),
|
|
205
|
+
[options]
|
|
206
|
+
);
|
|
207
|
+
if (!schema) {
|
|
208
|
+
throw new Error(
|
|
209
|
+
`Could not infer WebMCP schema for form "${options.name}". Provide an explicit schema or use concrete non-null defaults.`
|
|
210
|
+
);
|
|
211
|
+
}
|
|
212
|
+
const tools = (0, import_react.useMemo)(
|
|
213
|
+
() => createWebMcpFormTools(options),
|
|
214
|
+
[options]
|
|
215
|
+
);
|
|
216
|
+
(0, import_react2.useWebMcpTools)(tools, deps, { source: "form" });
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// src/formik.ts
|
|
220
|
+
function flattenErrors(errors, prefix = "") {
|
|
221
|
+
if (!errors || typeof errors !== "object") {
|
|
222
|
+
return [];
|
|
223
|
+
}
|
|
224
|
+
return Object.entries(errors).flatMap(
|
|
225
|
+
([key, value]) => {
|
|
226
|
+
const path = prefix ? `${prefix}.${key}` : key;
|
|
227
|
+
if (typeof value === "string") {
|
|
228
|
+
return [{ path, message: value }];
|
|
229
|
+
}
|
|
230
|
+
return flattenErrors(value, path);
|
|
231
|
+
}
|
|
232
|
+
);
|
|
233
|
+
}
|
|
234
|
+
function useFormikWebMcpTool({
|
|
235
|
+
formik,
|
|
236
|
+
...options
|
|
237
|
+
}) {
|
|
238
|
+
useWebMcpFormTool({
|
|
239
|
+
...options,
|
|
240
|
+
getValues: () => formik.values,
|
|
241
|
+
setValues: (values) => formik.setValues(values),
|
|
242
|
+
getErrors: () => flattenErrors(formik.errors),
|
|
243
|
+
getSummary: () => ({
|
|
244
|
+
submitting: formik.isSubmitting,
|
|
245
|
+
validating: formik.isValidating,
|
|
246
|
+
touched: Boolean(formik.touched)
|
|
247
|
+
}),
|
|
248
|
+
submit: async () => {
|
|
249
|
+
const errors = await formik.validateForm();
|
|
250
|
+
const flattened = flattenErrors(errors);
|
|
251
|
+
if (flattened.length > 0) {
|
|
252
|
+
return { success: false, errors: flattened };
|
|
253
|
+
}
|
|
254
|
+
await formik.submitForm();
|
|
255
|
+
return { success: true };
|
|
256
|
+
}
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
// src/native-form.ts
|
|
261
|
+
function formDataToObject(form) {
|
|
262
|
+
return Object.fromEntries(new FormData(form).entries());
|
|
263
|
+
}
|
|
264
|
+
function useNativeFormWebMcpTool(options) {
|
|
265
|
+
if (!options.schema) {
|
|
266
|
+
throw new Error("Native form WebMCP tools require an explicit schema.");
|
|
267
|
+
}
|
|
268
|
+
useWebMcpFormTool({
|
|
269
|
+
...options,
|
|
270
|
+
getValues: () => {
|
|
271
|
+
const form = options.formRef.current;
|
|
272
|
+
return form ? formDataToObject(form) : {};
|
|
273
|
+
},
|
|
274
|
+
setValues: (values) => {
|
|
275
|
+
const form = options.formRef.current;
|
|
276
|
+
if (!form) {
|
|
277
|
+
return;
|
|
278
|
+
}
|
|
279
|
+
for (const [name, value] of Object.entries(values)) {
|
|
280
|
+
const field = form.elements.namedItem(name);
|
|
281
|
+
if (field && "value" in field) {
|
|
282
|
+
field.value = String(value ?? "");
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
},
|
|
286
|
+
getErrors: () => [],
|
|
287
|
+
submit: () => {
|
|
288
|
+
const form = options.formRef.current;
|
|
289
|
+
if (!form) {
|
|
290
|
+
return { success: false, message: "Form is not mounted." };
|
|
291
|
+
}
|
|
292
|
+
if (!form.checkValidity()) {
|
|
293
|
+
form.reportValidity();
|
|
294
|
+
return { success: false, message: "Native form validation failed." };
|
|
295
|
+
}
|
|
296
|
+
form.requestSubmit();
|
|
297
|
+
return { success: true, message: "Native form submitted." };
|
|
298
|
+
}
|
|
299
|
+
});
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
// src/react-hook-form.ts
|
|
303
|
+
function flattenReactHookFormErrors(errors, prefix = "") {
|
|
304
|
+
if (!errors || typeof errors !== "object") {
|
|
305
|
+
return [];
|
|
306
|
+
}
|
|
307
|
+
return Object.entries(errors).flatMap(
|
|
308
|
+
([key, value]) => {
|
|
309
|
+
const path = prefix ? `${prefix}.${key}` : key;
|
|
310
|
+
if (value?.message || value?.type) {
|
|
311
|
+
return [
|
|
312
|
+
{
|
|
313
|
+
path,
|
|
314
|
+
message: String(value.message ?? value.type),
|
|
315
|
+
kind: value.type ? String(value.type) : void 0
|
|
316
|
+
}
|
|
317
|
+
];
|
|
318
|
+
}
|
|
319
|
+
return flattenReactHookFormErrors(value, path);
|
|
320
|
+
}
|
|
321
|
+
);
|
|
322
|
+
}
|
|
323
|
+
function useReactHookFormWebMcpTool({
|
|
324
|
+
form,
|
|
325
|
+
onValidSubmit,
|
|
326
|
+
...options
|
|
327
|
+
}) {
|
|
328
|
+
useWebMcpFormTool({
|
|
329
|
+
...options,
|
|
330
|
+
getValues: () => form.getValues(),
|
|
331
|
+
setValues: async (values) => {
|
|
332
|
+
form.reset(values);
|
|
333
|
+
await form.trigger();
|
|
334
|
+
},
|
|
335
|
+
getErrors: () => flattenReactHookFormErrors(form.formState.errors),
|
|
336
|
+
getSummary: () => ({
|
|
337
|
+
submitting: form.formState.isSubmitting,
|
|
338
|
+
validating: form.formState.isValidating,
|
|
339
|
+
dirty: form.formState.isDirty,
|
|
340
|
+
touched: Boolean(form.formState.touchedFields)
|
|
341
|
+
}),
|
|
342
|
+
submit: async () => {
|
|
343
|
+
let valid = false;
|
|
344
|
+
let data;
|
|
345
|
+
await form.handleSubmit(
|
|
346
|
+
async (values) => {
|
|
347
|
+
valid = true;
|
|
348
|
+
data = await onValidSubmit(values);
|
|
349
|
+
},
|
|
350
|
+
async () => {
|
|
351
|
+
valid = false;
|
|
352
|
+
}
|
|
353
|
+
)();
|
|
354
|
+
if (valid) {
|
|
355
|
+
return { success: true, data };
|
|
356
|
+
}
|
|
357
|
+
return {
|
|
358
|
+
success: false,
|
|
359
|
+
errors: flattenReactHookFormErrors(form.formState.errors)
|
|
360
|
+
};
|
|
361
|
+
}
|
|
362
|
+
});
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
// src/tanstack-form.ts
|
|
366
|
+
function flattenTanStackErrors(errors = []) {
|
|
367
|
+
return errors.map((error, index) => ({
|
|
368
|
+
path: typeof error === "object" && error && "path" in error ? String(error.path) : String(index),
|
|
369
|
+
message: typeof error === "object" && error && "message" in error ? String(error.message) : String(error)
|
|
370
|
+
}));
|
|
371
|
+
}
|
|
372
|
+
function useTanStackFormWebMcpTool({
|
|
373
|
+
form,
|
|
374
|
+
...options
|
|
375
|
+
}) {
|
|
376
|
+
useWebMcpFormTool({
|
|
377
|
+
...options,
|
|
378
|
+
getValues: () => form.state.values,
|
|
379
|
+
setValues: (values) => {
|
|
380
|
+
for (const [field, value] of Object.entries(
|
|
381
|
+
values
|
|
382
|
+
)) {
|
|
383
|
+
form.setFieldValue?.(field, value);
|
|
384
|
+
}
|
|
385
|
+
},
|
|
386
|
+
getErrors: () => flattenTanStackErrors(form.state.errors),
|
|
387
|
+
getSummary: () => ({
|
|
388
|
+
submitting: form.state.isSubmitting,
|
|
389
|
+
validating: form.state.isValidating,
|
|
390
|
+
dirty: form.state.isDirty
|
|
391
|
+
}),
|
|
392
|
+
submit: async () => {
|
|
393
|
+
await form.handleSubmit();
|
|
394
|
+
const errors = flattenTanStackErrors(form.state.errors);
|
|
395
|
+
return errors.length > 0 ? { success: false, errors } : { success: true };
|
|
396
|
+
}
|
|
397
|
+
});
|
|
398
|
+
}
|
|
399
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
400
|
+
0 && (module.exports = {
|
|
401
|
+
FormValidationError,
|
|
402
|
+
createFormSubmitTool,
|
|
403
|
+
createFormValidationSummaryTool,
|
|
404
|
+
createWebMcpFormTools,
|
|
405
|
+
flattenReactHookFormErrors,
|
|
406
|
+
formDataToObject,
|
|
407
|
+
inferSchemaFromValue,
|
|
408
|
+
useFormikWebMcpTool,
|
|
409
|
+
useNativeFormWebMcpTool,
|
|
410
|
+
useReactHookFormWebMcpTool,
|
|
411
|
+
useTanStackFormWebMcpTool,
|
|
412
|
+
useWebMcpFormTool
|
|
413
|
+
});
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @experimental Tooluminati form adapters — public APIs may change.
|
|
3
|
+
*/
|
|
4
|
+
export * from './errors';
|
|
5
|
+
export * from './formik';
|
|
6
|
+
export * from './infer-schema';
|
|
7
|
+
export * from './native-form';
|
|
8
|
+
export * from './react-hook-form';
|
|
9
|
+
export * from './tanstack-form';
|
|
10
|
+
export * from './types';
|
|
11
|
+
export * from './create-form-tools';
|
|
12
|
+
export * from './useWebMcpFormTool';
|
|
13
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,cAAc,UAAU,CAAC;AACzB,cAAc,UAAU,CAAC;AACzB,cAAc,gBAAgB,CAAC;AAC/B,cAAc,eAAe,CAAC;AAC9B,cAAc,mBAAmB,CAAC;AAClC,cAAc,iBAAiB,CAAC;AAChC,cAAc,SAAS,CAAC;AACxB,cAAc,qBAAqB,CAAC;AACpC,cAAc,qBAAqB,CAAC"}
|