@regle/mcp-server 1.21.0 → 1.21.2
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/dist/regle-mcp-server.js +20 -20
- package/package.json +1 -1
package/dist/regle-mcp-server.js
CHANGED
|
@@ -26,7 +26,7 @@ const rawData = {
|
|
|
26
26
|
"title": "markStatic",
|
|
27
27
|
"category": "advanced-usage",
|
|
28
28
|
"path": "advanced-usage/immutable-constructors.md",
|
|
29
|
-
"content": "# Handling immutable constructors\n\nRegle works by tracking changes in the state and updating the validation rules accordingly.\n\nThis works great for objects and arrays, but not for immutable constructors (like `Decimal` from `decimal.js` or `Moment` from `moment.js`, etc...).\n\nThis constructors will be interpreted as regular objects and their properties treated as nested fields.\n\n## Default Usage\n\nTo handle these cases, you can use the `markStatic` helper to mark the value as static and treat the constructor as a regular raw Field.\n\n```vue\n<template>\n <input :value=\"r$.decimal.$value?.toString()\" @input=\"handleDecimalInput\" />\n</template>\n\n```\n\n## Schema Usage\n\nWhen using Regle with `@regle/schemas`, you will have to also declare the static constructor in the schema.\n\n```ts\nimport { markStatic
|
|
29
|
+
"content": "# Handling immutable constructors\n\nRegle works by tracking changes in the state and updating the validation rules accordingly.\n\nThis works great for objects and arrays, but not for immutable constructors (like `Decimal` from `decimal.js` or `Moment` from `moment.js`, etc...).\n\nThis constructors will be interpreted as regular objects and their properties treated as nested fields.\n\n## Default Usage\n\nTo handle these cases, you can use the `markStatic` helper to mark the value as static and treat the constructor as a regular raw Field.\n\n```vue\n<template>\n <input :value=\"r$.decimal.$value?.toString()\" @input=\"handleDecimalInput\" />\n</template>\n\n```\n\n## Schema Usage\n\nWhen using Regle with `@regle/schemas`, you will have to also declare the static constructor in the schema.\n\n```ts\nimport { markStatic } from '@regle/core'\nimport { useRegleSchema } from '@regle/schemas'\nimport { z } from 'zod'\n\nconst StaticDecimal = markStatic(Decimal)\n\nconst schema = z.object({\n decimal: z.instanceof(StaticDecimal).refine((value) => value.toNumber() > 10),\n})\n\nconst { r$ } = useRegleSchema({ decimal: new StaticDecimal(0) }, schema)\n\n```\n\n## `isStatic` helper\n\nYou can use the `isStatic` helper to check if a value is a static value.\n\n```ts\nimport { isStatic } from '@regle/core';\n\nconst result = isStatic(r$.$value.decimal); // true\n```\n\n## `UnwrapStatic` type helper\n\nYou can use the `UnwrapStatic` type to unwrap a static value.\n\n```ts\nimport { type UnwrapStatic } from '@regle/core';\n\ntype value = UnwrapStatic<typeof r$.$value.decimal>; // Decimal\n```\n\n## `isRegleStatic` type helper\n\nYou can use the `isRegleStatic` type helper to check if a value is a static value.\n\n```ts\nimport { type isRegleStatic } from '@regle/core';\n\ntype isStatic = isRegleStatic<typeof state.decimal>; // true\n```"
|
|
30
30
|
},
|
|
31
31
|
{
|
|
32
32
|
"id": "advanced-usage-merge-regles",
|
|
@@ -47,7 +47,7 @@ const rawData = {
|
|
|
47
47
|
"title": "Scoped validation",
|
|
48
48
|
"category": "advanced-usage",
|
|
49
49
|
"path": "advanced-usage/scoped-validation.md",
|
|
50
|
-
"content": "# Scoped validation\n\nScoped validation in Regle is made to port Vuelidate's `nested component validation`.\n\nProblems with Vuelidate's approach:\n - Performances\n - Not declarative\n - Usage (too magic for the user)\n - Type safety\n - Restricted to DOM\n - Have to play with `$scope` and `$stopPropagation` to avoid unwanted behaviour\n\nRegle's solution solves all
|
|
50
|
+
"content": "# Scoped validation\n\nScoped validation in Regle is made to port Vuelidate's `nested component validation`.\n\nProblems with Vuelidate's approach:\n - Performances\n - Not declarative\n - Usage (too magic for the user)\n - Type safety\n - Restricted to DOM\n - Have to play with `$scope` and `$stopPropagation` to avoid unwanted behaviour\n\nRegle's solution solves all these problems\n\n## Collecting validation with `useCollectScope` and `useScopedRegle`\n\n### `useScopedRegle`\n\n`useScopedRegle` is a clone of `useRegle`, but with the difference that every time it's used and updated, its state will be collected by the same scope created using `createScopedUseRegle`.\n\nEvery time it's called, a instance will be added for `useCollectScope` to collect.\n\nIt can be called multiple times at any place, not only on components, as it's not restricted by DOM.\n\n### `useCollectScope`\n\nThis composable allow you to retrieve every Regle instances created using the sibling composable `useScopedRegle`.\n\nChildren properties like `$value` and `$errors` will not be objects, and are converted into arrays instead.\n\nYou will also have access to every validation properties like `$error`, `$invalid` etc...\n\n:::code-group\n\n```vue [Parent.vue]\n<template>\n <div>\n <Child1 />\n </div>\n\n <Child2 />\n\n Collected errors: <pre>{{ r$.$errors }}</pre>\n</template>\n\n```\n\n```vue [Child1.vue]\n<template>\n <input v-model=\"r$.$value.firstName\" placeholder=\"Type your firstname\" />\n <ul>\n <li v-for=\"error of r$.$errors.firstName\" :key=\"error\">\n {{ error }}\n </li>\n </ul>\n</template>\n\n```\n\n```vue [Child2.vue]\n<template>\n <input v-model=\"r$.$value.email\" placeholder=\"Type your email\" />\n <ul>\n <li v-for=\"error of r$.$errors.email\" :key=\"error\">\n {{ error }}\n </li>\n </ul>\n</template>\n\n```\n:::\n\nResult:\n\n## Multiple scopes\n\nIf you want to create your own separated scope, you can use `createScopedUseRegle` helper method.\n\nIt's advised to change the name of this composable to avoid conflicts or issues.\n\n```ts [scoped-config.ts]\nimport { createScopedUseRegle } from '@regle/core';\n\nexport const { useScopedRegle, useCollectScope } = createScopedUseRegle();\nexport const { \n useScopedRegle: useContactsRegle, \n useCollectScope: useCollectContacts \n} = createScopedUseRegle();\n```\n\n## Namespaces inside scopes\n\nEach scope can collect a specific namespace. Giving a namespace name will collect only the children with the same namespace name.\n\nThe namespace can be reactive, so it will update every time it changes.\n\nIn this example, only the components using the same scope and namespace will be collected.\n\n:::code-group\n```vue [Parent.vue]\n\n```\n\n```vue [Child1.vue]\n<template>\n <input v-model=\"r$.$value.firstName\" placeholder=\"Type your firstname\" />\n <ul>\n <li v-for=\"error of r$.$errors.firstName\" :key=\"error\">\n {{ error }}\n </li>\n </ul>\n</template>\n\n```\n\n```vue [Child2.vue]\n<template>\n <input v-model=\"r$.$value.email\" placeholder=\"Type your email\" />\n <ul>\n <li v-for=\"error of r$.$errors.email\" :key=\"error\">\n {{ error }}\n </li>\n </ul>\n</template>\n\n```\n\nYou can also collect multiple namespaces at once by passing an array of namespace names to the `useCollectScope` function.\n\n```ts\nconst { r$ } = useCollectScope(['contacts', 'persons']);\n```\n:::\n\n## Inject global config\n\nIf you have a global config already registered, simply pass it as a parameter to your `createScopedUseRegle` function.\n\n```ts twoslash [scoped-config.ts]\nimport { createScopedUseRegle, defineRegleConfig } from '@regle/core';\nimport { required, withMessage } from '@regle/rules';\n\nconst { useRegle } = defineRegleConfig({\n rules: () => ({\n custom: withMessage(required, 'Custom error'),\n }),\n});\n\nexport const { useScopedRegle, useCollectScope } = createScopedUseRegle({customUseRegle: useRegle});\n\nconst {r$} = useScopedRegle({name: ''}, {\n name: {\n cus\n\n }\n})\n\n```\n\n## Custom store for instances\n\nBy default collected instances are stored in a local ref. \n\nYou can provide your own store ref.\n\n```ts\nimport { createScopedUseRegle, type ScopedInstancesRecordLike } from '@regle/core';\n\n// Having a default \nconst myCustomStore = ref<ScopedInstancesRecordLike>({});\n\nconst { useScopedRegle, useCollectScope } = createScopedUseRegle({customStore: myCustomStore});\n```\n\n## Collect instances in a Record\n\nBy default collected instances are stored in a readonly array.\n\nIf you want to store your nested instances in a record it's possible with the `asRecord` option.\n\nThis will **require** every nested `useScopeRegle` to provide a parameter `id`.\n\n:::code-group\n\n```ts [scoped-config.ts]\nimport { createScopedUseRegle } from '@regle/core';\n\nexport const { \n useScopedRegle: useScopedRegleItem, \n useCollectScope: useCollectScopeRecord \n} = createScopedUseRegle({ asRecord: true });\n```\n```vue [Parent.vue]\n\n```\n\n```vue [Child1.vue]\n<template>\n <input v-model=\"r$.$value.firstName\" placeholder=\"Type your firstname\" />\n <ul>\n <li v-for=\"error of r$.$errors.firstName\" :key=\"error\">\n {{ error }}\n </li>\n </ul>\n</template>\n\n```\n\n```vue [Child2.vue]\n<template>\n <input v-model=\"r$.$value.email\" placeholder=\"Type your email\" />\n <ul>\n <li v-for=\"error of r$.$errors.email\" :key=\"error\">\n {{ error }}\n </li>\n </ul>\n</template>\n\n```\n:::\n\n## Manually dispose or register a scope entry\n\n`useScopedRegle` also returns two methods: `dispose` and `register`.\n\nYou can then programmatically handle if your component is collected from inside.\n\n```vue\n\n```\n\n## Manual typing\n\n:::warning\nUse with care, only if you're 100% sure of what return type your collected types will have.\n\nThe order of the collected values can change depending on if they added/deleted.\nThis is here for convenience but not advised.\n:::\n\n```ts twoslash\nimport { useCollectScope } from '@regle/core';\n\nconst { r$ } = useCollectScope<[{ foo: string }]>();\n\nconst { valid, data } = await r$.$validate();\n\n```\n\n## Testing\n\nIf you write unit test for a component using scoped validation, you may encounter problems with the tests failing.\n\nTo solve this, mount your component with the `attachTo` option set to the document element.\n\n```ts\nimport { mount } from '@vue/test-utils';\nimport ComponentUsingCollectScope from './ComponentUsingCollectScope.vue';\n\nconst wrapper = mount(ComponentUsingCollectScope, {\n attachTo: document.documentElement,\n});\n```"
|
|
51
51
|
},
|
|
52
52
|
{
|
|
53
53
|
"id": "advanced-usage-self-validation",
|
|
@@ -61,7 +61,7 @@ const rawData = {
|
|
|
61
61
|
"title": "Variants",
|
|
62
62
|
"category": "advanced-usage",
|
|
63
63
|
"path": "advanced-usage/variants.md",
|
|
64
|
-
"content": "# Variants or discriminated unions\n\nYour form may not be linear, and have multiple fields that depends on a condition or a toggle.\nIt can be complex and become a mess when trying to organise your types around it.\n\nRegle variants offer a way to simply declare and use this discriminated unions, while keeping all fields correctly types and also runtime safe.\n\n## `createVariant`\n\nThe first
|
|
64
|
+
"content": "# Variants or discriminated unions\n\nYour form may not be linear, and have multiple fields that depends on a condition or a toggle.\nIt can be complex and become a mess when trying to organise your types around it.\n\nRegle variants offer a way to simply declare and use this discriminated unions, while keeping all fields correctly types and also runtime safe.\n\n## `createVariant`\n\nThe first step to Regle variants is to have a type that includes a discriminated variant.\n\n```ts twoslash include form-types\ntype FormStateLoginType = \n| {type: 'EMAIL', email: string} \n| {type: 'GITHUB', username: string} \n| {type?: undefined}\n\ntype FormState = {\n firstName?: string;\n lastName?: string;\n} & FormStateLoginType\n```\n\nHere your state can have two possible outcomes, but with classic rules it's hard to handle fields statuses as they can always be undefined.\n\nThe solution to this is to first declare your variant-related rules inside `createVariant` like this:\n\n```ts twoslash include main\nimport {ref} from 'vue';\n\n// ---cut---\nimport { useRegle, createVariant} from '@regle/core';\nimport { literal, required, email } from '@regle/rules';\n\nconst state = ref<FormState>({})\n\n// ⚠️ Use getter syntax for your rules () => {} or a computed one\nconst {r$} = useRegle(state, () => {\n /** \n * Here you create you rules variations, see each member as a `OR` \n * `type` here is the discriminant\n * \n * Depending of the value of `type`, Regle will apply the corresponding rules.\n */\n const variant = createVariant(state, 'type', [\n {type: { literal: literal('EMAIL')}, email: { required, email }},\n {type: { literal: literal('GITHUB')}, username: { required }},\n {type: { required }},\n ]);\n\n return {\n firstName: {required},\n // Don't forget to return the computed rules\n ...variant.value,\n };\n})\n```\n\n## `narrowVariant`\n\nIn your form, you'll need to use type narrowing to access your field status somehow. \n\nFor this you'll have to discriminate the `$fields` depending on value. As the status uses deeply nested properties, this will not be possible with a standard guard `if (value === \"EMAIL\")`.\n\nIn your template or script, you can use Regle's `narrowVariant` helper to narrow the fields to the value.\n\nLet's take the previous example again:\n\n```vue twoslash\n<template>\n <input v-model=\"r$.firstName.$value\" placeholder='First name'/>\n <Errors :errors=\"r$.firstName.$errors\"/>\n\n <select v-model=\"r$.type.$value\">\n <option disabled value=\"\">Account type</option>\n <option value=\"EMAIL\">Email</option>\n <option value=\"GITHUB\">Github</option>\n </select>\n\n <div v-if=\"narrowVariant(r$, 'type', 'EMAIL')\">\n <!-- `email` is now a known field in this block -->\n <input v-model=\"r$.email.$value\" placeholder='Email'/>\n <Errors :errors=\"r$.email.$errors\"/>\n </div>\n \n <div v-else-if=\"narrowVariant(r$, 'type', 'GITHUB')\">\n <!-- `username` is now a known field in this block -->\n <input v-model=\"r$.username.$value\" placeholder='Username'/>\n <Errors :errors=\"r$.username.$errors\"/>\n \n </div>\n\n</template>\n\n```\n\nResult:\n\n### Nested variants\n\nAll the above also works for nested variants\n\n```ts twoslash include nested-types\ntype FormState = {\n firstName?: string;\n lastName?: string;\n login: \n | {type: 'EMAIL', email: string} \n | {type: 'GITHUB', username: string} \n | {type?: undefined}\n}\n\n```\n\n:::warning\nThe first argument of `createVariant` needs to be reactive. For nested values, use getter syntax.\n:::\n\n```ts twoslash include nested-regle\nimport {ref, defineComponent} from 'vue';\n\nconst Errors = defineComponent({});\n\n// ---cut---\nimport { useRegle, createVariant} from '@regle/core';\nimport { literal, required, email } from '@regle/rules';\n\nconst state = ref<FormState>({\n firstName: '',\n login: {}\n})\n\nconst {r$} = useRegle(state, () => {\n\n const loginVariant = createVariant(() => state.value.login, 'type', [\n {type: { literal: literal('EMAIL')}, email: { required, email }},\n {type: { literal: literal('GITHUB')}, username: { required }},\n {type: { required}},\n ]);\n\n return {\n firstName: {required},\n login: loginVariant.value\n };\n})\n```\n\nIn the component:\n\n```vue twoslash\n<template>\n <input v-model=\"r$.firstName.$value\" placeholder='First name'/>\n <Errors :errors=\"r$.firstName.$errors\"/>\n\n <select v-model=\"r$.login.type.$value\">\n <option disabled value=\"\">Account type</option>\n <option value=\"EMAIL\">Email</option>\n <option value=\"GITHUB\">Github</option>\n </select>\n\n <div v-if=\"narrowVariant(r$.login, 'type', 'EMAIL')\">\n <!-- `email` is now a known field in this block -->\n <input v-model=\"r$.login.email.$value\" placeholder='Email'/>\n <Errors :errors=\"r$.login.email.$errors\"/>\n </div>\n \n <div v-else-if=\"narrowVariant(r$.login, 'type', 'GITHUB')\">\n <!-- `username` is now a known field in this block -->\n <input v-model=\"r$.login.username.$value\" placeholder='Username'/>\n <Errors :errors=\"r$.login.username.$errors\"/>\n </div>\n\n</template>\n\n```\n\n## `variantToRef`\n\nA use case is also to have a narrowed **Ref** ready to be used and isn't tied to a block scope. Like in the root of a script setup component where you're sure only one variant is possible.\n\nHaving a `variantToRef` helper prevents you from creating custom `computed` methods, which would make you lose the `v-model` compatibilities of the `.$value`.\n\nThe **ref** will be reactive and already typed as the variant you defined, while still needing to be checked for nullish.\n\n:::code-group\n```vue twoslash [Github.vue]\n<template>\n <div v-if=\"githubVariant$\">\n <input v-model=\"githubVariant$.username.$value\" placeholder='Username'/>\n <Errors :errors=\"githubVariant$.username.$errors\"/>\n </div>\n</template>\n\n```\n\n```ts twoslash [form.store.ts]\nimport {ref} from 'vue';\nimport { defineStore, skipHydrate} from 'pinia';\nimport { useRegle, createVariant} from '@regle/core';\nimport { literal, required, email } from '@regle/rules';\n\nexport const useFormStore = defineStore('form', () => {\n \n const state = ref<FormState>({});\n\n const {r$} = useRegle(state, () => {\n \n const variant = createVariant(state, 'type', [\n {type: { literal: literal('EMAIL')}, email: { required, email }},\n {type: { literal: literal('GITHUB')}, username: { required }},\n {type: { required}},\n ]);\n\n return {\n firstName: {required},\n ...variant.value,\n };\n })\n\n return {\n r$: skipHydrate(r$),\n }\n})\n```\n\n:::\n\n### `unsafeAssertion` option\n\nWhen using `variantToRef` in a component it happens that the assertion is done by the parent component, which means you know the variant assertion will always be valid in the entire component.\n\nFor this case you can pass an option to assert that the variant is always defined.\n\n```ts\nimport { variantToRef } from '@regle/core';\n\nconst variant$ = variantToRef(r$, 'type', 'EMAIL', { unsafeAssertion: true });\n\n```"
|
|
65
65
|
},
|
|
66
66
|
{
|
|
67
67
|
"id": "cheat-sheet",
|
|
@@ -89,7 +89,7 @@ const rawData = {
|
|
|
89
89
|
"title": "Server errors",
|
|
90
90
|
"category": "common-usage",
|
|
91
91
|
"path": "common-usage/external-errors.md",
|
|
92
|
-
"content": "# External errors\n\nRegle handles only client side errors. But some validation may need to be submitted to a server and returned to the client.\n\nTo handle this, you can use the `externalErrors` modifier.\n\nIt matches the structure of your form, but you can also use dot path to define the errors.\n\n## Basic usage\n\n```ts\nimport { type RegleExternalErrorTree, useRegle } from '@regle/core'\n\nconst form = reactive({\n email: '',\n name: {\n pseudo: '',\n },\n})\n\nconst externalErrors = ref<RegleExternalErrorTree<typeof form>>({});\n\nconst { r$ } = useRegle(\n form,\n {\n email: { required },\n name: { pseudo: { required } },\n },\n {\n externalErrors,\n }\n);\n\nasync function submit() {\n const {valid} = await r$.$validate();\n\n if (valid) {\n r$.$setExternalErrors({\n email: [\"Email already exists\"],\n name: {\n pseudo: [\"Pseudo already exists\"]\n },\n })\n }\n}\n```\n\nResult:\n\n:::warning\n\nIf you're working with collections and server-only validations, you'll have to at least specify an empty `$each` object in the client rules to tell Regle that the array is to be treated as a collection\n\n```ts\nconst { r$ } = useRegle({collection: []}, {\n collection: {\n $each: {}\n },\n}, { externalErrors })\n\n```\n\n:::\n\n## Dot path errors\n\n`externalErrors` can also be used to handle dot path errors. \n\nIt can be handy for some backend frameworks that return errors with dot path.\n\n```ts\nimport { useRegle } from '@regle/core';\n\nconst form = reactive({\n email: '',\n name: {\n pseudo: '',\n },\n collection: [{name: ''}]\n})\n\nconst externalErrors = ref<Record<string, string[]>>({});\n\nconst { r$ } = useRegle(form, {}, { externalErrors })\n\nasync function submit() {\n const {valid} = await r$.$validate();\n\n if (valid) {\n r$.$setExternalErrors({\n email: [\"Email already exists\"],\n \"name.pseudo\": [\"Pseudo already exists\"],\n \"collection.0.name\": [\"Name already exists\"]\n })\n }\n}\n``` \n\n## Without the `externalErrors` option\n\n`r$.$setExternalErrors` also works when you don't provide the `externalErrors` option.\nRegle stores those errors internally.\n\n```ts\nconst form = reactive({\n email: '',\n name: {\n pseudo: '',\n },\n})\n\nconst { r$ } = useRegle(form, {\n email: { required },\n name: { pseudo: { required } },\n})\n\nasync function submit() {\n const { valid } = await r$.$validate();\n\n if (!valid) return;\n\n r$.$setExternalErrors({\n email: ['Email already exists'],\n 'name.pseudo': ['Pseudo already exists'],\n });\n}\n```\n\n## Clearing errors\n\nBy default, when you set the external errors, Regle will keep them until the form is validated or modified again.\n\nThis
|
|
92
|
+
"content": "# External errors\n\nRegle handles only client side errors. But some validation may need to be submitted to a server and returned to the client.\n\nTo handle this, you can use the `externalErrors` modifier.\n\nIt matches the structure of your form, but you can also use dot path to define the errors.\n\n## Basic usage\n\n```ts\nimport { type RegleExternalErrorTree, useRegle } from '@regle/core'\n\nconst form = reactive({\n email: '',\n name: {\n pseudo: '',\n },\n})\n\nconst externalErrors = ref<RegleExternalErrorTree<typeof form>>({});\n\nconst { r$ } = useRegle(\n form,\n {\n email: { required },\n name: { pseudo: { required } },\n },\n {\n externalErrors,\n }\n);\n\nasync function submit() {\n const {valid} = await r$.$validate();\n\n if (valid) {\n r$.$setExternalErrors({\n email: [\"Email already exists\"],\n name: {\n pseudo: [\"Pseudo already exists\"]\n },\n })\n }\n}\n```\n\nResult:\n\n:::warning\n\nIf you're working with collections and server-only validations, you'll have to at least specify an empty `$each` object in the client rules to tell Regle that the array is to be treated as a collection\n\n```ts\nconst { r$ } = useRegle({collection: []}, {\n collection: {\n $each: {}\n },\n}, { externalErrors })\n\n```\n\n:::\n\n## Dot path errors\n\n`externalErrors` can also be used to handle dot path errors. \n\nIt can be handy for some backend frameworks that return errors with dot path.\n\n```ts\nimport { useRegle } from '@regle/core';\n\nconst form = reactive({\n email: '',\n name: {\n pseudo: '',\n },\n collection: [{name: ''}]\n})\n\nconst externalErrors = ref<Record<string, string[]>>({});\n\nconst { r$ } = useRegle(form, {}, { externalErrors })\n\nasync function submit() {\n const {valid} = await r$.$validate();\n\n if (valid) {\n r$.$setExternalErrors({\n email: [\"Email already exists\"],\n \"name.pseudo\": [\"Pseudo already exists\"],\n \"collection.0.name\": [\"Name already exists\"]\n })\n }\n}\n``` \n\n## Without the `externalErrors` option\n\n`r$.$setExternalErrors` also works when you don't provide the `externalErrors` option.\nRegle stores those errors internally.\n\n```ts\nconst form = reactive({\n email: '',\n name: {\n pseudo: '',\n },\n})\n\nconst { r$ } = useRegle(form, {\n email: { required },\n name: { pseudo: { required } },\n})\n\nasync function submit() {\n const { valid } = await r$.$validate();\n\n if (!valid) return;\n\n r$.$setExternalErrors({\n email: ['Email already exists'],\n 'name.pseudo': ['Pseudo already exists'],\n });\n}\n```\n\n## Clearing errors\n\nBy default, when you set the external errors, Regle will keep them until the form is validated or modified again.\n\nThis behavior can be modified with these options:\n\n### `clearExternalErrors`\n\n```ts\nr$.$reset({ clearExternalErrors: false });\n```\n\n### `clearExternalErrorsOnValidate`\n\n```ts\nimport { useRegle } from '@regle/core';\n\nconst { r$ } = useRegle(form, {}, { \n externalErrors, \n clearExternalErrorsOnValidate: true \n})\n```\n\n### `clearExternalErrorsOnChange`\n\n```ts\nimport { useRegle } from '@regle/core';\n\nconst { r$ } = useRegle(form, {}, { \n externalErrors, \n clearExternalErrorsOnChange: false \n})\n```\n\n### `$clearExternalErrors()`\n\nYou can also clear the errors manually by calling the `$clearExternalErrors` method.\n\n```ts\nr$.$clearExternalErrors();\n```"
|
|
93
93
|
},
|
|
94
94
|
{
|
|
95
95
|
"id": "common-usage-reset-form",
|
|
@@ -131,14 +131,14 @@ const rawData = {
|
|
|
131
131
|
"title": "Modifiers",
|
|
132
132
|
"category": "core-concepts",
|
|
133
133
|
"path": "core-concepts/modifiers.md",
|
|
134
|
-
"content": "# Modifiers\n\nModifiers allow you to control the behavior and settings of validation rules in your application. They can be applied globally to all fields or customized per field.\n\n## Deep modifiers\n\nDeep modifiers are specified as the third argument of the `useRegle` composable. They apply recursively to all fields within your state.\n\n```ts\nconst { r$ } = useRegle({}, {}, {\n /* modifiers */\n})\n```\n\n### `autoDirty`\n\n__Type__: `boolean`\n\n__Default__: `true`\n\nAutomatically set the dirty set without the need of `$value` or `$touch`.\n\n### `immediateDirty`\n\n__Type__: `boolean`\n\n__Default__: `false`\n\nSet the dirty state to true when the form is initialized.\n\n### `disabled`\n\n__Type__: `boolean`\n\n__Default__: `false`\n\nTemporarily pauses Regle reactivity and validation computation.\n\nWhen `disabled` is `true`, state changes still happen, but validation status does not update until you enable it again.\n\n```ts\nimport { ref } from 'vue';\nimport { useRegle } from '@regle/core';\nimport { required } from '@regle/rules';\n\nconst disabled = ref(false);\n\nconst { r$ } = useRegle(\n { name: '' },\n { name: { required } },\n { disabled }\n);\n\ndisabled.value = true;\nr$.$value.name = 'John'; // value updates, validation is paused\n\ndisabled.value = false; // validation reactivity resumes\n```\n\n### `silent`\n\n__Type__: `boolean`\n\n__Default__: `false`\n\nRegle Automatically tracks changes in the state for all nested rules. If set to `true`, you must manually call `$touch` or `$validate` to display errors.\n\n### `lazy`\n\n__Type__: `boolean`\n\n__Default__: `false`\n\nUsage:\n\nWhen set to false, tells the rules to be called on init, otherwise they are lazy and only called when the field is dirty.\n\n### `externalErrors`\n\n__Type__: `RegleExternalErrorTree<State>` \n\nPass an object, matching your error state, that holds external validation errors. These can be from a backend validations or something else.\n\nCheck the [External errors](/common-usage/external-errors) section for more details.\n\n### `rewardEarly`\n\n__Type__: `boolean`\n\n__Default__: `false`\n\n__Side effect__: disable `$autoDirty` when `true`.\n\nEnables the `reward-early-punish-late` mode of Regle. This mode will not set fields as invalid once they are valid, unless manually triggered by `$validate` method.\n\nThis
|
|
134
|
+
"content": "# Modifiers\n\nModifiers allow you to control the behavior and settings of validation rules in your application. They can be applied globally to all fields or customized per field.\n\n## Deep modifiers\n\nDeep modifiers are specified as the third argument of the `useRegle` composable. They apply recursively to all fields within your state.\n\n```ts\nconst { r$ } = useRegle({}, {}, {\n /* modifiers */\n})\n```\n\n### `autoDirty`\n\n__Type__: `boolean`\n\n__Default__: `true`\n\nAutomatically set the dirty set without the need of `$value` or `$touch`.\n\n### `immediateDirty`\n\n__Type__: `boolean`\n\n__Default__: `false`\n\nSet the dirty state to true when the form is initialized.\n\n### `disabled`\n\n__Type__: `boolean`\n\n__Default__: `false`\n\nTemporarily pauses Regle reactivity and validation computation.\n\nWhen `disabled` is `true`, state changes still happen, but validation status does not update until you enable it again.\n\n```ts\nimport { ref } from 'vue';\nimport { useRegle } from '@regle/core';\nimport { required } from '@regle/rules';\n\nconst disabled = ref(false);\n\nconst { r$ } = useRegle(\n { name: '' },\n { name: { required } },\n { disabled }\n);\n\ndisabled.value = true;\nr$.$value.name = 'John'; // value updates, validation is paused\n\ndisabled.value = false; // validation reactivity resumes\n```\n\n### `silent`\n\n__Type__: `boolean`\n\n__Default__: `false`\n\nRegle Automatically tracks changes in the state for all nested rules. If set to `true`, you must manually call `$touch` or `$validate` to display errors.\n\n### `lazy`\n\n__Type__: `boolean`\n\n__Default__: `false`\n\nUsage:\n\nWhen set to false, tells the rules to be called on init, otherwise they are lazy and only called when the field is dirty.\n\n### `externalErrors`\n\n__Type__: `RegleExternalErrorTree<State>` \n\nPass an object, matching your error state, that holds external validation errors. These can be from a backend validations or something else.\n\nCheck the [External errors](/common-usage/external-errors) section for more details.\n\n### `rewardEarly`\n\n__Type__: `boolean`\n\n__Default__: `false`\n\n__Side effect__: disable `$autoDirty` when `true`.\n\nEnables the `reward-early-punish-late` mode of Regle. This mode will not set fields as invalid once they are valid, unless manually triggered by `$validate` method.\n\nThis has no effect when `autoDirty` is set to `true` (the default). Set `autoDirty: false` for `rewardEarly` to take effect.\n\n### `clearExternalErrorsOnChange`\n\n__Type__: `boolean`\n\n__Default__: `true`\n\nThis mode is similar to `rewardEarly`, but only applies to external errors.\nSetting it to `false` will keep the server errors until `$clearExternalErrors` is called.\n\n### `clearExternalErrorsOnValidate`\n\n__Type__: `boolean`\n\n__Default__: `false`\n\nThis mode is similar to `clearExternalErrorsOnChange`, but for the `$validate` and `$validateSync` methods.\nSetting it to `false` will keep the server errors until `$clearExternalErrors` is called manually, or the `externalErrors` is set again.\n\n### `validationGroups`\n\n__Type__: `(fields) => Record<string, (RegleFieldStatus |RegleCollectionStatus)[]>`\n\nValidation groups let you merge field properties under one, to better handle validation status.\n\nYou will have access to your declared groups in the `r$.$groups` object.\n\n```ts twoslash\n\nimport { ref } from 'vue';\n// ---cut---\nimport { useRegle } from '@regle/core';\nimport { required } from '@regle/rules';\n\nconst { r$ } = useRegle({ email: '', user: { firstName: '' } }, {\n email: { required },\n user: {\n firstName: { required },\n }\n}, {\n validationGroups: (fields) => ({\n group1: [fields.email, fields.user.firstName]\n })\n})\n\nr$.$groups.group1.\n\n```\n<br><br><br><br>\n\n## Per-field modifiers\n\nPer-field modifiers allow to customize more precisely which behavior you want for each field.\n\n```ts twoslash\n\nimport { useRegle } from '@regle/core';\n// ---cut---\nconst { r$ } = useRegle({ name: '' }, {\n name: { $ }\n\n})\n```\n\n<br><br>\n\n`$autoDirty` `$lazy`, `$silent`, `$immediateDirty` and `$rewardEarly` work the same as the deep modifiers.\n\n### `$debounce`\nType: `number` (ms)\n\nThis let you declare the number of milliseconds the rule needs to wait before executing. Useful for async or heavy computations.\n\n:::tip\nAll async rules have a default debounce of `200ms`, you can disable or modify this setting with `$debounce`\n:::\n\n### `$isEdited`\nType: `(currentValue: MaybeInput<TValue>, initialValue: MaybeInput<TValue>, defaultHandlerFn: (currentValue: unknown, initialValue: unknown) => boolean) => boolean`\n\nOverride the default `$edited` property handler. Useful to handle custom comparisons for complex object types.\n\n:::warning\nIt's highly recommended to use this modifier with the [`markStatic`](/advanced-usage/immutable-constructors) helper to handle immutable constructors.\n:::\n\n```ts\nimport { markStatic, useRegle } from '@regle/core';\nimport { Decimal } from 'decimal.js';\nimport { required } from '@regle/rules';\n\nconst { r$ } = useRegle({ decimal: markStatic(new Decimal(1)) }, {\n decimal: {\n required,\n $isEdited(currentValue, initialValue, defaultHandlerFn) {\n if (currentValue != null && initialValue != null) {\n return currentValue.toNearest(0.01).toString() !== initialValue.toNearest(0.01).toString();\n }\n // fallback to the default handler\n return defaultHandlerFn(currentValue, initialValue);\n },\n },\n})\n```\n\n## Array specific modifiers\n\nThis modifiers are only impacting Array collections.\n\n```ts\nconst { r$ } = useRegle({ collection: [] }, {\n collection: { /** Deep modifiers */ }\n})\n```\n\n### `$deepCompare`\nType: `boolean`\n\nDefault: `false`\n\nAllow deep compare of array children to compute the `$edited` property.\n\nIt's disabled by default for performance reasons."
|
|
135
135
|
},
|
|
136
136
|
{
|
|
137
137
|
"id": "core-concepts-rules-built-in-rules",
|
|
138
138
|
"title": "Built-in rules",
|
|
139
139
|
"category": "rules",
|
|
140
140
|
"path": "core-concepts/rules/built-in-rules.md",
|
|
141
|
-
"content": "# Built-in rules\n\nAll built-in rules are available through the `@regle/rules` package.\n\nDon't forget to install it if you haven't:\n\n::: code-group\n\n```sh [pnpm]\npnpm add @regle/rules\n```\n\n```sh [npm]\nnpm install @regle/rules\n```\n\n```sh [yarn]\nyarn add @regle/rules\n```\n\n```sh [bun]\nbun add @regle/rules\n```\n\n:::\n\n:::tip\nEvery built-in rule will check if the value of the field is set before checking if it's valid.\n\nThis allow to have rules even if the field is not required.\n:::\n\n## `alpha`\n\n_**Params**_\n - `allowSymbols?: MaybeRefOrGetter<boolean>`\n\nAllows only alphabetic characters.\n\n```ts\nimport { alpha } from '@regle/rules';\n\nconst { r$ } = useRegle({ name: '' }, {\n name: { \n alpha,\n // or\n alpha: alpha({ allowSymbols: true }),\n },\n})\n```\n\n## `alphaNum`\n\n_**Params**_\n - `allowSymbols?: MaybeRefOrGetter<boolean>`\n\nAllows only alphanumeric characters.\n\n```ts\nimport { useRegle } from '@regle/core';\nimport { alphaNum } from '@regle/rules';\n\nconst { r$ } = useRegle({ name: '' }, {\n name: { \n alphaNum,\n // or\n alphaNum: alphaNum({ allowSymbols: true }),\n },\n});\n```\n\n## `atLeastOne`\n\n_**Params**_\n - `keys?: MaybeRefOrGetter<string[]>` - Optional list of keys to check. If not provided, checks if the object has at least one filled property.\n\n_**Works with**_\n - `Record | object`\n\nChecks if at least one key is filled in the object. Useful for object-level validation with `$self`.\n\n```ts\nimport { atLeastOne } from '@regle/rules';\n\nconst { r$ } = useRegle({ user: { firstName: '', lastName: '' } }, {\n user: {\n $self: {\n // Check if any property is filled\n atLeastOne,\n // or check specific keys\n atLeastOne: atLeastOne(['firstName', 'lastName']),\n },\n },\n})\n```\n\n## `between`\n\n_**Params**_\n - `min: Ref<number> | number | () => number`\n - `max: Ref<number> | number | () => number`\n - `options?: {allowEqual?: boolean}`\n\nChecks if a number is in specified bounds. `min` and `max` are both inclusive.\n\n```ts\nimport { between } from '@regle/rules';\n\nconst maxCount = ref(6);\n\nconst { r$ } = useRegle({ count: 0 }, {\n count: {\n between: between(1, 6),\n between: between(1, maxCount, {allowEqual: false}),\n between: between(() => maxCount.value, 10)\n },\n})\n```\n\n## `boolean`\n\nRequires a value to be a native boolean type. Mainly used for typing.\n\n```ts\nimport {type InferInput} from '@regle/core';\nimport { boolean } from '@regle/rules';\n\nconst rules = {\n checkbox: { boolean },\n}\n\nconst state = ref<InferInput<typeof rules>>({});\n```\n\n## `checked`\n\nRequires a boolean value to be `true`. This is useful for checkbox inputs.\n\n### Note\n\nThis rule does not need `required` to be set, it will assert the value is set.\n\n```ts\nimport { checked } from '@regle/rules';\n\nconst { r$ } = useRegle({ confirm: false }, {\n confirm: { checked },\n})\n```\n\n## `contains`\n\n_**Params**_\n- `contain: Ref<string> | string | () => string`\n\nChecks if the string contains the specified substring.\n\n```ts\nimport { contains } from '@regle/rules';\n\nconst { r$ } = useRegle({ bestLib: '' }, {\n bestLib: {\n contains: contains('regle')\n },\n})\n```\n\n## `containsSpecialCharacter`\n\n_**Params**_\n- `minCharactersCount?: Ref<number> | number | () => number`\n\nRequires a string to contain at least a number of special characters.\n\n```ts\nimport { containsSpecialCharacter } from '@regle/rules';\n\nconst { r$ } = useRegle({ password: '' }, {\n password: {\n containsSpecialCharacter,\n // or with a custom minimum\n containsSpecialCharacter: containsSpecialCharacter(2),\n },\n})\n```\n\n## `containsUppercase`\n\n_**Params**_\n- `minUppercaseCount?: Ref<number> | number | () => number`\n\nRequires a string to contain at least a number of uppercase letters.\n\n```ts\nimport { containsUppercase } from '@regle/rules';\n\nconst { r$ } = useRegle({ password: '' }, {\n password: {\n containsUppercase,\n // or with a custom minimum\n containsUppercase: containsUppercase(2),\n },\n})\n```\n\n## `date`\n\nRequires a value to be a native Date constructor. Mainly used for typing.\n\n```ts\nimport {type InferInput} from '@regle/core';\nimport { date } from '@regle/rules';\n\nconst rules = {\n birthday: { date },\n}\n\nconst state = ref<InferInput<typeof rules>>({});\n```\n\n## `dateAfter`\n_**Params**_\n - `after: Ref<string | Date> | string | Date | () => string | Date`\n - `options?: {allowEqual?: boolean}`\n\nChecks if the date is after the given parameter.\n\n```ts\nimport { dateAfter } from '@regle/rules';\n\nconst today = ref(new Date());\n\nconst { r$ } = useRegle({ birthday: null as Date | null }, {\n birthday: {\n dateAfter: dateAfter(today),\n // or\n dateAfter: dateAfter(today, { allowEqual: false }),\n },\n})\n```\n\n## `dateBefore`\n_**Params**_\n - `before: Ref<string | Date> | string | Date | () => string | Date`\n - `options?: {allowEqual?: boolean}`\n\nChecks if the date is before the given parameter.\n\n```ts\nimport { dateBefore } from '@regle/rules';\n\nconst today = ref(new Date());\n\nconst { r$ } = useRegle({ birthday: null as Date | null }, {\n birthday: {\n dateBefore: dateBefore(today),\n // or\n dateBefore: dateBefore(today, { allowEqual: false }),\n },\n})\n```\n\n## `dateBetween`\n\n_**Params**_\n - `before: Ref<string | Date> | string | Date | () => string | Date`\n - `after: Ref<string | Date> | string | Date | () => string | Date`\n - `options?: {allowEqual?: boolean}`\n\nChecks if the date falls between the specified bounds.\n\n```ts\nimport { dateBetween } from '@regle/rules';\n\nconst before = ref(new Date());\nconst after = ref(new Date(2030, 3, 1));\n\nconst { r$ } = useRegle({ birthday: null as Date | null }, {\n birthday: {\n dateBetween: dateBetween(before, after),\n // or\n dateBetween: dateBetween(before, after, { allowEqual: false }),\n },\n})\n```\n\n## `decimal`\n\nAllows positive and negative decimal numbers.\n\n```ts\nimport { decimal } from '@regle/rules';\n\nconst { r$ } = useRegle({ price: 0 }, {\n price: { decimal },\n})\n```\n\n## `domain`\n\nValidates domain names only (for example `example.com` or `sub.example.com`).\n\n```ts\nimport { domain } from '@regle/rules';\n\nconst { r$ } = useRegle({ siteDomain: '' }, {\n siteDomain: { domain },\n})\n```\n\n## `email`\n\nValidates email addresses. Always verify on the server to ensure the address is real and not already in use.\n\n```ts\nimport { email } from '@regle/rules';\n\nconst { r$ } = useRegle({ email: '' }, {\n email: { email },\n})\n```\n\n## `emoji`\n\nValidates emojis.\n\n```ts\nimport { emoji } from '@regle/rules';\n\nconst { r$ } = useRegle({ emoji: '' }, {\n emoji: { emoji },\n})\n```\n\n## `endsWith`\n\n_**Params**_\n- `end: Ref<string> | string | () => string`\n\nChecks if the string ends with the specified substring.\n\n```ts\nimport { endsWith } from '@regle/rules';\n\nconst { r$ } = useRegle({ firstName: '' }, {\n firstName: { endsWith: endsWith('foo') },\n})\n```\n\n## `exactDigits`\n_**Params**_\n - `count: Ref<number> | number | () => number`\n\nRequires the input value to have a strict specified number of digits.\n\n```ts\nimport { exactDigits } from '@regle/rules';\n\nconst exactValue = ref(6);\n\nconst { r$ } = useRegle({ digits: '' }, {\n digits: {\n exactDigits: exactDigits(6),\n // or with ref\n exactDigits: exactDigits(exactValue),\n // or with getter\n exactDigits: exactDigits(() => exactValue.value)\n },\n})\n```\n\n## `exactLength`\n\n_**Params**_\n - `count: Ref<number> | number | () => number`\n\nRequires the input value to have a strict specified length, inclusive. Works with arrays, objects and strings.\n\n```ts\nimport { exactLength } from '@regle/rules';\n\nconst exactValue = ref(6);\n\nconst { r$ } = useRegle({ name: '' }, {\n name: {\n exactLength: exactLength(6),\n exactLength: exactLength(exactValue),\n exactLength: exactLength(() => exactValue.value)\n },\n})\n```\n\n## `exactValue`\n\n_**Params**_\n - `count: Ref<number> | number | () => number`\n\nRequires a field to have a strict numeric value.\n\n```ts\nimport { exactValue } from '@regle/rules';\n\nconst exactCount = ref(6);\n\nconst { r$ } = useRegle({ count: 0 }, {\n count: {\n exactValue: exactValue(6),\n exactValue: exactValue(exactCount),\n exactValue: exactValue(() => exactCount.value)\n },\n})\n```\n\n## `file`\n\nRequires a value to be a native File constructor. Mainly used for typing.\n\n```ts\nimport { file } from '@regle/rules';\n\nconst rules = {\n file: { file },\n}\n\nconst state = ref<InferInput<typeof rules>>({});\n```\n\n## `fileType`\n\nRequires a value to be a file with a specific type.\n\n```ts\nimport { fileType } from '@regle/rules';\n\nconst { r$ } = useRegle({ file: null as File | null }, {\n file: { fileType: fileType(['image/png', 'image/jpeg']) },\n})\n```\n\n## `hexadecimal`\n\nValidates hexadecimal values.\n\n```ts\nimport { hexadecimal } from '@regle/rules';\n\nconst { r$ } = useRegle({ hexadecimal: '' }, {\n hexadecimal: { hexadecimal },\n})\n```\n\n## `hostname`\n\nValidates hostnames.\n\n```ts\nimport { hostname } from '@regle/rules';\n\nconst { r$ } = useRegle({ siteHost: '' }, {\n siteHost: { hostname },\n})\n```\n\n## `httpUrl`\n\n_**Params**_\n- `options?: {protocol?: RegExp}`\n\nValidates HTTP URLs.\n\n```ts\nimport { httpUrl } from '@regle/rules';\n\nconst { r$ } = useRegle({ bestUrl: '' }, {\n bestUrl: { httpUrl },\n // or with custom protocol validation\n bestUrl: { httpUrl: httpUrl({ protocol: /^https$/ }) },\n})\n```\n\n## `integer`\n\nAllows only integers (positive and negative).\n\n```ts\nimport { integer } from '@regle/rules';\n\nconst { r$ } = useRegle({ count: 0 }, {\n count: { integer },\n})\n```\n\n## `ipv4Address`\n\nValidates IPv4 addresses in dotted decimal notation *127.0.0.1*.\n\n```ts\nimport { ipv4Address } from '@regle/rules';\n\nconst { r$ } = useRegle({ address: '' }, {\n address: { ipv4Address },\n})\n```\n\n## `literal`\n\nValidates literal values.\n\n### Note\n\nThis rule does not need `required` to be set, it will assert the value is set.\n\n```ts\nimport { literal } from '@regle/rules';\n\nconst { r$ } = useRegle({ value: '' }, {\n value: { literal: literal('foo') },\n})\n```\n\n## `lowercase`\n\nValidates lowercase strings.\n\n```ts\nimport { lowercase } from '@regle/rules';\n\nconst { r$ } = useRegle({ name: '' }, {\n name: { lowercase },\n})\n```\n\n## `macAddress`\n\n_**Params**_\n - `separator?: string | Ref<string> | () => string`\n\nValidates MAC addresses. Call as a function to specify a custom separator (e.g., ':' or an empty string for 00ff1122334455).\n\n```ts\nimport { useRegle } from '@regle/core';\nimport { macAddress } from '@regle/rules';\n\nconst { r$ } = useRegle({ address: '' }, {\n address: {\n macAddress,\n // or\n macAddress: macAddress('-')\n },\n});\n```\n\n## `maxFileSize`\n\nRequires a value to be a file with a maximum size.\n\n```ts\nimport { maxFileSize } from '@regle/rules';\n\nconst { r$ } = useRegle({ file: null as File | null }, {\n file: { maxFileSize: maxFileSize(10_000_000) }, // 10 MB\n})\n```\n\n## `maxLength`\n\n_**Params**_\n - `max: Ref<number> | number | () => number`\n - `options?: {allowEqual?: boolean}`\n\n_**Works with**_\n - `Array | Record | string | number`\n\nRequires the input value to have a maximum specified length, inclusive. Works with arrays, objects and strings.\n\n```ts\nimport { maxLength } from '@regle/rules';\n\nconst maxValue = ref(6);\n\nconst { r$ } = useRegle({ name: '' }, {\n name: {\n maxLength: maxLength(6),\n maxLength: maxLength(maxValue),\n maxLength: maxLength(() => maxValue.value)\n },\n})\n```\n\n## `maxValue`\n\n_**Params**_\n - `min: Ref<number> | number | () => number`\n - `options?: {allowEqual?: boolean}`\n\n Requires a field to have a specified maximum numeric value.\n\n```ts\nimport { maxValue } from '@regle/rules';\n\nconst maxCount = ref(6);\n\nconst { r$ } = useRegle({ count: 0 }, {\n count: {\n maxValue: maxValue(6),\n maxValue: maxValue(maxCount, {allowEqual: false}),\n maxValue: maxValue(() => maxCount.value)\n },\n})\n```\n\n## `minFileSize`\n\nRequires a value to be a file with a minimum size.\n\n```ts\nimport { minFileSize } from '@regle/rules';\n\nconst { r$ } = useRegle({ file: null as File | null }, {\n file: { minFileSize: minFileSize(1_000_000) }, // 1 MB\n})\n```\n\n## `minLength`\n\n_**Params**_\n - `min: Ref<number> | number | () => number`\n - `options?: {allowEqual?: boolean}`\n\n_**Works with**_\n - `Array | Record | string | number`\n\nRequires the input value to have a minimum specified length, inclusive. Works with arrays, objects and strings.\n\n```ts\nimport { minLength } from '@regle/rules';\n\nconst minValue = ref(6);\n\nconst { r$ } = useRegle({ name: '' }, {\n name: {\n minLength: minLength(6),\n minLength: minLength(minValue),\n minLength: minLength(() => minValue.value)\n },\n})\n```\n\n## `minValue`\n\n_**Params**_\n - `min: Ref<number> | number | () => number`\n - `options?: {allowEqual?: boolean}`\n\n_**Works with**_\n - `number`\n\nRequires a field to have a specified minimum numeric value.\n\n```ts\nimport { minValue } from '@regle/rules';\n\nconst minCount = ref(6);\n\nconst { r$ } = useRegle({ count: 0 }, {\n count: {\n minValue: minValue(6),\n minValue: minValue(minCount, {allowEqual: false}),\n minValue: minValue(() => minCount.value)\n },\n})\n```\n\n## `nativeEnum`\n\nValidate against a native Typescript enum value. Similar to Zod's `nativeEnum`\n\n```ts\nimport { nativeEnum } from '@regle/rules';\n\nenum Foo {\n Bar, Baz\n}\n\nconst { r$ } = useRegle({ type: '' }, {\n type: { nativeEnum: nativeEnum(Foo) },\n})\n```\n\n## `number`\n\nRequires a value to be a native number type. Mainly used for typing.\n\n```ts\nimport { number } from '@regle/rules';\n\nconst rules = {\n count: { number },\n}\n\nconst state = ref<InferInput<typeof rules>>({});\n```\n\n## `numeric`\n\nAllows only numeric values (including numeric strings).\n\n```ts\nimport { numeric } from '@regle/rules';\n\nconst { r$ } = useRegle({ count: 0 }, {\n count: { numeric },\n})\n```\n\n## `oneOf`\n\nAllow only one of the values from a fixed Array of possible entries.\n\n_**Params**_\n - `options: MaybeRefOrGetter<Array<string | number>>`\n\n```ts\nimport { oneOf } from '@regle/rules';\n\nconst foodEnum = {\n Fish: 'Fish',\n Meat: 'Meat',\n Bone: 'Bone',\n} as const;\n\nconst { r$ } = useRegle({ aliment: 'Fish' }, {\n aliment: {\n oneOf: oneOf(['Fish', 'Meat', 'Bone']),\n // or\n oneOf: oneOf(foodEnum),\n },\n})\n```\n\n## `regex`\n\n_**Params**_\n- `regexps: MaybeRefOrGetter<RegExp | RegExp[]>`\n\nChecks if the value matches one or more regular expressions.\n\n```ts\nimport { regex } from '@regle/rules';\n\nconst { r$ } = useRegle({ name: '' }, {\n name: {\n regex: regex(/^foo/),\n regex: regex([/^bar/, /baz$/]),\n },\n})\n```\n\n## `required`\n\nRequires non-empty data. Checks for empty arrays and strings containing only whitespaces.\n\n```ts\nimport {required} from '@regle/rules';\n\nconst {r$} = useRegle({name: ''}, {\n name: {required},\n})\n```\n\n## `requiredIf`\n\n_**Params**_\n - `condition: Ref<unknown> | unknown | () => unknown` - the property to base the `required` validator on.\n\nRequires non-empty data, only if provided data property, ref, or a function resolves to `true`.\n\n```ts\nimport { requiredIf } from '@regle/rules';\n\nconst form = ref({ name: '', condition: false });\n\nconst conditionRef = ref(false);\n\nconst { r$ } = useRegle(form, {\n name: {\n required: requiredIf(() => form.value.condition),\n required: requiredIf(conditionRef),\n },\n})\n```\n\n## `requiredUnless`\n\n_**Params**_\n - `condition: Ref<unknown> | unknown | () => unknown` - the property to base the `required` validator on.\n\nRequires non-empty data, only if provided data property, ref, or a function resolves to `false`.\n\n```ts\nimport { requiredUnless } from '@regle/rules';\n\nconst form = ref({ name: '', condition: false });\n\nconst conditionRef = ref(false);\n\nconst { r$ } = useRegle(form, {\n name: {\n required: requiredUnless(() => form.value.condition),\n required: requiredUnless(conditionRef)\n },\n})\n```\n\n## `sameAs`\n\n_**Params**_\n * `target: unknown`\n\nChecks if the value matches the specified property or ref.\n\n```ts\nimport { sameAs } from '@regle/rules';\n\nconst form = ref({\n password: '',\n confirmPassword: '',\n});\n\nconst { r$ } = useRegle(form, {\n confirmPassword: {\n sameAs: sameAs(() => form.value.password),\n }\n})\n```\n\n## `startsWith`\n\n_**Params**_\n- `start: Ref<string> | string | () => string`\n\nChecks if the string starts with the specified substring.\n\n```ts\nimport { startsWith } from '@regle/rules';\n\nconst { r$ } = useRegle({ bestLib: '' }, {\n bestLib: {\n startsWith: startsWith('regle')\n },\n})\n```\n\n## `string`\n\nRequires a value to be a native string type. Mainly used for typing\n\n```ts\nimport {type InferInput} from '@regle/core';\nimport { string } from '@regle/rules';\n\nconst rules = {\n firstName: { string },\n}\n\nconst state = ref<InferInput<typeof rules>>({});\n```\n\n## `type`\n\nDefine the input type of a rule. No runtime validation. \nOverride any input type set by other rules.\n\n```ts\nimport {type InferInput} from '@regle/core';\nimport { type } from '@regle/rules';\n\nconst rules = {\n firstName: { type: type<string>() },\n}\n\nconst state = ref<InferInput<typeof rules>>({});\n```\n\n## `uppercase`\n\nValidates uppercase strings.\n\n```ts\nimport { uppercase } from '@regle/rules';\n\nconst { r$ } = useRegle({ name: '' }, {\n name: { uppercase },\n})\n```\n\n## `url`\n\n_**Params**_\n- `options?: {protocol?: RegExp}`\n\nValidates URLs.\n\n```ts\nimport { url } from '@regle/rules';\n\nconst { r$ } = useRegle({ bestUrl: '' }, {\n bestUrl: { url },\n // or with custom protocol validation\n bestUrl: { url: url({ protocol: /^https?$/ }) },\n})\n```"
|
|
141
|
+
"content": "# Built-in rules\n\nAll built-in rules are available through the `@regle/rules` package.\n\nDon't forget to install it if you haven't:\n\n::: code-group\n\n```sh [pnpm]\npnpm add @regle/rules\n```\n\n```sh [npm]\nnpm install @regle/rules\n```\n\n```sh [yarn]\nyarn add @regle/rules\n```\n\n```sh [bun]\nbun add @regle/rules\n```\n\n:::\n\n:::tip\nEvery built-in rule will check if the value of the field is set before checking if it's valid.\n\nThis allow to have rules even if the field is not required.\n:::\n\n## `alpha`\n\n_**Params**_\n - `allowSymbols?: MaybeRefOrGetter<boolean>`\n\nAllows only alphabetic characters.\n\n```ts\nimport { alpha } from '@regle/rules';\n\nconst { r$ } = useRegle({ name: '' }, {\n name: { \n alpha,\n // or\n alpha: alpha({ allowSymbols: true }),\n },\n})\n```\n\n## `alphaNum`\n\n_**Params**_\n - `allowSymbols?: MaybeRefOrGetter<boolean>`\n\nAllows only alphanumeric characters.\n\n```ts\nimport { useRegle } from '@regle/core';\nimport { alphaNum } from '@regle/rules';\n\nconst { r$ } = useRegle({ name: '' }, {\n name: { \n alphaNum,\n // or\n alphaNum: alphaNum({ allowSymbols: true }),\n },\n});\n```\n\n## `atLeastOne`\n\n_**Params**_\n - `keys?: MaybeRefOrGetter<string[]>` - Optional list of keys to check. If not provided, checks if the object has at least one filled property.\n\n_**Works with**_\n - `Record | object`\n\nChecks if at least one key is filled in the object. Useful for object-level validation with `$self`.\n\n```ts\nimport { atLeastOne } from '@regle/rules';\n\nconst { r$ } = useRegle({ user: { firstName: '', lastName: '' } }, {\n user: {\n $self: {\n // Check if any property is filled\n atLeastOne,\n // or check specific keys\n atLeastOne: atLeastOne(['firstName', 'lastName']),\n },\n },\n})\n```\n\n## `between`\n\n_**Params**_\n - `min: Ref<number> | number | () => number`\n - `max: Ref<number> | number | () => number`\n - `options?: {allowEqual?: boolean}`\n\nChecks if a number is in specified bounds. `min` and `max` are both inclusive.\n\n```ts\nimport { between } from '@regle/rules';\n\nconst maxCount = ref(6);\n\nconst { r$ } = useRegle({ count: 0 }, {\n count: {\n between: between(1, 6),\n between: between(1, maxCount, {allowEqual: false}),\n between: between(() => maxCount.value, 10)\n },\n})\n```\n\n## `boolean`\n\nRequires a value to be a native boolean type. Mainly used for typing.\n\n```ts\nimport {type InferInput} from '@regle/core';\nimport { boolean } from '@regle/rules';\n\nconst rules = {\n checkbox: { boolean },\n}\n\nconst state = ref<InferInput<typeof rules>>({});\n```\n\n## `checked`\n\nRequires a boolean value to be `true`. This is useful for checkbox inputs.\n\n### Note\n\nThis rule does not need `required` to be set, it will assert the value is set.\n\n```ts\nimport { checked } from '@regle/rules';\n\nconst { r$ } = useRegle({ confirm: false }, {\n confirm: { checked },\n})\n```\n\n## `contains`\n\n_**Params**_\n- `contain: Ref<string> | string | () => string`\n\nChecks if the string contains the specified substring.\n\n```ts\nimport { contains } from '@regle/rules';\n\nconst { r$ } = useRegle({ bestLib: '' }, {\n bestLib: {\n contains: contains('regle')\n },\n})\n```\n\n## `containsSpecialCharacter`\n\n_**Params**_\n- `minCharactersCount?: Ref<number> | number | () => number`\n\nRequires a string to contain at least a number of special characters.\n\n```ts\nimport { containsSpecialCharacter } from '@regle/rules';\n\nconst { r$ } = useRegle({ password: '' }, {\n password: {\n containsSpecialCharacter,\n // or with a custom minimum\n containsSpecialCharacter: containsSpecialCharacter(2),\n },\n})\n```\n\n## `containsUppercase`\n\n_**Params**_\n- `minUppercaseCount?: Ref<number> | number | () => number`\n\nRequires a string to contain at least a number of uppercase letters.\n\n```ts\nimport { containsUppercase } from '@regle/rules';\n\nconst { r$ } = useRegle({ password: '' }, {\n password: {\n containsUppercase,\n // or with a custom minimum\n containsUppercase: containsUppercase(2),\n },\n})\n```\n\n## `date`\n\nRequires a value to be a native Date constructor. Mainly used for typing.\n\n```ts\nimport {type InferInput} from '@regle/core';\nimport { date } from '@regle/rules';\n\nconst rules = {\n birthday: { date },\n}\n\nconst state = ref<InferInput<typeof rules>>({});\n```\n\n## `dateAfter`\n_**Params**_\n - `after: Ref<string | Date> | string | Date | () => string | Date`\n - `options?: {allowEqual?: boolean}`\n\nChecks if the date is after the given parameter.\n\n```ts\nimport { dateAfter } from '@regle/rules';\n\nconst today = ref(new Date());\n\nconst { r$ } = useRegle({ birthday: null as Date | null }, {\n birthday: {\n dateAfter: dateAfter(today),\n // or\n dateAfter: dateAfter(today, { allowEqual: false }),\n },\n})\n```\n\n## `dateBefore`\n_**Params**_\n - `before: Ref<string | Date> | string | Date | () => string | Date`\n - `options?: {allowEqual?: boolean}`\n\nChecks if the date is before the given parameter.\n\n```ts\nimport { dateBefore } from '@regle/rules';\n\nconst today = ref(new Date());\n\nconst { r$ } = useRegle({ birthday: null as Date | null }, {\n birthday: {\n dateBefore: dateBefore(today),\n // or\n dateBefore: dateBefore(today, { allowEqual: false }),\n },\n})\n```\n\n## `dateBetween`\n\n_**Params**_\n - `before: Ref<string | Date> | string | Date | () => string | Date`\n - `after: Ref<string | Date> | string | Date | () => string | Date`\n - `options?: {allowEqual?: boolean}`\n\nChecks if the date falls between the specified bounds.\n\n```ts\nimport { dateBetween } from '@regle/rules';\n\nconst before = ref(new Date());\nconst after = ref(new Date(2030, 3, 1));\n\nconst { r$ } = useRegle({ birthday: null as Date | null }, {\n birthday: {\n dateBetween: dateBetween(before, after),\n // or\n dateBetween: dateBetween(before, after, { allowEqual: false }),\n },\n})\n```\n\n## `decimal`\n\nAllows positive and negative decimal numbers.\n\n```ts\nimport { decimal } from '@regle/rules';\n\nconst { r$ } = useRegle({ price: 0 }, {\n price: { decimal },\n})\n```\n\n## `domain`\n\nValidates domain names only (for example `example.com` or `sub.example.com`).\n\n```ts\nimport { domain } from '@regle/rules';\n\nconst { r$ } = useRegle({ siteDomain: '' }, {\n siteDomain: { domain },\n})\n```\n\n## `email`\n\nValidates email addresses. Always verify on the server to ensure the address is real and not already in use.\n\n```ts\nimport { email } from '@regle/rules';\n\nconst { r$ } = useRegle({ email: '' }, {\n email: { email },\n})\n```\n\n## `emoji`\n\nValidates emojis.\n\n```ts\nimport { emoji } from '@regle/rules';\n\nconst { r$ } = useRegle({ emoji: '' }, {\n emoji: { emoji },\n})\n```\n\n## `endsWith`\n\n_**Params**_\n- `end: Ref<string> | string | () => string`\n\nChecks if the string ends with the specified substring.\n\n```ts\nimport { endsWith } from '@regle/rules';\n\nconst { r$ } = useRegle({ firstName: '' }, {\n firstName: { endsWith: endsWith('foo') },\n})\n```\n\n## `exactDigits`\n_**Params**_\n - `count: Ref<number> | number | () => number`\n\nRequires the input value to have a strict specified number of digits.\n\n```ts\nimport { exactDigits } from '@regle/rules';\n\nconst exactValue = ref(6);\n\nconst { r$ } = useRegle({ digits: '' }, {\n digits: {\n exactDigits: exactDigits(6),\n // or with ref\n exactDigits: exactDigits(exactValue),\n // or with getter\n exactDigits: exactDigits(() => exactValue.value)\n },\n})\n```\n\n## `exactLength`\n\n_**Params**_\n - `count: Ref<number> | number | () => number`\n\nRequires the input value to have a strict specified length, inclusive. Works with arrays, objects and strings.\n\n```ts\nimport { exactLength } from '@regle/rules';\n\nconst exactValue = ref(6);\n\nconst { r$ } = useRegle({ name: '' }, {\n name: {\n exactLength: exactLength(6),\n exactLength: exactLength(exactValue),\n exactLength: exactLength(() => exactValue.value)\n },\n})\n```\n\n## `exactValue`\n\n_**Params**_\n - `count: Ref<number> | number | () => number`\n\nRequires a field to have a strict numeric value.\n\n```ts\nimport { exactValue } from '@regle/rules';\n\nconst exactCount = ref(6);\n\nconst { r$ } = useRegle({ count: 0 }, {\n count: {\n exactValue: exactValue(6),\n exactValue: exactValue(exactCount),\n exactValue: exactValue(() => exactCount.value)\n },\n})\n```\n\n## `file`\n\nRequires a value to be a native File constructor. Mainly used for typing.\n\n```ts\nimport { file } from '@regle/rules';\n\nconst rules = {\n file: { file },\n}\n\nconst state = ref<InferInput<typeof rules>>({});\n```\n\n## `fileType`\n\nRequires a value to be a file with a specific type.\n\n```ts\nimport { fileType } from '@regle/rules';\n\nconst { r$ } = useRegle({ file: null as File | null }, {\n file: { fileType: fileType(['image/png', 'image/jpeg']) },\n})\n```\n\n## `hexadecimal`\n\nValidates hexadecimal values.\n\n```ts\nimport { hexadecimal } from '@regle/rules';\n\nconst { r$ } = useRegle({ hexadecimal: '' }, {\n hexadecimal: { hexadecimal },\n})\n```\n\n## `hostname`\n\nValidates hostnames.\n\n```ts\nimport { hostname } from '@regle/rules';\n\nconst { r$ } = useRegle({ siteHost: '' }, {\n siteHost: { hostname },\n})\n```\n\n## `httpUrl`\n\n_**Params**_\n- `options?: {protocol?: RegExp}`\n\nValidates HTTP URLs.\n\n```ts\nimport { httpUrl } from '@regle/rules';\n\nconst { r$ } = useRegle({ bestUrl: '' }, {\n bestUrl: { httpUrl },\n // or with custom protocol validation\n bestUrl: { httpUrl: httpUrl({ protocol: /^https$/ }) },\n})\n```\n\n## `integer`\n\nAllows only integers (positive and negative).\n\n```ts\nimport { integer } from '@regle/rules';\n\nconst { r$ } = useRegle({ count: 0 }, {\n count: { integer },\n})\n```\n\n## `ipv4Address`\n\nValidates IPv4 addresses in dotted decimal notation *127.0.0.1*.\n\n```ts\nimport { ipv4Address } from '@regle/rules';\n\nconst { r$ } = useRegle({ address: '' }, {\n address: { ipv4Address },\n})\n```\n\n<Ipv4AddressDemo />\n\n## `literal`\n\nValidates literal values.\n\n### Note\n\nThis rule does not need `required` to be set, it will assert the value is set.\n\n```ts\nimport { literal } from '@regle/rules';\n\nconst { r$ } = useRegle({ value: '' }, {\n value: { literal: literal('foo') },\n})\n```\n\n## `lowercase`\n\nValidates lowercase strings.\n\n```ts\nimport { lowercase } from '@regle/rules';\n\nconst { r$ } = useRegle({ name: '' }, {\n name: { lowercase },\n})\n```\n\n## `macAddress`\n\n_**Params**_\n - `separator?: string | Ref<string> | () => string`\n\nValidates MAC addresses. Call as a function to specify a custom separator (e.g., ':' or an empty string for 00ff1122334455).\n\n```ts\nimport { useRegle } from '@regle/core';\nimport { macAddress } from '@regle/rules';\n\nconst { r$ } = useRegle({ address: '' }, {\n address: {\n macAddress,\n // or\n macAddress: macAddress('-')\n },\n});\n```\n\n## `maxFileSize`\n\nRequires a value to be a file with a maximum size.\n\n```ts\nimport { maxFileSize } from '@regle/rules';\n\nconst { r$ } = useRegle({ file: null as File | null }, {\n file: { maxFileSize: maxFileSize(10_000_000) }, // 10 MB\n})\n```\n\n## `maxLength`\n\n_**Params**_\n - `max: Ref<number> | number | () => number`\n - `options?: {allowEqual?: boolean}`\n\n_**Works with**_\n - `Array | Record | string | number`\n\nRequires the input value to have a maximum specified length, inclusive. Works with arrays, objects and strings.\n\n```ts\nimport { maxLength } from '@regle/rules';\n\nconst maxValue = ref(6);\n\nconst { r$ } = useRegle({ name: '' }, {\n name: {\n maxLength: maxLength(6),\n maxLength: maxLength(maxValue),\n maxLength: maxLength(() => maxValue.value)\n },\n})\n```\n\n## `maxValue`\n\n_**Params**_\n - `min: Ref<number> | number | () => number`\n - `options?: {allowEqual?: boolean}`\n\n Requires a field to have a specified maximum numeric value.\n\n```ts\nimport { maxValue } from '@regle/rules';\n\nconst maxCount = ref(6);\n\nconst { r$ } = useRegle({ count: 0 }, {\n count: {\n maxValue: maxValue(6),\n maxValue: maxValue(maxCount, {allowEqual: false}),\n maxValue: maxValue(() => maxCount.value)\n },\n})\n```\n\n## `minFileSize`\n\nRequires a value to be a file with a minimum size.\n\n```ts\nimport { minFileSize } from '@regle/rules';\n\nconst { r$ } = useRegle({ file: null as File | null }, {\n file: { minFileSize: minFileSize(1_000_000) }, // 1 MB\n})\n```\n\n## `minLength`\n\n_**Params**_\n - `min: Ref<number> | number | () => number`\n - `options?: {allowEqual?: boolean}`\n\n_**Works with**_\n - `Array | Record | string | number`\n\nRequires the input value to have a minimum specified length, inclusive. Works with arrays, objects and strings.\n\n```ts\nimport { minLength } from '@regle/rules';\n\nconst minValue = ref(6);\n\nconst { r$ } = useRegle({ name: '' }, {\n name: {\n minLength: minLength(6),\n minLength: minLength(minValue),\n minLength: minLength(() => minValue.value)\n },\n})\n```\n\n## `minValue`\n\n_**Params**_\n - `min: Ref<number> | number | () => number`\n - `options?: {allowEqual?: boolean}`\n\n_**Works with**_\n - `number`\n\nRequires a field to have a specified minimum numeric value.\n\n```ts\nimport { minValue } from '@regle/rules';\n\nconst minCount = ref(6);\n\nconst { r$ } = useRegle({ count: 0 }, {\n count: {\n minValue: minValue(6),\n minValue: minValue(minCount, {allowEqual: false}),\n minValue: minValue(() => minCount.value)\n },\n})\n```\n\n## `nativeEnum`\n\nValidate against a native Typescript enum value. Similar to Zod's `nativeEnum`\n\n```ts\nimport { nativeEnum } from '@regle/rules';\n\nenum Foo {\n Bar, Baz\n}\n\nconst { r$ } = useRegle({ type: '' }, {\n type: { nativeEnum: nativeEnum(Foo) },\n})\n```\n\n## `number`\n\nRequires a value to be a native number type. Mainly used for typing.\n\n```ts\nimport { number } from '@regle/rules';\n\nconst rules = {\n count: { number },\n}\n\nconst state = ref<InferInput<typeof rules>>({});\n```\n\n## `numeric`\n\nAllows only numeric values (including numeric strings).\n\n```ts\nimport { numeric } from '@regle/rules';\n\nconst { r$ } = useRegle({ count: 0 }, {\n count: { numeric },\n})\n```\n\n## `oneOf`\n\nAllow only one of the values from a fixed Array of possible entries.\n\n_**Params**_\n - `options: MaybeRefOrGetter<Array<string | number>>`\n\n```ts\nimport { oneOf } from '@regle/rules';\n\nconst foodEnum = {\n Fish: 'Fish',\n Meat: 'Meat',\n Bone: 'Bone',\n} as const;\n\nconst { r$ } = useRegle({ aliment: 'Fish' }, {\n aliment: {\n oneOf: oneOf(['Fish', 'Meat', 'Bone']),\n // or\n oneOf: oneOf(foodEnum),\n },\n})\n```\n\n## `regex`\n\n_**Params**_\n- `regexps: MaybeRefOrGetter<RegExp | RegExp[]>`\n\nChecks if the value matches one or more regular expressions.\n\n```ts\nimport { regex } from '@regle/rules';\n\nconst { r$ } = useRegle({ name: '' }, {\n name: {\n regex: regex(/^foo/),\n regex: regex([/^bar/, /baz$/]),\n },\n})\n```\n\n## `required`\n\nRequires non-empty data. Checks for empty arrays and strings containing only whitespaces.\n\n```ts\nimport {required} from '@regle/rules';\n\nconst {r$} = useRegle({name: ''}, {\n name: {required},\n})\n```\n\n## `requiredIf`\n\n_**Params**_\n - `condition: Ref<unknown> | unknown | () => unknown` - the property to base the `required` validator on.\n\nRequires non-empty data, only if provided data property, ref, or a function resolves to `true`.\n\n```ts\nimport { requiredIf } from '@regle/rules';\n\nconst form = ref({ name: '', condition: false });\n\nconst conditionRef = ref(false);\n\nconst { r$ } = useRegle(form, {\n name: {\n required: requiredIf(() => form.value.condition),\n required: requiredIf(conditionRef),\n },\n})\n```\n\n## `requiredUnless`\n\n_**Params**_\n - `condition: Ref<unknown> | unknown | () => unknown` - the property to base the `required` validator on.\n\nRequires non-empty data, only if provided data property, ref, or a function resolves to `false`.\n\n```ts\nimport { requiredUnless } from '@regle/rules';\n\nconst form = ref({ name: '', condition: false });\n\nconst conditionRef = ref(false);\n\nconst { r$ } = useRegle(form, {\n name: {\n required: requiredUnless(() => form.value.condition),\n required: requiredUnless(conditionRef)\n },\n})\n```\n\n## `sameAs`\n\n_**Params**_\n * `target: unknown`\n\nChecks if the value matches the specified property or ref.\n\n```ts\nimport { sameAs } from '@regle/rules';\n\nconst form = ref({\n password: '',\n confirmPassword: '',\n});\n\nconst { r$ } = useRegle(form, {\n confirmPassword: {\n sameAs: sameAs(() => form.value.password),\n }\n})\n```\n\n## `startsWith`\n\n_**Params**_\n- `start: Ref<string> | string | () => string`\n\nChecks if the string starts with the specified substring.\n\n```ts\nimport { startsWith } from '@regle/rules';\n\nconst { r$ } = useRegle({ bestLib: '' }, {\n bestLib: {\n startsWith: startsWith('regle')\n },\n})\n```\n\n## `string`\n\nRequires a value to be a native string type. Mainly used for typing\n\n```ts\nimport {type InferInput} from '@regle/core';\nimport { string } from '@regle/rules';\n\nconst rules = {\n firstName: { string },\n}\n\nconst state = ref<InferInput<typeof rules>>({});\n```\n\n## `type`\n\nDefine the input type of a rule. No runtime validation. \nOverride any input type set by other rules.\n\n```ts\nimport {type InferInput} from '@regle/core';\nimport { type } from '@regle/rules';\n\nconst rules = {\n firstName: { type: type<string>() },\n}\n\nconst state = ref<InferInput<typeof rules>>({});\n```\n\n## `uppercase`\n\nValidates uppercase strings.\n\n```ts\nimport { uppercase } from '@regle/rules';\n\nconst { r$ } = useRegle({ name: '' }, {\n name: { uppercase },\n})\n```\n\n## `url`\n\n_**Params**_\n- `options?: {protocol?: RegExp}`\n\nValidates URLs.\n\n```ts\nimport { url } from '@regle/rules';\n\nconst { r$ } = useRegle({ bestUrl: '' }, {\n bestUrl: { url },\n // or with custom protocol validation\n bestUrl: { url: url({ protocol: /^https?$/ }) },\n})\n```"
|
|
142
142
|
},
|
|
143
143
|
{
|
|
144
144
|
"id": "core-concepts-rules-index",
|
|
@@ -152,14 +152,14 @@ const rawData = {
|
|
|
152
152
|
"title": "Reusable rules",
|
|
153
153
|
"category": "rules",
|
|
154
154
|
"path": "core-concepts/rules/reusable-rules.md",
|
|
155
|
-
"content": "# Reusable rules\n\n## `createRule`\n\nTo create reusable rules, it’s recommended to use `createRule`. This utility simplifies defining the rule’s type, parameters, active state as well as track reactive dependencies automatically.\n\nExample: Recreating a simple `required` rule\n\n```ts\nimport { createRule } from '@regle/core';\nimport { isFilled } from '@regle/rules';\n\nexport const required = createRule({\n validator: (value: unknown) => {\n return isFilled(value);\n },\n message: 'This field is required',\n});\n```\n\n## Available options:\n\n### `validator`\n_**Type**_: `(value, ...params?) => boolean | {$valid: boolean, [x: string]: any}`\n\n*required*\n\nThe `validator` function determines whether the field is valid. You can write it in the same way as an inline rule.\n\n### `message`\n_**Type**_: `string | string[] | (metadata) => (string | string[])`\n\n*required*\n\nThis will define what error message you assign to your rule. It can be a string or a function receiving the value, params and metadata as parameters.\n\n### `type` \n_**Type**_: `string`\n\n*optional*\n\nSpecifies the type of validator. This is useful when multiple rules share the same target, such as `required` and `requiredIf`.\n\n### `active`\n_**Type**_: `boolean | (metadata) => boolean`\n\n*optional*\n\nDefines the `$active` state of the rule, indicating whether the rule is currently being validated. This can be computed dynamically.\nFor more details, see [Parameters and active mode](#parameters-and-active-mode).\n\n### `async`\n_**Type**_: `boolean`\n\n*optional*\n\nIf your validator function is not written with `async await` syntax, you can enforce the rule to be async with this parameter.\n\n### `tooltip`\n_**Type**_: `string | string[] | (metadata) => (string | string[])`\n\n*optional*\n\nUse `tooltip` to display non-error-related messages for your field. These tooltips are aggregated and made accessible via `xxx.$tooltips`. This is useful for providing additional information or guidance.\n\n## Reactive parameters\n\nWith `createRule` you can easily define a rule that will depend on external reactive parameters.\n\n### Declaration\n\nWhen declaring your validator, **Regle** will detect that your rule requires parameters and transform it into a function declaration:\n\n```ts\nimport { createRule, type Maybe } from '@regle/core';\n\nexport const myValidator = createRule({\n validator: (value: Maybe<string>, arg: number) => {\n return true;\n },\n message: ({ $params: [arg] }) => {\n return 'This field is invalid';\n }\n});\n```\n\nThe parameters detection also works with optional and spread parameters\n\n```ts\nimport { createRule, type Maybe } from '@regle/core';\n\nexport const myValidator = createRule({\n validator: (value: Maybe<string>, optionalArg?: number, otherOptional?: string) => {\n return true;\n },\n message: ({ $params: [optionalArg, otherOptional] }) => {\n return 'This field is invalid';\n }\n});\n\nconst {r$} = useRegle({foo: ''}, {\n foo: {\n // Can be used inline if first parameter is optional\n myValidator,\n // or\n myValidator: myValidator(5),\n // or\n myValidator: myValidator(5, 'foo')
|
|
155
|
+
"content": "# Reusable rules\n\n## `createRule`\n\nTo create reusable rules, it’s recommended to use `createRule`. This utility simplifies defining the rule’s type, parameters, active state as well as track reactive dependencies automatically.\n\nExample: Recreating a simple `required` rule\n\n```ts\nimport { createRule } from '@regle/core';\nimport { isFilled } from '@regle/rules';\n\nexport const required = createRule({\n validator: (value: unknown) => {\n return isFilled(value);\n },\n message: 'This field is required',\n});\n```\n\n## Available options:\n\n### `validator`\n_**Type**_: `(value, ...params?) => boolean | {$valid: boolean, [x: string]: any}`\n\n*required*\n\nThe `validator` function determines whether the field is valid. You can write it in the same way as an inline rule.\n\n### `message`\n_**Type**_: `string | string[] | (metadata) => (string | string[])`\n\n*required*\n\nThis will define what error message you assign to your rule. It can be a string or a function receiving the value, params and metadata as parameters.\n\n### `type` \n_**Type**_: `string`\n\n*optional*\n\nSpecifies the type of validator. This is useful when multiple rules share the same target, such as `required` and `requiredIf`.\n\n### `active`\n_**Type**_: `boolean | (metadata) => boolean`\n\n*optional*\n\nDefines the `$active` state of the rule, indicating whether the rule is currently being validated. This can be computed dynamically.\nFor more details, see [Parameters and active mode](#parameters-and-active-mode).\n\n### `async`\n_**Type**_: `boolean`\n\n*optional*\n\nIf your validator function is not written with `async await` syntax, you can enforce the rule to be async with this parameter.\n\n### `tooltip`\n_**Type**_: `string | string[] | (metadata) => (string | string[])`\n\n*optional*\n\nUse `tooltip` to display non-error-related messages for your field. These tooltips are aggregated and made accessible via `xxx.$tooltips`. This is useful for providing additional information or guidance.\n\n## Reactive parameters\n\nWith `createRule` you can easily define a rule that will depend on external reactive parameters.\n\n### Declaration\n\nWhen declaring your validator, **Regle** will detect that your rule requires parameters and transform it into a function declaration:\n\n```ts\nimport { createRule, type Maybe } from '@regle/core';\n\nexport const myValidator = createRule({\n validator: (value: Maybe<string>, arg: number) => {\n return true;\n },\n message: ({ $params: [arg] }) => {\n return 'This field is invalid';\n }\n});\n```\n\nThe parameters detection also works with optional and spread parameters\n\n```ts\nimport { createRule, type Maybe } from '@regle/core';\n\nexport const myValidator = createRule({\n validator: (value: Maybe<string>, optionalArg?: number, otherOptional?: string) => {\n return true;\n },\n message: ({ $params: [optionalArg, otherOptional] }) => {\n return 'This field is invalid';\n }\n});\n\nconst {r$} = useRegle({foo: ''}, {\n foo: {\n // Can be used inline if first parameter is optional\n myValidator,\n // or\n myValidator: myValidator(5),\n // or\n myValidator: myValidator(5, 'foo'),\n }\n})\n```\n\n:::warning\n\nWhile adding spread parameters `...anyOtherArg` is supported, keep in mind that it will receive every parameters, even the ones injected by a parent modifier like `applyIf`, `and`, `or` etc..\n\nSo it's not advised to use it\n:::\n\n### Reactivity\n\nThe real advantage of using `createRule` is that it automatically registers parameters as reactive dependencies. This means your rule works seamlessly with plain values, refs, or getter functions.\n\n```ts\nconst max = ref(5);\n\nuseRegle({name: ''},{\n name: {\n // Plain value\n rule1: myValidator(5),\n // Ref\n rule2: myValidator(max),\n // Getter value\n rule3: myValidator(() => max.value)\n }\n})\n```\n\n:::warning\nIf you pass a raw value as a parameter, it will only be reactive if all your rules are declared as a computed or a getter function\n\n```ts\nconst state = ref({name: ''})\nconst max = ref(5);\n\n// ❌ Not reactive\nuseRegle(state, {\n name: {\n maxLength: maxLength(max.value),\n ...(max.value === 3 && {\n required,\n })\n }\n})\n\n// ✅ Reactive\nuseRegle(state, () => ({\n name: {\n maxLength: maxLength(max.value),\n ...(max.value === 3 && {\n required,\n })\n }\n}))\n\n// ✅ Reactive\nconst rules = computed(() => ({\n name: {\n maxLength: maxLength(max.value),\n ...(max.value === 3 && {\n required,\n })\n }\n}))\nuseRegle(state, rules);\n\n```\n:::\n\n### Default values\n\nYou can set default value directly in the rule declaration. Be sure to type it correctly.\n\nThere will be difference in implementation if you want to access the parameter value in the message factory function as a function scope is unreadable from outside its scope.\n\nYou'll have to return explicitly the default value in the validator function, and declare the parameter as metadata.\n\n```ts\nexport const myValidator = createRule({\n validator: (value: Maybe<number>, arg: number = 0) => {\n return {\n $valid: value === arg,\n arg,\n };\n },\n message: ({ arg }) => `The value must be ${arg}`,\n});\n```\n\n### Active property\n\nThe `active` property option is a field that will provide the rule an `on/off` behaviour.\n\nSome rules have conditional validation properties, such as `requiredIf` or any rule using `applyIf`.\nThis property allows you to declare the active state of the rule.\n\nIt will then be exposed in the `$active` rule property and be used to reflect the validation to your user.\n\n```ts\nimport { createRule, useRegle } from '@regle/core';\n\nconst myConditionalRule = createRule({\n validator(value: unknown, param: string) {\n // Params like `condition` will always be unwrapped here\n // no need to check if it's a value, a ref or a getter function\n if (param === \"Yes\") {\n return isFilled(value);\n }\n return true;\n },\n active({ $params: [condition] }) {\n return condition === 'Yes';\n },\n});\n```\n\nUsage in a component:\n\n```vue\n\n<template>\n <label>\n Name <span v-if=\"r$.name.$rules.myConditionalRule.$active\">(required)</span>\n </label>\n</template>\n\n```\n\n### Recreating `requiredIf` rule\n\n::: code-group\n\n```vue [Form.vue]\n\n<template>\n <div>\n <input v-model=\"condition\" type='checkbox'/>\n <label>The field is required</label>\n </div>\n\n <div>\n <!-- Here we can use $active to know if the rule is enabled -->\n <input \n v-model='r$.$value.name'\n :placeholder='`Type your name${r$.name.$rules.required.$active ? \"*\": \"\"}`'\n />\n\n <button type=\"button\" @click=\"r$.$reset({toInitialState: true})\">Reset</button>\n </div>\n\n <ul v-if=\"r$.$errors.name.length\">\n <li v-for=\"error of r$.$errors.name\" :key='error'>\n {{ error }}\n </li>\n </ul>\n</template>\n```\n:::\n\nResult: \n\n## Async rules\n\nAsync rules are useful for server-side validations or computationally expensive local checks. They update the `$pending` state whenever invoked.\n\n```vue [App.vue]\n<template>\n <div class=\"demo-container\">\n <div>\n <input\n v-model=\"form.email\"\n :class=\"{ pending: r$.email.$pending }\"\n placeholder=\"Type your email\"\n />\n\n <button type=\"button\" @click=\"r$.$reset({toInitialState: true})\">Reset</button>\n <button type=\"button\" @click=\"r$.$validate()\">Submit</button>\n </div>\n\n <span v-if=\"r$.email.$pending\"> Checking... </span>\n \n <ul v-if=\"r$.$errors.email.length\">\n <li v-for=\"error of r$.$errors.email\" :key=\"error\">\n {{ error }}\n </li>\n </ul>\n </div>\n</template>\n\n```\n\n## Metadata\n\nLike in inline rules, you can return any data from your validator function as long as it returns an object containing at least `$valid: boolean`.\n\nIt can be useful for returning computed data from the validator, or in async function to process api result, or api errors.\n\n```ts twoslash {8}\nimport { createRule } from '@regle/core';\n\nexport const example = createRule({\n validator: (value) => {\n if (value === 'regle') {\n return {\n $valid: false,\n foo: 'bar'\n }\n }\n return true;\n },\n message({foo}) {\n\n return 'Error example';\n },\n});\n```"
|
|
156
156
|
},
|
|
157
157
|
{
|
|
158
158
|
"id": "core-concepts-rules-rule-wrappers",
|
|
159
159
|
"title": "Rule wrappers",
|
|
160
160
|
"category": "rules",
|
|
161
161
|
"path": "core-concepts/rules/rule-wrappers.md",
|
|
162
|
-
"content": "# Rule wrappers\n\nRule wrappers let you customize or upgrade your rules by injecting or replacing some properties.\n\n## Built-in wrappers\n\n### `withMessage`\n\nThe withMessage wrapper lets you associate an error message with a rule. Pass your rule as the first argument and the error message as the second.\n\n``` ts twoslash {5-13}\n\nimport { useRegle, type InlineRuleDeclaration, type Maybe, type MaybeInput } from '@regle/core';\n// ---cut---\nimport { withMessage } from '@regle/rules';\n\nconst customRuleInlineWithMetaData = ((value: Maybe<string>) => ({\n $valid: value === 'regle',\n foo: 'bar' as const\n})) satisfies InlineRuleDeclaration;\n\nconst { r$ } = useRegle({ name: '' }, {\n name: {\n // Inline functions can be also written... inline\n customRule1: withMessage((value: MaybeInput<string>) => !!value, \"Custom Error\"),\n customRule2: withMessage(customRuleInlineWithMetaData, \"Custom Error\"),\n\n // You can also access the current value and metadata with a getter function\n customRule3: withMessage(\n customRuleInlineWithMetaData, \n ({ $value, foo }) => `Custom Error: ${$value} ${foo}`\n\n ), \n }\n})\n```\n\nEvery error can be accessed in the `r$` object. In either `$errors` (if the field is dirty) or `$silentErrors` properties.\n\nIn this case:\n\n- `r$.$errors.name`\n- `r$.name.$errors`\n\n### `withParams`\n\nThe withParams wrapper allows your rule to depend on external parameters, such as a reactive property in your component or store.\n\nBy default, useRegle observes changes automatically when rules are defined using getter functions or computed properties.\n\n```ts\n/** Non reactive rules */\nuseRegle({}, { /* rules */})\n```\n\n⬇️\n\n```ts\nuseRegle({}, () => ({ /* rules */ }))\n// or\nconst rules = computed(() => ({/* rules */ }))\n\nuseRegle({}, rules)\n```\n\nHowever, sometimes dependencies cannot be tracked automatically, use `withParams` to manually define them:\n\n``` ts twoslash {7-9}\n\nimport { useRegle } from '@regle/core';\nimport { ref } from 'vue';\n// ---cut---\nimport { withParams } from '@regle/rules';\n\nconst base = ref('foo');\n\nconst { r$ } = useRegle({ name: '' }, {\n name: {\n customRule: withParams((value, param) => value === param, [base]),\n // or\n customRule: withParams((value, param) => value === param, [() => base.value]),\n }\n})\n```\n\n### `withAsync`\n\n`withAsync` works like `withParams`, but is specifically designed for async rules that depend on external values.\n\n``` ts\nimport { withAsync } from '@regle/rules';\n\nconst base = ref('foo');\n\nconst { r$ } = useRegle({ name: '' }, {\n name: {\n customRule: withAsync(async (value, param) => {\n await someAsyncCall(param)\n }, [base])\n }\n})\n```\n\n### `withTooltip`\n\nThe `withTooltip` wrapper allows you to display additional messages for your field that aren’t necessarily errors. \n\nTooltips are aggregated and accessible via `xxx.$tooltips`.\n\n## Chaining wrappers\n\nYou can combine multiple wrappers to create more powerful and flexible rules while keeping everything typed correctly.\n\n```ts twoslash {9-14}\n\nimport { useRegle } from '@regle/core';\nimport { ref } from 'vue';\nconst someAsyncCall = async (param: string) => await Promise.resolve(true);\n// ---cut---\nimport { withAsync, withMessage } from '@regle/rules';\n\nconst base = ref(1);\n\nconst { r$ } = useRegle({ name: '' },\n {\n name: {\n customRule: withMessage(\n withAsync(\n async (value, param) => await someAsyncCall(param),\n [base]\n ),\n ({$value, $params: [param] }) => `Custom error: ${$value} != ${param}`\n \n ),\n },\n }\n);\n```"
|
|
162
|
+
"content": "# Rule wrappers\n\nRule wrappers let you customize or upgrade your rules by injecting or replacing some properties.\n\n## Built-in wrappers\n\n### `withMessage`\n\nThe withMessage wrapper lets you associate an error message with a rule. Pass your rule as the first argument and the error message as the second.\n\n``` ts twoslash {5-13}\n\nimport { useRegle, type InlineRuleDeclaration, type Maybe, type MaybeInput } from '@regle/core';\n// ---cut---\nimport { withMessage } from '@regle/rules';\n\nconst customRuleInlineWithMetaData = ((value: Maybe<string>) => ({\n $valid: value === 'regle',\n foo: 'bar' as const\n})) satisfies InlineRuleDeclaration;\n\nconst { r$ } = useRegle({ name: '' }, {\n name: {\n // Inline functions can be also written... inline\n customRule1: withMessage((value: MaybeInput<string>) => !!value, \"Custom Error\"),\n customRule2: withMessage(customRuleInlineWithMetaData, \"Custom Error\"),\n\n // You can also access the current value and metadata with a getter function\n customRule3: withMessage(\n customRuleInlineWithMetaData, \n ({ $value, foo }) => `Custom Error: ${$value} ${foo}`\n\n ), \n }\n})\n```\n\nEvery error can be accessed in the `r$` object. In either `$errors` (if the field is dirty) or `$silentErrors` properties.\n\nIn this case:\n\n- `r$.$errors.name`\n- `r$.name.$errors`\n\n### `withParams`\n\nThe withParams wrapper allows your rule to depend on external parameters, such as a reactive property in your component or store.\n\nBy default, useRegle observes changes automatically when rules are defined using getter functions or computed properties.\n\n```ts\n/** Non reactive rules */\nuseRegle({}, { /* rules */})\n```\n\n⬇️\n\n```ts\nuseRegle({}, () => ({ /* rules */ }))\n// or\nconst rules = computed(() => ({/* rules */ }))\n\nuseRegle({}, rules)\n```\n\nHowever, sometimes dependencies cannot be tracked automatically, use `withParams` to manually define them:\n\n``` ts twoslash {7-9}\n\nimport { useRegle } from '@regle/core';\nimport { ref } from 'vue';\n// ---cut---\nimport { withParams } from '@regle/rules';\n\nconst base = ref('foo');\n\nconst { r$ } = useRegle({ name: '' }, {\n name: {\n customRule: withParams((value, param) => value === param, [base]),\n // or\n customRule: withParams((value, param) => value === param, [() => base.value]),\n }\n})\n```\n\n### `withAsync`\n\n`withAsync` works like `withParams`, but is specifically designed for async rules that depend on external values.\n\n``` ts\nimport { withAsync } from '@regle/rules';\n\nconst base = ref('foo');\n\nconst { r$ } = useRegle({ name: '' }, {\n name: {\n customRule: withAsync(async (value, param) => {\n await someAsyncCall(param)\n }, [base])\n }\n})\n```\n\n### `withTooltip`\n\nThe `withTooltip` wrapper allows you to display additional messages for your field that aren’t necessarily errors. \n\nTooltips are aggregated and accessible via `xxx.$tooltips`.\n\n```ts\nimport { withTooltip } from '@regle/rules';\n\nconst { r$ } = useRegle({ password: '' }, {\n password: {\n strong: withTooltip(\n (value) => !!value && value.length >= 8,\n 'Password should be at least 8 characters'\n ),\n }\n})\n\n// Access tooltips in template: r$.password.$tooltips\n```\n\n## Chaining wrappers\n\nYou can combine multiple wrappers to create more powerful and flexible rules while keeping everything typed correctly.\n\n```ts twoslash {9-14}\n\nimport { useRegle } from '@regle/core';\nimport { ref } from 'vue';\nconst someAsyncCall = async (param: string) => await Promise.resolve(true);\n// ---cut---\nimport { withAsync, withMessage } from '@regle/rules';\n\nconst base = ref(1);\n\nconst { r$ } = useRegle({ name: '' },\n {\n name: {\n customRule: withMessage(\n withAsync(\n async (value, param) => await someAsyncCall(param),\n [base]\n ),\n ({$value, $params: [param] }) => `Custom error: ${$value} != ${param}`\n \n ),\n },\n }\n);\n```"
|
|
163
163
|
},
|
|
164
164
|
{
|
|
165
165
|
"id": "core-concepts-rules-rules-operators",
|
|
@@ -180,21 +180,21 @@ const rawData = {
|
|
|
180
180
|
"title": "Validations helpers",
|
|
181
181
|
"category": "rules",
|
|
182
182
|
"path": "core-concepts/rules/validations-helpers.md",
|
|
183
|
-
"content": "# Validations helpers\n\nWhen writing custom rules, some checks or validations can become tedious, especially when handling values that might be null, undefined, or unset. It's also a best practice to verify whether a field is \"filled\" before proceeding with validation.\n\nTo simplify this process, Regle provides a set of utility functions to assist in creating custom rules.\n\nThese utilities can be accessed via:\n\n```ts\nimport { isFilled, isEmpty, getSize, ... } from '@regle/rules';\n```\n\n## Runtime and Type guards\n\n### `isFilled`\n\n_**Params**_\n - `value: unknown`\n - `considerEmptyArrayInvalid = true`\n\nThis is almost a must have for optional fields. It checks if any value you provided is defined (including arrays and objects).\nYou can base your validator result on this.\n\n`isFilled` also acts as a type guard.\n\nBy default, it considers empty array as `false`. You can override this behaviour with the `considerEmptyArrayInvalid`\n\n```ts\nimport { createRule } from '@regle/core';\nimport { isFilled } from '@regle/rules';\n\nconst rule = createRule({\n validator(value: unknown) {\n if (isFilled(value)) {\n return check(value);\n }\n return true;\n },\n message: 'Error'\n})\n```\n\n### `isEmpty`\n\n_**Params**_\n - `value: unknown`\n - `considerEmptyArrayInvalid = true`\n\nThis is the inverse of `isFilled`. It will check if the value is in any way empty (including arrays and objects)\n\n`isEmpty` also acts as a type guard.\n\nBy default, it considers empty array as `true`. You can override this behaviour with the `considerEmptyArrayInvalid`\n\n```ts\nimport { createRule, type Maybe } from '@regle/core';\nimport { isEmpty } from '@regle/rules';\n\nconst rule = createRule({\n validator(value: Maybe<string>) {\n if (isEmpty(value)) {\n return true;\n }\n return check(value);\n },\n message: 'Error'\n})\n```\n\n### `isNumber`\n\nThis is a type guard that will check if the passed value is a real `Number`.\nThis also returns false for `NaN`, so this is better than `typeof value === \"number\"`.\n\n```ts\nimport { createRule, type Maybe } from '@regle/core';\nimport { isFilled, isNumber } from '@regle/rules';\n\nconst rule = createRule({\n validator(value: Maybe<number | string>) {\n if (isFilled(value) && isNumber(value)) {\n return checkNumber(value);\n }\n return true;\n },\n message: 'Error'\n})\n```\n\n### `isDate`\n\nThis is a useful helper that can check if the provided value is a Date, it is used internally for `date` rules.\nThis can also check strings.\n\n```ts\nimport { createRule, type Maybe } from '@regle/core';\nimport { isFilled, isDate } from '@regle/rules';\n\nconst rule = createRule({\n validator(value: Maybe<string | Date>) {\n if (isFilled(value) && isDate(value)) {\n return checkDate(value);\n }\n return true;\n },\n message: 'Error'\n})\n```\n\n## Operations utils\n\n### `getSize`\n\nThis helper will return the length of any data type you pass.\nIt works with strings, arrays, objects and numbers.\n\n```ts\nimport { createRule, type Maybe } from '@regle/core';\nimport { isFilled, getSize } from '@regle/rules';\n\nconst rule = createRule({\n validator(value: Maybe<string | Array<number>>) {\n if (isFilled(value)) {\n return getSize(value) > 6;\n }\n return true;\n },\n message: 'Error'\n})\n```\n\n### `matchRegex`\n\nThis utility can take multiple regular expressions as arguments. It checks the input's validity and tests it against the provided regex patterns.\n\n```ts\nimport { createRule, type Maybe } from '@regle/core';\nimport { isFilled, matchRegex } from '@regle/rules';\n\nconst regex = createRule({\n validator(value: Maybe<string>, regexps: RegExp[]) {\n if (isFilled(value)) {\n return matchRegex(value, ...regexps);\n }\n return true;\n },\n message: 'Error'\n})\n```\n\n## Coerce utils\n\n### `toNumber`\n\nThis utility converts any string (or number) into a number using the `Number` constructor.\n\n:::warning\nThis helper returns `NaN` if the input cannot be coerced, which is technically still a number.\n\nIt can be safe to also check for `isNaN` additionally.\n:::\n\n### `toDate`\n\nThis utility will coerce any string, number or Date value into a Date using the `Date` constructor
|
|
183
|
+
"content": "# Validations helpers\n\nWhen writing custom rules, some checks or validations can become tedious, especially when handling values that might be null, undefined, or unset. It's also a best practice to verify whether a field is \"filled\" before proceeding with validation.\n\nTo simplify this process, Regle provides a set of utility functions to assist in creating custom rules.\n\nThese utilities can be accessed via:\n\n```ts\nimport { isFilled, isEmpty, getSize, ... } from '@regle/rules';\n```\n\n## Runtime and Type guards\n\n### `isFilled`\n\n_**Params**_\n - `value: unknown`\n - `considerEmptyArrayInvalid = true`\n\nThis is almost a must have for optional fields. It checks if any value you provided is defined (including arrays and objects).\nYou can base your validator result on this.\n\n`isFilled` also acts as a type guard.\n\nBy default, it considers empty array as `false`. You can override this behaviour with the `considerEmptyArrayInvalid`\n\n```ts\nimport { createRule } from '@regle/core';\nimport { isFilled } from '@regle/rules';\n\nconst rule = createRule({\n validator(value: unknown) {\n if (isFilled(value)) {\n return check(value);\n }\n return true;\n },\n message: 'Error'\n})\n```\n\n### `isEmpty`\n\n_**Params**_\n - `value: unknown`\n - `considerEmptyArrayInvalid = true`\n\nThis is the inverse of `isFilled`. It will check if the value is in any way empty (including arrays and objects)\n\n`isEmpty` also acts as a type guard.\n\nBy default, it considers empty array as `true`. You can override this behaviour with the `considerEmptyArrayInvalid`\n\n```ts\nimport { createRule, type Maybe } from '@regle/core';\nimport { isEmpty } from '@regle/rules';\n\nconst rule = createRule({\n validator(value: Maybe<string>) {\n if (isEmpty(value)) {\n return true;\n }\n return check(value);\n },\n message: 'Error'\n})\n```\n\n### `isNumber`\n\nThis is a type guard that will check if the passed value is a real `Number`.\nThis also returns false for `NaN`, so this is better than `typeof value === \"number\"`.\n\n```ts\nimport { createRule, type Maybe } from '@regle/core';\nimport { isFilled, isNumber } from '@regle/rules';\n\nconst rule = createRule({\n validator(value: Maybe<number | string>) {\n if (isFilled(value) && isNumber(value)) {\n return checkNumber(value);\n }\n return true;\n },\n message: 'Error'\n})\n```\n\n### `isDate`\n\nThis is a useful helper that can check if the provided value is a Date, it is used internally for `date` rules.\nThis can also check strings.\n\n```ts\nimport { createRule, type Maybe } from '@regle/core';\nimport { isFilled, isDate } from '@regle/rules';\n\nconst rule = createRule({\n validator(value: Maybe<string | Date>) {\n if (isFilled(value) && isDate(value)) {\n return checkDate(value);\n }\n return true;\n },\n message: 'Error'\n})\n```\n\n## Operations utils\n\n### `getSize`\n\nThis helper will return the length of any data type you pass.\nIt works with strings, arrays, objects and numbers.\n\n```ts\nimport { createRule, type Maybe } from '@regle/core';\nimport { isFilled, getSize } from '@regle/rules';\n\nconst rule = createRule({\n validator(value: Maybe<string | Array<number>>) {\n if (isFilled(value)) {\n return getSize(value) > 6;\n }\n return true;\n },\n message: 'Error'\n})\n```\n\n### `matchRegex`\n\nThis utility can take multiple regular expressions as arguments. It checks the input's validity and tests it against the provided regex patterns.\n\n```ts\nimport { createRule, type Maybe } from '@regle/core';\nimport { isFilled, matchRegex } from '@regle/rules';\n\nconst regex = createRule({\n validator(value: Maybe<string>, regexps: RegExp[]) {\n if (isFilled(value)) {\n return matchRegex(value, ...regexps);\n }\n return true;\n },\n message: 'Error'\n})\n```\n\n## Coerce utils\n\n### `toNumber`\n\nThis utility converts any string (or number) into a number using the `Number` constructor.\n\n:::warning\nThis helper returns `NaN` if the input cannot be coerced, which is technically still a number.\n\nIt can be safe to also check for `isNaN` additionally.\n:::\n\n```ts\nimport { createRule, type Maybe } from '@regle/core';\nimport { isFilled, toNumber } from '@regle/rules';\n\nconst minValue = createRule({\n validator(value: Maybe<string | number>, min: number) {\n if (isFilled(value)) {\n const num = toNumber(value);\n return !isNaN(num) && num >= min;\n }\n return true;\n },\n message: ({ $params: [min] }) => `Value must be at least ${min}`,\n});\n```\n\n### `toDate`\n\nThis utility will coerce any string, number or Date value into a Date using the `Date` constructor.\n\n```ts\nimport { createRule, type Maybe } from '@regle/core';\nimport { isFilled, toDate } from '@regle/rules';\n\nconst afterToday = createRule({\n validator(value: Maybe<string | Date>) {\n if (isFilled(value)) {\n const date = toDate(value);\n return date > new Date();\n }\n return true;\n },\n message: 'Date must be in the future',\n});\n```"
|
|
184
184
|
},
|
|
185
185
|
{
|
|
186
186
|
"id": "core-concepts-validation-properties",
|
|
187
187
|
"title": "Validation properties",
|
|
188
188
|
"category": "core-concepts",
|
|
189
189
|
"path": "core-concepts/validation-properties.md",
|
|
190
|
-
"content": "# Validation properties\n\nValidation properties are computed values or methods available for every nested rule status, including `r$` and `regle`.\n\nLet's take a look at a simple example to explain the different properties.\n\n``` vue twoslash\n\n```\n<br/>\n\n## Computed properties for fields\n\n### `$invalid` \n- Type: `readonly boolean`\n\nIndicates whether the field is invalid. It becomes `true` if any associated rules return `false`.\n\n### `$correct` \n- Type: `readonly boolean`\n \nThis is not the opposite of `$invalid`. Correct is meant to display UI validation report. \nThis will be `true` only if:\n- The field have at least one active rule\n- Is dirty and not empty\n- Passes validation\n\n### `$dirty` \n- Type: `readonly boolean`\n \nIndicates whether a field has been validated or interacted with by the user at least once. It's typically used to determine if a message should be displayed to the user. You can change this flag manually using the `$touch` and `$reset` methods. The `$dirty` flag is considered true if the current model has been touched or if all its children are dirty. \n\n### `$anyDirty` \n- Type: `readonly boolean`\n\nSimilar to `$dirty`, with one exception. The `$anyDirty` flag is considered true if given model was touched or any of its children are `$anyDirty` which means at least one descendant is `$dirty`.\n\n### `$edited` \n- Type: `readonly boolean`\n \nIndicates whether a field has been touched and if the value is different than the initial one.\n\n### `$anyEdited` \n- Type: `readonly boolean`\n\nSimilar to `$edited`, with one exception. The $anyEdited flag is considered true if given model was edited or any of its children are $anyEdited which means at least one descendant is `$edited`.\n\n### `$value` \n- Type: `TValue` (The current property value type)\n\nA reference to the original validated model. It can be used to bind your form with `v-model`.\n\n### `$silentValue` \n- Type: `TValue` (The current property value type)\n\n`$value` variant that will not \"touch\" the field and update the value silently, running only the rules, so you can easily swap values without impacting user interaction.\n\n### `$initialValue` \n- Type: `TValue` \n\nInitial value of the field. This value will be set to the current `$value` when using `$reset`.\n\n### `$originalValue` \n- Type: `TValue` \n\nOriginal value of the field. This value is the unmutated state that was passed to the form when it was initialized. This value will not be mutated when using `$reset`.\n\n \n### `$pending` \n- Type: `readonly boolean`\n\nIndicates if any async rule for the field is currently running. Always `false` for synchronous rules.\n\n### `$ready` \n- Type: `readonly boolean`\n\nIndicates whether the field is ready for submission. Equivalent to `!$invalid && !$pending`.\n\n### `$error` \n- Type: `readonly boolean`\n\nConvenience flag to easily decide if a message should be displayed. Equivalent to `$dirty && !$pending && $invalid`.\n\n### `$errors` \n- Type: `readonly string[]`\n\nCollection of all the error messages, collected for all children properties and nested forms. Only contains errors from properties where $dirty equals `true`.\n\n### `$silentErrors` \n- Type: `readonly string[]`\n\nCollection of all the error messages, collected for all children properties.\n\n### `$issues` \n- Type: `RegleFieldIssue[]`\n\nCollect all metadata of validators (errors, messages etc). Only contains metadata from properties where $dirty equals true.\n\n### `$name` \n- Type: `readonly string`\n\nReturn the current key name of the field.\n\n## Common methods for fields\n\n### `$validate` \n- Type: `(forceValues?: TState) => Promise<false | SafeOutput<TState>>`\n\nSets all properties as dirty, triggering all rules. \nIt returns a promise that will either resolve to `false` or a Headless copy of your form state. Values that had the `required` rule will be transformed into a non-nullable value (type only).\n\n#### `forceValues` parameter\n\nThe first argument is optional and can be used to assign a new state before validating. It's equivalent to use `r$.$value = x` and `r$.$validate();`.\n\n### `$validateSync` \n- Type: `(forceValues?: TState) => boolean`\n\nValidates the form synchronously without waiting for async rules. This method:\n- Does **NOT** wait for async validation results (async rules are skipped and assumed valid)\n- Returns a `boolean` directly instead of a `Promise`\n\nUse this when you need immediate validation feedback without side effects, such as for real-time UI feedback or form gating logic.\n\n```ts\n// Basic usage\nconst isValid = r$.$validateSync();\n\n// Field-level validation\nconst isEmailValid = r$.email.$validateSync();\n\n// Use with form submission gating\nfunction handleSubmit() {\n if (r$.$validateSync()) {\n // Proceed with submission\n }\n}\n```\n\n:::warning\nSince `$validateSync` skips async rules, use `$validate()` when you need to ensure all validations (including async) have passed before submission.\n:::\n\n### `$extractDirtyFields` \n- Type: `(filterNullishValues = true) => DeepPartial<TState>`\n\nWill return a copy of your state with only the fields that are dirty.\nBy default it will filter out nullish values or objects, but you can override it with the first parameter `$extractDirtyFields(false)`.\n\n### `$touch` \n- Type: `() => void`\n\nMarks the field and all nested properties as `$dirty`.\n\n### `$reset` \n- Type: `(options?: ResetOptions) => void`\n\nReset the validation status to a pristine state while keeping the current state.\nThe current state is treated as the new initial state.\n\n:::tip\nFor more information about the `$reset` method, check the [
|
|
190
|
+
"content": "# Validation properties\n\nValidation properties are computed values or methods available for every nested rule status, including `r$` and `regle`.\n\nLet's take a look at a simple example to explain the different properties.\n\n``` vue twoslash\n\n```\n<br/>\n\n## Computed properties for fields\n\n### `$invalid` \n- Type: `readonly boolean`\n\nIndicates whether the field is invalid. It becomes `true` if any associated rules return `false`.\n\n### `$correct` \n- Type: `readonly boolean`\n \nThis is not the opposite of `$invalid`. Correct is meant to display UI validation report. \nThis will be `true` only if:\n- The field have at least one active rule\n- Is dirty and not empty\n- Passes validation\n\n### `$dirty` \n- Type: `readonly boolean`\n \nIndicates whether a field has been validated or interacted with by the user at least once. It's typically used to determine if a message should be displayed to the user. You can change this flag manually using the `$touch` and `$reset` methods. The `$dirty` flag is considered true if the current model has been touched or if all its children are dirty. \n\n### `$anyDirty` \n- Type: `readonly boolean`\n\nSimilar to `$dirty`, with one exception. The `$anyDirty` flag is considered true if given model was touched or any of its children are `$anyDirty` which means at least one descendant is `$dirty`.\n\n### `$edited` \n- Type: `readonly boolean`\n \nIndicates whether a field has been touched and if the value is different than the initial one.\n\n### `$anyEdited` \n- Type: `readonly boolean`\n\nSimilar to `$edited`, with one exception. The $anyEdited flag is considered true if given model was edited or any of its children are $anyEdited which means at least one descendant is `$edited`.\n\n### `$value` \n- Type: `TValue` (The current property value type)\n\nA reference to the original validated model. It can be used to bind your form with `v-model`.\n\n### `$silentValue` \n- Type: `TValue` (The current property value type)\n\n`$value` variant that will not \"touch\" the field and update the value silently, running only the rules, so you can easily swap values without impacting user interaction.\n\n### `$initialValue` \n- Type: `TValue` \n\nInitial value of the field. This value will be set to the current `$value` when using `$reset`.\n\n### `$originalValue` \n- Type: `TValue` \n\nOriginal value of the field. This value is the unmutated state that was passed to the form when it was initialized. This value will not be mutated when using `$reset`.\n\n \n### `$pending` \n- Type: `readonly boolean`\n\nIndicates if any async rule for the field is currently running. Always `false` for synchronous rules.\n\n### `$ready` \n- Type: `readonly boolean`\n\nIndicates whether the field is ready for submission. Equivalent to `!$invalid && !$pending`.\n\n### `$error` \n- Type: `readonly boolean`\n\nConvenience flag to easily decide if a message should be displayed. Equivalent to `$dirty && !$pending && $invalid`.\n\n### `$errors` \n- Type: `readonly string[]`\n\nCollection of all the error messages, collected for all children properties and nested forms. Only contains errors from properties where $dirty equals `true`.\n\n### `$silentErrors` \n- Type: `readonly string[]`\n\nCollection of all the error messages, collected for all children properties.\n\n### `$issues` \n- Type: `RegleFieldIssue[]`\n\nCollect all metadata of validators (errors, messages etc). Only contains metadata from properties where $dirty equals true.\n\n### `$name` \n- Type: `readonly string`\n\nReturn the current key name of the field.\n\n## Common methods for fields\n\n### `$validate` \n- Type: `(forceValues?: TState) => Promise<false | SafeOutput<TState>>`\n\nSets all properties as dirty, triggering all rules. \nIt returns a promise that will either resolve to `false` or a Headless copy of your form state. Values that had the `required` rule will be transformed into a non-nullable value (type only).\n\n#### `forceValues` parameter\n\nThe first argument is optional and can be used to assign a new state before validating. It's equivalent to use `r$.$value = x` and `r$.$validate();`.\n\n### `$validateSync` \n- Type: `(forceValues?: TState) => boolean`\n\nValidates the form synchronously without waiting for async rules. This method:\n- Does **NOT** wait for async validation results (async rules are skipped and assumed valid)\n- Returns a `boolean` directly instead of a `Promise`\n\nUse this when you need immediate validation feedback without side effects, such as for real-time UI feedback or form gating logic.\n\n```ts\n// Basic usage\nconst isValid = r$.$validateSync();\n\n// Field-level validation\nconst isEmailValid = r$.email.$validateSync();\n\n// Use with form submission gating\nfunction handleSubmit() {\n if (r$.$validateSync()) {\n // Proceed with submission\n }\n}\n```\n\n:::warning\nSince `$validateSync` skips async rules, use `$validate()` when you need to ensure all validations (including async) have passed before submission.\n:::\n\n### `$extractDirtyFields` \n- Type: `(filterNullishValues = true) => DeepPartial<TState>`\n\nWill return a copy of your state with only the fields that are dirty.\nBy default it will filter out nullish values or objects, but you can override it with the first parameter `$extractDirtyFields(false)`.\n\n### `$touch` \n- Type: `() => void`\n\nMarks the field and all nested properties as `$dirty`.\n\n### `$reset` \n- Type: `(options?: ResetOptions) => void`\n\nReset the validation status to a pristine state while keeping the current state.\nThe current state is treated as the new initial state.\n\n:::tip\nFor more information about the `$reset` method, check the [resetting forms section](/common-usage/reset-form)\n:::\n\n### `$clearExternalErrors` \n- Type: `() => void`\n\nClears the `$externalErrors` state back to an empty object.\n\n### `$setExternalErrors`\n- Type: `(errors: RegleExternalErrorTree<TState> | Record<string, string[]>) => void`\n\nSets external errors from either a nested object or dot-path keys.\nThis method is available on the root `r$`, and works with or without passing the `externalErrors` option to `useRegle`.\n\n```ts\nr$.$setExternalErrors({\n email: ['Email already exists'],\n 'user.firstName': ['First name already exists'],\n});\n```\n\n### `$addRules`\n- Type: `(rules: RegleRuleDecl) => void`\n\nAdds runtime rules to the current field status.\nThis is useful when a child component receives a `RegleFieldStatus` and needs to attach its own field-specific validation rules.\n\n:::warning\nThis is convenient for children components to add rules to their parent field status, but you will lose a bit of type safety.\n:::\n\n```ts\nconst { r$ } = useRegle(form, {\n password: {},\n});\n\nr$.password.$addRules({\n required,\n minLength: minLength(8),\n});\n```\n\n### `addRules` (deprecated)\n- Type: `(rules: RegleRuleDecl) => void`\n\n:::warning Deprecated\nUse `$addRules` instead. This alias will be removed in a future version.\n:::\n\n## Specific properties for fields\n\n### `$rules` \n- Type: `Record<string, RegleRuleStatus>`\n\nThis is reactive tree containing all the declared rules of your field.\nTo know more about the rule properties check the [rules properties section](/core-concepts/rules/rules-properties)\n\n \n### `$silentIssues` \n- Type: `RegleFieldIssue[]`\n\nCollect all metadata of validators (errors, messages etc).\n \n\n## Specific properties for nested objects\n\n### `$fields` \n- Type: `Record<string, RegleStatus | RegleFieldStatus | RegleCollectionStatus>`\n\nThis 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.\n\n### `$self`\n- Type: `RegleFieldStatus`\nRepresents the status of the object itself. You can have validation rules on the object like `required`, this field represents the isolated status of the object.\n\n## Specific properties for collections\n\nCheck documentation for [collections here](/common-usage/collections)\n\n### `$each`\n- Type: `Array<string, RegleStatus>`\n\nThis will store the status of every item in your collection. Each item will be a field you can access, or map on it to display your elements.\n\n### `$self` \n- Type: `RegleFieldStatus`\nRepresents 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."
|
|
191
191
|
},
|
|
192
192
|
{
|
|
193
193
|
"id": "examples-advanced",
|
|
194
194
|
"title": "Advanced demo",
|
|
195
195
|
"category": "examples",
|
|
196
196
|
"path": "examples/advanced.md",
|
|
197
|
-
"content": "# Advanced demo\n\nYou can play with the code of this example in the stackblitz sandbox.\n\nDon't
|
|
197
|
+
"content": "# Advanced demo\n\nYou can play with the code of this example in the stackblitz sandbox.\n\nDon't forget to install the `Vue` extension in the online IDE.\n\n<a target='_blank' href=\"https://stackblitz.com/~/github.com/victorgarciaesgi/regle-examples/tree/main/examples/advanced-example?file=examples/advanced-example/src/App.vue&configPath=examples/advanced-example\">\n <img\n alt=\"Open in StackBlitz\"\n src=\"https://developer.stackblitz.com/img/open_in_stackblitz.svg\"\n />\n</a>\n\n<iframe style='width: 100%; height: 700px' src=\"https://stackblitz.com/github/victorgarciaesgi/regle-examples/tree/main/examples/advanced-example?embed=1&file=src%2FApp.vue&theme=dark&view=preview\" title=\"Sandbox editor\" sandbox=\"allow-modals allow-forms allow-popups allow-scripts allow-same-origin\"></iframe>"
|
|
198
198
|
},
|
|
199
199
|
{
|
|
200
200
|
"id": "examples-collections",
|
|
@@ -222,7 +222,7 @@ const rawData = {
|
|
|
222
222
|
"title": "index",
|
|
223
223
|
"category": "examples",
|
|
224
224
|
"path": "examples/index.md",
|
|
225
|
-
"content": "#
|
|
225
|
+
"content": "# Examples\n\n## Playground\n\nYou can try Regle in the [Regle Playground](https://play.reglejs.dev).\n\n## Simple Validation Example\n\nSee a minimal usage of Regle's validation logic: \n[Simple Validation Example](./simple.md)\n\n## Advanced Example\n\nAdvanced validation patterns and techniques: \n[Advanced Example](./advanced.md)\n\n## Collections Example\n\nWorking with arrays and collections in your forms: \n[Collections Example](./collections.md)\n\n## Server Validation Example\n\nHandling server-side validation and external errors: \n[Server Validation Example](./server-validation.md)\n\n## Conditional Rules Example\n\nApplying validation rules conditionally based on form state: \n[Conditional Rules Example](./conditional-rules.md)\n\n## Custom Rules Example\n\nDefine and use your own custom validation rules: \n[Custom Rules Example](./custom-rules.md)\n\n## Required Indicators Example\n\nDisplay required field indicators in your forms: \n[Required Indicators Example](./required-indicators.md)"
|
|
226
226
|
},
|
|
227
227
|
{
|
|
228
228
|
"id": "examples-required-indicators",
|
|
@@ -243,7 +243,7 @@ const rawData = {
|
|
|
243
243
|
"title": "Simple demo",
|
|
244
244
|
"category": "examples",
|
|
245
245
|
"path": "examples/simple.md",
|
|
246
|
-
"content": "# Simple demo\n\nYou can play with the code of this example in the stackblitz sandbox.\n\nDon't
|
|
246
|
+
"content": "# Simple demo\n\nYou can play with the code of this example in the stackblitz sandbox.\n\nDon't forget to install the `Vue` extension in the online IDE.\n\n<a target='_blank' href=\"https://stackblitz.com/~/github.com/victorgarciaesgi/regle-examples/tree/main/examples/simple-example?file=examples/simple-example/src/App.vue&configPath=examples/simple-example\">\n <img\n alt=\"Open in StackBlitz\"\n src=\"https://developer.stackblitz.com/img/open_in_stackblitz.svg\"\n />\n</a>\n\n<iframe style='width: 100%; height: 700px' src=\"https://stackblitz.com/github/victorgarciaesgi/regle-examples/tree/main/examples/simple-example?embed=1&file=src%2FApp.vue&theme=dark&view=preview\" title=\"Sandbox editor\" sandbox=\"allow-modals allow-forms allow-popups allow-scripts allow-same-origin\"></iframe>"
|
|
247
247
|
},
|
|
248
248
|
{
|
|
249
249
|
"id": "integrations-agent-skills",
|
|
@@ -257,7 +257,7 @@ const rawData = {
|
|
|
257
257
|
"title": "Regle MCP server",
|
|
258
258
|
"category": "integrations",
|
|
259
259
|
"path": "integrations/mcp-server.md",
|
|
260
|
-
"content": "# MCP Server\n\n[MCP (Model Context Protocol)](https://modelcontextprotocol.io/) is an open standard that enables AI assistants to interact with external tools and data sources. \n\nRegle offers an MCP server that can be used to get documentation and autocomplete in your favorite AI assistant editor. This allows your AI assistant to understand Regle's API and help you write validation rules more effectively.\n\nThe MCP server provides the following features:\n\n- Create form validation rules\n- Search documentation\n- Get precise information on any rule\n- Create custom rules\n- API information on every Regle helper\n\n## Cursor\n\n<a href=\"https://cursor.com/en-US/install-mcp?name=regle&config=eyJjb21tYW5kIjoibnB4IEByZWdsZS9tY3Atc2VydmVyIn0%3D\">\n <div class=\"light-only\">\n <img src=\"https://cursor.com/deeplink/mcp-install-dark.svg\" alt=\"Install MCP Server\" />\n </div>\n <div class=\"dark-only\">\n <img src=\"https://cursor.com/deeplink/mcp-install-light.svg\" alt=\"Install MCP Server\" />\n </div>\n</a>\n\nOr add to your `.cursor/mcp.json`\n```json\n{\n \"mcpServers\": {\n \"regle\": {\n \"command\": \"npx\",\n \"args\": [\"@regle/mcp-server\"]\n }\n }\n}\n```\n\n## Claude Code\n\nFor Claude Code, run the following command:\n\n```bash\nclaude mcp add-json regle --scope project '{\"command\":\"npx\",\"args\":[\"-y\",\"@regle/mcp-server\"]}'\n```"
|
|
260
|
+
"content": "# MCP Server\n\n[MCP (Model Context Protocol)](https://modelcontextprotocol.io/) is an open standard that enables AI assistants to interact with external tools and data sources. \n\nRegle offers an MCP server that can be used to get documentation and autocomplete in your favorite AI assistant editor. This allows your AI assistant to understand Regle's API and help you write validation rules more effectively.\n\nThe MCP server provides the following features:\n\n- Create form validation rules\n- Search documentation\n- Get precise information on any rule\n- Create custom rules\n- API information on every Regle helper\n\n## Cursor\n\n<a href=\"https://cursor.com/en-US/install-mcp?name=regle&config=eyJjb21tYW5kIjoibnB4IEByZWdsZS9tY3Atc2VydmVyIn0%3D\">\n <div class=\"light-only\">\n <img src=\"https://cursor.com/deeplink/mcp-install-dark.svg\" alt=\"Install MCP Server\" />\n </div>\n <div class=\"dark-only\">\n <img src=\"https://cursor.com/deeplink/mcp-install-light.svg\" alt=\"Install MCP Server\" />\n </div>\n</a>\n\nOr add to your `.cursor/mcp.json`\n```json\n{\n \"mcpServers\": {\n \"regle\": {\n \"command\": \"npx\",\n \"args\": [\"@regle/mcp-server\"]\n }\n }\n}\n```\n\n## Claude Code\n\nFor Claude Code, run the following command:\n\n```bash\nclaude mcp add-json regle --scope project '{\"command\":\"npx\",\"args\":[\"-y\",\"@regle/mcp-server\"]}'\n```\n\n## Claude Desktop\n\nFor Claude Desktop, add the following to your `claude_desktop_config.json`:\n\n```json\n{\n \"mcpServers\": {\n \"regle\": {\n \"command\": \"npx\",\n \"args\": [\"-y\", \"@regle/mcp-server\"]\n }\n }\n}\n```"
|
|
261
261
|
},
|
|
262
262
|
{
|
|
263
263
|
"id": "integrations-nuxt",
|
|
@@ -271,14 +271,14 @@ const rawData = {
|
|
|
271
271
|
"title": "Schemas libraries",
|
|
272
272
|
"category": "integrations",
|
|
273
273
|
"path": "integrations/schemas-libraries.md",
|
|
274
|
-
"content": "# Schemas libraries (Zod, Valibot, ...)\n\nRegle supports the [Standard Schema Spec](https://standardschema.dev/).\n\nThis means any Standard Schema compliant RPC library can be used with Regle.\n\nOfficial list of supported libraries:\n\n- Zod [docs](https://zod.dev/) <span data-title=\"zod\"></span> `3.24+`. \n- Valibot [docs](https://valibot.dev/) <span data-title=\"valibot\"></span> `1+`.\n- ArkType [docs](https://arktype.io/) <span data-title=\"arktype\"></span> `2+`\n- Any library following the [Standard Schema Spec](https://standardschema.dev/) \n\n::: code-group\n```sh [pnpm]\npnpm add @regle/schemas\n```\n\n```sh [npm]\nnpm install @regle/schemas\n```\n\n```sh [yarn]\nyarn add @regle/schemas\n```\n\n```sh [bun]\nbun add @regle/schemas\n```\n:::\n\n## Usage\n\nInstead of using the core `useRegle`, use `useRegleSchema` export from `@regle/schemas`.\n\n:::code-group\n```ts [Zod]\nimport { useRegleSchema } from '@regle/schemas';\nimport { z } from 'zod';\n\nconst { r$ } = useRegleSchema({ name: '' }, z.object({\n name: z.string().min(1)\n}))\n```\n\n```ts [Valibot]\nimport { useRegleSchema } from '@regle/schemas';\nimport * as v from 'valibot';\n\nconst { r$ } = useRegleSchema({ name: '' }, v.object({\n name: v.pipe(v.string(), v.minLength(3))\n}))\n```\n\n```ts [ArkType]\nimport { useRegleSchema } from '@regle/schemas';\nimport { type } from 'arktype';\n\nconst { r$ } = useRegleSchema({ name: '' }, type({\n name: \"string > 1\"\n}))\n```\n\n:::\n\n:::warning\nLimitations from the core behaviour\n\nUsing schema libraries uses a different mechanism than the core \"rules\" one. Regle will parse the entire tree instead of doing it per-field.
|
|
274
|
+
"content": "# Schemas libraries (Zod, Valibot, ...)\n\nRegle supports the [Standard Schema Spec](https://standardschema.dev/).\n\nThis means any Standard Schema compliant RPC library can be used with Regle.\n\nOfficial list of supported libraries:\n\n- Zod [docs](https://zod.dev/) <span data-title=\"zod\"></span> `3.24+`. \n- Valibot [docs](https://valibot.dev/) <span data-title=\"valibot\"></span> `1+`.\n- ArkType [docs](https://arktype.io/) <span data-title=\"arktype\"></span> `2+`\n- Any library following the [Standard Schema Spec](https://standardschema.dev/) \n\n::: code-group\n```sh [pnpm]\npnpm add @regle/schemas\n```\n\n```sh [npm]\nnpm install @regle/schemas\n```\n\n```sh [yarn]\nyarn add @regle/schemas\n```\n\n```sh [bun]\nbun add @regle/schemas\n```\n:::\n\n## Usage\n\nInstead of using the core `useRegle`, use `useRegleSchema` export from `@regle/schemas`.\n\n:::code-group\n```ts [Zod]\nimport { useRegleSchema } from '@regle/schemas';\nimport { z } from 'zod';\n\nconst { r$ } = useRegleSchema({ name: '' }, z.object({\n name: z.string().min(1)\n}))\n```\n\n```ts [Valibot]\nimport { useRegleSchema } from '@regle/schemas';\nimport * as v from 'valibot';\n\nconst { r$ } = useRegleSchema({ name: '' }, v.object({\n name: v.pipe(v.string(), v.minLength(3))\n}))\n```\n\n```ts [ArkType]\nimport { useRegleSchema } from '@regle/schemas';\nimport { type } from 'arktype';\n\nconst { r$ } = useRegleSchema({ name: '' }, type({\n name: \"string > 1\"\n}))\n```\n\n:::\n\n:::warning\nLimitations from the core behaviour\n\nUsing schema libraries uses a different mechanism than the core \"rules\" one. Regle will parse the entire tree instead of doing it per-field. That means that properties or methods are not available in nested values:\n\n- `$validate` (only at root)\n- `$pending` (only at root)\n\nOne other limitation is you won't have access to any children `$rules`, so checking if a field is required with `xx.$rules.required.active` is not possible with schemas.\n:::\n\n## Computed schema\n\nYou can also have a computed schema that can be based on other state values.\n\n:::warning\nWhen doing refinements or transform, Vue can't track what the schema depends on because you're in a function callback. \n\nSame way as `withParams` from `@regle/rules`, you can use the `withDeps` helper to force dependencies on any schema\n:::\n\n:::code-group\n\n```ts [Zod]\nimport { useRegleSchema, inferSchema, withDeps } from '@regle/schemas';\nimport { z } from 'zod';\nimport { ref, computed } from 'vue';\n\ntype Form = {\n firstName?: string;\n lastName?: string\n}\n\nconst form = ref<Form>({ firstName: '', lastName: '' })\n\nconst schema = computed(() =>\n inferSchema(form, z.object({\n firstName: z.string(),\n /** \n * Important to keep track of the dependency change\n * Without it, the validator wouldn't run if `firstName` changed\n */\n lastName: withDeps(\n z.string().refine((v) => v !== form.value.firstName, {\n message: \"Last name can't be equal to first name\",\n }),\n [() => form.value.firstName]\n ),\n }))\n);\n\nconst { r$ } = useRegleSchema(form, schema);\n\n```\n```ts [Valibot]\nimport { useRegleSchema, inferSchema, withDeps} from '@regle/schemas';\nimport * as v from 'valibot';\nimport { ref, computed } from 'vue';\n\ntype Form = {\n firstName?: string;\n lastName?: string\n}\n\nconst form = ref<Form>({ firstName: '', lastName: '' })\n\nconst schema = computed(() => \n inferSchema(form, v.object({\n firstName: v.string(),\n /** \n * Important to keep track of the dependency change\n * Without it, the validator wouldn't run if `firstName` changed\n */\n lastName: withDeps(\n v.pipe(\n v.string(),\n v.check((v) => v !== form.value.firstName, \"Last name can't be equal to first name\")\n ),\n [() => form.value.firstName]\n ),\n }))\n)\n\nconst { r$ } = useRegleSchema(form, schema);\n\n```\n\n:::\n\n## `syncState`\n\nBy default, Regle doesn't allow any transforms on the state. \n\nModifiers like `default`, `catch` or `transform` will not impact the validation.\n\nIf you want to allow the schema to update your form state you can use the `syncState` option. \nThe state will only be patched is the parse is successful.\n\n```ts\ntype RegleSchemaBehaviourOptions = {\n syncState?: {\n /**\n * Applies every transform on every update to the state\n */\n onUpdate?: boolean;\n /**\n * Applies every transform only when calling `$validate`\n */\n onValidate?: boolean;\n };\n};\n```\n\nUsage:\n\n```vue\n\n```\n\n## Type safe output\n\nSimilar to the main `useRegle` composable, `r$.$validate` also returns a type-safe output using Zod type schema parser.\n\n:::code-group\n```ts [Zod]\nimport { useRegleSchema, inferSchema } from '@regle/schemas';\nimport { z } from 'zod';\nimport { ref, computed } from 'vue';\n\ntype Form = {\n firstName?: string;\n lastName?: string\n}\n\nconst form = ref<Form>({ firstName: '', lastName: '' })\n\nconst schema = computed(() => inferSchema(form, z.object({\n firstName: z.string().optional(),\n lastName: z.string().min(1).refine(v => v !== form.value.firstName, {\n message: \"Last name can't be equal to first name\"\n }),\n})))\n\nconst { r$ } = useRegleSchema(form, schema);\n\nasync function submit() {\n const { valid, data } = await r$.$validate();\n if (valid) {\n console.log(data);\n }\n}\n\n```\n\n```ts [Valibot]\nimport { useRegleSchema, inferSchema } from '@regle/schemas';\nimport * as v from 'valibot';\nimport { ref, computed } from 'vue';\n\ntype Form = {\n firstName?: string;\n lastName?: string\n}\n\nconst form = ref<Form>({ firstName: '', lastName: '' })\n\nconst schema = computed(() => {\n return inferSchema(form, v.object({\n firstName: v.optional(v.string()),\n lastName: v.pipe(\n v.string(),\n v.minLength(3),\n v.check((v) => v !== form.value.firstName, \"Last name can't be equal to first name\")\n )\n }))\n})\n\nconst { r$ } = useRegleSchema(form, schema);\n\nasync function submit() {\n const { valid, data } = await r$.$validate();\n if (valid) {\n console.log(data);\n }\n}\n\n```\n\n```ts [ArkType]\nimport { useRegleSchema, inferSchema } from '@regle/schemas';\nimport { type } from 'arktype';\nimport { ref, computed } from 'vue';\n\ntype Form = {\n firstName?: string;\n lastName?: string\n}\n\nconst form = ref<Form>({ firstName: '', lastName: '' })\n\nconst schema = computed(() => {\n return inferSchema(form, type({\n 'firstName?': 'string',\n lastName: 'string > 3',\n }).narrow((data, ctx) => {\n if (data.firstName !== data.lastName) {\n return true;\n }\n return ctx.reject({\n expected: 'different than firstName',\n path: ['lastName'],\n });\n }))\n})\n\nconst { r$ } = useRegleSchema(form, schema);\n\nasync function submit() {\n const { valid, data } = await r$.$validate();\n if (valid) {\n console.log(data);\n }\n}\n```\n:::"
|
|
275
275
|
},
|
|
276
276
|
{
|
|
277
277
|
"id": "introduction-comparisons",
|
|
278
278
|
"title": "Comparison with other form libraries",
|
|
279
279
|
"category": "introduction",
|
|
280
280
|
"path": "introduction/comparisons.md",
|
|
281
|
-
"content": "# Comparison with other form libraries\n\n## Vuelidate\n\nRegle is successor of Vuelidate. As a long-time user of Vuelidate, I was inspired to create Regle after the project was discontinued.\n\nBoth libraries share a similar API and developer experience (DX), including:\n- Data-based validation\n- Unified reactivity\n- Simple declaration\n\nRegle builds upon these features and adds several improvements:\n- 100% type safety\n- Autocomplete\n- Zod/Valibot support (with more integrations planned)\n- Global config\n- Improved API in some areas, such as rules declaration, `$each`, `validationGroups`, `$validate`\n\n## VeeValidate\n\nRegle is a good VeeValidate alternative.\n\nVeeValidate is primarily focused on being component-centric. It now also offers Composition API helpers.\n\nIts API is less declarative compared to Regle, making it challenging to handle large forms or manage form state within a Pinia store.\n\nWhile VeeValidate supports typed schemas using libraries like Zod, Yup, or Valibot, this comes at the cost of losing some of VeeValidate's native features. In contrast, when using Zod with Regle, you retain all the features available in the default @regle/rules, ensuring a consistent developer experience.\n\n## Tanstack Forms\n\nI love Tanstack products and what
|
|
281
|
+
"content": "# Comparison with other form libraries\n\n## Vuelidate\n\nRegle is successor of Vuelidate. As a long-time user of Vuelidate, I was inspired to create Regle after the project was discontinued.\n\nBoth libraries share a similar API and developer experience (DX), including:\n- Data-based validation\n- Unified reactivity\n- Simple declaration\n\nRegle builds upon these features and adds several improvements:\n- 100% type safety\n- Autocomplete\n- Zod/Valibot support (with more integrations planned)\n- Global config\n- Improved API in some areas, such as rules declaration, `$each`, `validationGroups`, `$validate`\n\n## VeeValidate\n\nRegle is a good VeeValidate alternative.\n\nVeeValidate is primarily focused on being component-centric. It now also offers Composition API helpers.\n\nIts API is less declarative compared to Regle, making it challenging to handle large forms or manage form state within a Pinia store.\n\nWhile VeeValidate supports typed schemas using libraries like Zod, Yup, or Valibot, this comes at the cost of losing some of VeeValidate's native features. In contrast, when using Zod with Regle, you retain all the features available in the default @regle/rules, ensuring a consistent developer experience.\n\n## Tanstack Forms\n\nI love Tanstack products and what they're doing is so great for the JS community, especially making their tools framework agnostic.\n\nAs for Tanstack Forms, I feel the API for Vue keeps too much syntax logic from the React counterpart. \nIt doesn't take advantage of the Vue composition API enough.\n\nTanstack forms also relies on DOM components, Regle doesn't.\n\nRegle is a more lightweight and less boilerplate alternative to Tanstack Forms.\n\nYou can compare the [Regle playground](https://play.reglejs.dev) and the [Tanstack Forms Vue playground](https://tanstack.com/form/latest/docs/framework/vue/examples/simple?panel=code) to see that Regle is much more readable and uses way less code to do the same thing.\n\n## Formkit & VueForms\n\nFormkit and VueForms are centered around DOM components.\nRegle is headless and data-driven, so you can work with your state anywhere you want.\n\nWorking exclusively with a data-driven model enables stronger type safety and a better developer experience.\n\nRegle is an alternative to Formkit and VueForms if you don't want the UI and validation to be tied.\n\n## Formwerk\n\nFrom Formwerk website: `[Formwerk] is ideal for those building internal UI design systems or UI libraries intended for other developers.`.\nThe focus is not on validation, but on building a scalable UI around the form fields, so it differs completely in usage.\nFormwerk also offers the same feature to validate forms using Standard Schema Spec (Zod, Valibot, Arktype)"
|
|
282
282
|
},
|
|
283
283
|
{
|
|
284
284
|
"id": "introduction-devtools",
|
|
@@ -292,7 +292,7 @@ const rawData = {
|
|
|
292
292
|
"title": "Introduction",
|
|
293
293
|
"category": "introduction",
|
|
294
294
|
"path": "introduction/index.md",
|
|
295
|
-
"content": "# Introduction\n\n## What is Regle?\n\nIf you've ever built
|
|
295
|
+
"content": "# Introduction\n\n## What is Regle?\n\nIf you've ever built forms and wrote repetitive validation logic, struggling with complex error states, or losing type safety along the way, Regle is the perfect solution.\n\nRegle is a type-safe, headless form validation library that lets you write validation rules that mirror your data structure. Think of it as the perfect evolution of Vuelidate, but with modern TypeScript support and a more intuitive API.\n\n## Why Choose Regle?\n\n- **🔒 Type Safe**: Full TypeScript inference means autocomplete everywhere and catch errors at compile time\n- **🌳 Model-Based**: Your validation tree matches your data model—no mental gymnastics required \n- **🔌 Headless**: Works with any UI framework, CSS library, or design system\n- **🔍 Devtools**: Built-in Vue devtools extension for easy debugging and testing.\n- **📦 Modular**: Use built-in rules or create custom ones that fit your exact needs\n- **⚡ Performance**: Efficient reactivity system that only updates what changed\n- **🛠 Developer Experience**: If you've used Vuelidate, you'll feel right at home\n\n## Basic example\n\nHere's a real form that you can copy and use right away:\n\nFrom `r$`, you can build any UI you want. The validation logic is completely separate from your presentation layer.\n\n**Live Result:**\n\n## What's Next?\n\nReady to dive deeper? Here's your learning path:\n\n1. **[Installation](/introduction/installation)** - Get Regle set up in your project\n2. **[Core Concepts](/core-concepts/)** - Understand how `useRegle` works\n3. **[Built-in Rules](/core-concepts/rules/built-in-rules)** - Explore all available validation rules\n4. **[Examples](/examples/simple)** - See Regle in action with real-world scenarios\n\n:::tip Coming from Vuelidate?\nRegle's API is intentionally similar to Vuelidate's. Check out our [comparison guide](/introduction/comparisons#vuelidate) to see what's changed and what's stayed the same.\n:::"
|
|
296
296
|
},
|
|
297
297
|
{
|
|
298
298
|
"id": "introduction-installation",
|
|
@@ -306,14 +306,14 @@ const rawData = {
|
|
|
306
306
|
"title": "Migrate from Vuelidate",
|
|
307
307
|
"category": "introduction",
|
|
308
308
|
"path": "introduction/migrate-from-vuelidate.md",
|
|
309
|
-
"content": "# Migrate from Vuelidate\n\nMigrating from Vuelidate is really simple. Regle API is similar to Vuelidate's one on purpose, so the mental model stays the same.\n\nRegle type safety will ensure you make no mistakes while making the migration.\n\n## Imports\n\n```ts\nimport { useVuelidate } from '@vuelidate/core'; // [!code --]\nimport { required } from '@vuelidate/validators'; // [!code --]\nimport { useRegle } from '@regle/core'; // [!code ++]\nimport { required } from '@regle/rules'; // [!code ++]\n```\n\n```ts\nconst v$ = useVuelidate(rules, state, options); // [!code --]\nconst { r$ } = useRegle(state, rules, options); // [!code ++]\n```\n\n## Helpers\n\n```ts\nimport { helpers } from '@vuelidate/validators'; // [!code --]\nimport { withMessage, withParams, withAsync, isEmpty, ... } from '@regle/rules'; // [!code ++]\n```\n\nHelpers which have been renamed:\n\n- `req` -> `isFilled`\n- `len` -> `getSize`\n- `regex` -> `matchRegex`\n- `forEach` -> Deleted, you can use `$each` directly.\n- `unwrap` -> use `toValue` from [Vue](https://vuejs.org/api/reactivity-utilities#tovalue)\n - Parameters are automatically unwrapped when using `createRule`\n\n## Displaying errors\n\nVuelidate:\n```vue\n<template> \n <p \n v-for=\"error of v$.name.$errors\"\n :key=\"error.$uid\" \n >\n {{error.$message}}\n </p>\n</template>\n```\n\nRegle: \n```vue\n<template>\n <p\n v-for=\"(error, index) of r$.$errors.name\"\n :key=\"index\"\n >\n {{ error }} \n </p>\n</template>\n```\n\n### `withMessage`\n\nOrder of parameters are swapped\n\n```ts\nconst rule = helpers.withMessage('This field cannot be empty', required) // [!code --]\nconst rule = withMessage(required, 'This field cannot be empty') // [!code ++]\n```\n\n### `withParams`\n\nYou can create rules with parameters with [createRule](/core-concepts/rules/reusable-rules#createrule) helper\n\n```ts\nconst contains = (param) => // [!code --]\n helpers.withParams( // [!code --]\n { type: 'contains', value: param }, // [!code --]\n (value) => !helpers.req(value) || value.includes(param) // [!code --]\n ) // [!code --]\n\nconst contains = createRule({ // [!code ++]\n validator(value: Maybe<string>, param: Maybe<string>) { // [!code ++]\n return isEmpty(value) || value.includes(param); // [!code ++]\n }, // [!code ++]\n message: ({$params: [param]}) => `Value must contain ${param}
|
|
309
|
+
"content": "# Migrate from Vuelidate\n\nMigrating from Vuelidate is really simple. Regle API is similar to Vuelidate's one on purpose, so the mental model stays the same.\n\nRegle type safety will ensure you make no mistakes while making the migration.\n\n## Imports\n\n```ts\nimport { useVuelidate } from '@vuelidate/core'; // [!code --]\nimport { required } from '@vuelidate/validators'; // [!code --]\nimport { useRegle } from '@regle/core'; // [!code ++]\nimport { required } from '@regle/rules'; // [!code ++]\n```\n\n```ts\nconst v$ = useVuelidate(rules, state, options); // [!code --]\nconst { r$ } = useRegle(state, rules, options); // [!code ++]\n```\n\n## Helpers\n\n```ts\nimport { helpers } from '@vuelidate/validators'; // [!code --]\nimport { withMessage, withParams, withAsync, isEmpty, ... } from '@regle/rules'; // [!code ++]\n```\n\nHelpers which have been renamed:\n\n- `req` -> `isFilled`\n- `len` -> `getSize`\n- `regex` -> `matchRegex`\n- `forEach` -> Deleted, you can use `$each` directly.\n- `unwrap` -> use `toValue` from [Vue](https://vuejs.org/api/reactivity-utilities#tovalue)\n - Parameters are automatically unwrapped when using `createRule`\n\n## Displaying errors\n\nVuelidate:\n```vue\n<template> \n <p \n v-for=\"error of v$.name.$errors\"\n :key=\"error.$uid\" \n >\n {{error.$message}}\n </p>\n</template>\n```\n\nRegle: \n```vue\n<template>\n <p\n v-for=\"(error, index) of r$.$errors.name\"\n :key=\"index\"\n >\n {{ error }} \n </p>\n</template>\n```\n\n### `withMessage`\n\nOrder of parameters are swapped\n\n```ts\nconst rule = helpers.withMessage('This field cannot be empty', required) // [!code --]\nconst rule = withMessage(required, 'This field cannot be empty') // [!code ++]\n```\n\n### `withParams`\n\nYou can create rules with parameters with [createRule](/core-concepts/rules/reusable-rules#createrule) helper\n\n```ts\nconst contains = (param) => // [!code --]\n helpers.withParams( // [!code --]\n { type: 'contains', value: param }, // [!code --]\n (value) => !helpers.req(value) || value.includes(param) // [!code --]\n ) // [!code --]\n\nconst contains = createRule({ // [!code ++]\n validator(value: Maybe<string>, param: Maybe<string>) { // [!code ++]\n return isEmpty(value) || value.includes(param); // [!code ++]\n }, // [!code ++]\n message: ({$params: [param]}) => `Value must contain ${param}`, // [!code ++]\n}) // [!code ++]\n```\n\n## Properties\n\nSome properties have been renamed\n\n- `$model` -> `$value`\n- `$response` -> `$metadata` [Using metadata from rules](/advanced-usage/rule-metadata#using-metadata-from-rules)\n- `$externalResults` -> `$externalErrors`\n\n### Accessing nested fields\n\n```ts\nv$.nested.child.$error // [!code --]\nr$.nested.child.$error // [!code ++]\n```\n\n## Collections\n\nSee [docs for validating arrays](/common-usage/collections)\n\n```ts\nconst v$ = useVuelidate({ // [!code --]\n collection: { // [!code --]\n $each: helpers.forEach({ // [!code --]\n name: { // [!code --]\n required // [!code --]\n } // [!code --]\n }) // [!code --]\n } // [!code --]\n}, {collection: [{name: ''}]}) // [!code --]\nconst { r$ } = useRegle({ collection: [{name: ''}]}, { // [!code ++]\n collection: {// [!code ++]\n $each: {// [!code ++]\n name: {// [!code ++]\n required// [!code ++]\n }// [!code ++]\n }// [!code ++]\n }// [!code ++]\n})// [!code ++]\n```\n\n## Methods\n\nSee [docs for type safe output](/typescript/type-safe-output)\n\n```ts\nconst result = await v$.$validate(); // [!code --]\nconst { valid, data } = await r$.$validate(); // [!code ++]\n```\n\n## Custom messages\n\nIf you used to declare this kind of helper methods with Vuelidate:\n\n```ts\nimport {helpers, required, numeric, minLength} from '@vuelidate/validators';\n\nexport const requiredValidator = helpers.withMessage(\n 'This field is required.',\n required\n);\nexport const numericValidator = helpers.withMessage(\n 'Please enter a valid value.',\n numeric\n);\n\nexport const minLengthValidator = (value) =>\n helpers.withMessage(\n ({ $model, $params }) =>\n `Please enter a value greater than or equal to ${$params.max}.`,\n minLength(value)\n );\n```\n\nYou can remove it and configure it with [global config](/advanced-usage/global-config#replace-built-in-rules-messages).\n\n:::tip\nIf you use Nuxt <span data-title=\"nuxt\"></span>, check the [Nuxt module documentation](/integrations/nuxt) for even easier error message sharing.\n:::\n\n```ts\nimport { defineRegleConfig } from '@regle/core';\nimport { withMessage, minLength, required, numeric } from '@regle/rules';\n\nconst { useRegle: useCustomRegle } = defineRegleConfig({\n rules: () => ({\n required: withMessage(required, 'This field is required.'),\n numeric: withMessage(numeric, 'Please enter a valid value.'),\n minLength: withMessage(minLength, ({ $value, $params: [max] }) => {\n return `Minimum length is ${max}. Current length: ${$value?.length}`;\n })\n })\n})\n\nconst { r$ } = useCustomRegle({ name: '' }, {\n name: {\n required,\n numeric,\n minLength: minLength(6)\n }\n})\n```\n\n## Nested component validation\n\n__**Nested component**__ validation is replaced by __**Scoped validation**__.\n\nSee [docs for scoped validation](/advanced-usage/scoped-validation) for more details\n\n```ts\n// [scoped-config.ts]\nimport { useScopedRegle, useCollectScope, useRegle } from '@regle/core'; // [!code ++]\n\n// Parent.vue\nconst v$ = useVuelidate(); // [!code --]\nconst v$ = useVuelidate({}, {}, {$scope: 'foo'}); // [!code --]\n\nconst { r$ } = useCollectScope(); // [!code ++]\nconst { r$ } = useCollectScope('foo'); // [!code ++]\n\n// Child.vue\n\nconst v$ = useVuelidate(validations, state); // [!code --]\nconst v$ = useVuelidate(validations, state, { $scope: false }); // [!code --]\nconst v$ = useVuelidate(validations, state, { $scope: 'foo' }); // [!code --]\nconst v$ = useVuelidate(validations, state, { $stopPropagation: true }); // [!code --]\n\nconst { r$ } = useScopedRegle(state, validations); // [!code ++]\nconst { r$ } = useRegle(state, validations); // [!code ++]\nconst { r$ } = useScopedRegle(state, validations, {namespace: 'foo'}); // [!code ++]\nconst { r$ } = useScopedRegle(state, validations); // [!code ++]\n```\n\n## Validation groups\n\n```ts\nconst rules = { // [!code --]\n number: { isEven },// [!code --]\n nested: {// [!code --]\n word: { required: v => !!v }// [!code --]\n },// [!code --]\n $validationGroups: {// [!code --]\n firstGroup: ['number', 'nested.word']// [!code --]\n }// [!code --]\n}// [!code --]\nconst v$ = useVuelidate(rules, ...);// [!code --]\n\nconst { r$ } = useRegle(..., { // [!code ++]\n number: {isEven},// [!code ++]\n nested: {// [!code ++]\n word: { required: v => !!v }// [!code ++]\n }// [!code ++]\n}, {// [!code ++]\n validationGroups: (fields) => ({// [!code ++]\n firstGroup: [fields.number, fields.nested.word]// [!code ++]\n })// [!code ++]\n})// [!code ++]\nr$.$groups.firstGroup// [!code ++]\n```"
|
|
310
310
|
},
|
|
311
311
|
{
|
|
312
312
|
"id": "troubleshooting-reactivity",
|
|
313
313
|
"title": "Reactivity caveats",
|
|
314
314
|
"category": "troubleshooting",
|
|
315
315
|
"path": "troubleshooting/reactivity.md",
|
|
316
|
-
"content": "# Reactivity caveats\n\n## Tracking Dependencies\n\nWhen using `useRegle` with a getter function or a computed property, Regle automatically tracks dependencies. However, sometimes dependencies cannot be tracked automatically. In such cases, you can either use the `withParams` wrapper to manually define dependencies or use the `createRule` function which automatically tracks dependencies for you.\n\nTo illustrate the issue, consider the following example:\n\n```ts\nimport { ref, computed } from 'vue';\nimport { withMessage } from '@regle/rules';\nimport { type Maybe, type RegleComputedRules } from '@regle/core';\n\nconst condition = ref(false)\n\nconst weight = (greeting: string) => {\n return withMessage((value: Maybe<number>) => {\n return !!value && value > 1 && condition.value === true\n }, `Weight must be greater than 1, ${greeting}`)\n}\n\nconst rules = computed(() => {\n return {\n items: {\n $each: (item) => ({\n weight: {\n weight: weight('Hello World')\n }\n })\n }\n } satisfies RegleComputedRules<typeof form>\n})\n```\n\nIn the above example, the `weight` rule depends on the `condition` ref, which is not tracked by Regle because it is inside a function and Vue cannot collect the reference. To fix this, you can either use the `withParams` wrapper or use the `createRule` function which automatically tracks dependencies for you.\n\n```ts\nimport { ref } from 'vue';\nimport { withParams, withMessage } from '@regle/rules';\nimport { createRule, type Maybe } from '@regle/core';\n\nconst condition = ref(false)\n\n// Usage with `withParams`\nconst weight1 = (greeting: string) => {\n return withMessage(\n withParams((value: Maybe<number>) => {\n return !!value && value > 1 && condition.value === true\n }, [condition]),\n `Weight must be greater than 1, ${greeting}`\n )\n}\n\n// Usage with `createRule`\nconst weight2 = createRule({\n validator(value: Maybe<number>, greeting: string, condition: boolean) {\n return !!value && value > 1 && condition === true\n },\n\n message: ({ $params: [greeting] }) => {\n return `Weight must be greater than 1, ${greeting}`\n }\n})\n```\n\nNow the `condition` ref is tracked by Regle and the rule will be re-evaluated whenever the `condition` ref changes.\n\n## Pinia hydration issues\n\nIf you use `store.$dispose()` or Nuxt in SSR mode, you may encounter this error:\n\n```\nUncaught TypeError: 'set' on proxy: trap returned falsish for property 'xxx'\n```\n\nThis is because Pinia tries to hydrate the stateful property `r$`.\nTo avoid this, you can use [skipHydrate](https://pinia.vuejs.org/api/pinia/functions/skipHydrate.html#skipHydrate-)\n\n```ts [pinia.store.ts]\nimport { skipHydrate } from 'pinia';\n\nexport const usePiniaStore = defineStore('pinia-store', () => {\n const {r$} = useRegle(/** */)\n\n return { r$: skipHydrate(r$) };\n});\n```\n\n:::info\nIf you are using Nuxt, you can use the `@regle/nuxt` module that will automatically skip the hydration of the `r$` property.\n:::"
|
|
316
|
+
"content": "# Reactivity caveats\n\n## Tracking Dependencies\n\nWhen using `useRegle` with a getter function or a computed property, Regle automatically tracks dependencies. However, sometimes dependencies cannot be tracked automatically. In such cases, you can either use the `withParams` wrapper to manually define dependencies or use the `createRule` function which automatically tracks dependencies for you.\n\nTo illustrate the issue, consider the following example:\n\n```ts\nimport { ref, computed } from 'vue';\nimport { withMessage } from '@regle/rules';\nimport { useRegle, type Maybe, type RegleComputedRules } from '@regle/core';\n\nconst form = ref({ items: [{ weight: 0 }] });\nconst condition = ref(false)\n\nconst weight = (greeting: string) => {\n return withMessage((value: Maybe<number>) => {\n return !!value && value > 1 && condition.value === true\n }, `Weight must be greater than 1, ${greeting}`)\n}\n\nconst rules = computed(() => {\n return {\n items: {\n $each: (item) => ({\n weight: {\n weight: weight('Hello World')\n }\n })\n }\n } satisfies RegleComputedRules<typeof form>\n})\n```\n\nIn the above example, the `weight` rule depends on the `condition` ref, which is not tracked by Regle because it is inside a function and Vue cannot collect the reference. To fix this, you can either use the `withParams` wrapper or use the `createRule` function which automatically tracks dependencies for you.\n\n```ts\nimport { ref } from 'vue';\nimport { withParams, withMessage } from '@regle/rules';\nimport { createRule, type Maybe } from '@regle/core';\n\nconst condition = ref(false)\n\n// Usage with `withParams`\nconst weight1 = (greeting: string) => {\n return withMessage(\n withParams((value: Maybe<number>) => {\n return !!value && value > 1 && condition.value === true\n }, [condition]),\n `Weight must be greater than 1, ${greeting}`\n )\n}\n\n// Usage with `createRule`\nconst weight2 = createRule({\n validator(value: Maybe<number>, greeting: string, condition: boolean) {\n return !!value && value > 1 && condition === true\n },\n\n message: ({ $params: [greeting] }) => {\n return `Weight must be greater than 1, ${greeting}`\n }\n})\n```\n\nNow the `condition` ref is tracked by Regle and the rule will be re-evaluated whenever the `condition` ref changes.\n\n## Pinia hydration issues\n\nIf you use `store.$dispose()` or Nuxt in SSR mode, you may encounter this error:\n\n```\nUncaught TypeError: 'set' on proxy: trap returned falsish for property 'xxx'\n```\n\nThis is because Pinia tries to hydrate the stateful property `r$`.\nTo avoid this, you can use [skipHydrate](https://pinia.vuejs.org/api/pinia/functions/skipHydrate.html#skipHydrate-)\n\n```ts [pinia.store.ts]\nimport { skipHydrate } from 'pinia';\n\nexport const usePiniaStore = defineStore('pinia-store', () => {\n const {r$} = useRegle(/** */)\n\n return { r$: skipHydrate(r$) };\n});\n```\n\n:::info\nIf you are using Nuxt, you can use the `@regle/nuxt` module that will automatically skip the hydration of the `r$` property.\n:::"
|
|
317
317
|
},
|
|
318
318
|
{
|
|
319
319
|
"id": "typescript-type-safe-output",
|
|
@@ -2002,7 +2002,7 @@ function searchApi(query) {
|
|
|
2002
2002
|
});
|
|
2003
2003
|
return results;
|
|
2004
2004
|
}
|
|
2005
|
-
var version = "1.21.
|
|
2005
|
+
var version = "1.21.2";
|
|
2006
2006
|
let posthogClient = null;
|
|
2007
2007
|
posthogClient = new PostHog("phc_kqgJoylCpKkGkkRGxb4MyN2mViehoQcUFEGwVkk4l8E", {
|
|
2008
2008
|
host: "https://eu.i.posthog.com",
|