@acp-autoform/core 3.0.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 ADDED
@@ -0,0 +1,13 @@
1
+ # AutoForm Core
2
+
3
+ This package provides the core functionality for AutoForm. You'll most likely use UI-library specific packages like `@acp-autoform/mui` instead of this package - unless you want to create your own renderer.
4
+
5
+ The core is agnostic to both the schema and the UI library used to render the form.
6
+
7
+ ## Installation
8
+
9
+ ```bash
10
+ npm install @acp-autoform/core
11
+ ```
12
+
13
+ For more information on how to use this package, see the [AutoForm documentation](https://autoform.vantezzen.io/docs/react/getting-started).
@@ -0,0 +1,117 @@
1
+ type Renderable<AdditionalRenderable = null> = string | number | boolean | null | undefined | AdditionalRenderable;
2
+ interface FieldConfig<AdditionalRenderable = null, FieldTypes = string, FieldWrapper = any, CustomData = Record<string, any>> {
3
+ /** Help text or supporting content rendered near the field. */
4
+ description?: Renderable<AdditionalRenderable>;
5
+ /** Props forwarded to the rendered input component. */
6
+ inputProps?: Record<string, any>;
7
+ /** Custom label content. Falls back to the schema field name when omitted. */
8
+ label?: Renderable<AdditionalRenderable>;
9
+ /** Override for the inferred field renderer key. */
10
+ fieldType?: FieldTypes;
11
+ /** Sort order for generated fields. Lower values render first. */
12
+ order?: number;
13
+ /** Wrapper component override for this field only. */
14
+ fieldWrapper?: FieldWrapper;
15
+ /** Integration or app-specific metadata for custom components. */
16
+ customData?: CustomData;
17
+ }
18
+ interface ParsedField<AdditionalRenderable = null, FieldTypes = string> {
19
+ /** Field key from the schema object. */
20
+ key: string;
21
+ /** Parsed field type used to select a form component. */
22
+ type: string;
23
+ /** Whether the schema marks this field as required. */
24
+ required: boolean;
25
+ /** Default value inferred from the schema provider, when available. */
26
+ default?: any;
27
+ /** Description inferred from schema metadata, when available. */
28
+ description?: Renderable;
29
+ /** AutoForm field configuration attached through the schema provider. */
30
+ fieldConfig?: FieldConfig<AdditionalRenderable, FieldTypes>;
31
+ /** Enum/select choices as value-label pairs. */
32
+ options?: [string, string][];
33
+ /** Child fields for object fields, or the item schema for array fields. */
34
+ schema?: ParsedField<AdditionalRenderable, FieldTypes>[];
35
+ }
36
+ interface ParsedSchema<AdditionalRenderable = null, FieldTypes = string> {
37
+ fields: ParsedField<AdditionalRenderable, FieldTypes>[];
38
+ }
39
+ type SuccessfulSchemaValidation = {
40
+ success: true;
41
+ data: any;
42
+ };
43
+ type SchemaValidationError = {
44
+ path: (string | number)[];
45
+ message: string;
46
+ };
47
+ type ErrorSchemaValidation = {
48
+ success: false;
49
+ errors: SchemaValidationError[];
50
+ };
51
+ type SchemaValidation = SuccessfulSchemaValidation | ErrorSchemaValidation;
52
+ type SchemaType = "zod" | "yup" | "joi";
53
+
54
+ /**
55
+ * Get the best label to use for a field.
56
+ * This will use user-provided labels, descriptions from the schema, or the field key.
57
+ *
58
+ * @param field Parsed field
59
+ * @returns Label for the field
60
+ */
61
+ declare function getLabel(field: ParsedField): string | number | true;
62
+
63
+ /**
64
+ * Schema provider interface
65
+ *
66
+ * Schema providers are responsible for converting schemas from different libraries into
67
+ * a format that AutoForm can use.
68
+ * They also handle validating the form value and returning error messages in a consistent format.
69
+ */
70
+ interface SchemaProvider<T = any> {
71
+ /**
72
+ * Parse the schema into a format that AutoForm can use.
73
+ */
74
+ parseSchema(): ParsedSchema;
75
+ /**
76
+ * Validate the form values against the schema.
77
+ * This returns either the transformed result (e.g. with date coersion) or an array of errors.
78
+ *
79
+ * @param values - Form values to validate
80
+ */
81
+ validateSchema(_values: T): SchemaValidation;
82
+ /**
83
+ * Get the default values for the fields.
84
+ * This is used to populate the form with initial values.
85
+ *
86
+ * @returns Default values for the form
87
+ */
88
+ getDefaultValues(): Record<string, any>;
89
+ /**
90
+ * Schema library used by consumers that need library-specific integrations.
91
+ */
92
+ schemaType?: SchemaType;
93
+ /**
94
+ * Get the original schema instance.
95
+ */
96
+ getSchema?(): unknown;
97
+ }
98
+
99
+ declare function parseSchema(schemaProvider: SchemaProvider): ParsedSchema;
100
+ declare function validateSchema(schemaProvider: SchemaProvider, values: any): SchemaValidation;
101
+ declare function getDefaultValues(schemaProvider: SchemaProvider): Record<string, any>;
102
+ /**
103
+ * Recursively remove empty values from an object (null, undefined, '', [], {})
104
+ * Used in AutoForm before calling the resolver to prevent empty values from being validated.
105
+ * This is necessary because zod's optional() allows undefined, but form fields default to
106
+ * empty strings, arrays, or objects even when untouched. These defaults bypass the optional
107
+ * check and cause validation issues in react-hook-form.
108
+ */
109
+ declare function removeEmptyValues<T extends Record<string, any>>(values: T): Partial<T>;
110
+ declare function replaceEmptyValue<T extends Record<string, any>>(values: T): T;
111
+ /**
112
+ * Sort the fields by order.
113
+ * If no order is set, the field will be sorted based on the order in the schema.
114
+ */
115
+ declare function sortFieldsByOrder(fields: ParsedField[] | undefined): ParsedField[];
116
+
117
+ export { type ErrorSchemaValidation, type FieldConfig, type ParsedField, type ParsedSchema, type Renderable, type SchemaProvider, type SchemaType, type SchemaValidation, type SchemaValidationError, type SuccessfulSchemaValidation, getDefaultValues, getLabel, parseSchema, removeEmptyValues, replaceEmptyValue, sortFieldsByOrder, validateSchema };
@@ -0,0 +1,117 @@
1
+ type Renderable<AdditionalRenderable = null> = string | number | boolean | null | undefined | AdditionalRenderable;
2
+ interface FieldConfig<AdditionalRenderable = null, FieldTypes = string, FieldWrapper = any, CustomData = Record<string, any>> {
3
+ /** Help text or supporting content rendered near the field. */
4
+ description?: Renderable<AdditionalRenderable>;
5
+ /** Props forwarded to the rendered input component. */
6
+ inputProps?: Record<string, any>;
7
+ /** Custom label content. Falls back to the schema field name when omitted. */
8
+ label?: Renderable<AdditionalRenderable>;
9
+ /** Override for the inferred field renderer key. */
10
+ fieldType?: FieldTypes;
11
+ /** Sort order for generated fields. Lower values render first. */
12
+ order?: number;
13
+ /** Wrapper component override for this field only. */
14
+ fieldWrapper?: FieldWrapper;
15
+ /** Integration or app-specific metadata for custom components. */
16
+ customData?: CustomData;
17
+ }
18
+ interface ParsedField<AdditionalRenderable = null, FieldTypes = string> {
19
+ /** Field key from the schema object. */
20
+ key: string;
21
+ /** Parsed field type used to select a form component. */
22
+ type: string;
23
+ /** Whether the schema marks this field as required. */
24
+ required: boolean;
25
+ /** Default value inferred from the schema provider, when available. */
26
+ default?: any;
27
+ /** Description inferred from schema metadata, when available. */
28
+ description?: Renderable;
29
+ /** AutoForm field configuration attached through the schema provider. */
30
+ fieldConfig?: FieldConfig<AdditionalRenderable, FieldTypes>;
31
+ /** Enum/select choices as value-label pairs. */
32
+ options?: [string, string][];
33
+ /** Child fields for object fields, or the item schema for array fields. */
34
+ schema?: ParsedField<AdditionalRenderable, FieldTypes>[];
35
+ }
36
+ interface ParsedSchema<AdditionalRenderable = null, FieldTypes = string> {
37
+ fields: ParsedField<AdditionalRenderable, FieldTypes>[];
38
+ }
39
+ type SuccessfulSchemaValidation = {
40
+ success: true;
41
+ data: any;
42
+ };
43
+ type SchemaValidationError = {
44
+ path: (string | number)[];
45
+ message: string;
46
+ };
47
+ type ErrorSchemaValidation = {
48
+ success: false;
49
+ errors: SchemaValidationError[];
50
+ };
51
+ type SchemaValidation = SuccessfulSchemaValidation | ErrorSchemaValidation;
52
+ type SchemaType = "zod" | "yup" | "joi";
53
+
54
+ /**
55
+ * Get the best label to use for a field.
56
+ * This will use user-provided labels, descriptions from the schema, or the field key.
57
+ *
58
+ * @param field Parsed field
59
+ * @returns Label for the field
60
+ */
61
+ declare function getLabel(field: ParsedField): string | number | true;
62
+
63
+ /**
64
+ * Schema provider interface
65
+ *
66
+ * Schema providers are responsible for converting schemas from different libraries into
67
+ * a format that AutoForm can use.
68
+ * They also handle validating the form value and returning error messages in a consistent format.
69
+ */
70
+ interface SchemaProvider<T = any> {
71
+ /**
72
+ * Parse the schema into a format that AutoForm can use.
73
+ */
74
+ parseSchema(): ParsedSchema;
75
+ /**
76
+ * Validate the form values against the schema.
77
+ * This returns either the transformed result (e.g. with date coersion) or an array of errors.
78
+ *
79
+ * @param values - Form values to validate
80
+ */
81
+ validateSchema(_values: T): SchemaValidation;
82
+ /**
83
+ * Get the default values for the fields.
84
+ * This is used to populate the form with initial values.
85
+ *
86
+ * @returns Default values for the form
87
+ */
88
+ getDefaultValues(): Record<string, any>;
89
+ /**
90
+ * Schema library used by consumers that need library-specific integrations.
91
+ */
92
+ schemaType?: SchemaType;
93
+ /**
94
+ * Get the original schema instance.
95
+ */
96
+ getSchema?(): unknown;
97
+ }
98
+
99
+ declare function parseSchema(schemaProvider: SchemaProvider): ParsedSchema;
100
+ declare function validateSchema(schemaProvider: SchemaProvider, values: any): SchemaValidation;
101
+ declare function getDefaultValues(schemaProvider: SchemaProvider): Record<string, any>;
102
+ /**
103
+ * Recursively remove empty values from an object (null, undefined, '', [], {})
104
+ * Used in AutoForm before calling the resolver to prevent empty values from being validated.
105
+ * This is necessary because zod's optional() allows undefined, but form fields default to
106
+ * empty strings, arrays, or objects even when untouched. These defaults bypass the optional
107
+ * check and cause validation issues in react-hook-form.
108
+ */
109
+ declare function removeEmptyValues<T extends Record<string, any>>(values: T): Partial<T>;
110
+ declare function replaceEmptyValue<T extends Record<string, any>>(values: T): T;
111
+ /**
112
+ * Sort the fields by order.
113
+ * If no order is set, the field will be sorted based on the order in the schema.
114
+ */
115
+ declare function sortFieldsByOrder(fields: ParsedField[] | undefined): ParsedField[];
116
+
117
+ export { type ErrorSchemaValidation, type FieldConfig, type ParsedField, type ParsedSchema, type Renderable, type SchemaProvider, type SchemaType, type SchemaValidation, type SchemaValidationError, type SuccessfulSchemaValidation, getDefaultValues, getLabel, parseSchema, removeEmptyValues, replaceEmptyValue, sortFieldsByOrder, validateSchema };
package/dist/index.js ADDED
@@ -0,0 +1,137 @@
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
+ getDefaultValues: () => getDefaultValues,
24
+ getLabel: () => getLabel,
25
+ parseSchema: () => parseSchema,
26
+ removeEmptyValues: () => removeEmptyValues,
27
+ replaceEmptyValue: () => replaceEmptyValue,
28
+ sortFieldsByOrder: () => sortFieldsByOrder,
29
+ validateSchema: () => validateSchema
30
+ });
31
+ module.exports = __toCommonJS(index_exports);
32
+
33
+ // src/label.ts
34
+ function getLabel(field) {
35
+ return field.fieldConfig?.label || field.description || beautifyLabel(field.key);
36
+ }
37
+ function beautifyLabel(label) {
38
+ if (!label) {
39
+ return "";
40
+ }
41
+ let output = label.replace(/([A-Z])/g, " $1");
42
+ output = output.charAt(0).toUpperCase() + output.slice(1);
43
+ if (!isNaN(Number(output))) {
44
+ return "";
45
+ }
46
+ if (output === "*") {
47
+ return "";
48
+ }
49
+ return output;
50
+ }
51
+
52
+ // src/logic.ts
53
+ function parseSchema(schemaProvider) {
54
+ const schema = schemaProvider.parseSchema();
55
+ return {
56
+ ...schema,
57
+ fields: sortFieldsByOrder(schema.fields)
58
+ };
59
+ }
60
+ function validateSchema(schemaProvider, values) {
61
+ return schemaProvider.validateSchema(values);
62
+ }
63
+ function getDefaultValues(schemaProvider) {
64
+ return schemaProvider.getDefaultValues();
65
+ }
66
+ function removeEmptyValues(values) {
67
+ const result = {};
68
+ for (const key in values) {
69
+ const value = values[key];
70
+ if ([null, void 0, "", [], {}].includes(value)) {
71
+ continue;
72
+ }
73
+ if (Array.isArray(value)) {
74
+ const newArray = value.map((item) => {
75
+ if (typeof item === "object") {
76
+ return removeEmptyValues(item);
77
+ }
78
+ return item;
79
+ });
80
+ result[key] = newArray.filter((item) => item !== null);
81
+ } else if (typeof value === "object") {
82
+ result[key] = removeEmptyValues(value);
83
+ } else {
84
+ result[key] = value;
85
+ }
86
+ }
87
+ return result;
88
+ }
89
+ var isPlainObject = (value) => typeof value === "object" && Object.prototype.toString.call(value) === "[object Object]";
90
+ var isEmpty = (value) => [null, void 0, ""].includes(value);
91
+ function replaceEmptyValue(values) {
92
+ const result = { ...values };
93
+ for (const key in values) {
94
+ const value = values[key];
95
+ if (isEmpty(value)) {
96
+ result[key] = void 0;
97
+ continue;
98
+ }
99
+ if (Array.isArray(value)) {
100
+ const cleaned = Object.values(replaceEmptyValue({ ...value }));
101
+ result[key] = cleaned;
102
+ } else if (isPlainObject(value)) {
103
+ const cleaned = replaceEmptyValue(value);
104
+ result[key] = cleaned;
105
+ } else {
106
+ result[key] = value;
107
+ }
108
+ }
109
+ return result;
110
+ }
111
+ function sortFieldsByOrder(fields) {
112
+ if (!fields) return [];
113
+ const sortedFields = fields.map((field) => {
114
+ if (field.schema) {
115
+ return {
116
+ ...field,
117
+ schema: sortFieldsByOrder(field.schema)
118
+ };
119
+ }
120
+ return field;
121
+ }).sort((a, b) => {
122
+ const fieldA = a.fieldConfig?.order ?? 0;
123
+ const fieldB = b.fieldConfig?.order ?? 0;
124
+ return fieldA - fieldB;
125
+ });
126
+ return sortedFields;
127
+ }
128
+ // Annotate the CommonJS export names for ESM import in node:
129
+ 0 && (module.exports = {
130
+ getDefaultValues,
131
+ getLabel,
132
+ parseSchema,
133
+ removeEmptyValues,
134
+ replaceEmptyValue,
135
+ sortFieldsByOrder,
136
+ validateSchema
137
+ });
package/dist/index.mjs ADDED
@@ -0,0 +1,104 @@
1
+ // src/label.ts
2
+ function getLabel(field) {
3
+ return field.fieldConfig?.label || field.description || beautifyLabel(field.key);
4
+ }
5
+ function beautifyLabel(label) {
6
+ if (!label) {
7
+ return "";
8
+ }
9
+ let output = label.replace(/([A-Z])/g, " $1");
10
+ output = output.charAt(0).toUpperCase() + output.slice(1);
11
+ if (!isNaN(Number(output))) {
12
+ return "";
13
+ }
14
+ if (output === "*") {
15
+ return "";
16
+ }
17
+ return output;
18
+ }
19
+
20
+ // src/logic.ts
21
+ function parseSchema(schemaProvider) {
22
+ const schema = schemaProvider.parseSchema();
23
+ return {
24
+ ...schema,
25
+ fields: sortFieldsByOrder(schema.fields)
26
+ };
27
+ }
28
+ function validateSchema(schemaProvider, values) {
29
+ return schemaProvider.validateSchema(values);
30
+ }
31
+ function getDefaultValues(schemaProvider) {
32
+ return schemaProvider.getDefaultValues();
33
+ }
34
+ function removeEmptyValues(values) {
35
+ const result = {};
36
+ for (const key in values) {
37
+ const value = values[key];
38
+ if ([null, void 0, "", [], {}].includes(value)) {
39
+ continue;
40
+ }
41
+ if (Array.isArray(value)) {
42
+ const newArray = value.map((item) => {
43
+ if (typeof item === "object") {
44
+ return removeEmptyValues(item);
45
+ }
46
+ return item;
47
+ });
48
+ result[key] = newArray.filter((item) => item !== null);
49
+ } else if (typeof value === "object") {
50
+ result[key] = removeEmptyValues(value);
51
+ } else {
52
+ result[key] = value;
53
+ }
54
+ }
55
+ return result;
56
+ }
57
+ var isPlainObject = (value) => typeof value === "object" && Object.prototype.toString.call(value) === "[object Object]";
58
+ var isEmpty = (value) => [null, void 0, ""].includes(value);
59
+ function replaceEmptyValue(values) {
60
+ const result = { ...values };
61
+ for (const key in values) {
62
+ const value = values[key];
63
+ if (isEmpty(value)) {
64
+ result[key] = void 0;
65
+ continue;
66
+ }
67
+ if (Array.isArray(value)) {
68
+ const cleaned = Object.values(replaceEmptyValue({ ...value }));
69
+ result[key] = cleaned;
70
+ } else if (isPlainObject(value)) {
71
+ const cleaned = replaceEmptyValue(value);
72
+ result[key] = cleaned;
73
+ } else {
74
+ result[key] = value;
75
+ }
76
+ }
77
+ return result;
78
+ }
79
+ function sortFieldsByOrder(fields) {
80
+ if (!fields) return [];
81
+ const sortedFields = fields.map((field) => {
82
+ if (field.schema) {
83
+ return {
84
+ ...field,
85
+ schema: sortFieldsByOrder(field.schema)
86
+ };
87
+ }
88
+ return field;
89
+ }).sort((a, b) => {
90
+ const fieldA = a.fieldConfig?.order ?? 0;
91
+ const fieldB = b.fieldConfig?.order ?? 0;
92
+ return fieldA - fieldB;
93
+ });
94
+ return sortedFields;
95
+ }
96
+ export {
97
+ getDefaultValues,
98
+ getLabel,
99
+ parseSchema,
100
+ removeEmptyValues,
101
+ replaceEmptyValue,
102
+ sortFieldsByOrder,
103
+ validateSchema
104
+ };
package/package.json ADDED
@@ -0,0 +1,34 @@
1
+ {
2
+ "name": "@acp-autoform/core",
3
+ "version": "3.0.0",
4
+ "main": "./dist/index.js",
5
+ "module": "./dist/index.mjs",
6
+ "types": "./dist/index.d.ts",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/adityacodepublic/autoform.git"
10
+ },
11
+ "scripts": {
12
+ "lint": "eslint . --max-warnings 0",
13
+ "build": "tsup src/index.ts --format cjs,esm --dts",
14
+ "dev": "tsup src/index.ts --format cjs,esm --dts --watch"
15
+ },
16
+ "devDependencies": {
17
+ "@acp-autoform/eslint-config": "*",
18
+ "@acp-autoform/typescript-config": "*",
19
+ "@turbo/gen": "^1.12.4",
20
+ "@types/eslint": "^8.56.5",
21
+ "@types/node": "^20.11.24",
22
+ "@types/react": "^19.1.8",
23
+ "@types/react-dom": "^19.1.6",
24
+ "eslint": "^8.57.0",
25
+ "tsup": "^8.3.0",
26
+ "typescript": "^5.3.3"
27
+ },
28
+ "files": [
29
+ "dist"
30
+ ],
31
+ "publishConfig": {
32
+ "access": "public"
33
+ }
34
+ }