@formadapter/core 0.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/LICENSE +21 -0
- package/README.md +70 -0
- package/dist/index.d.ts +328 -0
- package/dist/index.js +1172 -0
- package/dist/index.js.map +1 -0
- package/package.json +56 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Ludicrous
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# `@formadapter/core`
|
|
2
|
+
|
|
3
|
+
Framework-free schema compilation, value preparation, validation helpers, and
|
|
4
|
+
submission state for FormAdapter.
|
|
5
|
+
|
|
6
|
+
```sh
|
|
7
|
+
bun add @formadapter/core zod
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
## Schema contract
|
|
11
|
+
|
|
12
|
+
A schema must expose both Standard Schema V1 validation and Standard JSON
|
|
13
|
+
Schema V1 input conversion. Zod 4 and ArkType 2 provide those interfaces
|
|
14
|
+
natively, so this package has no runtime dependency on either library.
|
|
15
|
+
|
|
16
|
+
```ts
|
|
17
|
+
import { compileForm, getDefaultValues, validate } from "@formadapter/core";
|
|
18
|
+
import { z } from "zod";
|
|
19
|
+
|
|
20
|
+
const schema = z.object({
|
|
21
|
+
email: z.email(),
|
|
22
|
+
seats: z.number().int().min(1),
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
const model = compileForm(schema);
|
|
26
|
+
const defaults = getDefaultValues(model);
|
|
27
|
+
const result = await validate(schema, { email: "ada@example.com", seats: 2 });
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
FormAdapter reads only the schema's input-side JSON Schema for rendering, then
|
|
31
|
+
delegates validation and transformation back to the original schema. Input and
|
|
32
|
+
output types may therefore differ without losing type safety.
|
|
33
|
+
|
|
34
|
+
`compileForm(schema, config)` normalizes objects, scalar/object arrays,
|
|
35
|
+
constraints, formats, options, defaults, references, `allOf`, nullable values,
|
|
36
|
+
and discriminated object unions into a renderer-neutral model. Configuration
|
|
37
|
+
can add presentation rules, dynamic options, async field validators, array
|
|
38
|
+
labels, and custom control names without changing the schema.
|
|
39
|
+
|
|
40
|
+
## Prepared values and presentation rules
|
|
41
|
+
|
|
42
|
+
`prepareFormValues(model, values)` applies browser-to-schema absence semantics:
|
|
43
|
+
optional blanks become absent, nullable blanks become `null`, empty optional
|
|
44
|
+
objects disappear, and fields hidden by presentation predicates are pruned.
|
|
45
|
+
`validatePresentationRules` enforces `requiredWhenVisible` independently of the
|
|
46
|
+
schema. Use both at a server trust boundary when presentation rules matter.
|
|
47
|
+
|
|
48
|
+
## Submission state
|
|
49
|
+
|
|
50
|
+
`SubmissionState<Data>` is the serializable contract shared by the React,
|
|
51
|
+
server, Next.js, TanStack Start, oRPC, and HTTP packages. Use
|
|
52
|
+
`submissionSuccess`, `submissionFailure`, `initialSubmissionState`, and
|
|
53
|
+
`isSubmissionState` when writing a custom transport.
|
|
54
|
+
|
|
55
|
+
The package also exports typed path helpers, option serialization, issue/error
|
|
56
|
+
normalization, schema input/output inference, default construction, and the
|
|
57
|
+
canonical `isReservedFormPathSegment` guard for custom transports.
|
|
58
|
+
|
|
59
|
+
## Boundaries
|
|
60
|
+
|
|
61
|
+
Tuples, dynamic record keys, general structural unions, circular references,
|
|
62
|
+
and discriminated object unions inside arrays compile to explicit unsupported
|
|
63
|
+
nodes. Renderers can show those nodes through their own diagnostic slot instead
|
|
64
|
+
of silently dropping schema structure. Multiple file selection must use an
|
|
65
|
+
array-of-files schema; `multiple: true` is rejected on a scalar file field.
|
|
66
|
+
|
|
67
|
+
Property names must also be losslessly encodable as form paths. Empty,
|
|
68
|
+
numeric-like, transport-reserved, `root`, or prototype-reserved names and names that
|
|
69
|
+
contain `.`, `[`, `]`, `'`, or `"` compile to an unsupported node instead of
|
|
70
|
+
binding to the wrong value.
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,328 @@
|
|
|
1
|
+
//#region src/path-segment.d.ts
|
|
2
|
+
declare const STANDARD_OBJECT_PROTOTYPE_SEGMENTS: readonly ["__defineGetter__", "__defineSetter__", "__lookupGetter__", "__lookupSetter__", "__proto__", "constructor", "hasOwnProperty", "isPrototypeOf", "propertyIsEnumerable", "toLocaleString", "toString", "valueOf"];
|
|
3
|
+
type ReservedFormPathSegment = (typeof STANDARD_OBJECT_PROTOTYPE_SEGMENTS)[number] | "prototype" | "root";
|
|
4
|
+
/** Names that collide with JavaScript object lookup or form-runtime state. */
|
|
5
|
+
declare const RESERVED_FORM_PATH_SEGMENTS: ReadonlySet<string>;
|
|
6
|
+
/** True when a schema property cannot be represented losslessly as one path segment. */
|
|
7
|
+
declare function isReservedFormPathSegment(segment: string): boolean;
|
|
8
|
+
//#endregion
|
|
9
|
+
//#region src/path.d.ts
|
|
10
|
+
type Primitive = string | number | boolean | bigint | symbol | null | undefined;
|
|
11
|
+
type FileLike = {
|
|
12
|
+
readonly name: string;
|
|
13
|
+
readonly size: number;
|
|
14
|
+
readonly type: string;
|
|
15
|
+
readonly lastModified: number;
|
|
16
|
+
};
|
|
17
|
+
type Atomic = Primitive | Date | RegExp | Error | Function | Promise<unknown> | Map<unknown, unknown> | Set<unknown> | ArrayBuffer | DataView | FileLike;
|
|
18
|
+
type IsAny<T> = 0 extends 1 & T ? true : false;
|
|
19
|
+
type IsTraversable<T> = IsAny<T> extends true ? false : NonNullable<T> extends Atomic ? false : NonNullable<T> extends object ? true : false;
|
|
20
|
+
type NextDepth<Depth extends readonly unknown[]> = [...Depth, 0];
|
|
21
|
+
type AtMaxDepth<Depth extends readonly unknown[]> = Depth["length"] extends 8 ? true : false;
|
|
22
|
+
type Join<Prefix extends string, Key extends string> = Prefix extends "" ? Key : `${Prefix}.${Key}`;
|
|
23
|
+
type PathWhitespace = " " | "\f" | "\n" | "\r" | " " | "\v";
|
|
24
|
+
type TrimPathWhitespace<Key extends string> = Key extends `${PathWhitespace}${infer Rest}` ? TrimPathWhitespace<Rest> : Key extends `${infer Rest}${PathWhitespace}` ? TrimPathWhitespace<Rest> : Key;
|
|
25
|
+
type NumericLikePathKey<Key extends string> = TrimPathWhitespace<Key> extends "" ? true : TrimPathWhitespace<Key> extends `${number}` | "+Infinity" | "-Infinity" | "Infinity" ? true : false;
|
|
26
|
+
type SafePathKey<Key extends string> = Key extends "" | ReservedFormPathSegment ? never : NumericLikePathKey<Key> extends true ? never : Key extends `$ACTION_${string}` | `__formadapter_${string}` | `${string}.${string}` | `${string}[${string}` | `${string}]${string}` | `${string}'${string}` | `${string}"${string}` ? never : Key;
|
|
27
|
+
type PathsForValue<Value, Prefix extends string, Depth extends readonly unknown[]> = Prefix | (AtMaxDepth<Depth> extends true ? never : NonNullable<Value> extends readonly (infer Item)[] ? `${Prefix}[]` | (IsTraversable<Item> extends true ? PathsForValue<NonNullable<Item>, `${Prefix}[]`, NextDepth<Depth>> : never) : IsTraversable<Value> extends true ? ObjectPaths<NonNullable<Value>, Prefix, NextDepth<Depth>> : never);
|
|
28
|
+
type ObjectPaths<Value, Prefix extends string, Depth extends readonly unknown[]> = Value extends unknown ? Value extends object ? { [Key in Extract<keyof Value, string>]-?: PathsForValue<Value[Key], Join<Prefix, SafePathKey<Key>>, Depth> }[Extract<keyof Value, string>] : never : never;
|
|
29
|
+
/** Canonical schema field paths. Array items use `[]`, for example `users[].email`. */
|
|
30
|
+
type FieldPath<Value> = IsTraversable<Value> extends true ? Extract<ObjectPaths<NonNullable<Value>, "", []>, string> : never;
|
|
31
|
+
type SegmentValue<Value, Segment extends string> = Value extends unknown ? Segment extends `${infer Prefix}[]` ? SegmentValue<Value, Prefix> extends readonly (infer Item)[] ? Item : never : Segment extends keyof NonNullable<Value> ? NonNullable<Value>[Segment] : never : never;
|
|
32
|
+
type PathValueInternal<Value, Path extends string> = Path extends `${infer Head}.${infer Tail}` ? PathValueInternal<SegmentValue<Value, Head>, Tail> : SegmentValue<Value, Path>;
|
|
33
|
+
type PathValue<Value, Path extends FieldPath<Value>> = PathValueInternal<Value, Path>;
|
|
34
|
+
type DeepPartial<Value> = IsAny<Value> extends true ? Value : Value extends Atomic ? Value : Value extends readonly (infer Item)[] ? Array<DeepPartial<Item>> : Value extends object ? { [Key in keyof Value]?: DeepPartial<Value[Key]> } : Value;
|
|
35
|
+
declare function pathToName(path: ReadonlyArray<string | number>): string;
|
|
36
|
+
declare function pathToConfigPath(path: ReadonlyArray<string | number>): string;
|
|
37
|
+
//#endregion
|
|
38
|
+
//#region src/types.d.ts
|
|
39
|
+
type JsonPrimitive = string | number | boolean | null;
|
|
40
|
+
type JsonValue = JsonPrimitive | readonly JsonValue[] | {
|
|
41
|
+
readonly [key: string]: JsonValue;
|
|
42
|
+
};
|
|
43
|
+
type JsonSchema = boolean | JsonSchemaObject;
|
|
44
|
+
interface JsonSchemaObject {
|
|
45
|
+
readonly $ref?: string;
|
|
46
|
+
readonly $defs?: Readonly<Record<string, JsonSchema>>;
|
|
47
|
+
readonly definitions?: Readonly<Record<string, JsonSchema>>;
|
|
48
|
+
readonly type?: string | readonly string[];
|
|
49
|
+
readonly title?: string;
|
|
50
|
+
readonly description?: string;
|
|
51
|
+
readonly default?: unknown;
|
|
52
|
+
readonly readOnly?: boolean;
|
|
53
|
+
readonly properties?: Readonly<Record<string, JsonSchema>>;
|
|
54
|
+
readonly additionalProperties?: JsonSchema;
|
|
55
|
+
readonly patternProperties?: Readonly<Record<string, JsonSchema>>;
|
|
56
|
+
readonly propertyNames?: JsonSchema;
|
|
57
|
+
readonly required?: readonly string[];
|
|
58
|
+
readonly items?: JsonSchema | readonly JsonSchema[];
|
|
59
|
+
readonly prefixItems?: readonly JsonSchema[];
|
|
60
|
+
readonly enum?: readonly unknown[];
|
|
61
|
+
readonly const?: unknown;
|
|
62
|
+
readonly anyOf?: readonly JsonSchema[];
|
|
63
|
+
readonly oneOf?: readonly JsonSchema[];
|
|
64
|
+
readonly allOf?: readonly JsonSchema[];
|
|
65
|
+
readonly format?: string;
|
|
66
|
+
readonly minLength?: number;
|
|
67
|
+
readonly maxLength?: number;
|
|
68
|
+
readonly pattern?: string;
|
|
69
|
+
readonly minimum?: number;
|
|
70
|
+
readonly maximum?: number;
|
|
71
|
+
readonly exclusiveMinimum?: number;
|
|
72
|
+
readonly exclusiveMaximum?: number;
|
|
73
|
+
readonly multipleOf?: number;
|
|
74
|
+
readonly minItems?: number;
|
|
75
|
+
readonly maxItems?: number;
|
|
76
|
+
readonly uniqueItems?: boolean;
|
|
77
|
+
readonly contentEncoding?: string;
|
|
78
|
+
readonly contentMediaType?: string;
|
|
79
|
+
readonly [keyword: string]: unknown;
|
|
80
|
+
}
|
|
81
|
+
type BuiltInControl = "text" | "email" | "url" | "tel" | "password" | "search" | "date" | "datetime-local" | "time" | "textarea" | "number" | "range" | "checkbox" | "select" | "radio" | "file" | "hidden";
|
|
82
|
+
type NativeInputType = Exclude<BuiltInControl, "textarea" | "select" | "radio">;
|
|
83
|
+
type FieldDataType = "string" | "number" | "integer" | "boolean" | "file" | "unknown";
|
|
84
|
+
type FieldPredicate<Values> = (values: Readonly<DeepPartial<Values>>) => boolean;
|
|
85
|
+
type FieldState<Values> = boolean | FieldPredicate<Values>;
|
|
86
|
+
type MaybePromise<Value> = Promise<Value> | Value;
|
|
87
|
+
/** The portable subset of AbortSignal exposed to field validators. */
|
|
88
|
+
interface AsyncFieldValidationSignal {
|
|
89
|
+
readonly aborted: boolean;
|
|
90
|
+
readonly reason: unknown;
|
|
91
|
+
addEventListener(type: "abort", listener: () => void, options?: {
|
|
92
|
+
readonly once?: boolean;
|
|
93
|
+
}): void;
|
|
94
|
+
removeEventListener(type: "abort", listener: () => void): void;
|
|
95
|
+
throwIfAborted(): void;
|
|
96
|
+
}
|
|
97
|
+
interface AsyncFieldValidationContext {
|
|
98
|
+
/** Aborted when a newer validation for the same runtime field starts. */
|
|
99
|
+
readonly signal: AsyncFieldValidationSignal;
|
|
100
|
+
}
|
|
101
|
+
type AsyncFieldValidator<Value, Values> = (value: Value, values: Readonly<DeepPartial<Values>>, context: AsyncFieldValidationContext) => MaybePromise<readonly string[] | string | undefined>;
|
|
102
|
+
interface FormOption<Value extends JsonPrimitive = JsonPrimitive> {
|
|
103
|
+
readonly label: string;
|
|
104
|
+
readonly value: Value;
|
|
105
|
+
}
|
|
106
|
+
type FieldOptionValue<Value> = unknown extends Value ? JsonPrimitive : Extract<Value, JsonPrimitive>;
|
|
107
|
+
type FieldOptions<Values, Value = unknown> = readonly FormOption<FieldOptionValue<Value>>[] | ((values: Readonly<DeepPartial<Values>>) => readonly FormOption<FieldOptionValue<Value>>[]);
|
|
108
|
+
interface ArrayConfig<Values = unknown> {
|
|
109
|
+
readonly addLabel?: string;
|
|
110
|
+
readonly removeLabel?: string;
|
|
111
|
+
readonly moveUpLabel?: string;
|
|
112
|
+
readonly moveDownLabel?: string;
|
|
113
|
+
readonly itemLabel?: string | ((index: number, item: unknown, values: Readonly<DeepPartial<Values>>) => string);
|
|
114
|
+
}
|
|
115
|
+
interface FieldConfig<Control extends string = never, Values = unknown, Value = unknown> {
|
|
116
|
+
readonly label?: string;
|
|
117
|
+
readonly description?: string;
|
|
118
|
+
readonly placeholder?: string;
|
|
119
|
+
readonly control?: BuiltInControl | Control;
|
|
120
|
+
readonly hidden?: FieldState<Values>;
|
|
121
|
+
readonly disabled?: FieldState<Values>;
|
|
122
|
+
readonly readOnly?: FieldState<Values>;
|
|
123
|
+
readonly order?: number;
|
|
124
|
+
readonly className?: string;
|
|
125
|
+
readonly controlProps?: Readonly<Record<string, unknown>>;
|
|
126
|
+
readonly defaultValue?: Value;
|
|
127
|
+
/** A static list or a synchronous projection of query-backed application state. */
|
|
128
|
+
readonly options?: FieldOptions<Values, Value>;
|
|
129
|
+
readonly multiple?: boolean;
|
|
130
|
+
/** Makes an optional schema field required only while it is rendered. */
|
|
131
|
+
readonly requiredWhenVisible?: FieldState<Values>;
|
|
132
|
+
readonly requiredMessage?: string;
|
|
133
|
+
/** Runs after authoritative schema validation succeeds for this field. */
|
|
134
|
+
readonly asyncValidate?: AsyncFieldValidator<Value, Values>;
|
|
135
|
+
/** Debounces async validation. Defaults to 250ms. */
|
|
136
|
+
readonly asyncValidationDebounceMs?: number;
|
|
137
|
+
readonly array?: ArrayConfig<Values>;
|
|
138
|
+
}
|
|
139
|
+
interface FormConfig<Input, Control extends string = never> {
|
|
140
|
+
readonly fields?: { readonly [Path in FieldPath<Input>]?: FieldConfig<Control, Input, PathValue<Input, Path>> | undefined };
|
|
141
|
+
readonly jsonSchema?: {
|
|
142
|
+
readonly libraryOptions?: Readonly<Record<string, unknown>>;
|
|
143
|
+
readonly opaqueRefinements?: "base" | "error";
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
interface ResolvedFieldConfig<Control extends string = never, Values = unknown, Value = unknown> {
|
|
147
|
+
readonly control?: BuiltInControl | Control;
|
|
148
|
+
readonly placeholder?: string;
|
|
149
|
+
readonly hidden: FieldState<Values>;
|
|
150
|
+
readonly disabled: FieldState<Values>;
|
|
151
|
+
readonly readOnly: FieldState<Values>;
|
|
152
|
+
readonly order?: number;
|
|
153
|
+
readonly className?: string;
|
|
154
|
+
readonly controlProps?: Readonly<Record<string, unknown>>;
|
|
155
|
+
readonly multiple: boolean;
|
|
156
|
+
readonly options?: FieldOptions<Values, Value>;
|
|
157
|
+
readonly requiredWhenVisible: FieldState<Values>;
|
|
158
|
+
readonly requiredMessage?: string;
|
|
159
|
+
readonly asyncValidate?: AsyncFieldValidator<Value, Values>;
|
|
160
|
+
readonly asyncValidationDebounceMs: number;
|
|
161
|
+
readonly array?: ArrayConfig<Values>;
|
|
162
|
+
readonly extensions: Readonly<Record<string, unknown>>;
|
|
163
|
+
}
|
|
164
|
+
interface ScalarConstraints {
|
|
165
|
+
readonly format?: string;
|
|
166
|
+
readonly minLength?: number;
|
|
167
|
+
readonly maxLength?: number;
|
|
168
|
+
readonly pattern?: string;
|
|
169
|
+
readonly minimum?: number;
|
|
170
|
+
readonly maximum?: number;
|
|
171
|
+
readonly exclusiveMinimum?: number;
|
|
172
|
+
readonly exclusiveMaximum?: number;
|
|
173
|
+
readonly multipleOf?: number;
|
|
174
|
+
readonly accept?: string;
|
|
175
|
+
readonly multiple: boolean;
|
|
176
|
+
readonly contentEncoding?: string;
|
|
177
|
+
readonly contentMediaType?: string;
|
|
178
|
+
}
|
|
179
|
+
interface BaseField<Kind extends FormNode["kind"], Control extends string, Values> {
|
|
180
|
+
readonly kind: Kind;
|
|
181
|
+
readonly key: string;
|
|
182
|
+
readonly path: string;
|
|
183
|
+
readonly label: string;
|
|
184
|
+
readonly description?: string;
|
|
185
|
+
readonly required: boolean;
|
|
186
|
+
readonly nullable: boolean;
|
|
187
|
+
readonly defaultValue?: unknown;
|
|
188
|
+
readonly config: ResolvedFieldConfig<Control, Values>;
|
|
189
|
+
readonly source: JsonSchema;
|
|
190
|
+
}
|
|
191
|
+
interface ScalarField<Control extends string = never, Values = unknown> extends BaseField<"scalar", Control, Values> {
|
|
192
|
+
readonly dataType: FieldDataType;
|
|
193
|
+
readonly control: BuiltInControl | Control;
|
|
194
|
+
readonly inputType?: NativeInputType;
|
|
195
|
+
readonly constraints: ScalarConstraints;
|
|
196
|
+
readonly options?: readonly FormOption[];
|
|
197
|
+
}
|
|
198
|
+
interface ObjectField<Control extends string = never, Values = unknown> extends BaseField<"object", Control, Values> {
|
|
199
|
+
readonly children: readonly FormNode<Control, Values>[];
|
|
200
|
+
}
|
|
201
|
+
interface ArrayField<Control extends string = never, Values = unknown> extends BaseField<"array", Control, Values> {
|
|
202
|
+
readonly item: FormNode<Control, Values>;
|
|
203
|
+
readonly minItems?: number;
|
|
204
|
+
readonly maxItems?: number;
|
|
205
|
+
readonly uniqueItems: boolean;
|
|
206
|
+
}
|
|
207
|
+
interface UnsupportedField<Control extends string = never, Values = unknown> extends BaseField<"unsupported", Control, Values> {
|
|
208
|
+
readonly reason: string;
|
|
209
|
+
}
|
|
210
|
+
type FormNode<Control extends string = never, Values = unknown> = ScalarField<Control, Values> | ObjectField<Control, Values> | ArrayField<Control, Values> | UnsupportedField<Control, Values>;
|
|
211
|
+
interface FormModel<Input = unknown, Control extends string = never> {
|
|
212
|
+
readonly root: FormNode<Control, Input>;
|
|
213
|
+
readonly fields: readonly FormNode<Control, Input>[];
|
|
214
|
+
readonly fieldMap: Readonly<Record<string, FormNode<Control, Input>>>;
|
|
215
|
+
}
|
|
216
|
+
//#endregion
|
|
217
|
+
//#region src/standard.d.ts
|
|
218
|
+
/** A Standard Schema issue. Kept structural so schema libraries remain optional. */
|
|
219
|
+
interface StandardIssue {
|
|
220
|
+
readonly message: string;
|
|
221
|
+
readonly path?: ReadonlyArray<PropertyKey | {
|
|
222
|
+
readonly key: PropertyKey;
|
|
223
|
+
}> | undefined;
|
|
224
|
+
}
|
|
225
|
+
interface StandardSuccess<Output> {
|
|
226
|
+
readonly value: Output;
|
|
227
|
+
readonly issues?: undefined;
|
|
228
|
+
}
|
|
229
|
+
interface StandardFailure {
|
|
230
|
+
readonly issues: ReadonlyArray<StandardIssue>;
|
|
231
|
+
}
|
|
232
|
+
type StandardResult<Output> = StandardSuccess<Output> | StandardFailure;
|
|
233
|
+
interface StandardTypes<Input = unknown, Output = Input> {
|
|
234
|
+
readonly input: Input;
|
|
235
|
+
readonly output: Output;
|
|
236
|
+
}
|
|
237
|
+
interface StandardJSONSchemaOptions {
|
|
238
|
+
readonly target: "draft-2020-12" | "draft-07" | "openapi-3.0" | (string & {});
|
|
239
|
+
readonly libraryOptions?: Readonly<Record<string, unknown>> | undefined;
|
|
240
|
+
}
|
|
241
|
+
interface FormSchema<Input = unknown, Output = Input> {
|
|
242
|
+
readonly "~standard": {
|
|
243
|
+
readonly version: 1;
|
|
244
|
+
readonly vendor: string;
|
|
245
|
+
readonly types?: StandardTypes<Input, Output> | undefined;
|
|
246
|
+
readonly validate: (value: unknown, options?: {
|
|
247
|
+
readonly libraryOptions?: Readonly<Record<string, unknown>> | undefined;
|
|
248
|
+
}) => StandardResult<Output> | Promise<StandardResult<Output>>;
|
|
249
|
+
readonly jsonSchema: {
|
|
250
|
+
readonly input: (options: StandardJSONSchemaOptions) => Record<string, unknown>;
|
|
251
|
+
readonly output: (options: StandardJSONSchemaOptions) => Record<string, unknown>;
|
|
252
|
+
};
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
type InferInput<Schema extends FormSchema> = NonNullable<Schema["~standard"]["types"]>["input"];
|
|
256
|
+
type InferOutput<Schema extends FormSchema> = NonNullable<Schema["~standard"]["types"]>["output"];
|
|
257
|
+
//#endregion
|
|
258
|
+
//#region src/compile.d.ts
|
|
259
|
+
declare class SchemaConversionError extends Error {
|
|
260
|
+
override readonly name = "SchemaConversionError";
|
|
261
|
+
constructor(message: string, options?: ErrorOptions);
|
|
262
|
+
}
|
|
263
|
+
declare function toInputJsonSchema<Schema extends FormSchema>(schema: Schema, config?: FormConfig<InferInput<Schema>, string>): JsonSchemaObject;
|
|
264
|
+
declare function compileForm<Schema extends FormSchema, Control extends string = never>(schema: Schema, config?: FormConfig<InferInput<Schema>, Control>): FormModel<InferInput<Schema>, Control>;
|
|
265
|
+
//#endregion
|
|
266
|
+
//#region src/defaults.d.ts
|
|
267
|
+
declare function defaultValueForNode(node: FormNode<string, unknown>): unknown;
|
|
268
|
+
declare function getDefaultValues<Input, Control extends string>(model: FormModel<Input, Control>): DeepPartial<Input>;
|
|
269
|
+
//#endregion
|
|
270
|
+
//#region src/prepare.d.ts
|
|
271
|
+
/**
|
|
272
|
+
* Converts browser form values to schema input: optional blanks become absent,
|
|
273
|
+
* nullable blanks become null, and presentation-hidden branches are pruned.
|
|
274
|
+
*/
|
|
275
|
+
declare function prepareFormValues<Input, Control extends string>(model: FormModel<Input, Control>, values: unknown): unknown;
|
|
276
|
+
//#endregion
|
|
277
|
+
//#region src/options.d.ts
|
|
278
|
+
/** Stable DOM serialization for non-string option values. */
|
|
279
|
+
declare function serializeOptionValue(value: JsonPrimitive): string;
|
|
280
|
+
declare function optionForSerializedValue(options: readonly FormOption[], serialized: string): FormOption | undefined;
|
|
281
|
+
//#endregion
|
|
282
|
+
//#region src/submission.d.ts
|
|
283
|
+
type SubmissionErrorKind = "business" | "transport" | "validation";
|
|
284
|
+
interface IdleSubmission {
|
|
285
|
+
readonly status: "idle";
|
|
286
|
+
}
|
|
287
|
+
interface SuccessfulSubmission<Data = unknown> {
|
|
288
|
+
readonly status: "success";
|
|
289
|
+
readonly data?: Data;
|
|
290
|
+
readonly message?: string;
|
|
291
|
+
}
|
|
292
|
+
interface FailedSubmission {
|
|
293
|
+
readonly status: "error";
|
|
294
|
+
readonly errorKind: SubmissionErrorKind;
|
|
295
|
+
readonly fieldErrors: Readonly<Record<string, readonly string[]>>;
|
|
296
|
+
readonly formErrors: readonly string[];
|
|
297
|
+
}
|
|
298
|
+
type SubmissionState<Data = unknown> = FailedSubmission | IdleSubmission | SuccessfulSubmission<Data>;
|
|
299
|
+
type SubmissionAction<Payload, Data = unknown> = (previousState: SubmissionState<Data>, payload: Payload) => Promise<SubmissionState<Data>>;
|
|
300
|
+
declare const initialSubmissionState: IdleSubmission;
|
|
301
|
+
declare function submissionFailure(options?: {
|
|
302
|
+
readonly errorKind?: SubmissionErrorKind;
|
|
303
|
+
readonly fieldErrors?: Readonly<Record<string, readonly string[]>>;
|
|
304
|
+
readonly formErrors?: readonly string[];
|
|
305
|
+
}): FailedSubmission;
|
|
306
|
+
declare function submissionSuccess<Data = undefined>(data?: Data, message?: string): SuccessfulSubmission<Data>;
|
|
307
|
+
declare function isSubmissionState(value: unknown): value is SubmissionState;
|
|
308
|
+
//#endregion
|
|
309
|
+
//#region src/rules.d.ts
|
|
310
|
+
declare function resolveFieldState<Values>(state: FieldState<Values> | undefined, values: Readonly<DeepPartial<Values>>, fallback?: boolean): boolean;
|
|
311
|
+
declare function isEmptyFieldValue(value: unknown): boolean;
|
|
312
|
+
/** Validates portable presentation rules that are intentionally outside the schema. */
|
|
313
|
+
declare function validatePresentationRules<Input, Control extends string>(model: FormModel<Input, Control>, values: Readonly<DeepPartial<Input>>): readonly StandardIssue[];
|
|
314
|
+
//#endregion
|
|
315
|
+
//#region src/validation.d.ts
|
|
316
|
+
type ValidationResult<Output> = {
|
|
317
|
+
readonly success: true;
|
|
318
|
+
readonly data: Output;
|
|
319
|
+
} | {
|
|
320
|
+
readonly success: false;
|
|
321
|
+
readonly issues: readonly StandardIssue[];
|
|
322
|
+
};
|
|
323
|
+
declare function issuePath(issue: StandardIssue): readonly PropertyKey[];
|
|
324
|
+
declare function issuesToFieldErrors(issues: readonly StandardIssue[]): Readonly<Record<string, readonly string[]>>;
|
|
325
|
+
declare function validate<Schema extends FormSchema>(schema: Schema, value: unknown): Promise<ValidationResult<InferOutput<Schema>>>;
|
|
326
|
+
//#endregion
|
|
327
|
+
export { type ArrayConfig, type ArrayField, type AsyncFieldValidationContext, type AsyncFieldValidationSignal, type AsyncFieldValidator, type BuiltInControl, type DeepPartial, type FailedSubmission, type FieldConfig, type FieldDataType, type FieldOptions, type FieldPath, type FieldPredicate, type FieldState, type FormConfig, type FormModel, type FormNode, type FormOption, type FormSchema, type IdleSubmission, type InferInput, type InferOutput, type JsonPrimitive, type JsonSchema, type JsonSchemaObject, type JsonValue, type MaybePromise, type NativeInputType, type ObjectField, type PathValue, RESERVED_FORM_PATH_SEGMENTS, type ReservedFormPathSegment, type ResolvedFieldConfig, type ScalarConstraints, type ScalarField, SchemaConversionError, type StandardFailure, type StandardIssue, type StandardJSONSchemaOptions, type StandardResult, type StandardSuccess, type StandardTypes, type SubmissionAction, type SubmissionErrorKind, type SubmissionState, type SuccessfulSubmission, type UnsupportedField, type ValidationResult, compileForm, defaultValueForNode, getDefaultValues, initialSubmissionState, isEmptyFieldValue, isReservedFormPathSegment, isSubmissionState, issuePath, issuesToFieldErrors, optionForSerializedValue, pathToConfigPath, pathToName, prepareFormValues, resolveFieldState, serializeOptionValue, submissionFailure, submissionSuccess, toInputJsonSchema, validate, validatePresentationRules };
|
|
328
|
+
//# sourceMappingURL=index.d.ts.map
|