@regle/schemas 0.7.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.
@@ -0,0 +1,411 @@
1
+ import { RegleShortcutDefinition, RegleCommonStatus, JoinDiscriminatedUnions, RegleErrorTree, RegleRuleStatus, RegleCollectionErrors, MismatchInfo, DeepReactiveState, DeepMaybeRef, RegleBehaviourOptions, LocalRegleBehaviourOptions, NoInferLegacy, PrimitiveTypes } from '@regle/core';
2
+ import { StandardSchemaV1 } from '@standard-schema/spec';
3
+ import { Raw, UnwrapNestedRefs, MaybeRef, Ref } from 'vue';
4
+
5
+ /**
6
+ Matches any [primitive value](https://developer.mozilla.org/en-US/docs/Glossary/Primitive).
7
+
8
+ @category Type
9
+ */
10
+ type Primitive =
11
+ | null
12
+ | undefined
13
+ | string
14
+ | number
15
+ | boolean
16
+ | symbol
17
+ | bigint;
18
+
19
+ declare global {
20
+ // eslint-disable-next-line @typescript-eslint/consistent-type-definitions -- It has to be an `interface` so that it can be merged.
21
+ interface SymbolConstructor {
22
+ readonly observable: symbol;
23
+ }
24
+ }
25
+
26
+ declare const emptyObjectSymbol: unique symbol;
27
+
28
+ /**
29
+ Represents a strictly empty plain object, the `{}` value.
30
+
31
+ When you annotate something as the type `{}`, it can be anything except `null` and `undefined`. This means that you cannot use `{}` to represent an empty plain object ([read more](https://stackoverflow.com/questions/47339869/typescript-empty-object-and-any-difference/52193484#52193484)).
32
+
33
+ @example
34
+ ```
35
+ import type {EmptyObject} from 'type-fest';
36
+
37
+ // The following illustrates the problem with `{}`.
38
+ const foo1: {} = {}; // Pass
39
+ const foo2: {} = []; // Pass
40
+ const foo3: {} = 42; // Pass
41
+ const foo4: {} = {a: 1}; // Pass
42
+
43
+ // With `EmptyObject` only the first case is valid.
44
+ const bar1: EmptyObject = {}; // Pass
45
+ const bar2: EmptyObject = 42; // Fail
46
+ const bar3: EmptyObject = []; // Fail
47
+ const bar4: EmptyObject = {a: 1}; // Fail
48
+ ```
49
+
50
+ Unfortunately, `Record<string, never>`, `Record<keyof any, never>` and `Record<never, never>` do not work. See {@link https://github.com/sindresorhus/type-fest/issues/395 #395}.
51
+
52
+ @category Object
53
+ */
54
+ type EmptyObject = {[emptyObjectSymbol]?: never};
55
+
56
+ /**
57
+ Returns a boolean for whether the two given types are equal.
58
+
59
+ @link https://github.com/microsoft/TypeScript/issues/27024#issuecomment-421529650
60
+ @link https://stackoverflow.com/questions/68961864/how-does-the-equals-work-in-typescript/68963796#68963796
61
+
62
+ Use-cases:
63
+ - If you want to make a conditional branch based on the result of a comparison of two types.
64
+
65
+ @example
66
+ ```
67
+ import type {IsEqual} from 'type-fest';
68
+
69
+ // This type returns a boolean for whether the given array includes the given item.
70
+ // `IsEqual` is used to compare the given array at position 0 and the given item and then return true if they are equal.
71
+ type Includes<Value extends readonly any[], Item> =
72
+ Value extends readonly [Value[0], ...infer rest]
73
+ ? IsEqual<Value[0], Item> extends true
74
+ ? true
75
+ : Includes<rest, Item>
76
+ : false;
77
+ ```
78
+
79
+ @category Type Guard
80
+ @category Utilities
81
+ */
82
+ type IsEqual<A, B> =
83
+ (<G>() => G extends A ? 1 : 2) extends
84
+ (<G>() => G extends B ? 1 : 2)
85
+ ? true
86
+ : false;
87
+
88
+ /**
89
+ Extract the element of an array that also works for array union.
90
+
91
+ Returns `never` if T is not an array.
92
+
93
+ It creates a type-safe way to access the element type of `unknown` type.
94
+ */
95
+ type ArrayElement<T> = T extends readonly unknown[] ? T[0] : never;
96
+
97
+ /**
98
+ Returns a boolean for whether either of two given types are true.
99
+
100
+ Use-case: Constructing complex conditional types where multiple conditions must be satisfied.
101
+
102
+ @example
103
+ ```
104
+ import type {Or} from 'type-fest';
105
+
106
+ Or<true, false>;
107
+ //=> true
108
+
109
+ Or<false, false>;
110
+ //=> false
111
+ ```
112
+
113
+ @see {@link And}
114
+ */
115
+ type Or<A extends boolean, B extends boolean> = [A, B][number] extends false
116
+ ? false
117
+ : true extends [IsEqual<A, true>, IsEqual<B, true>][number]
118
+ ? true
119
+ : never;
120
+
121
+ /**
122
+ Matches any primitive, `void`, `Date`, or `RegExp` value.
123
+ */
124
+ type BuiltIns = Primitive | void | Date | RegExp;
125
+
126
+ /**
127
+ @see PartialDeep
128
+ */
129
+ type PartialDeepOptions = {
130
+ /**
131
+ Whether to affect the individual elements of arrays and tuples.
132
+
133
+ @default false
134
+ */
135
+ readonly recurseIntoArrays?: boolean;
136
+ };
137
+
138
+ /**
139
+ Create a type from another type with all keys and nested keys set to optional.
140
+
141
+ Use-cases:
142
+ - Merging a default settings/config object with another object, the second object would be a deep partial of the default object.
143
+ - Mocking and testing complex entities, where populating an entire object with its keys would be redundant in terms of the mock or test.
144
+
145
+ @example
146
+ ```
147
+ import type {PartialDeep} from 'type-fest';
148
+
149
+ const settings: Settings = {
150
+ textEditor: {
151
+ fontSize: 14;
152
+ fontColor: '#000000';
153
+ fontWeight: 400;
154
+ }
155
+ autocomplete: false;
156
+ autosave: true;
157
+ };
158
+
159
+ const applySavedSettings = (savedSettings: PartialDeep<Settings>) => {
160
+ return {...settings, ...savedSettings};
161
+ }
162
+
163
+ settings = applySavedSettings({textEditor: {fontWeight: 500}});
164
+ ```
165
+
166
+ By default, this does not affect elements in array and tuple types. You can change this by passing `{recurseIntoArrays: true}` as the second type argument:
167
+
168
+ ```
169
+ import type {PartialDeep} from 'type-fest';
170
+
171
+ interface Settings {
172
+ languages: string[];
173
+ }
174
+
175
+ const partialSettings: PartialDeep<Settings, {recurseIntoArrays: true}> = {
176
+ languages: [undefined]
177
+ };
178
+ ```
179
+
180
+ @category Object
181
+ @category Array
182
+ @category Set
183
+ @category Map
184
+ */
185
+ type PartialDeep<T, Options extends PartialDeepOptions = {}> = T extends BuiltIns | (((...arguments_: any[]) => unknown)) | (new (...arguments_: any[]) => unknown)
186
+ ? T
187
+ : T extends Map<infer KeyType, infer ValueType>
188
+ ? PartialMapDeep<KeyType, ValueType, Options>
189
+ : T extends Set<infer ItemType>
190
+ ? PartialSetDeep<ItemType, Options>
191
+ : T extends ReadonlyMap<infer KeyType, infer ValueType>
192
+ ? PartialReadonlyMapDeep<KeyType, ValueType, Options>
193
+ : T extends ReadonlySet<infer ItemType>
194
+ ? PartialReadonlySetDeep<ItemType, Options>
195
+ : T extends object
196
+ ? T extends ReadonlyArray<infer ItemType> // Test for arrays/tuples, per https://github.com/microsoft/TypeScript/issues/35156
197
+ ? Options['recurseIntoArrays'] extends true
198
+ ? ItemType[] extends T // Test for arrays (non-tuples) specifically
199
+ ? readonly ItemType[] extends T // Differentiate readonly and mutable arrays
200
+ ? ReadonlyArray<PartialDeep<ItemType | undefined, Options>>
201
+ : Array<PartialDeep<ItemType | undefined, Options>>
202
+ : PartialObjectDeep<T, Options> // Tuples behave properly
203
+ : T // If they don't opt into array testing, just use the original type
204
+ : PartialObjectDeep<T, Options>
205
+ : unknown;
206
+
207
+ /**
208
+ Same as `PartialDeep`, but accepts only `Map`s and as inputs. Internal helper for `PartialDeep`.
209
+ */
210
+ type PartialMapDeep<KeyType, ValueType, Options extends PartialDeepOptions> = {} & Map<PartialDeep<KeyType, Options>, PartialDeep<ValueType, Options>>;
211
+
212
+ /**
213
+ Same as `PartialDeep`, but accepts only `Set`s as inputs. Internal helper for `PartialDeep`.
214
+ */
215
+ type PartialSetDeep<T, Options extends PartialDeepOptions> = {} & Set<PartialDeep<T, Options>>;
216
+
217
+ /**
218
+ Same as `PartialDeep`, but accepts only `ReadonlyMap`s as inputs. Internal helper for `PartialDeep`.
219
+ */
220
+ type PartialReadonlyMapDeep<KeyType, ValueType, Options extends PartialDeepOptions> = {} & ReadonlyMap<PartialDeep<KeyType, Options>, PartialDeep<ValueType, Options>>;
221
+
222
+ /**
223
+ Same as `PartialDeep`, but accepts only `ReadonlySet`s as inputs. Internal helper for `PartialDeep`.
224
+ */
225
+ type PartialReadonlySetDeep<T, Options extends PartialDeepOptions> = {} & ReadonlySet<PartialDeep<T, Options>>;
226
+
227
+ /**
228
+ Same as `PartialDeep`, but accepts only `object`s as inputs. Internal helper for `PartialDeep`.
229
+ */
230
+ type PartialObjectDeep<ObjectType extends object, Options extends PartialDeepOptions> = {
231
+ [KeyType in keyof ObjectType]?: PartialDeep<ObjectType[KeyType], Options>
232
+ };
233
+
234
+ type RegleSchemaMode = 'rules' | 'schema';
235
+ type isModeRules<T> = T extends 'rules' ? true : false;
236
+ type RegleSchemaModeOptions<T extends RegleSchemaMode = 'rules'> = {
237
+ mode?: T;
238
+ };
239
+
240
+ interface RegleSchema<TState extends Record<string, any>, TSchema extends Record<string, any>, TMode extends RegleSchemaMode = 'rules', TShortcuts extends RegleShortcutDefinition = {}> {
241
+ /**
242
+ * r$ is a reactive object containing the values, errors, dirty state and all the necessary validations properties you'll need to display informations.
243
+ *
244
+ * To see the list of properties: {@link https://reglejs.dev/core-concepts/validation-properties}
245
+ */
246
+ r$: Raw<RegleSchemaStatus<TState, TSchema, TMode, TShortcuts, true>>;
247
+ }
248
+ type RegleSchemaResult<TSchema extends unknown> = {
249
+ result: false;
250
+ data: PartialDeep<TSchema>;
251
+ } | {
252
+ result: true;
253
+ data: TSchema;
254
+ };
255
+ /**
256
+ * @public
257
+ */
258
+ type RegleSchemaStatus<TState extends Record<string, any> = Record<string, any>, TSchema extends Record<string, any> = Record<string, any>, TMode extends RegleSchemaMode = 'rules', TShortcuts extends RegleShortcutDefinition = {}, IsRoot extends boolean = false> = Omit<RegleCommonStatus<TState>, Or<isModeRules<TMode>, IsRoot> extends false ? '$pending' : ''> & {
259
+ /** Represents all the children of your object. You can access any nested child at any depth to get the relevant data you need for your form. */
260
+ readonly $fields: {
261
+ readonly [TKey in keyof JoinDiscriminatedUnions<TState>]: TKey extends keyof JoinDiscriminatedUnions<TSchema> ? InferRegleSchemaStatusType<NonNullable<JoinDiscriminatedUnions<TSchema>[TKey]>, JoinDiscriminatedUnions<TState>[TKey], TMode, TShortcuts> : never;
262
+ } & {
263
+ readonly [TKey in keyof JoinDiscriminatedUnions<TState> as TKey extends keyof JoinDiscriminatedUnions<TSchema> ? JoinDiscriminatedUnions<TSchema>[TKey] extends NonNullable<JoinDiscriminatedUnions<TSchema>[TKey]> ? TKey : never : never]-?: TKey extends keyof JoinDiscriminatedUnions<TSchema> ? InferRegleSchemaStatusType<NonNullable<JoinDiscriminatedUnions<TSchema>[TKey]>, JoinDiscriminatedUnions<TState>[TKey], TMode, TShortcuts> : never;
264
+ };
265
+ /** Collection of all the error messages, collected for all children properties and nested forms.
266
+ *
267
+ * Only contains errors from properties where $dirty equals true. */
268
+ readonly $errors: RegleErrorTree<TState>;
269
+ /** Collection of all the error messages, collected for all children properties. */
270
+ readonly $silentErrors: RegleErrorTree<TState>;
271
+ /** Will return a copy of your state with only the fields that are dirty. By default it will filter out nullish values or objects, but you can override it with the first parameter $extractDirtyFields(false). */
272
+ $extractDirtyFields: (filterNullishValues?: boolean) => PartialDeep<TState>;
273
+ } & (Or<isModeRules<TMode>, IsRoot> extends true ? {
274
+ /** Sets all properties as dirty, triggering all rules. It returns a promise that will either resolve to false or a type safe copy of your form state. Values that had the required rule will be transformed into a non-nullable value (type only). */
275
+ $validate: () => Promise<RegleSchemaResult<TSchema>>;
276
+ } : {}) & ([TShortcuts['nested']] extends [never] ? {} : {
277
+ [K in keyof TShortcuts['nested']]: ReturnType<NonNullable<TShortcuts['nested']>[K]>;
278
+ });
279
+ /**
280
+ * @public
281
+ */
282
+ type InferRegleSchemaStatusType<TSchema extends unknown, TState extends unknown, TMode extends RegleSchemaMode = 'rules', TShortcuts extends RegleShortcutDefinition = {}> = NonNullable<TSchema> extends Array<infer A extends Record<string, any>> ? RegleSchemaCollectionStatus<A, TState extends Array<any> ? TState : [], TMode, TShortcuts> : NonNullable<TState> extends Date | File ? RegleSchemaFieldStatus<TSchema, TState, TMode, TShortcuts> : unknown extends TState ? RegleSchemaFieldStatus<TSchema extends EmptyObject ? unknown : TSchema, TState, TMode, TShortcuts> : NonNullable<TSchema> extends Record<string, any> ? RegleSchemaStatus<NonNullable<TState> extends Record<string, any> ? NonNullable<TState> : {}, NonNullable<TSchema>, TMode, TShortcuts> : RegleSchemaFieldStatus<TSchema, TState, TMode, TShortcuts>;
283
+ /**
284
+ * @public
285
+ */
286
+ type RegleSchemaFieldStatus<TSchema extends unknown, TState = any, TMode extends RegleSchemaMode = 'rules', TShortcuts extends RegleShortcutDefinition = {}> = Omit<RegleCommonStatus<TState>, isModeRules<TMode> extends false ? '$pending' : ''> & {
287
+ /** Collection of all the error messages, collected for all children properties and nested forms.
288
+ *
289
+ * Only contains errors from properties where $dirty equals true. */
290
+ readonly $errors: string[];
291
+ /** Collection of all the error messages, collected for all children properties and nested forms. */
292
+ readonly $silentErrors: string[];
293
+ /** Will return a copy of your state with only the fields that are dirty. By default it will filter out nullish values or objects, but you can override it with the first parameter $extractDirtyFields(false). */
294
+ readonly $externalErrors?: string[];
295
+ /** Represents the inactive status. Is true when this state have empty rules */
296
+ readonly $inactive: boolean;
297
+ /** This is reactive tree containing all the declared rules of your field. To know more about the rule properties check the rules properties section */
298
+ readonly $rules: {
299
+ [`~validator`]: RegleRuleStatus<TState, []>;
300
+ };
301
+ /** Will return a copy of your state with only the fields that are dirty. By default it will filter out nullish values or objects, but you can override it with the first parameter $extractDirtyFields(false). */
302
+ $extractDirtyFields: (filterNullishValues?: boolean) => PartialDeep<TState>;
303
+ } & (isModeRules<TMode> extends true ? {
304
+ /** Sets the property as dirty, triggering all rules. It returns a promise that will either resolve to false or a type safe copy of your form state. Values that had the required rule will be transformed into a non-nullable value (type only). */
305
+ $validate: () => Promise<RegleSchemaResult<TSchema>>;
306
+ } : {}) & ([TShortcuts['fields']] extends [never] ? {} : {
307
+ [K in keyof TShortcuts['fields']]: ReturnType<NonNullable<TShortcuts['fields']>[K]>;
308
+ });
309
+ /**
310
+ * @public
311
+ */
312
+ type RegleSchemaCollectionStatus<TSchema extends Record<string, any>, TState extends any[], TMode extends RegleSchemaMode = 'rules', TShortcuts extends RegleShortcutDefinition = {}> = Omit<RegleSchemaFieldStatus<TSchema, TState, TMode, TShortcuts>, '$errors' | '$silentErrors' | '$validate'> & {
313
+ /** Collection of status of every item in your collection. Each item will be a field you can access, or map on it to display your elements. */
314
+ readonly $each: Array<InferRegleSchemaStatusType<NonNullable<TSchema>, ArrayElement<TState>, TMode, TShortcuts>>;
315
+ /** Represents the status of the collection itself. You can have validation rules on the array like minLength, this field represents the isolated status of the collection. */
316
+ readonly $self: RegleSchemaFieldStatus<TSchema, TState, TMode, TShortcuts>;
317
+ /** Collection of all the error messages, collected for all children properties and nested forms.
318
+ *
319
+ * Only contains errors from properties where $dirty equals true. */
320
+ readonly $errors: RegleCollectionErrors<TSchema>;
321
+ /** Collection of all the error messages, collected for all children properties and nested forms. */
322
+ readonly $silentErrors: RegleCollectionErrors<TSchema>;
323
+ /** Will return a copy of your state with only the fields that are dirty. By default it will filter out nullish values or objects, but you can override it with the first parameter $extractDirtyFields(false). */
324
+ $extractDirtyFields: (filterNullishValues?: boolean) => PartialDeep<TState>;
325
+ } & (isModeRules<TMode> extends true ? {
326
+ /** Sets the property as dirty, triggering all rules. It returns a promise that will either resolve to false or a type safe copy of your form state. Values that had the required rule will be transformed into a non-nullable value (type only). */
327
+ $validate: () => Promise<RegleSchemaResult<TSchema>>;
328
+ } : {}) & ([TShortcuts['collections']] extends [never] ? {} : {
329
+ [K in keyof TShortcuts['collections']]: ReturnType<NonNullable<TShortcuts['collections']>[K]>;
330
+ });
331
+
332
+ type useRegleSchemaFn<TShortcuts extends RegleShortcutDefinition<any> = never> = <TState extends Record<string, any>, TSchema extends StandardSchemaV1<Record<string, any>> & TValid, TMode extends RegleSchemaMode = never, TValid = UnwrapNestedRefs<TState> extends PartialDeep<StandardSchemaV1.InferInput<TSchema>, {
333
+ recurseIntoArrays: true;
334
+ }> ? {} : MismatchInfo<UnwrapNestedRefs<TState>, PartialDeep<StandardSchemaV1.InferInput<TSchema>, {
335
+ recurseIntoArrays: true;
336
+ }>>>(state: MaybeRef<TState> | DeepReactiveState<TState>, schema: MaybeRef<TSchema>, options?: Partial<DeepMaybeRef<RegleBehaviourOptions>> & LocalRegleBehaviourOptions<UnwrapNestedRefs<TState>, {}, never> & RegleSchemaModeOptions<TMode>) => RegleSchema<UnwrapNestedRefs<TState>, StandardSchemaV1.InferInput<TSchema>, [
337
+ TMode
338
+ ] extends [never] ? 'rules' : TMode, TShortcuts>;
339
+ /**
340
+ * useRegle serves as the foundation for validation logic.
341
+ *
342
+ * It accepts the following inputs:
343
+ *
344
+ * @param state - This can be a plain object, a ref, a reactive object, or a structure containing nested refs.
345
+ * @param schema - These should align with the structure of your state.
346
+ * @param modifiers - customize regle behaviour
347
+ *
348
+ * ```ts
349
+ * import { useRegleSchema } from '@regle/schemas';
350
+ import * as v from 'valibot';
351
+
352
+ const { r$ } = useRegleSchema({ name: '' }, v.object({
353
+ name: v.pipe(v.string(), v.minLength(3))
354
+ }))
355
+ * ```
356
+ * Docs: {@link https://reglejs.dev/integrations/valibot#usage}
357
+ */
358
+ declare const useRegleSchema: useRegleSchemaFn<RegleShortcutDefinition<any>>;
359
+
360
+ /**
361
+ * ⚠️ Not compatible with `schema` mode
362
+ *
363
+ * Force dependency on any RPC schema
364
+ * ```ts
365
+ * const foo = ref('');
366
+ * withDeps(
367
+ * v.pipe(v.string(), v.check((value) => value === foo.value)),
368
+ * [foo]
369
+ * )
370
+ * ```
371
+ */
372
+ declare function withDeps<TSchema extends StandardSchemaV1, TParams extends (Ref<unknown> | (() => unknown))[] = []>(schema: TSchema, depsArray: [...TParams]): TSchema & {
373
+ __depsArray: TParams;
374
+ };
375
+
376
+ interface inferValibotSchemaFn {
377
+ <TState extends Record<string, any>, TSchema extends StandardSchemaV1<Record<string, any>> & TValid, TValid = UnwrapNestedRefs<TState> extends PartialDeep<StandardSchemaV1.InferInput<TSchema>, {
378
+ recurseIntoArrays: true;
379
+ }> ? {} : MismatchInfo<UnwrapNestedRefs<TState>, PartialDeep<StandardSchemaV1.InferInput<TSchema>, {
380
+ recurseIntoArrays: true;
381
+ }>>>(state: MaybeRef<TState> | DeepReactiveState<TState> | undefined, rulesFactory: TSchema): NoInferLegacy<TSchema>;
382
+ <TState extends PrimitiveTypes, TSchema extends StandardSchemaV1 & TValid, TValid = TState extends StandardSchemaV1.InferInput<TSchema> ? {} : MismatchInfo<TState, StandardSchemaV1.InferInput<TSchema>>>(state: MaybeRef<TState>, rulesFactory: TSchema): NoInferLegacy<TSchema>;
383
+ }
384
+ /**
385
+ * Rule type helper to provide autocomplete and typecheck to your form rules or part of your form rules
386
+ * It will just return the rules without any processing.
387
+ *
388
+ * @param state - The state reference
389
+ * @param schema - Your valibot schema
390
+ */
391
+ declare const inferSchema: inferValibotSchemaFn;
392
+
393
+ /**
394
+ * Define a global regle configuration, where you can:
395
+ * - Define global modifiers
396
+ * - Define shortcuts
397
+ *
398
+ * It will return:
399
+ *
400
+ * - a `useRegleSchema` composable that can typecheck your custom rules
401
+ * - an `inferSchema` helper that can typecheck your custom rules
402
+ */
403
+ declare function defineRegleSchemaConfig<TShortcuts extends RegleShortcutDefinition>({ modifiers, shortcuts, }: {
404
+ modifiers?: RegleBehaviourOptions;
405
+ shortcuts?: TShortcuts;
406
+ }): {
407
+ useRegleSchema: useRegleSchemaFn<TShortcuts>;
408
+ inferSchema: inferValibotSchemaFn;
409
+ };
410
+
411
+ export { type InferRegleSchemaStatusType, type RegleSchema, type RegleSchemaCollectionStatus, type RegleSchemaFieldStatus, type RegleSchemaResult, type RegleSchemaStatus, defineRegleSchemaConfig, inferSchema, useRegleSchema, withDeps };