@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.
Files changed (45) hide show
  1. package/LICENSE +21 -0
  2. package/dist/create-form-tools.d.ts +6 -0
  3. package/dist/create-form-tools.d.ts.map +1 -0
  4. package/dist/create-form-tools.js +96 -0
  5. package/dist/create-form-tools.test.d.ts +2 -0
  6. package/dist/create-form-tools.test.d.ts.map +1 -0
  7. package/dist/create-form-tools.test.js +45 -0
  8. package/dist/errors.d.ts +7 -0
  9. package/dist/errors.d.ts.map +1 -0
  10. package/dist/errors.js +9 -0
  11. package/dist/form-tool-definition.d.ts +11 -0
  12. package/dist/form-tool-definition.d.ts.map +1 -0
  13. package/dist/form-tool-definition.js +1 -0
  14. package/dist/formik.d.ts +16 -0
  15. package/dist/formik.d.ts.map +1 -0
  16. package/dist/formik.js +35 -0
  17. package/dist/index.cjs +413 -0
  18. package/dist/index.d.ts +13 -0
  19. package/dist/index.d.ts.map +1 -0
  20. package/dist/index.js +375 -0
  21. package/dist/infer-schema.d.ts +3 -0
  22. package/dist/infer-schema.d.ts.map +1 -0
  23. package/dist/infer-schema.js +42 -0
  24. package/dist/infer-schema.test.d.ts +2 -0
  25. package/dist/infer-schema.test.d.ts.map +1 -0
  26. package/dist/infer-schema.test.js +27 -0
  27. package/dist/native-form.d.ts +8 -0
  28. package/dist/native-form.d.ts.map +1 -0
  29. package/dist/native-form.js +41 -0
  30. package/dist/react-hook-form.d.ts +21 -0
  31. package/dist/react-hook-form.d.ts.map +1 -0
  32. package/dist/react-hook-form.js +53 -0
  33. package/dist/react-hook-form.test.d.ts +2 -0
  34. package/dist/react-hook-form.test.d.ts.map +1 -0
  35. package/dist/react-hook-form.test.js +28 -0
  36. package/dist/tanstack-form.d.ts +17 -0
  37. package/dist/tanstack-form.d.ts.map +1 -0
  38. package/dist/tanstack-form.js +33 -0
  39. package/dist/types.d.ts +41 -0
  40. package/dist/types.d.ts.map +1 -0
  41. package/dist/types.js +1 -0
  42. package/dist/useWebMcpFormTool.d.ts +5 -0
  43. package/dist/useWebMcpFormTool.d.ts.map +1 -0
  44. package/dist/useWebMcpFormTool.js +14 -0
  45. 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
+ });
@@ -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"}