@whatsaaaa/form-builder-schema 0.1.0 → 0.2.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 +55 -6
- package/dist/index.d.ts +147 -24
- package/dist/index.js +333 -54
- package/dist/index.js.map +1 -1
- package/package.json +9 -9
package/README.md
CHANGED
|
@@ -11,12 +11,12 @@ pnpm add @whatsaaaa/form-builder-schema valibot
|
|
|
11
11
|
## Usage
|
|
12
12
|
|
|
13
13
|
```ts
|
|
14
|
-
import { createSchemaBuilder, type
|
|
14
|
+
import { createSchemaBuilder, type JsonForm } from '@whatsaaaa/form-builder-schema'
|
|
15
15
|
|
|
16
|
-
const json:
|
|
16
|
+
const json: JsonForm = {
|
|
17
17
|
version: '1',
|
|
18
18
|
fields: [
|
|
19
|
-
{ name: 'email', type: 'email', required: { message: 'Email is required' } },
|
|
19
|
+
{ name: 'email', type: 'email', label: 'Email', required: { message: 'Email is required' } },
|
|
20
20
|
{
|
|
21
21
|
name: 'password',
|
|
22
22
|
type: 'password',
|
|
@@ -26,13 +26,62 @@ const json: JsonFormSchema = {
|
|
|
26
26
|
],
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
-
const build = createSchemaBuilder()
|
|
29
|
+
const { build, validate } = createSchemaBuilder()
|
|
30
30
|
const schema = build(json) // v.ObjectSchema, ready for Formisch or v.parse
|
|
31
31
|
```
|
|
32
32
|
|
|
33
|
-
Built-in field types are `text`, `email`, `password` and `number`.
|
|
33
|
+
Built-in field types are `text`, `email`, `password` and `number`. Built-in rules are `trim`, `minLength`, `maxLength`, `regex`, `email`, `url`, `minValue`, `maxValue` and `integer`. A rule without a `message` uses Valibot's message, so [`@valibot/i18n`](https://valibot.dev/guides/internationalization/) works.
|
|
34
34
|
|
|
35
|
-
|
|
35
|
+
Empty optional fields output `undefined`. Rules never run on empty values, and a required empty field reports only the required message. `trim` runs before the required check, so `' '` counts as empty.
|
|
36
|
+
|
|
37
|
+
## Checking definitions
|
|
38
|
+
|
|
39
|
+
`validate(json)` never throws. It returns every problem in the definition, in document order:
|
|
40
|
+
|
|
41
|
+
```ts
|
|
42
|
+
const result = validate(jsonFromDatabase)
|
|
43
|
+
if (!result.success) {
|
|
44
|
+
for (const issue of result.issues) console.log(formatPath(issue.path), issue.code, issue.message)
|
|
45
|
+
}
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
`build(json)` runs the same checks and throws one `DefinitionError` with all of them. Issue codes are `invalid_shape`, `unsupported_version`, `unknown_key`, `unknown_type`, `missing_name`, `duplicate_name`, `invalid_props`, `unknown_rule`, `unknown_param`, `invalid_params` and `kind_mismatch`.
|
|
49
|
+
|
|
50
|
+
Keys on a field must be reserved keys (`RESERVED_FIELD_KEYS`) or props of its type. Put renderer settings under `ui`, which is never checked.
|
|
51
|
+
|
|
52
|
+
## Extending
|
|
53
|
+
|
|
54
|
+
`createSchemaBuilder({ kinds, fieldTypes, rules })` merges your entries over the defaults by key. The merged registries are returned as `kinds`, `fieldTypes` and `rules`.
|
|
55
|
+
|
|
56
|
+
```ts
|
|
57
|
+
const select = defineFieldType({
|
|
58
|
+
kind: 'string',
|
|
59
|
+
props: v.object({ options: v.array(v.string()) }),
|
|
60
|
+
schema: (field) => v.picklist(field.options), // field.options is typed
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
const even = defineRule({
|
|
64
|
+
kinds: ['number'],
|
|
65
|
+
create: (_, message) => v.check((n: number) => n % 2 === 0, message),
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
const { build } = createSchemaBuilder({ fieldTypes: { select }, rules: { even } })
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
- A **kind** says what "empty" means for a value (`isEmpty`) and adds an optional extra `required` check. Built-in kinds are `string`, `number`, `boolean`, `date`, `array`, `file`, `object` and `none`. A type with kind `none` has no value, like a heading.
|
|
72
|
+
- A **rule** with `stage: 'normalize'` runs before the required check. Its `params` schema lists the only keys the rule accepts.
|
|
73
|
+
- Mistakes in the registry, such as an unknown kind or a reserved props key, throw a `TypeError` from `createSchemaBuilder`.
|
|
74
|
+
|
|
75
|
+
## Messages
|
|
76
|
+
|
|
77
|
+
```ts
|
|
78
|
+
createSchemaBuilder({
|
|
79
|
+
messages: { required: 'Obavezno' },
|
|
80
|
+
resolveMessage: (key) => t(key), // every message from the definition, and messages.required
|
|
81
|
+
})
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
`resolveMessage` returning `undefined` keeps the text as written.
|
|
36
85
|
|
|
37
86
|
## License
|
|
38
87
|
|
package/dist/index.d.ts
CHANGED
|
@@ -1,64 +1,178 @@
|
|
|
1
1
|
import * as v from "valibot";
|
|
2
2
|
type AnySchema = v.GenericSchema;
|
|
3
3
|
type AnyAction = v.GenericPipeAction;
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
/**
|
|
5
|
+
* A `v.object()` schema. The builder reads its `entries` to find unknown keys.
|
|
6
|
+
*/
|
|
7
|
+
type AnyObjectSchema = v.ObjectSchema<v.ObjectEntries, v.ErrorMessage<v.ObjectIssue> | undefined>;
|
|
8
|
+
interface KindDef {
|
|
9
|
+
/** True when the input means "no value". */
|
|
10
|
+
isEmpty(input: unknown): boolean;
|
|
11
|
+
/** Extra check for required fieldsd after normalize rules ran. Omit it when isEmpty is enough. */
|
|
12
|
+
required?(message: string): AnyAction | null;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* The built-in kinds, plus any string so consumers can register their own.
|
|
16
|
+
*/
|
|
17
|
+
type ValueKind = 'string' | 'number' | 'boolean' | 'date' | 'array' | 'file' | 'object' | 'none' | (string & {});
|
|
18
|
+
interface JsonForm {
|
|
19
|
+
version: '1';
|
|
20
|
+
fields: readonly JsonField[];
|
|
21
|
+
}
|
|
6
22
|
interface JsonField {
|
|
7
|
-
name
|
|
8
|
-
type:
|
|
23
|
+
name?: string;
|
|
24
|
+
type: string;
|
|
25
|
+
label?: string;
|
|
26
|
+
description?: string;
|
|
27
|
+
placeholder?: string;
|
|
28
|
+
default?: unknown;
|
|
9
29
|
required?: boolean | {
|
|
10
30
|
message?: string;
|
|
11
31
|
};
|
|
12
32
|
rules?: readonly JsonRule[];
|
|
33
|
+
/** For renderers. This package never reads or checks it. */
|
|
34
|
+
ui?: Record<string, unknown>;
|
|
35
|
+
/** Validated by the field type's props schema, */
|
|
36
|
+
[prop: string]: unknown;
|
|
13
37
|
}
|
|
14
38
|
interface JsonRule {
|
|
15
39
|
rule: string;
|
|
16
40
|
message?: string;
|
|
17
41
|
[param: string]: unknown;
|
|
18
42
|
}
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
43
|
+
/** Where something is in a definition, e.g. `['fields', 0, 'rules', 1]`. */
|
|
44
|
+
type DefinitionPath = readonly (string | number)[];
|
|
45
|
+
/** Public API: adding a code is a minor change, renaming one is breaking, */
|
|
46
|
+
type DefinitionIssueCode = 'invalid_shape' | 'unsupported_version' | 'unknown_key' | 'unknown_type' | 'missing_name' | 'duplicate_name' | 'invalid_props' | 'unknown_rule' | 'unknown_param' | 'invalid_params' | 'kind_mismatch';
|
|
47
|
+
interface DefinitionIssue {
|
|
48
|
+
code: DefinitionIssueCode;
|
|
49
|
+
path: DefinitionPath;
|
|
50
|
+
message: string;
|
|
22
51
|
}
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
52
|
+
type ValidateResult = {
|
|
53
|
+
success: true;
|
|
54
|
+
issues: [];
|
|
55
|
+
} | {
|
|
56
|
+
success: false;
|
|
57
|
+
issues: DefinitionIssue[];
|
|
58
|
+
};
|
|
59
|
+
export declare function formatPath(path: DefinitionPath): string;
|
|
60
|
+
/** Thrown by `build()` when the definition has issues. Lists all of them. */
|
|
61
|
+
export declare class DefinitionError extends Error {
|
|
62
|
+
readonly issues: readonly DefinitionIssue[];
|
|
63
|
+
constructor(issues: readonly DefinitionIssue[]);
|
|
27
64
|
}
|
|
28
|
-
interface FieldTypeDef {
|
|
65
|
+
interface FieldTypeDef<TProps extends AnyObjectSchema = AnyObjectSchema> {
|
|
29
66
|
kind: ValueKind;
|
|
30
|
-
|
|
67
|
+
/** Extra keys this type accepts on a field, e.g. `options` on a select. */
|
|
68
|
+
props?: TProps;
|
|
69
|
+
/**
|
|
70
|
+
* The base schema for the field's value. Required unless `kind` is `'none'`.
|
|
71
|
+
* Method syntax keeps it bivariant, so typed field types fit in a `Record<string, FieldTypeDef>`.
|
|
72
|
+
*/
|
|
73
|
+
schema?(field: FieldWithProps<TProps>, ctx: BuildContext): AnySchema;
|
|
74
|
+
/** Rules every field of this type gets. A field rule with the same name replaces one. */
|
|
31
75
|
defaultRules?: readonly JsonRule[];
|
|
32
76
|
}
|
|
77
|
+
/** A field with its type's props parsed and typed. */
|
|
78
|
+
type FieldWithProps<TProps extends AnyObjectSchema = AnyObjectSchema> = JsonField & v.InferOutput<TProps>;
|
|
79
|
+
interface BuildContext {
|
|
80
|
+
/** Where the field is in the definition, e.g. `['fields', 2]`. */
|
|
81
|
+
readonly path: DefinitionPath;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* `normalize` rules (trim, change case) run before the required check, so `' '` with `trim` counts
|
|
85
|
+
* as empty. `validate` rules run after it.
|
|
86
|
+
*/
|
|
87
|
+
type RuleStage = 'normalize' | 'validate';
|
|
88
|
+
interface RuleDef<TParams extends AnyObjectSchema = AnyObjectSchema> {
|
|
89
|
+
kinds: readonly ValueKind[];
|
|
90
|
+
/** Defaults to `'validate'`. */
|
|
91
|
+
stage?: RuleStage;
|
|
92
|
+
/** The params the rule takes. Omit it for rules without params. Unknown params are rejected. */
|
|
93
|
+
params?: TParams;
|
|
94
|
+
/** Method syntax keeps `create` bivariant, so specific rules fit in a `Record<string, RuleDef`>. */
|
|
95
|
+
create(params: v.InferOutput<TParams>, message?: string): AnyAction;
|
|
96
|
+
}
|
|
97
|
+
interface MessageContext {
|
|
98
|
+
field: JsonField;
|
|
99
|
+
rule?: string;
|
|
100
|
+
path: DefinitionPath;
|
|
101
|
+
}
|
|
33
102
|
interface SchemaBuilderOptions {
|
|
103
|
+
kinds?: Record<string, KindDef>;
|
|
34
104
|
fieldTypes?: Record<string, FieldTypeDef>;
|
|
35
105
|
rules?: Record<string, RuleDef>;
|
|
106
|
+
messages?: {
|
|
107
|
+
required?: string;
|
|
108
|
+
};
|
|
109
|
+
/**
|
|
110
|
+
* Called with every message from the definition and with `messages.required`, e.g. to treat
|
|
111
|
+
* them as i18n keys. Return undefined to use the text as written.
|
|
112
|
+
*/
|
|
113
|
+
resolveMessage?(key: string, ctx: MessageContext): string | undefined;
|
|
36
114
|
}
|
|
37
|
-
export declare function createSchemaBuilder(
|
|
115
|
+
export declare function createSchemaBuilder(options?: SchemaBuilderOptions): {
|
|
116
|
+
build: (json: JsonForm) => v.ObjectSchema<v.ObjectEntries, undefined>;
|
|
117
|
+
validate: (json: unknown) => ValidateResult;
|
|
118
|
+
kinds: Readonly<Record<string, KindDef>>;
|
|
119
|
+
fieldTypes: Readonly<Record<string, FieldTypeDef<AnyObjectSchema>>>;
|
|
120
|
+
rules: Readonly<Record<string, RuleDef<AnyObjectSchema>>>;
|
|
121
|
+
};
|
|
38
122
|
export declare const defaultFieldTypes: {
|
|
39
123
|
text: {
|
|
40
124
|
kind: "string";
|
|
41
|
-
|
|
125
|
+
schema: () => v.StringSchema<undefined>;
|
|
42
126
|
};
|
|
43
127
|
password: {
|
|
44
128
|
kind: "string";
|
|
45
|
-
|
|
129
|
+
schema: () => v.StringSchema<undefined>;
|
|
46
130
|
};
|
|
47
131
|
email: {
|
|
48
132
|
kind: "string";
|
|
49
|
-
|
|
133
|
+
schema: () => v.StringSchema<undefined>;
|
|
50
134
|
defaultRules: {
|
|
51
135
|
rule: string;
|
|
52
|
-
message: string;
|
|
53
136
|
}[];
|
|
54
137
|
};
|
|
55
138
|
number: {
|
|
56
139
|
kind: "number";
|
|
57
|
-
|
|
140
|
+
schema: () => v.NumberSchema<undefined>;
|
|
141
|
+
};
|
|
142
|
+
};
|
|
143
|
+
export declare const defineFieldType: <T extends AnyObjectSchema>(def: FieldTypeDef<T>) => FieldTypeDef<T>;
|
|
144
|
+
export declare const defaultKinds: {
|
|
145
|
+
string: {
|
|
146
|
+
isEmpty: (input: unknown) => input is "" | null | undefined;
|
|
147
|
+
required: (message: string) => v.NonEmptyAction<any, string>;
|
|
148
|
+
};
|
|
149
|
+
number: {
|
|
150
|
+
isEmpty: (input: unknown) => boolean;
|
|
151
|
+
};
|
|
152
|
+
boolean: {
|
|
153
|
+
isEmpty: (input: unknown) => input is null | undefined;
|
|
154
|
+
required: (message: string) => v.ValueAction<any, true, string>;
|
|
155
|
+
};
|
|
156
|
+
date: {
|
|
157
|
+
isEmpty: (input: unknown) => input is "" | null | undefined;
|
|
158
|
+
required: (message: string) => v.NonEmptyAction<any, string>;
|
|
159
|
+
};
|
|
160
|
+
array: {
|
|
161
|
+
isEmpty: (input: unknown) => boolean;
|
|
162
|
+
required: (message: string) => v.MinLengthAction<any, 1, string>;
|
|
163
|
+
};
|
|
164
|
+
file: {
|
|
165
|
+
isEmpty: (input: unknown) => input is null | undefined;
|
|
166
|
+
};
|
|
167
|
+
object: {
|
|
168
|
+
isEmpty: (input: unknown) => input is null | undefined;
|
|
169
|
+
};
|
|
170
|
+
none: {
|
|
171
|
+
isEmpty: () => true;
|
|
58
172
|
};
|
|
59
173
|
};
|
|
60
|
-
export declare const defineRule: <T extends AnySchema>(def: RuleDef<T>) => RuleDef<T>;
|
|
61
174
|
export declare const defaultRules: {
|
|
175
|
+
trim: RuleDef<AnyObjectSchema>;
|
|
62
176
|
minLength: RuleDef<v.ObjectSchema<{
|
|
63
177
|
readonly value: v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 0, undefined>]>;
|
|
64
178
|
}, undefined>>;
|
|
@@ -69,15 +183,24 @@ export declare const defaultRules: {
|
|
|
69
183
|
readonly pattern: v.StringSchema<undefined>;
|
|
70
184
|
readonly flags: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
|
|
71
185
|
}, undefined>>;
|
|
72
|
-
email: RuleDef<
|
|
73
|
-
url: RuleDef<
|
|
186
|
+
email: RuleDef<AnyObjectSchema>;
|
|
187
|
+
url: RuleDef<AnyObjectSchema>;
|
|
74
188
|
minValue: RuleDef<v.ObjectSchema<{
|
|
75
189
|
readonly value: v.NumberSchema<undefined>;
|
|
76
190
|
}, undefined>>;
|
|
77
191
|
maxValue: RuleDef<v.ObjectSchema<{
|
|
78
192
|
readonly value: v.NumberSchema<undefined>;
|
|
79
193
|
}, undefined>>;
|
|
80
|
-
integer: RuleDef<
|
|
194
|
+
integer: RuleDef<AnyObjectSchema>;
|
|
81
195
|
};
|
|
82
|
-
export
|
|
196
|
+
export declare const defineRule: <T extends AnyObjectSchema>(def: RuleDef<T>) => RuleDef<T>;
|
|
197
|
+
/**
|
|
198
|
+
* Field keys this package owns. A field type's props can't use them.
|
|
199
|
+
*/
|
|
200
|
+
export declare const RESERVED_FIELD_KEYS: readonly ["name", "type", "label", "description", "placeholder", "default", "required", "rules", "ui"];
|
|
201
|
+
/**
|
|
202
|
+
* Rule keys this package own. A rule's params can't use them.
|
|
203
|
+
*/
|
|
204
|
+
export declare const RESERVED_RULE_KEYS: readonly ["rule", "message"];
|
|
205
|
+
export type { AnyAction, AnyObjectSchema, AnySchema, BuildContext, DefinitionIssue, DefinitionIssueCode, DefinitionPath, FieldTypeDef, FieldWithProps, JsonField, JsonForm, JsonRule, KindDef, MessageContext, RuleDef, RuleStage, SchemaBuilderOptions, ValidateResult, ValueKind };
|
|
83
206
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.js
CHANGED
|
@@ -1,32 +1,91 @@
|
|
|
1
1
|
import * as v from "valibot";
|
|
2
|
-
// src/field-
|
|
2
|
+
// src/builder/build-field-schema.ts
|
|
3
|
+
/**
|
|
4
|
+
* The schema runs in this order: empty check, base schema, normalize rules, required check,
|
|
5
|
+
* then the type's default rules and the field's rules.
|
|
6
|
+
*/
|
|
7
|
+
function buildFieldSchema(checked, messages) {
|
|
8
|
+
const { field, path, type, kind } = checked;
|
|
9
|
+
const required = !!field.required;
|
|
10
|
+
const requiredMessage = messages.resolve(typeof field.required === "object" && field.required.message || messages.required, {
|
|
11
|
+
field,
|
|
12
|
+
path
|
|
13
|
+
});
|
|
14
|
+
const normalize = [];
|
|
15
|
+
const validate = [];
|
|
16
|
+
for (const rule of checked.rules) {
|
|
17
|
+
const message = rule.message === void 0 ? void 0 : messages.resolve(rule.message, {
|
|
18
|
+
field,
|
|
19
|
+
rule: rule.name,
|
|
20
|
+
path
|
|
21
|
+
});
|
|
22
|
+
const action = rule.def.create(rule.params, message);
|
|
23
|
+
if (rule.def.stage === "normalize") normalize.push(action);
|
|
24
|
+
else validate.push(action);
|
|
25
|
+
}
|
|
26
|
+
const base = type.schema(field, { path });
|
|
27
|
+
const requiredCheck = required ? kind.required?.(requiredMessage) : void 0;
|
|
28
|
+
const head = pipe(base, requiredCheck ? [...normalize, requiredCheck] : normalize);
|
|
29
|
+
const empty = required ? v.never(requiredMessage) : v.pipe(v.unknown(), v.transform(() => void 0));
|
|
30
|
+
const whenFilled = (schema) => v.lazy((input) => kind.isEmpty(input) ? empty : schema);
|
|
31
|
+
const schema = whenFilled(v.pipe(head, whenFilled(pipe(v.unknown(), validate))));
|
|
32
|
+
return required ? schema : v.optional(schema);
|
|
33
|
+
}
|
|
34
|
+
const pipe = (schema, actions) => actions.reduce((acc, action) => v.pipe(acc, action), schema);
|
|
35
|
+
// src/field-types/default-field-types.ts
|
|
3
36
|
const defaultFieldTypes = {
|
|
4
37
|
text: {
|
|
5
38
|
kind: "string",
|
|
6
|
-
|
|
39
|
+
schema: () => v.string()
|
|
7
40
|
},
|
|
8
41
|
password: {
|
|
9
42
|
kind: "string",
|
|
10
|
-
|
|
43
|
+
schema: () => v.string()
|
|
11
44
|
},
|
|
12
45
|
email: {
|
|
13
46
|
kind: "string",
|
|
14
|
-
|
|
15
|
-
defaultRules: [{
|
|
16
|
-
rule: "email",
|
|
17
|
-
message: "Invalid email"
|
|
18
|
-
}]
|
|
47
|
+
schema: () => v.string(),
|
|
48
|
+
defaultRules: [{ rule: "email" }]
|
|
19
49
|
},
|
|
20
50
|
number: {
|
|
21
51
|
kind: "number",
|
|
22
|
-
|
|
52
|
+
schema: () => v.number()
|
|
23
53
|
}
|
|
24
54
|
};
|
|
25
|
-
// src/
|
|
55
|
+
// src/kinds/default-kinds.ts
|
|
56
|
+
const isNil = (input) => input === void 0 || input === null;
|
|
57
|
+
const defaultKinds = {
|
|
58
|
+
string: {
|
|
59
|
+
isEmpty: (input) => isNil(input) || input === "",
|
|
60
|
+
required: (message) => v.nonEmpty(message)
|
|
61
|
+
},
|
|
62
|
+
number: { isEmpty: (input) => isNil(input) || input === "" || Number.isNaN(input) },
|
|
63
|
+
boolean: {
|
|
64
|
+
isEmpty: isNil,
|
|
65
|
+
required: (message) => v.value(true, message)
|
|
66
|
+
},
|
|
67
|
+
date: {
|
|
68
|
+
isEmpty: (input) => isNil(input) || input === "",
|
|
69
|
+
required: (message) => v.nonEmpty(message)
|
|
70
|
+
},
|
|
71
|
+
array: {
|
|
72
|
+
isEmpty: (input) => isNil(input) || Array.isArray(input) && input.length === 0,
|
|
73
|
+
required: (message) => v.minLength(1, message)
|
|
74
|
+
},
|
|
75
|
+
file: { isEmpty: isNil },
|
|
76
|
+
object: { isEmpty: isNil },
|
|
77
|
+
none: { isEmpty: () => true }
|
|
78
|
+
};
|
|
79
|
+
// src/rules/define-rule.ts
|
|
26
80
|
const defineRule = (def) => def;
|
|
81
|
+
// src/rules/default-rules.ts
|
|
27
82
|
const nonNegInt = v.pipe(v.number(), v.integer(), v.minValue(0));
|
|
28
|
-
const noParams = v.object({});
|
|
29
83
|
const defaultRules = {
|
|
84
|
+
trim: defineRule({
|
|
85
|
+
kinds: ["string"],
|
|
86
|
+
stage: "normalize",
|
|
87
|
+
create: () => v.trim()
|
|
88
|
+
}),
|
|
30
89
|
minLength: defineRule({
|
|
31
90
|
kinds: ["string"],
|
|
32
91
|
params: v.object({ value: nonNegInt }),
|
|
@@ -47,12 +106,10 @@ const defaultRules = {
|
|
|
47
106
|
}),
|
|
48
107
|
email: defineRule({
|
|
49
108
|
kinds: ["string"],
|
|
50
|
-
params: noParams,
|
|
51
109
|
create: (_, m) => v.email(m)
|
|
52
110
|
}),
|
|
53
111
|
url: defineRule({
|
|
54
112
|
kinds: ["string"],
|
|
55
|
-
params: noParams,
|
|
56
113
|
create: (_, m) => v.url(m)
|
|
57
114
|
}),
|
|
58
115
|
minValue: defineRule({
|
|
@@ -67,58 +124,280 @@ const defaultRules = {
|
|
|
67
124
|
}),
|
|
68
125
|
integer: defineRule({
|
|
69
126
|
kinds: ["number"],
|
|
70
|
-
params: noParams,
|
|
71
127
|
create: (_, m) => v.integer(m)
|
|
72
128
|
})
|
|
73
129
|
};
|
|
74
|
-
// src/
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
130
|
+
// src/builder/registry.ts
|
|
131
|
+
/** Consumer entries replace defaults with the same key. */
|
|
132
|
+
function mergeRegistry(custom) {
|
|
133
|
+
return {
|
|
134
|
+
kinds: Object.freeze({
|
|
135
|
+
...defaultKinds,
|
|
136
|
+
...custom.kinds
|
|
137
|
+
}),
|
|
138
|
+
fieldTypes: Object.freeze({
|
|
139
|
+
...defaultFieldTypes,
|
|
140
|
+
...custom.fieldTypes
|
|
141
|
+
}),
|
|
142
|
+
rules: Object.freeze({
|
|
143
|
+
...defaultRules,
|
|
144
|
+
...custom.rules
|
|
145
|
+
})
|
|
79
146
|
};
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
147
|
+
}
|
|
148
|
+
function lookup(record, key) {
|
|
149
|
+
return Object.hasOwn(record, key) ? record[key] : void 0;
|
|
150
|
+
}
|
|
151
|
+
// src/errors/definition-error.ts
|
|
152
|
+
function formatPath(path) {
|
|
153
|
+
return path.reduce((out, key) => {
|
|
154
|
+
if (typeof key === "number") return `${out}[${key}]`;
|
|
155
|
+
return out ? `${out}.${key}` : key;
|
|
156
|
+
}, "");
|
|
157
|
+
}
|
|
158
|
+
/** Thrown by `build()` when the definition has issues. Lists all of them. */
|
|
159
|
+
var DefinitionError = class extends Error {
|
|
160
|
+
issues;
|
|
161
|
+
constructor(issues) {
|
|
162
|
+
const lines = issues.map((issue) => ` ${formatPath(issue.path) || "(root)"}: ${issue.message}`);
|
|
163
|
+
super(`Invalid form definition:\n${lines.join("\n")}`);
|
|
164
|
+
this.name = "DefinitionError";
|
|
165
|
+
this.issues = issues;
|
|
166
|
+
}
|
|
167
|
+
};
|
|
168
|
+
// src/shared/reserved-keys.ts
|
|
169
|
+
/**
|
|
170
|
+
* Field keys this package owns. A field type's props can't use them.
|
|
171
|
+
*/
|
|
172
|
+
const RESERVED_FIELD_KEYS = [
|
|
173
|
+
"name",
|
|
174
|
+
"type",
|
|
175
|
+
"label",
|
|
176
|
+
"description",
|
|
177
|
+
"placeholder",
|
|
178
|
+
"default",
|
|
179
|
+
"required",
|
|
180
|
+
"rules",
|
|
181
|
+
"ui"
|
|
182
|
+
];
|
|
183
|
+
/**
|
|
184
|
+
* Rule keys this package own. A rule's params can't use them.
|
|
185
|
+
*/
|
|
186
|
+
const RESERVED_RULE_KEYS = ["rule", "message"];
|
|
187
|
+
const fieldKeys = new Set(RESERVED_FIELD_KEYS);
|
|
188
|
+
const ruleKeys = new Set(RESERVED_RULE_KEYS);
|
|
189
|
+
const isReservedFieldKey = (key) => fieldKeys.has(key);
|
|
190
|
+
const isReservedRuleKey = (key) => ruleKeys.has(key);
|
|
191
|
+
// src/builder/validate-definition.ts
|
|
192
|
+
const formShape = v.object({
|
|
193
|
+
version: v.string(),
|
|
194
|
+
fields: v.array(v.unknown())
|
|
195
|
+
});
|
|
196
|
+
const fieldShape = v.looseObject({
|
|
197
|
+
name: v.optional(v.string()),
|
|
198
|
+
type: v.string(),
|
|
199
|
+
label: v.optional(v.string()),
|
|
200
|
+
description: v.optional(v.string()),
|
|
201
|
+
placeholder: v.optional(v.string()),
|
|
202
|
+
default: v.optional(v.unknown()),
|
|
203
|
+
required: v.optional(v.union([v.boolean(), v.strictObject({ message: v.optional(v.string()) })])),
|
|
204
|
+
rules: v.optional(v.array(v.unknown())),
|
|
205
|
+
ui: v.optional(v.record(v.string(), v.unknown()))
|
|
206
|
+
});
|
|
207
|
+
const ruleShape = v.looseObject({
|
|
208
|
+
rule: v.string(),
|
|
209
|
+
message: v.optional(v.string())
|
|
210
|
+
});
|
|
211
|
+
const noParams = v.object({});
|
|
212
|
+
/**
|
|
213
|
+
* Checks a whole definition and collects every issue, in document order. Never throws.
|
|
214
|
+
*/
|
|
215
|
+
function checkDefinition(json, registry) {
|
|
216
|
+
const issues = [];
|
|
217
|
+
const fields = [];
|
|
218
|
+
const form = v.safeParse(formShape, json);
|
|
219
|
+
if (!form.success) return {
|
|
220
|
+
issues: toIssues("invalid_shape", [], form.issues),
|
|
221
|
+
fields
|
|
222
|
+
};
|
|
223
|
+
if (form.output.version !== "1") return {
|
|
224
|
+
issues: [{
|
|
225
|
+
code: "unsupported_version",
|
|
226
|
+
path: ["version"],
|
|
227
|
+
message: `Unsupported version "${form.output.version}". This package reads version '1'.`
|
|
228
|
+
}],
|
|
229
|
+
fields
|
|
83
230
|
};
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
if (
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
231
|
+
const names = /* @__PURE__ */ new Set();
|
|
232
|
+
form.output.fields.forEach((raw, i) => {
|
|
233
|
+
const field = checkField(raw, ["fields", i], registry, names, issues);
|
|
234
|
+
if (field) fields.push(field);
|
|
235
|
+
});
|
|
236
|
+
return {
|
|
237
|
+
issues,
|
|
238
|
+
fields
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
function checkField(raw, path, registry, names, issues) {
|
|
242
|
+
const shape = v.safeParse(fieldShape, raw);
|
|
243
|
+
if (!shape.success) {
|
|
244
|
+
issues.push(...toIssues("invalid_shape", path, shape.issues));
|
|
245
|
+
return;
|
|
91
246
|
}
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
247
|
+
const field = shape.output;
|
|
248
|
+
const issueCount = issues.length;
|
|
249
|
+
const type = lookup(registry.fieldTypes, field.type);
|
|
250
|
+
const hasValue = type?.kind !== "none";
|
|
251
|
+
if (!field.name) {
|
|
252
|
+
if (type && hasValue) issues.push(issue("missing_name", [...path, "name"], "A field needs a name"));
|
|
253
|
+
} else if (hasValue) {
|
|
254
|
+
if (names.has(field.name)) issues.push(issue("duplicate_name", [...path, "name"], `Duplicate name "${field.name}"`));
|
|
255
|
+
names.add(field.name);
|
|
256
|
+
}
|
|
257
|
+
if (!type) {
|
|
258
|
+
issues.push(issue("unknown_type", [...path, "type"], `Unknown field type "${field.type}"`));
|
|
259
|
+
return;
|
|
260
|
+
}
|
|
261
|
+
const props = checkProps(field, type, path, issues);
|
|
262
|
+
if (!hasValue) {
|
|
263
|
+
for (const key of ["required", "rules"]) if (field[key] !== void 0) {
|
|
264
|
+
const message = `A "${field.type}" field has no value, so it can't have "${key}"`;
|
|
265
|
+
issues.push(issue("invalid_shape", [...path, key], message));
|
|
266
|
+
}
|
|
267
|
+
return;
|
|
268
|
+
}
|
|
269
|
+
const fieldRules = (shape.output.rules ?? []).flatMap((rule, j) => checkRule(rule, type?.kind, [
|
|
270
|
+
...path,
|
|
271
|
+
"rules",
|
|
272
|
+
j
|
|
273
|
+
], registry, issues) ?? []);
|
|
274
|
+
if (issues.length > issueCount || !field.name) return void 0;
|
|
275
|
+
const overridden = new Set(fieldRules.map((rule) => rule.name));
|
|
276
|
+
const defaultRules = (type?.defaultRules ?? []).filter((rule) => !overridden.has(rule.rule)).flatMap((rule) => checkRule(rule, type?.kind, path, registry, []) ?? []);
|
|
277
|
+
return {
|
|
278
|
+
name: field.name,
|
|
279
|
+
field: {
|
|
280
|
+
...field,
|
|
281
|
+
...props
|
|
282
|
+
},
|
|
283
|
+
path,
|
|
284
|
+
type,
|
|
285
|
+
kind: registry.kinds[type?.kind],
|
|
286
|
+
rules: [...defaultRules, ...fieldRules]
|
|
287
|
+
};
|
|
288
|
+
}
|
|
289
|
+
function checkProps(field, type, path, issues) {
|
|
290
|
+
const propKeys = Object.keys(type.props?.entries ?? {});
|
|
291
|
+
const input = {};
|
|
292
|
+
for (const key of Object.keys(field)) {
|
|
293
|
+
if (isReservedFieldKey(key)) continue;
|
|
294
|
+
if (propKeys.includes(key)) input[key] = field[key];
|
|
295
|
+
else issues.push(issue("unknown_key", [...path, key], `Unknown key "${key}" on a "${field.type}" field`));
|
|
296
|
+
}
|
|
297
|
+
if (!type.props) return {};
|
|
298
|
+
const parsed = v.safeParse(type.props, input);
|
|
299
|
+
if (parsed.success) return parsed.output;
|
|
300
|
+
issues.push(...toIssues("invalid_props", path, parsed.issues));
|
|
301
|
+
return {};
|
|
302
|
+
}
|
|
303
|
+
function checkRule(raw, kind, path, registry, issues) {
|
|
304
|
+
const shape = v.safeParse(ruleShape, raw);
|
|
305
|
+
if (!shape.success) {
|
|
306
|
+
issues.push(...toIssues("invalid_shape", path, shape.issues));
|
|
307
|
+
return;
|
|
308
|
+
}
|
|
309
|
+
const rule = shape.output;
|
|
310
|
+
const def = lookup(registry.rules, rule.rule);
|
|
311
|
+
if (!def) {
|
|
312
|
+
issues.push(issue("unknown_rule", [...path, "rule"], `Unknown rule "${rule.rule}"`));
|
|
313
|
+
return;
|
|
314
|
+
}
|
|
315
|
+
const issueCount = issues.length;
|
|
316
|
+
if (!def.kinds.includes(kind)) {
|
|
317
|
+
const message = `Rule "${rule.rule} can't be used on ${kind} fields`;
|
|
318
|
+
issues.push(issue("kind_mismatch", [...path, "rule"], message));
|
|
319
|
+
}
|
|
320
|
+
const paramKeys = Object.keys(def.params?.entries ?? {});
|
|
321
|
+
const input = {};
|
|
322
|
+
for (const key of Object.keys(rule)) {
|
|
323
|
+
if (isReservedRuleKey(key)) continue;
|
|
324
|
+
if (paramKeys.includes(key)) input[key] = rule[key];
|
|
325
|
+
else issues.push(issue("unknown_param", [...path, key], `Rule "${rule.rule}" has no param "${key}"`));
|
|
326
|
+
}
|
|
327
|
+
const parsed = v.safeParse(def.params ?? noParams, input);
|
|
328
|
+
if (!parsed.success) issues.push(...toIssues("invalid_params", path, parsed.issues));
|
|
329
|
+
if (!parsed.success || issues.length > issueCount) return void 0;
|
|
330
|
+
return {
|
|
331
|
+
name: rule.rule,
|
|
332
|
+
def,
|
|
333
|
+
params: parsed.output,
|
|
334
|
+
message: rule.message
|
|
335
|
+
};
|
|
336
|
+
}
|
|
337
|
+
function checkRegistry(registry) {
|
|
338
|
+
for (const [name, rule] of Object.entries(registry.rules)) for (const key of Object.keys(rule.params?.entries ?? {})) if (isReservedRuleKey(key)) throw new TypeError(`Rule "${name}": params can't use the reserved key "${key}"`);
|
|
339
|
+
for (const [name, type] of Object.entries(registry.fieldTypes)) {
|
|
340
|
+
const at = `Field type "${name}"`;
|
|
341
|
+
if (!lookup(registry.kinds, type.kind)) throw new TypeError(`${at}: kind "${type.kind}" is not registered`);
|
|
342
|
+
if (type.kind !== "none" && !type.schema) throw new TypeError(`${at}: schema() is required unless the kind is 'none'`);
|
|
343
|
+
for (const key of Object.keys(type.props?.entries ?? {})) if (isReservedFieldKey(key)) throw new TypeError(`${at}: props can't use the reserved key "${key}"`);
|
|
344
|
+
const issues = [];
|
|
345
|
+
type.defaultRules?.forEach((rule, j) => {
|
|
346
|
+
checkRule(rule, type.kind, ["defaultRules", j], registry, issues);
|
|
105
347
|
});
|
|
106
|
-
const
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
348
|
+
const [first] = issues;
|
|
349
|
+
if (first) throw new TypeError(`${at}: ${formatPath(first.path)}: ${first.message}`);
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
function issue(code, path, message) {
|
|
353
|
+
return {
|
|
354
|
+
code,
|
|
355
|
+
path,
|
|
356
|
+
message
|
|
357
|
+
};
|
|
358
|
+
}
|
|
359
|
+
function toIssues(code, path, valibotIssues) {
|
|
360
|
+
return valibotIssues.map((valibotIssue) => {
|
|
361
|
+
const keys = valibotIssue.path?.map((item) => item.key) ?? [];
|
|
362
|
+
return issue(code, [...path, ...keys], valibotIssue.message);
|
|
363
|
+
});
|
|
364
|
+
}
|
|
365
|
+
// src/builder/create-schema-builder.ts
|
|
366
|
+
function createSchemaBuilder(options = {}) {
|
|
367
|
+
const registry = mergeRegistry(options);
|
|
368
|
+
checkRegistry(registry);
|
|
369
|
+
const messages = {
|
|
370
|
+
required: options.messages?.required ?? "Required",
|
|
371
|
+
resolve: (text, ctx) => options.resolveMessage?.(text, ctx) ?? text
|
|
372
|
+
};
|
|
373
|
+
/** Every issue in the definition, in document order. Never throws on bad input. */
|
|
374
|
+
function validate(json) {
|
|
375
|
+
const { issues } = checkDefinition(json, registry);
|
|
376
|
+
return issues.length ? {
|
|
377
|
+
success: false,
|
|
378
|
+
issues
|
|
379
|
+
} : {
|
|
380
|
+
success: true,
|
|
381
|
+
issues: []
|
|
382
|
+
};
|
|
112
383
|
}
|
|
113
|
-
|
|
384
|
+
function build(json) {
|
|
385
|
+
const { issues, fields } = checkDefinition(json, registry);
|
|
386
|
+
if (issues.length) throw new DefinitionError(issues);
|
|
114
387
|
const entries = {};
|
|
115
|
-
|
|
116
|
-
if (field.name in entries) throw new Error(`fields[${i}]: duplicate name "${field.name}"`);
|
|
117
|
-
entries[field.name] = buildField(field, i);
|
|
118
|
-
});
|
|
388
|
+
for (const field of fields) entries[field.name] = buildFieldSchema(field, messages);
|
|
119
389
|
return v.object(entries);
|
|
390
|
+
}
|
|
391
|
+
return {
|
|
392
|
+
build,
|
|
393
|
+
validate,
|
|
394
|
+
kinds: registry.kinds,
|
|
395
|
+
fieldTypes: registry.fieldTypes,
|
|
396
|
+
rules: registry.rules
|
|
120
397
|
};
|
|
121
398
|
}
|
|
122
|
-
|
|
399
|
+
// src/field-types/define-field-type.ts
|
|
400
|
+
const defineFieldType = (def) => def;
|
|
401
|
+
export { DefinitionError, RESERVED_FIELD_KEYS, RESERVED_RULE_KEYS, createSchemaBuilder, defaultFieldTypes, defaultKinds, defaultRules, defineFieldType, defineRule, formatPath };
|
|
123
402
|
|
|
124
403
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../src/field-types.ts","../src/rules.ts","../src/create-schema-builder.ts"],"sourcesContent":["import * as v from 'valibot'\nimport type { FieldTypeDef } from './types'\n\nexport const defaultFieldTypes = {\n text: { kind: 'string', base: (m) => v.string(m) },\n password: { kind: 'string', base: (m) => v.string(m) },\n email: {\n kind: 'string',\n base: (m) => v.string(m),\n defaultRules: [{ rule: 'email', message: 'Invalid email' }],\n },\n number: { kind: 'number', base: (m) => v.number(m) },\n} satisfies Record<string, FieldTypeDef>\n","import * as v from 'valibot'\nimport type { AnySchema, RuleDef } from './types'\n\nexport const defineRule = <T extends AnySchema>(def: RuleDef<T>): RuleDef<T> => def\n\nconst nonNegInt = v.pipe(v.number(), v.integer(), v.minValue(0))\nconst noParams = v.object({})\n\nexport const defaultRules = {\n minLength: defineRule({\n kinds: ['string'],\n params: v.object({ value: nonNegInt }),\n create: (p, m) => v.minLength(p.value, m),\n }),\n maxLength: defineRule({\n kinds: ['string'],\n params: v.object({ value: nonNegInt }),\n create: (p, m) => v.maxLength(p.value, m),\n }),\n regex: defineRule({\n kinds: ['string'],\n params: v.object({ pattern: v.string(), flags: v.optional(v.string()) }),\n create: (p, m) => v.regex(new RegExp(p.pattern, p.flags), m),\n }),\n email: defineRule({ kinds: ['string'], params: noParams, create: (_, m) => v.email(m) }),\n url: defineRule({ kinds: ['string'], params: noParams, create: (_, m) => v.url(m) }),\n minValue: defineRule({\n kinds: ['number'],\n params: v.object({ value: v.number() }),\n create: (p, m) => v.minValue(p.value, m),\n }),\n maxValue: defineRule({\n kinds: ['number'],\n params: v.object({ value: v.number() }),\n create: (p, m) => v.maxValue(p.value, m),\n }),\n integer: defineRule({ kinds: ['number'], params: noParams, create: (_, m) => v.integer(m) }),\n} satisfies Record<string, RuleDef>\n","import * as v from 'valibot'\nimport { defaultFieldTypes } from './field-types'\nimport { defaultRules } from './rules'\nimport type {\n AnyAction,\n AnySchema,\n FieldTypeDef,\n JsonField,\n JsonFormSchema,\n JsonRule,\n RuleDef,\n ValueKind,\n} from './types'\n\nexport interface SchemaBuilderOptions {\n fieldTypes?: Record<string, FieldTypeDef>\n rules?: Record<string, RuleDef>\n}\n\nexport function createSchemaBuilder(opts: SchemaBuilderOptions = {}) {\n const fieldTypes: Record<string, FieldTypeDef> = { ...defaultFieldTypes, ...opts.fieldTypes }\n const rules: Record<string, RuleDef> = { ...defaultRules, ...opts.rules }\n\n function buildAction(rule: JsonRule, kind: ValueKind, at: string): AnyAction {\n const def = rules[rule.rule]\n if (!def) throw new Error(`${at}: unknown rule \"${rule.rule}\"`)\n if (!def.kinds.includes(kind))\n throw new Error(`${at}: rule \"${rule.rule}\" can't be used on ${kind} fields`)\n\n const parsed = v.safeParse(def.params, rule)\n if (!parsed.success)\n throw new Error(`${at}: invalid params for \"${rule.rule}\"\\n${v.summarize(parsed.issues)}`)\n\n return def.create(parsed.output, rule.message)\n }\n\n function buildField(field: JsonField, i: number): AnySchema {\n const at = `fields[${i}] \"${field.name}\"`\n const type = fieldTypes[field.type]\n if (!type) throw new Error(`${at}: unknown field type \"${field.type}\"`)\n\n const userRules = field.rules ?? []\n const overridden = new Set(userRules.map((r) => r.rule))\n const allRules = [\n ...(type.defaultRules ?? []).filter((r) => !overridden.has(r.rule)),\n ...userRules,\n ]\n\n const required = !!field.required\n const requiredMsg = typeof field.required === 'object' ? field.required.message : undefined\n\n const actions: AnyAction[] = []\n\n if (required && type.kind === 'string') {\n actions.push(v.nonEmpty(requiredMsg ?? 'Required'))\n }\n\n allRules.forEach((r, j) => {\n actions.push(buildAction(r, type.kind, `${at}.rules[${j}]`))\n })\n\n // An empty number input is not a number, so required ones report it via the type check.\n const baseMsg = required && type.kind === 'number' ? (requiredMsg ?? 'Required') : requiredMsg\n const schema = actions.reduce<AnySchema>((s, a) => v.pipe(s, a), type.base(baseMsg))\n\n if (required) return schema\n if (type.kind === 'string') return v.optional(v.union([v.literal(''), schema]))\n\n // A cleared number input holds '', which means \"no value\".\n const empty = v.pipe(\n v.literal(''),\n v.transform(() => undefined),\n )\n return v.optional(v.union([empty, schema]))\n }\n\n return function build(json: JsonFormSchema) {\n const entries: v.ObjectEntries = {}\n\n json.fields.forEach((field, i) => {\n if (field.name in entries) throw new Error(`fields[${i}]: duplicate name \"${field.name}\"`)\n entries[field.name] = buildField(field, i)\n })\n\n return v.object(entries)\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../src/builder/build-field-schema.ts","../src/field-types/default-field-types.ts","../src/kinds/default-kinds.ts","../src/rules/define-rule.ts","../src/rules/default-rules.ts","../src/builder/registry.ts","../src/errors/definition-error.ts","../src/shared/reserved-keys.ts","../src/builder/validate-definition.ts","../src/builder/create-schema-builder.ts","../src/field-types/define-field-type.ts"],"sourcesContent":["import * as v from 'valibot'\nimport type { JsonField } from '../definition/json-form-schema'\nimport type { DefinitionPath } from '../errors/definition-error'\nimport type { CheckedField } from './validate-definition'\nimport type { AnyAction, AnySchema } from '../shared/valibot-types'\n\nexport interface MessageContext {\n field: JsonField\n rule?: string\n path: DefinitionPath\n}\n\nexport interface Messages {\n required: string\n resolve(text: string, ctx: MessageContext): string\n}\n\n/**\n * The schema runs in this order: empty check, base schema, normalize rules, required check,\n * then the type's default rules and the field's rules.\n */\nexport function buildFieldSchema(checked: CheckedField, messages: Messages): AnySchema {\n const { field, path, type, kind } = checked\n const required = !!field.required\n const requiredMessage = messages.resolve(\n (typeof field.required === 'object' && field.required.message) || messages.required,\n { field, path },\n )\n\n const normalize: AnyAction[] = []\n const validate: AnyAction[] = []\n\n for (const rule of checked.rules) {\n const message =\n rule.message === undefined\n ? undefined\n : messages.resolve(rule.message, { field, rule: rule.name, path })\n const action = rule.def.create(rule.params, message)\n if (rule.def.stage === 'normalize') {\n normalize.push(action)\n } else {\n validate.push(action)\n }\n }\n\n const base = (type.schema as NonNullable<typeof type.schema>)(field, { path })\n const requiredCheck = required ? kind.required?.(requiredMessage) : undefined\n const head = pipe(base, requiredCheck ? [...normalize, requiredCheck] : normalize)\n\n const empty = required\n ? v.never(requiredMessage)\n : v.pipe(\n v.unknown(),\n v.transform(() => undefined),\n )\n\n const whenFilled = (schema: AnySchema): AnySchema =>\n v.lazy((input) => (kind.isEmpty(input) ? empty : schema))\n\n const schema = whenFilled(v.pipe(head, whenFilled(pipe(v.unknown(), validate))))\n\n return required ? schema : v.optional(schema)\n}\n\nconst pipe = (schema: AnySchema, actions: readonly AnyAction[]): AnySchema =>\n actions.reduce<AnySchema>((acc, action) => v.pipe(acc, action), schema)\n","import * as v from 'valibot'\nimport type { FieldTypeDef } from './field-type-def'\n\nexport const defaultFieldTypes = {\n text: { kind: 'string', schema: () => v.string() },\n password: { kind: 'string', schema: () => v.string() },\n email: { kind: 'string', schema: () => v.string(), defaultRules: [{ rule: 'email' }] },\n number: { kind: 'number', schema: () => v.number() },\n} satisfies Record<string, FieldTypeDef>\n","import * as v from 'valibot'\nimport type { KindDef } from './kind-def'\n\nconst isNil = (input: unknown) => input === undefined || input === null\n\nexport const defaultKinds = {\n string: {\n isEmpty: (input) => isNil(input) || input === '',\n required: (message) => v.nonEmpty(message),\n },\n // A cleared <input type=\"number\"/> holds '' or NaN, depending on the adapter.\n number: {\n isEmpty: (input) => isNil(input) || input === '' || Number.isNaN(input),\n },\n // Required means \"must be ticked\", e.g accepting terms.\n boolean: {\n isEmpty: isNil,\n required: (message) => v.value(true, message),\n },\n date: {\n isEmpty: (input) => isNil(input) || input === '',\n required: (message) => v.nonEmpty(message),\n },\n array: {\n isEmpty: (input) => isNil(input) || (Array.isArray(input) && input.length === 0),\n required: (message) => v.minLength(1, message),\n },\n file: { isEmpty: isNil },\n object: { isEmpty: isNil },\n // Display elements (headings, dividers) have no value, so the builder never calls this.\n none: { isEmpty: () => true },\n} satisfies Record<string, KindDef>\n","import type { AnyObjectSchema } from '../shared/valibot-types'\nimport type { RuleDef } from './rule-def'\n\nexport const defineRule = <T extends AnyObjectSchema>(def: RuleDef<T>): RuleDef<T> => def\n","import * as v from 'valibot'\nimport { defineRule } from './define-rule'\nimport type { RuleDef } from './rule-def'\n\nconst nonNegInt = v.pipe(v.number(), v.integer(), v.minValue(0))\n\nexport const defaultRules = {\n trim: defineRule({ kinds: ['string'], stage: 'normalize', create: () => v.trim() }),\n minLength: defineRule({\n kinds: ['string'],\n params: v.object({ value: nonNegInt }),\n create: (p, m) => v.minLength(p.value, m),\n }),\n maxLength: defineRule({\n kinds: ['string'],\n params: v.object({ value: nonNegInt }),\n create: (p, m) => v.maxLength(p.value, m),\n }),\n regex: defineRule({\n kinds: ['string'],\n params: v.object({ pattern: v.string(), flags: v.optional(v.string()) }),\n create: (p, m) => v.regex(new RegExp(p.pattern, p.flags), m),\n }),\n email: defineRule({ kinds: ['string'], create: (_, m) => v.email(m) }),\n url: defineRule({ kinds: ['string'], create: (_, m) => v.url(m) }),\n minValue: defineRule({\n kinds: ['number'],\n params: v.object({ value: v.number() }),\n create: (p, m) => v.minValue(p.value, m),\n }),\n maxValue: defineRule({\n kinds: ['number'],\n params: v.object({ value: v.number() }),\n create: (p, m) => v.maxValue(p.value, m),\n }),\n integer: defineRule({ kinds: ['number'], create: (_, m) => v.integer(m) }),\n} satisfies Record<string, RuleDef>\n","import { defaultFieldTypes } from '../field-types/default-field-types'\nimport type { FieldTypeDef } from '../field-types/field-type-def'\nimport { defaultKinds } from '../kinds/default-kinds'\nimport type { KindDef } from '../kinds/kind-def'\nimport { defaultRules } from '../rules/default-rules'\nimport type { RuleDef } from '../rules/rule-def'\n\nexport interface Registry {\n readonly kinds: Readonly<Record<string, KindDef>>\n readonly fieldTypes: Readonly<Record<string, FieldTypeDef>>\n readonly rules: Readonly<Record<string, RuleDef>>\n}\n\n/** Consumer entries replace defaults with the same key. */\nexport function mergeRegistry(custom: Partial<Registry>): Registry {\n return {\n kinds: Object.freeze({ ...defaultKinds, ...custom.kinds }),\n fieldTypes: Object.freeze({ ...defaultFieldTypes, ...custom.fieldTypes }),\n rules: Object.freeze({ ...defaultRules, ...custom.rules }),\n }\n}\n\nexport function lookup<T>(record: Readonly<Record<string, T>>, key: string): T | undefined {\n return Object.hasOwn(record, key) ? record[key] : undefined\n}\n","/** Where something is in a definition, e.g. `['fields', 0, 'rules', 1]`. */\nexport type DefinitionPath = readonly (string | number)[]\n\n/** Public API: adding a code is a minor change, renaming one is breaking, */\nexport type DefinitionIssueCode =\n | 'invalid_shape'\n | 'unsupported_version'\n | 'unknown_key'\n | 'unknown_type'\n | 'missing_name'\n | 'duplicate_name'\n | 'invalid_props'\n | 'unknown_rule'\n | 'unknown_param'\n | 'invalid_params'\n | 'kind_mismatch'\n\nexport interface DefinitionIssue {\n code: DefinitionIssueCode\n path: DefinitionPath\n message: string\n}\n\nexport type ValidateResult =\n | { success: true; issues: [] }\n | { success: false; issues: DefinitionIssue[] }\n\nexport function formatPath(path: DefinitionPath): string {\n return path.reduce<string>((out, key) => {\n if (typeof key === 'number') return `${out}[${key}]`\n return out ? `${out}.${key}` : key\n }, '')\n}\n\n/** Thrown by `build()` when the definition has issues. Lists all of them. */\nexport class DefinitionError extends Error {\n readonly issues: readonly DefinitionIssue[]\n\n constructor(issues: readonly DefinitionIssue[]) {\n const lines = issues.map(\n (issue) => ` ${formatPath(issue.path) || '(root)'}: ${issue.message}`,\n )\n super(`Invalid form definition:\\n${lines.join('\\n')}`)\n this.name = 'DefinitionError'\n this.issues = issues\n }\n}\n","/**\n * Field keys this package owns. A field type's props can't use them.\n */\nexport const RESERVED_FIELD_KEYS = [\n 'name',\n 'type',\n 'label',\n 'description',\n 'placeholder',\n 'default',\n 'required',\n 'rules',\n 'ui',\n] as const\n\n/**\n * Rule keys this package own. A rule's params can't use them.\n */\nexport const RESERVED_RULE_KEYS = ['rule', 'message'] as const\n\nconst fieldKeys: ReadonlySet<string> = new Set(RESERVED_FIELD_KEYS)\nconst ruleKeys: ReadonlySet<string> = new Set(RESERVED_RULE_KEYS)\n\nexport const isReservedFieldKey = (key: string): boolean => fieldKeys.has(key)\nexport const isReservedRuleKey = (key: string): boolean => ruleKeys.has(key)\n","import * as v from 'valibot'\nimport type { JsonField, JsonRule } from '../definition/json-form-schema'\nimport {\n type DefinitionPath,\n type DefinitionIssue,\n type DefinitionIssueCode,\n formatPath,\n} from '../errors/definition-error'\nimport type { FieldTypeDef } from '../field-types/field-type-def'\nimport type { KindDef } from '../kinds/kind-def'\nimport type { RuleDef } from '../rules/rule-def'\nimport { lookup, type Registry } from './registry'\nimport { isReservedFieldKey, isReservedRuleKey } from '../shared/reserved-keys'\nimport type { ValueKind } from '../shared/value-kind'\n\nexport interface CheckedField {\n name: string\n field: JsonField\n path: DefinitionPath\n type: FieldTypeDef\n kind: KindDef\n rules: CheckedRule[]\n}\n\nexport interface CheckedRule {\n name: string\n def: RuleDef\n params: Record<string, unknown>\n message?: string\n}\n\ninterface CheckResult {\n issues: DefinitionIssue[]\n fields: CheckedField[]\n}\n\nconst formShape = v.object({ version: v.string(), fields: v.array(v.unknown()) })\nconst fieldShape = v.looseObject({\n name: v.optional(v.string()),\n type: v.string(),\n label: v.optional(v.string()),\n description: v.optional(v.string()),\n placeholder: v.optional(v.string()),\n default: v.optional(v.unknown()),\n required: v.optional(v.union([v.boolean(), v.strictObject({ message: v.optional(v.string()) })])),\n rules: v.optional(v.array(v.unknown())),\n ui: v.optional(v.record(v.string(), v.unknown())),\n})\nconst ruleShape = v.looseObject({ rule: v.string(), message: v.optional(v.string()) })\nconst noParams = v.object({})\n\n/**\n * Checks a whole definition and collects every issue, in document order. Never throws.\n */\nexport function checkDefinition(json: unknown, registry: Registry): CheckResult {\n const issues: DefinitionIssue[] = []\n const fields: CheckedField[] = []\n\n const form = v.safeParse(formShape, json)\n if (!form.success) return { issues: toIssues('invalid_shape', [], form.issues), fields }\n\n if (form.output.version !== '1') {\n const message = `Unsupported version \"${form.output.version}\". This package reads version '1'.`\n return { issues: [{ code: 'unsupported_version', path: ['version'], message }], fields }\n }\n\n const names = new Set<string>()\n form.output.fields.forEach((raw, i) => {\n const field = checkField(raw, ['fields', i], registry, names, issues)\n if (field) fields.push(field)\n })\n\n return { issues, fields }\n}\n\nfunction checkField(\n raw: unknown,\n path: DefinitionPath,\n registry: Registry,\n names: Set<string>,\n issues: DefinitionIssue[],\n): CheckedField | undefined {\n const shape = v.safeParse(fieldShape, raw)\n if (!shape.success) {\n issues.push(...toIssues('invalid_shape', path, shape.issues))\n return undefined\n }\n\n const field = shape.output as JsonField\n const issueCount = issues.length\n const type = lookup(registry.fieldTypes, field.type)\n const hasValue = type?.kind !== 'none'\n\n if (!field.name) {\n if (type && hasValue) {\n issues.push(issue('missing_name', [...path, 'name'], 'A field needs a name'))\n }\n } else if (hasValue) {\n if (names.has(field.name)) {\n issues.push(issue('duplicate_name', [...path, 'name'], `Duplicate name \"${field.name}\"`))\n }\n names.add(field.name)\n }\n\n if (!type) {\n issues.push(issue('unknown_type', [...path, 'type'], `Unknown field type \"${field.type}\"`))\n return undefined\n }\n\n const props = checkProps(field, type, path, issues)\n\n if (!hasValue) {\n for (const key of ['required', 'rules'] as const) {\n if (field[key] !== undefined) {\n const message = `A \"${field.type}\" field has no value, so it can't have \"${key}\"`\n issues.push(issue('invalid_shape', [...path, key], message))\n }\n }\n return undefined\n }\n\n const fieldRules = (shape.output.rules ?? []).flatMap(\n (rule, j) => checkRule(rule, type?.kind, [...path, 'rules', j], registry, issues) ?? [],\n )\n if (issues.length > issueCount || !field.name) return undefined\n\n const overridden = new Set(fieldRules.map((rule) => rule.name))\n const defaultRules = (type?.defaultRules ?? [])\n .filter((rule) => !overridden.has(rule.rule))\n .flatMap((rule) => checkRule(rule, type?.kind, path, registry, []) ?? [])\n\n return {\n name: field.name,\n field: { ...field, ...props },\n path,\n type,\n kind: registry.kinds[type?.kind] as KindDef,\n rules: [...defaultRules, ...fieldRules],\n }\n}\n\nfunction checkProps(\n field: JsonField,\n type: FieldTypeDef,\n path: DefinitionPath,\n issues: DefinitionIssue[],\n): Record<string, unknown> {\n const propKeys = Object.keys(type.props?.entries ?? {})\n const input: Record<string, unknown> = {}\n\n for (const key of Object.keys(field)) {\n if (isReservedFieldKey(key)) continue\n if (propKeys.includes(key)) {\n input[key] = field[key]\n } else {\n issues.push(\n issue('unknown_key', [...path, key], `Unknown key \"${key}\" on a \"${field.type}\" field`),\n )\n }\n }\n\n if (!type.props) return {}\n const parsed = v.safeParse(type.props, input)\n if (parsed.success) return parsed.output\n issues.push(...toIssues('invalid_props', path, parsed.issues))\n return {}\n}\n\nexport function checkRule(\n raw: unknown,\n kind: ValueKind,\n path: DefinitionPath,\n registry: Registry,\n issues: DefinitionIssue[],\n): CheckedRule | undefined {\n const shape = v.safeParse(ruleShape, raw)\n if (!shape.success) {\n issues.push(...toIssues('invalid_shape', path, shape.issues))\n return undefined\n }\n\n const rule: JsonRule = shape.output\n const def = lookup(registry.rules, rule.rule)\n if (!def) {\n issues.push(issue('unknown_rule', [...path, 'rule'], `Unknown rule \"${rule.rule}\"`))\n return undefined\n }\n\n const issueCount = issues.length\n if (!def.kinds.includes(kind)) {\n const message = `Rule \"${rule.rule} can't be used on ${kind} fields`\n issues.push(issue('kind_mismatch', [...path, 'rule'], message))\n }\n\n const paramKeys = Object.keys(def.params?.entries ?? {})\n const input: Record<string, unknown> = {}\n for (const key of Object.keys(rule)) {\n if (isReservedRuleKey(key)) continue\n if (paramKeys.includes(key)) {\n input[key] = rule[key]\n } else {\n issues.push(\n issue('unknown_param', [...path, key], `Rule \"${rule.rule}\" has no param \"${key}\"`),\n )\n }\n }\n\n const parsed = v.safeParse(def.params ?? noParams, input)\n if (!parsed.success) issues.push(...toIssues('invalid_params', path, parsed.issues))\n if (!parsed.success || issues.length > issueCount) return undefined\n\n return { name: rule.rule, def, params: parsed.output, message: rule.message }\n}\n\nexport function checkRegistry(registry: Registry): void {\n for (const [name, rule] of Object.entries(registry.rules)) {\n for (const key of Object.keys(rule.params?.entries ?? {})) {\n if (isReservedRuleKey(key)) {\n throw new TypeError(`Rule \"${name}\": params can't use the reserved key \"${key}\"`)\n }\n }\n }\n\n for (const [name, type] of Object.entries(registry.fieldTypes)) {\n const at = `Field type \"${name}\"`\n if (!lookup(registry.kinds, type.kind)) {\n throw new TypeError(`${at}: kind \"${type.kind}\" is not registered`)\n }\n if (type.kind !== 'none' && !type.schema) {\n throw new TypeError(`${at}: schema() is required unless the kind is 'none'`)\n }\n for (const key of Object.keys(type.props?.entries ?? {})) {\n if (isReservedFieldKey(key)) {\n throw new TypeError(`${at}: props can't use the reserved key \"${key}\"`)\n }\n }\n\n const issues: DefinitionIssue[] = []\n type.defaultRules?.forEach((rule, j) => {\n checkRule(rule, type.kind, ['defaultRules', j], registry, issues)\n })\n const [first] = issues\n if (first) throw new TypeError(`${at}: ${formatPath(first.path)}: ${first.message}`)\n }\n}\n\nfunction issue(code: DefinitionIssueCode, path: DefinitionPath, message: string): DefinitionIssue {\n return { code, path, message }\n}\n\nfunction toIssues(\n code: DefinitionIssueCode,\n path: DefinitionPath,\n valibotIssues: readonly v.BaseIssue<unknown>[],\n): DefinitionIssue[] {\n return valibotIssues.map((valibotIssue) => {\n const keys = valibotIssue.path?.map((item) => item.key as string | number) ?? []\n return issue(code, [...path, ...keys], valibotIssue.message)\n })\n}\n","import * as v from 'valibot'\nimport type { KindDef } from '../kinds/kind-def'\nimport type { FieldTypeDef } from '../field-types/field-type-def'\nimport type { RuleDef } from '../rules/rule-def'\nimport { buildFieldSchema, type MessageContext, type Messages } from './build-field-schema'\nimport { mergeRegistry } from './registry'\nimport { checkDefinition, checkRegistry } from './validate-definition'\nimport { DefinitionError, type ValidateResult } from '../errors/definition-error'\nimport type { JsonForm } from '../definition/json-form-schema'\n\nexport interface SchemaBuilderOptions {\n kinds?: Record<string, KindDef>\n fieldTypes?: Record<string, FieldTypeDef>\n rules?: Record<string, RuleDef>\n messages?: { required?: string }\n /**\n * Called with every message from the definition and with `messages.required`, e.g. to treat\n * them as i18n keys. Return undefined to use the text as written.\n */\n resolveMessage?(key: string, ctx: MessageContext): string | undefined\n}\n\nexport function createSchemaBuilder(options: SchemaBuilderOptions = {}) {\n const registry = mergeRegistry(options)\n checkRegistry(registry)\n\n const messages: Messages = {\n required: options.messages?.required ?? 'Required',\n resolve: (text, ctx) => options.resolveMessage?.(text, ctx) ?? text,\n }\n\n /** Every issue in the definition, in document order. Never throws on bad input. */\n function validate(json: unknown): ValidateResult {\n const { issues } = checkDefinition(json, registry)\n return issues.length ? { success: false, issues } : { success: true, issues: [] }\n }\n\n function build(json: JsonForm) {\n const { issues, fields } = checkDefinition(json, registry)\n if (issues.length) throw new DefinitionError(issues)\n\n const entries: v.ObjectEntries = {}\n for (const field of fields) {\n entries[field.name] = buildFieldSchema(field, messages)\n }\n return v.object(entries)\n }\n\n return {\n build,\n validate,\n kinds: registry.kinds,\n fieldTypes: registry.fieldTypes,\n rules: registry.rules,\n }\n}\n","import type { AnyObjectSchema } from '../shared/valibot-types'\nimport type { FieldTypeDef } from './field-type-def'\n\nexport const defineFieldType = <T extends AnyObjectSchema>(def: FieldTypeDef<T>): FieldTypeDef<T> =>\n def\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@whatsaaaa/form-builder-schema",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Build Formisch-ready Valibot schemas from JSON form definitions.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"form",
|
|
7
|
+
"formisch",
|
|
8
|
+
"json",
|
|
9
|
+
"schema"
|
|
10
|
+
],
|
|
11
|
+
"homepage": "https://github.com/whatsaaaa/form-builder",
|
|
12
|
+
"bugs": "https://github.com/whatsaaaa/form-builder/issues",
|
|
5
13
|
"license": "MIT",
|
|
6
14
|
"author": "Whatsaaaa",
|
|
7
15
|
"repository": {
|
|
@@ -12,14 +20,6 @@
|
|
|
12
20
|
"files": [
|
|
13
21
|
"dist"
|
|
14
22
|
],
|
|
15
|
-
"homepage": "https://github.com/whatsaaaa/form-builder",
|
|
16
|
-
"bugs": "https://github.com/whatsaaaa/form-builder/issues",
|
|
17
|
-
"keywords": [
|
|
18
|
-
"formisch",
|
|
19
|
-
"form",
|
|
20
|
-
"json",
|
|
21
|
-
"schema"
|
|
22
|
-
],
|
|
23
23
|
"type": "module",
|
|
24
24
|
"sideEffects": false,
|
|
25
25
|
"exports": {
|