@regle/mcp-server 1.14.0-beta.1 → 1.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -87
- package/dist/regle-mcp-server.js +1749 -393
- package/package.json +1 -1
package/dist/regle-mcp-server.js
CHANGED
|
@@ -3,321 +3,1507 @@ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
|
3
3
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
4
4
|
import { z } from "zod";
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
"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 { alphaNum } from '@regle/rules';\n\nconst { r$ } = useRegle({ name: '' }, {\n name: { \n alphaNum,\n // or\n alphaNum: alphaNum({ allowSymbols: true }),\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```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## `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 { r$ } = useRegle({ birthday: null as Date | null }, {\n birthday: {\n dateAfter: dateAfter(new Date()),\n // or\n dateAfter: dateAfter(new Date(), { 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 { r$ } = useRegle({ birthday: null as Date | null }, {\n birthday: {\n dateBefore: dateBefore(new Date()),\n // or\n dateBefore: dateBefore(new Date(), { allowEqual: false }),\n },\n})\n```\n\n## `dateBetweeen`\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 { r$ } = useRegle({ birthday: null as Date | null }, {\n birthday: {\n dateBetween: dateBetween(new Date(), new Date(2030, 3, 1)),\n // or\n dateBetween: dateBetween(new Date(), new Date(2030, 3, 1), { 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## `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## `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## `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## `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## `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## `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\n\nimport { macAddress } from '@regle/rules';\n\nconst maxCount = ref(6);\n\nconst { r$ } = useRegle({ address: '' }, {\n address: {\n macAddress,\n // or\n macAddress: macAddress('-')\n },\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## `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 { r$ } = useRegle({ aliment: 'Fish' }, {\n aliment: {\n oneOf: oneOf(['Fish', 'Meat', 'Bone'])\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## `url`\n\nValidates URLs.\n\n```ts\nimport { url } from '@regle/rules';\n\nconst { r$ } = useRegle({ bestUrl: '' }, {\n bestUrl: { url },\n})\n```"
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
6
|
+
var docs_data_default = {
|
|
7
|
+
docs: [
|
|
8
|
+
{
|
|
9
|
+
"id": "advanced-usage-extend-properties",
|
|
10
|
+
"title": "Extend properties",
|
|
11
|
+
"category": "advanced-usage",
|
|
12
|
+
"path": "advanced-usage/extend-properties.md",
|
|
13
|
+
"content": "# Extend properties\n\nRegle offers a way to extend the default validation properties with `defineRegleConfig`.\n\nFor more information about global config [check here](/advanced-usage/global-config)\n\n## Extending field properties\n\n```ts twoslash\nimport { required } from '@regle/rules';\n\n// ---cut---\nimport { defineRegleConfig } from '@regle/core';\n\nconst { useRegle } = defineRegleConfig({\n shortcuts: {\n fields: {\n $isRequired: (field) => field.$rules.required?.$active ?? false;\n }\n }\n});\n\nconst { r$ } = useRegle({ name: '' }, {\n name: {\n required\n }\n})\n\nr$.name.$isRe\n\n```\n\n## Extending nested object properties\n\n```ts twoslash\nimport { required } from '@regle/rules';\n\n// ---cut---\nimport { defineRegleConfig } from '@regle/core';\n\nconst { useRegle } = defineRegleConfig({\n shortcuts: {\n nested: {\n $isEmpty: (nest) => Object.keys(nest.$fields).length === 0;\n }\n }\n});\n\nconst { r$ } = useRegle({ user: {} } as { user: { firstName?: string, lastName?: string } }, {\n user: {\n firstName: {required}\n }\n})\n\nr$.user.$is\n\n```\n\n## Extending collections properties\n\n```ts twoslash\nimport { required } from '@regle/rules';\n\n// ---cut---\nimport { defineRegleConfig } from '@regle/core';\n\nconst { useRegle } = defineRegleConfig({\n shortcuts: {\n collections: {\n $isArrayEmpty: (collection) => collection.$each.length === 0;\n }\n }\n});\n\nconst { r$ } = useRegle({ projects: [{ name: '' }] }, {\n projects: {\n $each: {\n name: { required }\n }\n }\n})\n\nr$.projects.$is\n\n```\n\n## Typing shortcuts in component props <span data-title='*.ts'></span>\n\nWhen defining shortcuts, it can be hard to type props in common Input components.\nFor this Regle provides a type helper that can ease the declaration of these props.\n\n:::code-group\n\n```ts twoslash include config [config.ts]\n\n// ---cut---\nimport { defineRegleConfig } from '@regle/core';\nexport const { useRegle: useCustomRegle } = defineRegleConfig({\n shortcuts: {\n fields: {\n $test: () => true,\n },\n },\n});\n```\n\n```vue twoslash [myInput.vue]\n<script lang='ts' setup>\nimport { defineRegleConfig } from '@regle/core';\n\n// ---cut---\n\nimport type { RegleCustomFieldStatus } from '@regle/core';\nimport {useCustomRegle} from './config'\n\nconst props = defineProps<{\n field: RegleCustomFieldStatus<typeof useCustomRegle, string, 'required'>;\n placeholder: string;\n}>();\n\nprops.field.$test\n<\/script>\n```\n\n:::"
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
"id": "advanced-usage-global-config",
|
|
17
|
+
"title": "Global configuration",
|
|
18
|
+
"category": "advanced-usage",
|
|
19
|
+
"path": "advanced-usage/global-config.md",
|
|
20
|
+
"content": "# Global configuration\n\nIf your app includes multiple forms, it can be helpful to define a global configuration that centralizes your custom validators, error messages, and modifiers. This eliminates the need to declare these settings repeatedly for every `useRegle` call, improving both code consistency and developer experience with features like autocompletion and type checking.\n\n## Replace built-in rules messages\n\nEach `@regle/rules` rule provides a default error message. You may may not want to call `withMessage` every time you need to use one with a custom error message.\n\n`defineRegleConfig` allows you to redefine the default messages of built-in rules.\n\n```ts\nimport { defineRegleConfig } from '@regle/core';\nimport { withMessage, minLength, required } from '@regle/rules';\n\nconst { useRegle: useCustomRegle } = defineRegleConfig({\n rules: () => ({\n required: withMessage(required, 'You need to provide a 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 minLength: minLength(6)\n }\n})\n```\n\nResult: \n\n:::tip\nIf you use Nuxt, check out the [Nuxt module](/integrations/nuxt) for a even better DX. \nIt provides a way to add your custom global config to your auto-imports.\n:::\n\n### i18n\n\nYou can also use any i18n library directly inside the config.\n\n```ts\nimport { defineRegleConfig } from '@regle/core';\nimport { withMessage, minLength, required } from '@regle/rules';\nimport { useI18n } from 'vue-i18n';\n\nconst { useRegle: useCustomRegle } = defineRegleConfig({\n rules: () => {\n const { t } = useI18n()\n\n return {\n required: withMessage(required, t('general.required')),\n minLength: withMessage(minLength, ({ $value, $params: [max] }) => {\n return t('general.minLength', {max});\n })\n }\n }\n})\n```\n\n## Declare new rules\n\nWhile `useRegle` allows you to use any rule key, adding custom rules to the global configuration provides autocompletion and type checking. This improves maintainability and consistency across your application.\n\n```ts twoslash\nconst someAsyncCall = async () => await Promise.resolve(true);\n// ---cut---\n\nimport { defineRegleConfig, createRule, Maybe } from '@regle/core';\nimport { withMessage, isFilled } from '@regle/rules';\n\nconst asyncEmail = createRule({\n async validator(value: Maybe<string>) {\n if (!isFilled(value)) {\n return true;\n }\n\n const result = await someAsyncCall();\n return result;\n },\n message: 'Email already exists',\n});\n\nconst { useRegle: useCustomRegle } = defineRegleConfig({\n rules: () => ({\n asyncEmail\n })\n})\n\nconst { r$ } = useCustomRegle({ name: '' }, {\n name: {\n asy\n\n }\n})\n```\n\n## Declare modifiers\n\nYou can include global modifiers in your configuration to automatically apply them wherever you use the `useRegle` composable. This avoids repetitive declarations and keeps your code clean.\n\n```ts\nimport { defineRegleConfig } from '@regle/core';\nimport { withMessage, minLength, required } from '@regle/rules';\n\nexport const { useRegle: useCustomRegle } = defineRegleConfig({\n modifiers: {\n autoDirty: false,\n silent: true,\n lazy: true,\n rewardEarly: true\n }\n})\n```\n\n## Export scoped `inferRules` helper\n\n`defineRegleConfig` also returns a scoped `inferRules` helper, similar to the one exported from `@regle/core`, but that will autocomplete and check your custom rules.\n\nFor information about `inferRules`, check [Typing rules docs](/typescript/typing-rules)\n\n```ts\nimport { defineRegleConfig } from '@regle/core';\nimport { withMessage, minLength, required } from '@regle/rules';\n\nexport const { useRegle, inferRules } = defineRegleConfig({/* */})\n```\n\n## Extend global config\n\nIt's also possible to add additional config to an already created custom `useRegle`.\n\nWith `extendRegleConfig`, you can recreate a custom one with a existing composable as an input.\n\n```ts twoslash\n\nimport { defineRegleConfig, extendRegleConfig, createRule } from '@regle/core';\nimport { withMessage, required } from '@regle/rules';\n\nconst { useRegle: useCustomRegle } = defineRegleConfig({\n rules: () => ({\n customRule: withMessage(required, 'Custom rule'),\n })\n})\n\nconst {useRegle: useExtendedRegle} = extendRegleConfig(useCustomRegle, {\n rules: () => ({\n customRuleExtended: withMessage(required, 'Custom rule 2'),\n })\n})\n\nuseExtendedRegle({name: ''}, {\n name: {\n custom\n \n }\n})\n\n```"
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
"id": "advanced-usage-immutable-constructors",
|
|
24
|
+
"title": "immutable-constructors",
|
|
25
|
+
"category": "advanced-usage",
|
|
26
|
+
"path": "advanced-usage/immutable-constructors.md",
|
|
27
|
+
"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, useRegleSchema } from '@regle/core'\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 isStatic = 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```"
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
"id": "advanced-usage-merge-regles",
|
|
31
|
+
"title": "Merge multiple Regles",
|
|
32
|
+
"category": "advanced-usage",
|
|
33
|
+
"path": "advanced-usage/merge-regles.md",
|
|
34
|
+
"content": "# Merge multiple Regles\n\nIf you need to combine multiple Regle instances into one, it's possible with the `mergeRegles` helper.\n\nit will return an output similar to the main `r$`, while still being able to call `$touch` or `$validate`.\n\nAll types are preserved.\n\n```ts twoslash\nimport {required, numeric, email} from '@regle/rules';\n// ---cut---\n\nimport { mergeRegles, useRegle } from '@regle/core';\n\nconst { r$ } = useRegle({email: ''}, {\n email: { required, email },\n});\n\nconst { r$: otherR$ } = useRegle({firstName: ''}, {\n firstName: { required },\n});\n\nconst r$Merged = mergeRegles({ r$, otherR$ });\n\nr$Merged.$value.otherR$.\n\n```"
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
"id": "advanced-usage-rule-metadata",
|
|
38
|
+
"title": "Rules metadata",
|
|
39
|
+
"category": "advanced-usage",
|
|
40
|
+
"path": "advanced-usage/rule-metadata.md",
|
|
41
|
+
"content": "# Rules metadata\n\nRule validator functions can return more than just a boolean. It can return any object as long as it returns an object containing at least `$valid: boolean`. \n\nThis additional data can be utilized by your `message` handler, `active` handler, or any other part of your application that has access to the regle instance.\n\n```ts twoslash\n\nimport {withMessage} from '@regle/rules';\nimport {useRegle} from '@regle/core';\n\nconst inlineRule = withMessage((value: unknown) => {\n return { \n $valid: true,\n myCustomMetadata: 100\n }\n}, ({myCustomMetadata}) => `Hello ${myCustomMetadata}`)\n\nconst { r$ } = useRegle({name: ''}, {\n name: {inlineRule}\n})\n\nr$.name.$rules.inlineRule.$metadata.\n\n```\n\n## Using metadata in `createRule`\n\nYou can use `createRule` to define your custom rules. Let's explore a real-world example by creating a password strength validator.\n\n:::code-group\n\n```ts twoslash include strongPassword [strongPassword.ts] \n\n// ---cut---\nimport { createRule, Maybe } from '@regle/core';\nimport { isFilled } from '@regle/rules';\nimport { passwordStrength, type Options } from 'check-password-strength';\n\nexport const strongPassword = createRule({\n validator(value: Maybe<string>, options?: Options<string>) {\n if (isFilled(value)) {\n const result = passwordStrength(value, options);\n return {\n $valid: result.id > 1,\n result,\n };\n }\n\n return { $valid: true };\n },\n message({ result }) {\n return `Your password is ${result?.value}`;\n },\n});\n```\n\n``` vue twoslash [ComponentA.vue]\n<template>\n <div>\n <input\n v-model=\"r$.$value.password\"\n :class=\"{ valid: r$.password.$correct }\"\n placeholder=\"Type your password\"\n />\n\n <button type=\"button\" @click=\"r$.$reset({toInitialState: true})\">Reset</button>\n </div>\n\n <div\n class=\"password-strength\"\n :class=\"[`level-${r$.password.$rules.strongPassword.$metadata.result?.id}`]\">\n </div>\n\n <ul v-if=\"r$.$errors.password.length\">\n <li v-for=\"error of r$.$errors.password\" :key=\"error\">\n {{ error }}\n </li>\n </ul>\n\n <div v-else-if=\"r$.password.$correct\" class=\"success\">\n Your password is strong enough\n </div>\n</template>\n\n```\n\n:::\n\nResult:"
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
"id": "advanced-usage-scoped-validation",
|
|
45
|
+
"title": "Scoped validation",
|
|
46
|
+
"category": "advanced-usage",
|
|
47
|
+
"path": "advanced-usage/scoped-validation.md",
|
|
48
|
+
"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 this 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```"
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
"id": "advanced-usage-variants",
|
|
52
|
+
"title": "Variants",
|
|
53
|
+
"category": "advanced-usage",
|
|
54
|
+
"path": "advanced-usage/variants.md",
|
|
55
|
+
"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 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='Email'/>\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='Email'/>\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='Email'/>\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```"
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
"id": "cheat-sheet",
|
|
59
|
+
"title": "Cheat Sheet",
|
|
60
|
+
"category": "general",
|
|
61
|
+
"path": "cheat-sheet.md",
|
|
62
|
+
"content": "# Regle Cheat Sheet\n\nQuick reference for common Regle patterns and usage scenarios.\n\n## Basic Setup\n\n```ts\nimport { useRegle } from '@regle/core'\nimport { required, email, minLength } from '@regle/rules'\n\nconst { r$ } = useRegle(\n { name: '', email: '' }, \n { \n name: { required, minLength: minLength(2) },\n email: { required, email }\n }\n)\n```\n\n## Essential Properties\n\n| Property | Description | Example |\n|----------|-------------|---------|\n| `r$.$value` | Form data (reactive) | `r$.$value.email` |\n| `r$.$correct` | Form is dirty and valid | `<button :disabled=\"!r$.$correct\">` |\n| `r$.$invalid` | Form is invalid | `v-if=\"r$.$invalid\"` |\n| `r$.$errors` | All error messages | `r$.$errors.email` |\n| `r$.x.$error` | Field has errors | `v-if=\"r$.email.$error\"` |\n| `r$.x.$correct` | Field is dirty and valid | `v-if=\"r$.email.$correct\"` |\n| `r$.x.$dirty` | Field was touched | `v-if=\"r$.email.$dirty\"` |\n| `r$.$validate()` | Validate form | `await r$.$validate()` |\n| `r$.$reset()` | Reset form | `r$.$reset()` |\n\n## Common Rules\n\n```ts\nimport {useRegle} from '@regle/core';\nimport { \n required, email, minLength, maxLength,\n numeric, between, url, regex,\n alphaNum, alpha, sameAs\n} from '@regle/rules';\n\ntype FormState = {\n name?: string,\n email?: string,\n age?: number,\n username?: string,\n website?: string,\n description?: string,\n phone?: string,\n password?: string,\n confirmPassword?: string,\n}\n\nconst state = ref<FormState>({})\n\nconst { r$ } = useRegle(state, {\n // Basic validation\n name: { required, minLength: minLength(2) },\n email: { required, email },\n age: { required, numeric, between: between(18, 99) },\n \n // String validation\n username: { required, alphaNum, minLength: minLength(3) },\n website: { url },\n description: { maxLength: maxLength(500) },\n \n // Custom patterns\n phone: { regex: regex(/^\\+?[\\d\\s-()]+$/) },\n \n // Password confirmation\n password: { required, minLength: minLength(8) },\n confirmPassword: { \n required, \n sameAs: sameAs(() => state.value.password) \n }\n})\n```\n\n## Field Patterns\n\n### Basic Field with Error Display\n\n```vue\n<template>\n <div>\n <input v-model=\"r$.$value.email\" type=\"email\" />\n <span v-for=\"error of r$.email.$errors\" class=\"error\">\n {{ error }}\n </span>\n </div>\n</template>\n```\n\n### Field with Visual States\n```vue\n<template>\n <input \n v-model=\"r$.$value.email\"\n :class=\"{\n 'error': r$.email.$error,\n 'correct': r$.email.$correct,\n }\"\n />\n</template>\n```\n\n### Optional Field with Conditional Validation\n\n```ts \nimport {inferRules} from '@regle/core';\nimport {requiredIf, minLength, regex} from '@regle/rules';\n\nconst state = ref({phone: ''});\n\nconst rules = computed(() => inferRules(state, {\n phone: {\n // Only required if form have no email\n required: requiredIf(() => !r$.$value.email)\n minLength: minLength(10),\n regex: regex(/^\\+?[\\d\\s-()]+$/)\n }\n}))\n```\n\n## Single field validation\n\n```vue\n\n<template>\n <input v-model=\"r$.$value\" />\n <ul v-if=\"r$.$errors.length\">\n <li v-for=\"error of r$.$errors\" :key=\"error\">\n {{ error }}\n </li>\n </ul>\n</template>\n```\n\n## Custom Error Messages\n\n```ts\nimport {useRegle} from '@regle/core';\nimport { withMessage } from '@regle/rules'\n\nconst { r$ } = useRegle({email: '', password: ''}, {\n email: { \n required: withMessage(required, 'Email is required'),\n email: withMessage(email, 'Please enter a valid email address')\n },\n password: {\n minLength: withMessage(\n minLength(8), \n ({ $params: [min] }) => `Password must be at least ${min} characters`\n )\n }\n})\n```\n\n## Form Submission\n\n```ts\nimport {useRegle} from '@regle/core';\nimport {required} from '@regle/rules';\n\nconst {r$} = useRegle({name: ''}, {name: {required}});\n\nfunction handleSubmit() {\n // Validate entire form\n const {valid, data} = await r$.$validate()\n \n if (!valid) {\n console.log('Form has errors')\n return\n }\n \n // Submit data\n try {\n await submitForm(data)\n r$.$reset() // Reset form after success\n } catch (error) {\n // Handle submission error\n }\n}\n```\n\n## Collections (Arrays)\n\n```ts\nimport {useRegle} from '@regle/core';\nimport {required, email} from '@regle/rules';\n\nconst { r$ } = useRegle(\n { users: [{ name: '', email: '' }] },\n {\n users: {\n $each: {\n name: { required },\n email: { required, email }\n }\n }\n }\n)\n\n// Access array validation\nr$.users.$each[0].name.$error\n```\n\n## Nested Objects\n\n```ts\nimport {useRegle} from '@regle/core';\nimport {required, email, maxLength} from '@regle/rules';\n\nconst { r$ } = useRegle(\n { \n user: { \n profile: { name: '', bio: '' },\n contact: { email: '', phone: '' }\n }\n },\n {\n user: {\n profile: {\n name: { required },\n bio: { maxLength: maxLength(200) }\n },\n contact: {\n email: { required, email },\n phone: { required }\n }\n }\n }\n)\n\n// Access nested validation\nr$.user.profile.name.$error\n```\n\n## Global Configuration\n\n```ts\nimport { defineRegleConfig } from '@regle/core';\nimport { withMessage, required, minLength } from '@regle/rules';\n\n// Set up global defaults\nconst { useRegle: useCustomRegle } = defineRegleConfig({\n rules: () => ({\n required: withMessage(required, 'You need to provide a value'),\n minLength: withMessage(minLength, ({ $value, $params: [max] }) => {\n return `Minimum length is ${max}. Current length: ${$value?.length}`;\n })\n }),\n modifiers: {\n rewardEarly: true,\n }\n})\n```\n\n## Schema Integration (Zod)\n\n```ts\nimport { z } from 'zod/v3'\nimport { useRegleSchema } from '@regle/schemas'\n\nconst schema = z.object({\n name: z.string().min(2),\n email: z.string().email(),\n age: z.number().min(18)\n})\n\nconst { r$ } = useRegleSchema({\n name: '',\n email: '',\n age: 0\n}, schema)\n```\n\n### TypeScript Errors?\n\n```ts\nimport { inferRules } from '@regle/core';\nimport { required } from '@regle/rules';\n\nconst state = ref({name: ''});\n\n// ✅ Use inferRules for better type inference\nconst rules = computed(() => {\n return inferRules(state, {\n name: { required }\n })\n})\n```"
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
"id": "common-usage-async-validation",
|
|
66
|
+
"title": "Async validations",
|
|
67
|
+
"category": "common-usage",
|
|
68
|
+
"path": "common-usage/async-validation.md",
|
|
69
|
+
"content": "# Async validation\n\nA common pattern in forms is to asynchronously check for a value on the server side.\nAsync rules can perform these tasks, and they update the `$pending` state whenever invoked.\n\n:::tip\nBy default, all async rules will be debounced by `200ms`. It can be overriden with the `$debounce` modifier.\n:::\n\n## Inline Async rule\n\nTo declare an async rule, you simply have to use the `async await` syntax.\n\n```ts\nconst myAsyncRule = async (value: Maybe<number>) => {\n if (isFilled(value)) {\n return await someStuff();\n }\n return true;\n}\n```\n\nIf your rule doesn't use `async await` syntax, but still returns a `Promise`, you have to use the `withAsync` helper when using your rule in your form. Otherwise, Regle can't know it's an async rule.\n\n## Async using `createRule`\n\nIn the same way of an inline rule, your validator function must be using `async await` to be declared as an async rule.\n\n```ts\nconst myAsyncRule = createRule({\n async validator(value: Maybe<string>) {\n if (isFilled(value)) {\n return await someStuff();\n }\n return true;\n },\n message: 'Error'\n})\n```\n\n## `$pending` property\n\nEvery time you update the `$value` of the field using an async rule, the `$pending` [validation property](/core-concepts/validation-properties#pending) will be updated. The `$error` status depends on `$pending` so a field cannot be errored if it's still pending.\n\nThis can be used to display a loading icon and a custom message indicating that an operation is taking time.\n\n## Full example\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```"
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
"id": "common-usage-collections",
|
|
73
|
+
"title": "Working with arrays - Declaring rules",
|
|
74
|
+
"category": "common-usage",
|
|
75
|
+
"path": "common-usage/collections.md",
|
|
76
|
+
"content": "# Validating arrays\n\n## Declaring rules for collections\n\nYour forms may often include validations for collections where you need to validate multiple items sharing a nested structure. This can be easily achieved using `$each` in the rules declaration.\n\nYou can also add validations for the field containing the array itself.\n\n:::warning\nDue to a JavaScript limitation with [Primitives](https://developer.mozilla.org/en-US/docs/Glossary/Primitive), it's recommended to use only arrays of objects.\n\nPrimitives (Strings, Numbers etc...) are immutable, so they can't be modified to add a tracking ID (which is how Regle works for collections).\n:::\n\n```ts\nconst form = ref<{ collection: { name: string }[] }>({\n collection: []\n})\n\nconst { r$ } = useRegle(form, {\n collection: {\n $each: {\n name: { required },\n }\n }\n})\n```\n\n## Displaying collection errors\n\nFor collections, the best way to display errors is to bind your list to the `$each` linked to your state. In this example, `r$.collection.$each`.\n\nAlternatively, you can map your errors using `r$.$errors.collection.$each`.\n\n```vue twoslash\n<template>\n <div \n v-for=\"item of r$.collection.$each\" \n :key=\"item.$id\">\n <div>\n <input\n v-model=\"item.$value.name\"\n :class=\"{ valid: item.name.$correct }\"\n placeholder=\"Type an item value\"\n />\n\n <ul>\n <li v-for=\"error of item.name.$errors\" :key=\"error\">\n {{ error }}\n </li>\n </ul>\n </div>\n </div>\n\n <button type=\"button\" @click=\"form.collection.push({ name: '' })\">Add item</button>\n <button type=\"button\" @click=\"r$.$reset({toInitialState: true})\">Reset</button>\n</template>\n\n```\n\nResult: \n\n:::warning\n\nIf your array is empty, Regle can't know if it's supposed to be considered a field or a collection, only type-wise. Be sure to declare even an `$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: [] as {name: string}}, {\n collection: {\n $each: {}\n },\n})\n```\n\n:::\n\n## Validating the array independently\n\nSometimes, you may want to validate not only each field in every element of the array but also the array itself, such as its size.\n\nYou can do this just like you would with a normal field.\n\nErrors can be displayed either using `r$.$errors.[field].$self` or `r$.[field].$self.$errors`.\n\n```ts\nimport { useRegle } from '@regle/core';\n\nconst form = ref<{ collection: Array<{ name: string }> }>({\n collection: [{ name: '' }],\n});\n\nconst { r$ } = useRegle(form, {\n collection: {\n $rewardEarly: true,\n minLength: minLength(4),\n $each: {\n name: { required },\n },\n },\n});\n```\n\nResult:\n\n## Accessing the current item state\n\nIn each item of your collection, you may have a validation that depends on another property of the item.\nYou can access the current item's state and index by providing a function callback to `$each`.\n\n```ts\nimport { useRegle } from '@regle/core';\n\nconst form = ref({\n collection: [{ name: '', condition: false }],\n});\n\nconst { r$ } = useRegle(form, {\n collection: {\n $each: (item, index) => ({\n name: { required: requiredIf(() => item.value.condition) },\n }),\n },\n});\n```\n\nResult:\n\n## Providing a custom key to track items\n\nBy default, Regle generates a random ID to track your items and maintain their state through mutations. This ID is stored in `$id` and can be used in Vue as a `key` for rendering.\n\nYou can also provide your own key to the rule for custom tracking:\n\n```ts\nimport { useRegle } from '@regle/core';\n\nconst form = ref({\n collection: [{ name: '', uuid: '28xja83' }],\n});\n\nconst { r$ } = useRegle(form, {\n collection: {\n $each: (item) => ({\n $key: item.value.uuid,\n name: { required },\n }),\n },\n});\n```"
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
"id": "common-usage-external-errors",
|
|
80
|
+
"title": "Server errors",
|
|
81
|
+
"category": "common-usage",
|
|
82
|
+
"path": "common-usage/external-errors.md",
|
|
83
|
+
"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 externalErrors.value = {\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 externalErrors.value = {\n email: [\"Email already exists\"],\n \"name.pseudo\": [\"Pseudo already exists\"],\n \"collection.0.name\": [\"Name already exists\"]\n }\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\nYou can modify this behavior by setting the `clearExternalErrorsOnChange` modifier to `false`.\n\n```ts\nimport { useRegle } from '@regle/core';\n\nconst { r$ } = useRegle(form, {}, { \n externalErrors, \n clearExternalErrorsOnChange: false \n})\n```\n\nYou can also clear the errors manually by calling the `$clearExternalErrors` method.\n\n```ts\nr$.$clearExternalErrors();\n```"
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
"id": "common-usage-standard-schema",
|
|
87
|
+
"title": "Standard Schema",
|
|
88
|
+
"category": "common-usage",
|
|
89
|
+
"path": "common-usage/standard-schema.md",
|
|
90
|
+
"content": "# Standard Schema\n\nRegle implements the [Standard Schema](https://standardschema.dev/) specification.\n\nThis means that you can use Regle on any third party package that supports the Standard Schema spec.\n\nRegle can also use itself as a schema library when using `useRegleSchema`.\n\n## Usage\n\n```ts\nimport { useRegle } from '@regle/core';\nimport { required } from '@regle/rules';\n\nconst { r$ } = useRegle({ name: '' }, {\n name: { required }\n})\n\nconst result = await r$.['~standard'].validate({ name: '' });\n\nconsole.log(result.issues);\n```\n\n### Schema only usage\n\n```ts\nimport { useRules } from '@regle/core';\nimport { required, string } from '@regle/rules';\n\nconst schema = useRules({\n name: { string, required }\n})\n\nconst result = await schema['~standard'].validate({ name: '' });\n```\n\n## Composition usage\n\n```vue\n<template>\n <input \n v-model='r$.$value.email' \n :class=\"{ error: r$.email.$error }\" \n placeholder='Type your email'\n />\n\n <li v-for=\"error of r$.email.$errors\" :key='error'>\n {{ error }}\n </li>\n</template>\n\n```\n\n### `InferInput`\n\n`InferInput` is an utility type that can produce a object state from any rules object.\n\nIt will try to extract the possible state type that a rule may have, prioritizing rules that have a strict input type.\n\nSome rules may have `unknown` type because it could apply to any value. To cover this, there is now type-helpers rules to help you type your state from the rules: `type`, `string`, `number`, `boolean`, `date`.\n\n:::info\nSome types like `numeric` will feel weird as it's typed `string | number`, it's normal as the rule can also validate numeric strings. You can enforce the type by applying `number` rule to it.\n:::\n\n```ts twoslash\nimport {ref} from 'vue';\n// ---cut---\nimport { defineRules, type InferInput} from '@regle/core';\nimport { required, string, numeric, type } from '@regle/rules';\n\n/* defineRules is not required, but it helps you catch errors in structure */\nconst rules = defineRules({\n firstName: { required, string },\n count: { numeric },\n enforceType: { required, type: type<'FOO' | 'BAR'>()}\n})\n\ntype State = InferInput<typeof rules>;\n\n```\n\n<br/>\n<br/>\n<br/>\n\n## `useRules`\n\n`useRules` is a composable that allows you to write your rules in a more declarative way.\n\nIt works exactly like `useRegle`, but it doesn't accept a state parameter, it will create a emp from the rules.\n\n```ts twoslash\n\nimport { useRules, type InferInput } from '@regle/core';\nimport { required, string } from '@regle/rules';\n\nconst r$ = useRules({\n name: { required, string },\n});\n```\n\n## `refineRules`\n\nRegle is state first because in real world forms, rules can depend a state values. \nThis make it a problem for dynamic rules as it would make a cyclic type error when trying to use the state inside the rules.\n\nTo cover this case and inspired by Zod's `refine`, Regle provides a `refineRules` helper to write dynamic rules that depend on the state, while making it possible to access a typed state.\n\nAnything returned by the rule refine function will override what's defined in the default rules.\n\n```ts twoslash\nimport {ref} from 'vue';\n// ---cut---\nimport { refineRules, type InferInput} from '@regle/core';\nimport { required, string, sameAs } from '@regle/rules';\n\nconst rules = refineRules({\n password: { required, string },\n}, \n (state) => ({\n confirmPassword: { required, sameAs: sameAs(() => state.value.password) }\n })\n)\n\ntype State = InferInput<typeof rules>;\n\n```"
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
"id": "common-usage-usage-with-pinia",
|
|
94
|
+
"title": "Usage with Pinia",
|
|
95
|
+
"category": "common-usage",
|
|
96
|
+
"path": "common-usage/usage-with-pinia.md",
|
|
97
|
+
"content": "# Usage with Pinia <span data-title='pinia'></span>\n\nSince Regle is headless, you can use it anywhere in your app — whether in a composable or a store.\n\nUsing a Pinia store is an excellent way to avoid prop drilling with multiple properties while maintaining type inference seamlessly across your components.\n\n## Using regle in a Pinia store\n\n::: code-group\n```ts [demo.store.ts] \nimport { required, minLength, email } from '@regle/rules';\nimport { defineStore } from 'pinia';\nimport { useRegle } from '@regle/core';\n\nexport const useDemoStore = defineStore('demo-store', () => {\n const { r$ } = useRegle({ email: '' }, {\n email: { required, minLength: minLength(4), email }\n })\n\n return {\n r$\n }\n})\n```\n\n``` vue [ComponentA.vue]\n<template>\n <input v-model='r$.$value.email' placeholder='Type your email'/>\n <button type=\"button\" @click=\"r$.$reset({toInitialState: true})\">Reset</button>\n</template>\n\n```\n\n``` vue [ComponentB.vue]\n<template>\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\nComponent A:\n\nComponent B:\n\n## Avoid 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```"
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
"id": "core-concepts-displaying-errors",
|
|
101
|
+
"title": "Displaying errors",
|
|
102
|
+
"category": "core-concepts",
|
|
103
|
+
"path": "core-concepts/displaying-errors.md",
|
|
104
|
+
"content": "# Displaying errors\n\nRegle is a headless library, allowing you to display error messages in any way you choose. You can also use its internal state to apply classes or trigger behaviors dynamically.\n\n## Showing errors messages\n\nYou can display your errors by iterating though `r$.xxx.$errors`, `xxx` being the field you need to check.\n\nYou can also access `r$.$errors.xxx` or `r$.$silentErrors.xxx`.\n\nResult:\n\n## Display custom error messages\n\nTo display custom error messages, you can use the [withMessage](/core-concepts/rules/rule-wrappers#withmessage) helper. \nYou have access to additional data like parameters or rule status to write your message.\n\n:::tip\nIf you fall into this case:\n- You have a lot of forms in your app\n- You want to share translations easily between your forms\n\nConsider using [defineRegleConfig](/advanced-usage/global-config#replace-built-in-rules-messages) instead.\n:::\n\n``` vue [App.vue]\n\n```\n\n## i18n and translations\n\nRegle is library agnostic so you can use any i18n library freely, and there is nothing specific to configure, it will just work out of the box.\n\n```vue\n\n```\n\n## Applying an error and valid class\n\nResult:\n\n## Display flat errors\n\nIf you want to display the complete list of errors of a form, or the total count of errors, you can use the `flatErrors` utility.\n\nIt will return an array of error strings.\n\n```ts\nimport { flatErrors, useRegle } from '@regle/core';\nimport { email, minLength, required } from '@regle/rules';\n\nconst { r$ } = useRegle(\n { name: '', level0: { email: 'bar' } },\n {\n name: { required, minLength: minLength(5) },\n level0: {\n email: { email },\n },\n }\n);\n\nr$.$validate();\n\nconst flattenErrors = flatErrors(r$.$errors);\n// [\n// \"This field is required\", \n// \"Value must be an valid email address\"\n// ]\n```\n\n### `includePath` option\n\nThis helper also include an option to have the path of the property and returns the issues in Standard Schema Issue format.\n\n```ts\nimport { flatErrors, useRegle } from '@regle/core';\nimport { email, minLength, required } from '@regle/rules';\n\nconst { r$ } = useRegle(\n { name: '', level0: { email: 'bar' } },\n {\n name: { required, minLength: minLength(5) },\n level0: {\n email: { email },\n },\n }\n);\n\nr$.$validate();\n\nconst flattenErrors = flatErrors(r$.$errors, {includePath: true});\n// [\n// { message: \"This field is required\", path: [\"name\"] }, \n// { message: \"Value must be an valid email address\", path: [\"level0\", \"email\"]}\n// ]\n```"
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
"id": "core-concepts-index",
|
|
108
|
+
"title": "useRegle",
|
|
109
|
+
"category": "core-concepts",
|
|
110
|
+
"path": "core-concepts/index.md",
|
|
111
|
+
"content": "# Understanding `useRegle`\n\n`useRegle` is the heart of Regle. It's the composable that transforms your data and validation rules into a powerful, reactive validation system.\n\nThink of it as a bridge between your form data and your validation logic. You give it your state and rules, and it gives you back everything you need.\n\n## The Big Picture\n\nHere's how `useRegle` works at a high level:\n\n```vue [App.vue]\n\n```\n\nLet's break down each piece:\n\n:::tip\nRegle only works with the [Composition API](https://vuejs.org/guide/extras/composition-api-faq). There's no plan to support the Options or Class API—the Composition API's reactivity system is essential for Regle to work.\n:::\n\n## State: Your Form Data\n\nThe first parameter is your form data—the actual values users will be entering. Regle is flexible about how you define this state.\n\n### Different Ways to Define State\n\n**Raw Object** (simplest approach):\n```ts\nconst { r$ } = useRegle(\n { name: '', email: '', age: 0 }, \n /* rules */\n)\n```\n\n**Reactive Object** (when you need the state elsewhere):\n```ts\nconst formData = reactive({ name: '', email: '', age: 0 });\nconst { r$ } = useRegle(formData, /* rules */)\n\n// You can bind to either formData or r$.$value\n```\n\n**Ref Object** (for complex scenarios):\n```ts\nconst formData = ref({ name: '', email: '', age: 0 });\nconst { r$ } = useRegle(formData, /* rules */)\n```\n\n**Mixed Refs** (when individual fields need to be refs):\n```ts\nconst formData = { \n name: ref(''), \n email: ref(''), \n age: ref(0) \n}\nconst { r$ } = useRegle(formData, /* rules */)\n```\n\n**Single Value** (for validating just one field):\n```ts\nconst email = ref('');\nconst { r$ } = useRegle(email, /* rules */)\n```\n\n## Rules: Your Validation Logic\n\nThe second parameter defines how your data should be validated. The beautiful thing about Regle is that your rules structure mirrors your data structure exactly.\n\n### Basic Rules Declaration\n\n```ts\nimport { required, email, minLength } from '@regle/rules';\n\nconst { r$ } = useRegle(\n { \n user: { \n name: '', \n email: '' \n },\n message: ''\n },\n {\n user: {\n name: { required, minLength: minLength(2) },\n email: { required, email }\n },\n message: { required, minLength: minLength(10) }\n }\n)\n```\n\nSee how the rules structure matches the data structure? This makes it easy to understand and maintain.\n\n### Dynamic Rules object\n\nSometimes your validation rules need to change based on other values or conditions. Regle handles this elegantly:\n\n```ts [Inline]\nimport { useRegle } from '@regle/core';\n\n// The rule object will not react to computed changes\nuseRegle({ name: '' }, {\n name: { required }\n})\n```\n\n```ts [Getter]\nimport { useRegle } from '@regle/core';\n\n// The rules can now detect computed properties inside the object\nuseRegle({ name: '' }, () => ({\n name: { required }\n}))\n```\n\n```ts [Computed]\nimport { inferRules } from '@regle/core';\n\nconst state = ref({name: ''});\n\n// inferRules preserves TypeScript autocompletion\nconst rules = computed(() => {\n return inferRules(state, {\n name: { required }\n })\n})\n\nconst { r$ } = useRegle(state, rules);\n```\n\n### Available Rules\n\nRegle comes with a comprehensive set of built-in rules:\n\n```ts\nimport { \n required, email, minLength, maxLength,\n numeric, between, url, regex,\n // ... and many more\n} from '@regle/rules';\n```\n\nCheck out the [complete list of built-in rules](/core-concepts/rules/built-in-rules) to see everything available.\n\n:::tip Type-First Validation\nIf you prefer to define your validation schema first (like with Zod) and infer your TypeScript types from it, check out [Standard Schema usage](/common-usage/standard-schema).\n:::\n\n## The stored result : `r$`\n\nWhen you call `useRegle`, you get back an object containing `r$`—your validation state. If you've used Vuelidate before, `r$` works similarly to `v$`.\n\n`r$` is a reactive object that contains everything you need to build your form UI:\n\n<br/>\n\n### Common `r$` Properties\n\nIf you’ve used Vuelidate before, useRegle behaves similarly to `v$`.\n\n`r$` is a reactive object containing the values, errors, dirty state and all the necessary validations properties you'll need to display information.\n\nYou can find all the [available properties here](/core-concepts/validation-properties)\n\n``` vue twoslash [App.vue]\n\n```"
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
"id": "core-concepts-modifiers",
|
|
115
|
+
"title": "Modifiers",
|
|
116
|
+
"category": "core-concepts",
|
|
117
|
+
"path": "core-concepts/modifiers.md",
|
|
118
|
+
"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### `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 will have no effect only if you use `autoDirty: true`.\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### `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` 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## 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."
|
|
119
|
+
},
|
|
120
|
+
{
|
|
121
|
+
"id": "core-concepts-rules-built-in-rules",
|
|
122
|
+
"title": "Built-in rules",
|
|
123
|
+
"category": "rules",
|
|
124
|
+
"path": "core-concepts/rules/built-in-rules.md",
|
|
125
|
+
"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 { alphaNum } from '@regle/rules';\n\nconst { r$ } = useRegle({ name: '' }, {\n name: { \n alphaNum,\n // or\n alphaNum: alphaNum({ allowSymbols: true }),\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```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## `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 { r$ } = useRegle({ birthday: null as Date | null }, {\n birthday: {\n dateAfter: dateAfter(new Date()),\n // or\n dateAfter: dateAfter(new Date(), { 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 { r$ } = useRegle({ birthday: null as Date | null }, {\n birthday: {\n dateBefore: dateBefore(new Date()),\n // or\n dateBefore: dateBefore(new Date(), { allowEqual: false }),\n },\n})\n```\n\n## `dateBetweeen`\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 { r$ } = useRegle({ birthday: null as Date | null }, {\n birthday: {\n dateBetween: dateBetween(new Date(), new Date(2030, 3, 1)),\n // or\n dateBetween: dateBetween(new Date(), new Date(2030, 3, 1), { 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## `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## `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## `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## `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## `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## `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\n\nimport { macAddress } from '@regle/rules';\n\nconst maxCount = ref(6);\n\nconst { r$ } = useRegle({ address: '' }, {\n address: {\n macAddress,\n // or\n macAddress: macAddress('-')\n },\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## `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 { r$ } = useRegle({ aliment: 'Fish' }, {\n aliment: {\n oneOf: oneOf(['Fish', 'Meat', 'Bone'])\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## `url`\n\nValidates URLs.\n\n```ts\nimport { url } from '@regle/rules';\n\nconst { r$ } = useRegle({ bestUrl: '' }, {\n bestUrl: { url },\n})\n```"
|
|
126
|
+
},
|
|
127
|
+
{
|
|
128
|
+
"id": "core-concepts-rules-index",
|
|
129
|
+
"title": "Rules",
|
|
130
|
+
"category": "rules",
|
|
131
|
+
"path": "core-concepts/rules/index.md",
|
|
132
|
+
"content": "# Rules\n\nRules are the core concept of Regle (and also it's name 🙄).\n\nA rule is created using either:\n\n- An inline function\n- `createRule`\n\nA rule takes the value (and optional parameters) as input and returns a result as output.\n\nThe **result** can either be:\n\n- `boolean`\n- An object containing at least the `{ $valid: boolean }` property.\n\nThe boolean represents the result of the validation.\n\n:::tip\nYou can jump directly to the [createRule section](/core-concepts/rules/reusable-rules) to see more advanced features.\n:::\n\n## Inline rules\n\nYou can write inline rules as simple functions that receive the value being evaluated as a parameter. Use the `InlineRuleDeclaration` type helper for enhanced type safety.\n\nSimple rule\n```ts\nconst { r$ } = useRegle({name: ''}, {\n name: {\n simpleRule: (value) => value === 'regle'\n }\n})\n```\n\nAsync rule\n``` ts\nconst { r$ } = useRegle({name: ''}, {\n name: {\n asyncRule: async (value) => await someAsyncCall()\n }\n})\n```\n\nRule with metadata\n\n```ts\nconst { r$ } = useRegle({name: ''}, {\n name: {\n metadataRule: (value) => ({\n $valid: value === 'regle',\n foo: 'bar'\n })\n }\n})\n\n```\n\n## Adding error messages\n\nAny rule can be wrapped with the `withMessage` helper to provide error messages.\n\n```ts\nconst { r$ } = useRegle({name: ''}, {\n name: {\n foo: withMessage((value: Maybe<string>) => value === \"foo\", \"Value must be 'foo'\"),\n }\n})\n```\n\n:::tip\nYou can read more informations on wrappers [here](/core-concepts/rules/rule-wrappers)\n:::\n\n## Handling `optional` and `required` rules\n\nIn the **Regle** pattern (borrowed from Vuelidate), all rules are by default optional.\n\nThat means they will only run if a value is defined.\n\nTo enforce a required field, you just have to add the `required` validator or any of it's variations.\n\nThis allow to separate the core rule logic of how a field should look and whether or not it's required.\n\nIt will also output better errors.\n\nIt's advised to keep this logic in mind when writing custom rules.\n\n```ts\nimport {isFilled} from '@regle/rules'\n\nconst { r$ } = useRegle({name: ''}, {\n name: {\n mustBeFoo: (value) => {\n return isFilled(value) && value === 'foo'\n }\n }\n})\n```"
|
|
133
|
+
},
|
|
134
|
+
{
|
|
135
|
+
"id": "core-concepts-rules-reusable-rules",
|
|
136
|
+
"title": "Reusable rules",
|
|
137
|
+
"category": "rules",
|
|
138
|
+
"path": "core-concepts/rules/reusable-rules.md",
|
|
139
|
+
"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### 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='form.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```"
|
|
140
|
+
},
|
|
141
|
+
{
|
|
142
|
+
"id": "core-concepts-rules-rule-wrappers",
|
|
143
|
+
"title": "Rule wrappers",
|
|
144
|
+
"category": "rules",
|
|
145
|
+
"path": "core-concepts/rules/rule-wrappers.md",
|
|
146
|
+
"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```"
|
|
147
|
+
},
|
|
148
|
+
{
|
|
149
|
+
"id": "core-concepts-rules-rules-operators",
|
|
150
|
+
"title": "Rules operators",
|
|
151
|
+
"category": "rules",
|
|
152
|
+
"path": "core-concepts/rules/rules-operators.md",
|
|
153
|
+
"content": "# Rules operators\n\nRegle provides tools to combine and operate on different rules. It includes four built-in operators, available in `@regle/rules`:\n\n- `and`\n- `or`\n- `not`\n- `applyIf`\n- `assignIf`\n\nThese operators work with any rules you provide, but combining rules with incompatible input types may lead to errors.\n\n:::tip\nAll operators are compatible with wrappers\n:::\n\n## `and`\n\nThe `and` operator combines multiple rules and validates successfully only if all provided rules are valid.\n\n```ts\nimport { useRegle } from '@regle/core';\nimport { and, startsWith, endsWith, withMessage } from '@regle/rules';\n\nconst { r$ } = useRegle(\n { regex: '' },\n {\n regex: {\n myError: withMessage(\n and(startsWith('^'), endsWith('$')),\n ({ $params: [start, end] }) =>\n `Regex should start with \"${start}\" and end with \"${end}\"`\n ),\n },\n }\n);\n```\n\nResult: \n\n## `or`\n\nThe `or` operator validates successfully if at least one of the provided rules is valid.\n\n```ts twoslash\nimport { useRegle } from '@regle/core';\nimport { or, startsWith, endsWith, withMessage } from '@regle/rules';\n\nconst { r$ } = useRegle(\n { regex: '' },\n {\n regex: {\n myError: withMessage(\n or(startsWith('^'), endsWith('$')),\n ({ $params: [start, end] }) =>\n `Field should start with \"${start}\" or end with \"${end}\"`\n ),\n },\n }\n);\n```\n\nResult: \n\n## `not`\n\nThe `not` operator passes when the provided rule fails and fails when the rule passes. It can be combined with other rules.\n\n```ts\nimport { useRegle } from '@regle/core';\nimport { not, required, sameAs, withMessage } from '@regle/rules';\nimport { ref } from 'vue';\n\nconst form = ref({ oldPassword: '', newPassword: '' });\nconst { r$ } = useRegle(form, {\n oldPassword: {\n required,\n },\n newPassword: {\n notEqual: withMessage(\n not(sameAs(() => form.value.oldPassword)),\n 'Your confirm new password must not be the same as your old password'\n ),\n },\n});\n```\n\nResult: \n\n## `applyIf`\n\nThe `applyIf` operator is similar to `requiredIf`, but it can be used with any rule. It simplifies conditional rule declarations.\n\n```ts\nimport { minLength, applyIf } from '@regle/rules';\n\nconst condition = ref(false);\n\nconst { r$ } = useRegle({name: ''}, {\n name: {\n minLength: applyIf(condition, minLength(6))\n },\n});\n```\n\n## `assignIf`\n\nThe `assignIf` is a shorthand for conditional destructuring assignment.\nIt allows to apply multiple rules to a field conditionally.\n\n```ts\nimport { required, email, minLength, assignIf } from '@regle/rules';\n\nconst condition = ref(false);\n\nconst { r$ } = useRegle(ref({ name: '', email: '' }), {\n name: assignIf(condition, { required, minLength: minLength(4) }),\n email: { email },\n});\n```"
|
|
154
|
+
},
|
|
155
|
+
{
|
|
156
|
+
"id": "core-concepts-rules-rules-properties",
|
|
157
|
+
"title": "Rule properties",
|
|
158
|
+
"category": "rules",
|
|
159
|
+
"path": "core-concepts/rules/rules-properties.md",
|
|
160
|
+
"content": "# Rule properties\n\nRule properties are computed values or methods available in every nested rule status (including `regle`)\n\nLet's take a look at a simple example to explain the different properties.\n\n``` vue twoslash\n\n<template>\n <input v-model='form.user.firstName' placeholder='Type your firstName'/>\n\n <ul>\n <li v-for=\"error of r$.$errors.user.firstName\" :key='error'>\n {{ error }}\n </li>\n </ul>\n</template>\n```\n<br/>\n\n## Computed properties for rules\n\n### `$valid` \n- Type: `readonly boolean`\n \nIndicates the state of validation for this validator.\n\n### `$pending` \n- Type: `readonly boolean`\n \n\nIf the rule is async, indicates if it's currently pending. Always `false` if it's synchronous.\n\n### `$message` \n- Type: `readonly string | string[]`\n\nReturns the computed error message or messages for the current rule.\n\n### `$active` \n- Type: `readonly boolean`\n \nIndicates whether or not the rule is enabled (for rules like `requiredIf`)\n\n### `$metadata` \n- Type `RegleRuleMetadataDefinition`\n\nContains the metadata returned by the validator function.\n\n### `$type` \n- Type: `readonly string`\n\nThe name of the rule type.\n\n### `$validator` \n- Type: `readonly (value, ...metadata) => boolean | {$valid: true, [x:string]: any}`\n\nReturns the original rule validator function.\n\n### `$path` \n- Type: `readonly string[]`\n\nReturns the current path of the rule (used internally for tracking)\n\n## Common methods for rules\n\n### `$parse` \n- Type: `() => Promise<boolean>`\n\nRun the rule validator and compute its properties like `$message` and `$active`\n\n### `$reset` \n- Type: `() => void`\n\nReset the `$valid`, `$metadata` and `$pending` states"
|
|
161
|
+
},
|
|
162
|
+
{
|
|
163
|
+
"id": "core-concepts-rules-validations-helpers",
|
|
164
|
+
"title": "Validations helpers",
|
|
165
|
+
"category": "rules",
|
|
166
|
+
"path": "core-concepts/rules/validations-helpers.md",
|
|
167
|
+
"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."
|
|
168
|
+
},
|
|
169
|
+
{
|
|
170
|
+
"id": "core-concepts-validation-properties",
|
|
171
|
+
"title": "Validation properties",
|
|
172
|
+
"category": "core-concepts",
|
|
173
|
+
"path": "core-concepts/validation-properties.md",
|
|
174
|
+
"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.\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### `$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#### Options\n\n| Option | Type | Description |\n|-----------------------|-------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|\n| `toInitialState` | `boolean` | Reset validation status and reset form state to its initial state.<br><br>Initial state is different from the original state, as it can be mutated when using `$reset`. This serves as the base comparison for `$edited`.<br><br>⚠️ This doesn't work if the state is a `reactive` object. |\n| `toOriginalState` | `boolean` | Reset validation status and reset form state to its original state.<br><br>Original state is the unmutated state that was passed to the form when it was initialized. |\n| `toState` | `TState` or `() => TState` | Reset validation status and reset form state to the given state. Also sets the new state as the new initial state. |\n| `clearExternalErrors` | `boolean` | Clears the `$externalErrors` state back to an empty object. |\n \n\nExample:\n\n```ts\nr$.$reset(); // Only reset validation state\n```\n\n```ts\nr$.$reset({ toInitialState: true }); // Reset validation state and form state to initial state\n```\n\n```ts\nr$.$reset({ toOriginalState: true }); // Reset validation state and form state to original state\n```\n\n```ts\nr$.$reset({ toState: { email: 'test@test.com' } }); // Reset validation state and form state to the given state\n```\n\n```ts\nr$.$reset({ clearExternalErrors: true }); // Clear $externalErrors state\n```\n\n### `$clearExternalErrors` \n- Type: `() => void`\n\nClears the $externalResults state back to an empty object.\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## 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."
|
|
175
|
+
},
|
|
176
|
+
{
|
|
177
|
+
"id": "examples-advanced",
|
|
178
|
+
"title": "Advanced demo",
|
|
179
|
+
"category": "examples",
|
|
180
|
+
"path": "examples/advanced.md",
|
|
181
|
+
"content": "# Advanced demo\n\nYou can play with the code of this example in the stackblitz sandbox.\n\nDon't forgot 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>"
|
|
182
|
+
},
|
|
183
|
+
{
|
|
184
|
+
"id": "examples-collections",
|
|
185
|
+
"title": "Collections",
|
|
186
|
+
"category": "examples",
|
|
187
|
+
"path": "examples/collections.md",
|
|
188
|
+
"content": "# Collections Demo\n\nThis example demonstrates how to use collections (arrays) in your form.\n\n<a target='_blank' href=\"https://stackblitz.com/~/github.com/victorgarciaesgi/regle-examples/tree/main/examples/collections-example?file=examples/collections-example/src/App.vue&configPath=examples/collections-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/collections-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>"
|
|
189
|
+
},
|
|
190
|
+
{
|
|
191
|
+
"id": "examples-conditional-rules",
|
|
192
|
+
"title": "Conditional Rules",
|
|
193
|
+
"category": "examples",
|
|
194
|
+
"path": "examples/conditional-rules.md",
|
|
195
|
+
"content": "# Conditional Rules Demo\n\nIt is often necessary to add a rule only when a certain condition is met. This can be done by using the `applyIf` modifier.\n\n<a target='_blank' href=\"https://stackblitz.com/~/github.com/victorgarciaesgi/regle-examples/tree/main/examples/conditional-rules-example?file=examples/conditional-rules-example/src/App.vue&configPath=examples/conditional-rules-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/conditional-rules-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>"
|
|
196
|
+
},
|
|
197
|
+
{
|
|
198
|
+
"id": "examples-custom-rules",
|
|
199
|
+
"title": "Custom Rules",
|
|
200
|
+
"category": "examples",
|
|
201
|
+
"path": "examples/custom-rules.md",
|
|
202
|
+
"content": "# Custom Rules Demo\n\nYou can create your own custom rules either by using an inline function or the `createRule` utility. It is recommended to create custom rules using the `createRule` function rather than an inline function because it automatically tracks reactive dependencies and enables you to add custom `active` behavior to the rule.\n\n<a target='_blank' href=\"https://stackblitz.com/~/github.com/victorgarciaesgi/regle-examples/tree/main/examples/custom-rules-example?file=examples/custom-rules-example/src/App.vue&configPath=examples/custom-rules-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/custom-rules-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>"
|
|
203
|
+
},
|
|
204
|
+
{
|
|
205
|
+
"id": "examples-index",
|
|
206
|
+
"title": "index",
|
|
207
|
+
"category": "examples",
|
|
208
|
+
"path": "examples/index.md",
|
|
209
|
+
"content": "# Exemples\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)"
|
|
210
|
+
},
|
|
211
|
+
{
|
|
212
|
+
"id": "examples-required-indicators",
|
|
213
|
+
"title": "Required Indicators",
|
|
214
|
+
"category": "examples",
|
|
215
|
+
"path": "examples/required-indicators.md",
|
|
216
|
+
"content": "# Required Indicators Demo\n\nIt is often required to add an indicator to form fields to show that they are required. This is often done by adding an asterisk (*) to the field label. This example demonstrates how to add required indicators to form fields, taking advantage of property extensions.\n\n<a target='_blank' href=\"https://stackblitz.com/~/github.com/victorgarciaesgi/regle-examples/tree/main/examples/required-indicators-example?file=examples/required-indicators-example/src/App.vue&configPath=examples/required-indicators-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/required-indicators-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>"
|
|
217
|
+
},
|
|
218
|
+
{
|
|
219
|
+
"id": "examples-server-validation",
|
|
220
|
+
"title": "Server Validation",
|
|
221
|
+
"category": "examples",
|
|
222
|
+
"path": "examples/server-validation.md",
|
|
223
|
+
"content": "# Server Validation Demo\n\nThis example demonstrates how to apply server-side validation to a form.\n\n<a target='_blank' href=\"https://stackblitz.com/~/github.com/victorgarciaesgi/regle-examples/tree/main/examples/server-validation-example?file=examples/server-validation-example/src/App.vue&configPath=examples/server-validation-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/server-validation-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>"
|
|
224
|
+
},
|
|
225
|
+
{
|
|
226
|
+
"id": "examples-simple",
|
|
227
|
+
"title": "Simple demo",
|
|
228
|
+
"category": "examples",
|
|
229
|
+
"path": "examples/simple.md",
|
|
230
|
+
"content": "# Simple demo\n\nYou can play with the code of this example in the stackblitz sandbox.\n\nDon't forgot 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>"
|
|
231
|
+
},
|
|
232
|
+
{
|
|
233
|
+
"id": "index",
|
|
234
|
+
"title": "Regle - Headless form validation library for Vue.js",
|
|
235
|
+
"category": "general",
|
|
236
|
+
"path": "index.md",
|
|
237
|
+
"content": "<h2 class=\"hidden-title\">Vue validation library</h2>\n<h2 class=\"hidden-title\">Vue form library</h2>\n<h2 class=\"hidden-title\">Vue zod forms</h2>\n<h2 class=\"hidden-title\">Vue zod</h2>\n<h2 class=\"hidden-title\">Vuelidate Zod</h2>\n<h2 class=\"hidden-title\">Vuelidate alternative</h2>\n<h2 class=\"hidden-title\">Veevalidate alternative</h2>\n<h2 class=\"hidden-title\">Vueforms alternative</h2>\n<h2 class=\"hidden-title\">Tanstack forms alternative</h2>"
|
|
238
|
+
},
|
|
239
|
+
{
|
|
240
|
+
"id": "integrations-nuxt",
|
|
241
|
+
"title": "Nuxt",
|
|
242
|
+
"category": "integrations",
|
|
243
|
+
"path": "integrations/nuxt.md",
|
|
244
|
+
"content": "# Nuxt <span data-title=\"nuxt\"></span>\n\nAdding the Nuxt module enables auto-imports for selected exports.\n\nRun the following command in your Nuxt application:\n\n```bash\nnpx nuxi module add regle\n```\n\nOr do it manually\n\n:::code-group\n<!-- ```bash [nuxt]\nnpx nuxi module add regle\n``` -->\n```sh [pnpm]\npnpm add @regle/core @regle/rules @regle/nuxt\n```\n\n```sh [npm]\nnpm install @regle/core @regle/rules @regle/nuxt\n```\n\n```sh [yarn]\nyarn add @regle/core @regle/rules @regle/nuxt\n```\n\n```sh [bun]\nbun add @regle/core @regle/rules @regle/nuxt\n```\n:::\n\nYou can then declare the module inside your `nuxt.config.ts`.\n\n```ts [nuxt.config.ts]\nexport default defineNuxtConfig({\n modules: ['@regle/nuxt']\n})\n```\n\n## `setupFile`\n\nThe Regle Nuxt module allow you to define a global configuration plugin to provide all your forms with the same translations, options and custom rules.\n\n```ts [nuxt.config.ts]\nexport default defineNuxtConfig({\n modules: ['@regle/nuxt'],\n regle: {\n setupFile: '~/regle-config.ts'\n }\n})\n```\n\n```ts [app/regle-config.ts]\nimport { defineRegleNuxtPlugin } from '@regle/nuxt/setup';\nimport { defineRegleConfig } from '@regle/core';\nimport { required, withMessage } from '@regle/rules';\n\nexport default defineRegleNuxtPlugin(() => {\n return defineRegleConfig({\n rules: () => {\n const {t} = useI18n();\n\n return {\n required: withMessage(required, t('general.required')),\n customRule: myCustomRule,\n }\n },\n });\n});\n\n```\n\nThis will inject the fellowing composables to your auto-imports and `#imports`, loaded with your custom error messages and rules: \n\n- `useRegle` \n- `inferRules` \n- `useCollectScope` \n- `useScopedRegle` \n- `type RegleFieldStatus`\n\n```vue [app.vue] {2}\n\n```\n\n## No options\n\nIf no setup file is provided, the following auto-imports will be added to your app.\n\n- `@regle/core`\n - useRegle \n - inferRules\n - createRule\n - defineRegleConfig\n - extendRegleConfig\n - createVariant\n - narrowVariant\n - useScopedRegle\n - useCollectScope\n \n- `@regle/rules` Note: Built-in rules are not auto-injected to minimize the risk of name conflicts.\n - withAsync\n - withMessage\n - withParams\n - withTooltip\n \n- `@regle/schemas` (if present)\n - useRegleSchema\n - inferSchema\n - withDeps\n - defineRegleSchemaConfig"
|
|
245
|
+
},
|
|
246
|
+
{
|
|
247
|
+
"id": "integrations-schemas-libraries",
|
|
248
|
+
"title": "Schemas libraries",
|
|
249
|
+
"category": "integrations",
|
|
250
|
+
"path": "integrations/schemas-libraries.md",
|
|
251
|
+
"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. Than 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 depency 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 depency 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 pacthed 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:::"
|
|
252
|
+
},
|
|
253
|
+
{
|
|
254
|
+
"id": "introduction-comparisons",
|
|
255
|
+
"title": "Comparison with other form libraries",
|
|
256
|
+
"category": "introduction",
|
|
257
|
+
"path": "introduction/comparisons.md",
|
|
258
|
+
"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 he's doing is so great for the JS community, specially 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 is 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 completly in usage.\nFormwerk also offers the same feature to validate forms using Standard Schema Spec (Zod, Valibot, Arktype)"
|
|
259
|
+
},
|
|
260
|
+
{
|
|
261
|
+
"id": "introduction-devtools",
|
|
262
|
+
"title": "devtools",
|
|
263
|
+
"category": "introduction",
|
|
264
|
+
"path": "introduction/devtools.md",
|
|
265
|
+
"content": "# Regle Devtools\n\nRegle offers a devtools extension for [Vue Devtools](https://devtools.vuejs.org/) to help you debug your validation tree.\n\n\n\n## Installation\n\nTo enable devtools, you need to install the Regle plugin in your app.\n\n:::tip\nIf you use the `@regle/nuxt` module, the devtools will be automatically enabled.\n:::\n\n```ts [main.ts]\nimport { createApp } from 'vue';\nimport App from './App.vue';\nimport { RegleVuePlugin } from '@regle/core';\n\nconst app = createApp(App);\n\napp.use(RegleVuePlugin); // <--\n\napp.mount('#app');\n```\n\n## Usage\n\nRegle devtools can inspect every variation of `useRegle`:\n\n- `useRegle`\n- `useRules`\n- `useRegleSchema`\n- `useScopedRegle`\n- `useScopedRegleSchema`\n\nYou can inspect every nested properties and rules of the `r$` instance.\n\n:::warning\nRules details inspection is not available for `useRegleSchema`\n:::\n\n### Actions\n\nYou can perform actions on the `r$` instance by clicking on the actions buttons in the devtools.\n\n\n\n- Validate: Validate the `r$` instance (with `$validate` method)\n- Reset validation state: Reset the validation state of the `r$` instance (with `$reset` method)\n- Restore to original state: Restore the `r$` instance to the original state (with `$reset` method)\n\n## Providing custom `r$` ids to devtools\n\nBy default, the devtools will use a generic name to display the `r$` instance. \n\nYou can provide a custom name to the `useRegle` composable to display a more descriptive name in the devtools.\n\n```ts [App.vue]\nimport { useRegle } from '@regle/core';\n\nconst { r$ } = useRegle({ name: '' }, {\n name: { required }\n}, {\n id: 'my-form'\n});\n```\n\n## Devtools demo\n\nYou can go in any of the [Stablitz exemples](/examples/index) and open the devtools by clicking on the <span data-title=\"vue\"></span> \"Open Devtools\" button in the bottom middle of the page.\n\n### Vite Devtools Integration\n\nRegle devtools also integrate cleanly with [Vite](https://vitejs.dev/) when you're running your app in development mode.\n\nYou should see the Regle panel show up automatically in the Vite devtools if you've installed the plugin correctly.\n\n<img src=\"/screenshots/vite-devtools.png\" alt=\"Vite Devtools with Regle panel\" width=\"100\"/>\n\nYou will see the Regle icon showing in the devtools. Just click on it!\n\n<img src=\"/screenshots/vite-devtools-regle.png\" alt=\"Vite Devtools with Regle panel\" />"
|
|
266
|
+
},
|
|
267
|
+
{
|
|
268
|
+
"id": "introduction-index",
|
|
269
|
+
"title": "Introduction",
|
|
270
|
+
"category": "introduction",
|
|
271
|
+
"path": "introduction/index.md",
|
|
272
|
+
"content": "# Introduction\n\n## What is Regle?\n\nIf you've ever built a 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 extention 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:::"
|
|
273
|
+
},
|
|
274
|
+
{
|
|
275
|
+
"id": "introduction-installation",
|
|
276
|
+
"title": "Installation",
|
|
277
|
+
"category": "introduction",
|
|
278
|
+
"path": "introduction/installation.md",
|
|
279
|
+
"content": "# Installation\n\n## Prerequisites\n\nRequired\n- [Vue](https://vuejs.org/) <span data-title=\"vue\"></span> `3.3+`.\n- [Typescript](https://www.typescriptlang.org/) <span data-title=\"ee.ts\"></span> `4.8+`. \n - Compatible with plain javascript.\n- Text Editor with Vue syntax support.\n - [VSCode](https://code.visualstudio.com/) <span data-title=\".vscode\"></span> is recommended, along with the [official Vue extension](https://marketplace.visualstudio.com/items?itemName=Vue.volar).\n\nOptional\n- [Nuxt](https://nuxt.com/) <span data-title=\"nuxt\"></span> \n - Nuxt `3.1+`, and check docs for [Nuxt module](/integrations/nuxt)\n- [Pinia](https://pinia.vuejs.org/) <span data-title=\"pinia\"></span> \n - Pinia `2.2.5+`\n\nSchema libraries: [Docs](/integrations/schemas-libraries)\n\n- [Zod](https://zod.dev/) <span data-title=\"zod\"></span> `3.24+`. \n- [Valibot](https://valibot.dev/) <span data-title=\"valibot\"></span> `1+`.\n- [ArkType](https://arktype.io/) <span data-title=\"arktype\"></span> `2+`\n- Any library using the [Standard Schema Spec](https://standardschema.dev/) \n\n<br/>\n\n::: code-group\n\n```sh [pnpm]\npnpm add @regle/core @regle/rules\n```\n\n```sh [npm]\nnpm install @regle/core @regle/rules\n```\n\n```sh [yarn]\nyarn add @regle/core @regle/rules\n```\n\n```sh [bun]\nbun add @regle/core @regle/rules\n```\n\n:::\n\n## Devtools\n\nTo enable devtools, you need to install the Regle plugin in your app.\n\n:::tip\nIf you use the `@regle/nuxt` module, the devtools will be automatically enabled.\n:::\n\n```ts [main.ts]\nimport { createApp } from 'vue';\nimport { RegleVuePlugin } from '@regle/core';\nimport App from './App.vue';\n\nconst app = createApp(App);\n\napp.use(RegleVuePlugin); // <--\n\napp.mount('#app');\n```\n\n## MCP server\n\nRegle offers an MCP server that can be used to get documentation and autocomplete for Regle.\n\nYou can install it using the following configurations:\n\n- [Cursor](/integrations/mcp-server#cursor)\n- [Claude Desktop](/integrations/mcp-server#claude-desktop)"
|
|
280
|
+
},
|
|
281
|
+
{
|
|
282
|
+
"id": "introduction-migrate-from-vuelidate",
|
|
283
|
+
"title": "Migrate from Vuelidate",
|
|
284
|
+
"category": "introduction",
|
|
285
|
+
"path": "introduction/migrate-from-vuelidate.md",
|
|
286
|
+
"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```"
|
|
287
|
+
},
|
|
288
|
+
{
|
|
289
|
+
"id": "troubleshooting-reactivity",
|
|
290
|
+
"title": "Reactivity caveats",
|
|
291
|
+
"category": "troubleshooting",
|
|
292
|
+
"path": "troubleshooting/reactivity.md",
|
|
293
|
+
"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```"
|
|
294
|
+
},
|
|
295
|
+
{
|
|
296
|
+
"id": "typescript-type-safe-output",
|
|
297
|
+
"title": "Type safe output",
|
|
298
|
+
"category": "typescript",
|
|
299
|
+
"path": "typescript/type-safe-output.md",
|
|
300
|
+
"content": "# Type safe output\n\nWhat would be the benefit of building a validation library without a type safe output?\n\nInspired by the `Zod` parse output type, `Regle` will also infer your validator types to know which properties are _**guaranteed**_ to be defined.\n\n## `$validate` \n\nUsing `r$.$validate` will asynchronously run and wait for all your validators to finish, and will return an object containing the status of your form.\n\n```ts\nconst { valid, data, errors, issues } = await r$.$validate();\n```\n\nIf your *_result_* is `true`, the **data** will be type safe.\n\nIt will check if the `required` rule is present, but it will not work with `requiredIf` or `requiredUnless`, because we can't know the condition at build time.\n\n```ts twoslash\nimport { useRegle } from '@regle/core';\nimport { ref, type Ref, computed } from 'vue';\nimport { required } from '@regle/rules';\n// ---cut---\ntype Form = {\n firstName?: string;\n lastName?: string;\n}\n\nconst form = ref<Form>({ firstName: '', lastName: '' })\n\nconst { r$ } = useRegle(form, {\n lastName: { required },\n});\n\nasync function submit() {\n const { valid, data, errors, issues } = await r$.$validate();\n\n if (valid) {\n console.log(data);\n \n }\n}\n```\n\n<br/>\n\n### `InferSafeOutput`\n\nYou can also statically infer the safe output from any `r$` instance.\n\n```ts twoslash\nimport { ref, type Ref, computed } from 'vue';\nimport { required } from '@regle/rules';\n// ---cut---\nimport { useRegle, InferSafeOutput } from '@regle/core';\ntype Form = {\n firstName?: string;\n lastName?: string;\n}\n\nconst form: Ref<Form> = ref({ firstName: '', lastName: '' })\n\nconst { r$ } = useRegle(form, {\n lastName: { required },\n});\n\ntype FormRequest = InferSafeOutput<typeof r$>;\n\n```"
|
|
301
|
+
},
|
|
302
|
+
{
|
|
303
|
+
"id": "typescript-typing-props",
|
|
304
|
+
"title": "Typing props",
|
|
305
|
+
"category": "typescript",
|
|
306
|
+
"path": "typescript/typing-props.md",
|
|
307
|
+
"content": "# Typing props\n\nForms often span multiple components, and splitting your logic across components is a common practice. Regle offers tools to help type your props correctly, ensuring type safety and improving developer experience.\n\nThe best way to manage a centralized form state with inferred types is by using a Pinia store. Learn more in the Usage with Pinia guide [explained here](/common-usage/usage-with-pinia).\n\nIf you cannot use Pinia, here are the alternative approaches.\n\n## Typing component props\n\nAs Regle's types are complex and based on both your state and your rules, it's hard to replicate manually.\n\n`@regle/core` exports all its utility types, it can be long to explain each one of them, so we'll show the simplest way to type your props.\n\nTo avoid juggling with complex generic types, you can declare your form in a composable inside a file outside your component, and use this composable to type your props.\n\n:::code-group\n\n```ts twoslash include useMyForm [useMyForm.ts]\nimport { useRegle } from '@regle/core';\nimport { email, maxValue, minLength, numeric, required } from '@regle/rules';\n\nexport function useMyForm() {\n return useRegle(\n { email: '', user: { firstName: '', lastName: '' } },\n {\n email: { required, email: email },\n user: {\n firstName: {\n required,\n minLength: minLength(6),\n },\n lastName: {\n minLength: minLength(6),\n },\n },\n }\n );\n}\n```\n\n```vue twoslash [Parent.vue]\n<template>\n <input v-model=\"r$.$value.email\" placeholder=\"age\" />\n <Child :regle=\"r$\" />\n</template>\n\n```\n\n```vue twoslash [Child.vue]\n<template>\n <ul>\n <li v-for=\"error of regle.$errors.email\" :key=\"error\">\n {{ error }}\n </li>\n </ul>\n</template>\n\n```\n:::\n\n:::tip\n`InferRegleRoot` also works with `@regle/schemas`\n:::\n\n## Typing a field prop\n\nIt's possible that you have a `MyInput` like component that contains your business logic.\nYou may want to pass regle computed properties to this component to display useful information to the user.\n\nHere's how you can do it:\n\n:::code-group\n\n```vue [MyInput.vue]\n<template>\n <div class=\"my-input\">\n <input\n v-model=\"modelValue\"\n :class=\"{ valid: field.$correct, error: field.$error }\"\n :placeholder=\"placeholder\"\n />\n\n <ul v-if=\"field.$errors.length\">\n <li v-for=\"error of field.$errors\" :key=\"error\">\n {{ error }}\n </li>\n </ul>\n </div>\n</template>\n\n```\n\n```vue [myForm.vue]\n<template>\n <form>\n <MyInput v-model=\"r$.$value.name\" :field=\"r$.name\" placeholder=\"Type your name\" />\n <MyInput v-model=\"r$.$value.email\" :field=\"r$.email\" placeholder=\"Type your email\" />\n </form>\n</template>\n\n```\n:::\n\n## Typing a field prop with global configuration\n\n:::code-group\n\n```ts twoslash include config [config.ts]\n\nimport { withMessage } from '@regle/rules';\n// ---cut---\nimport { defineRegleConfig } from '@regle/core';\n\nexport const { useRegle: useCustomRegle } = defineRegleConfig({\n rules: () => ({\n strongPassword: withMessage(() => true, 'test')\n }),\n shortcuts: {\n fields: {\n $test: () => true\n }\n }\n});\n```\n\n```vue twoslash [MyPassword.vue]\n\n```\n\n:::\n\n## Enforcing rules for a specific component\n\nOn your common Input component, you can also enforce a rule to be present in the field.\n\n```vue twoslash [MyPassword.vue]\n\n```\n\n### For a custom configurations\n\n:::code-group\n\n```ts twoslash include config [config.ts]\n\nimport { withMessage } from '@regle/rules';\n// ---cut---\nimport { defineRegleConfig } from '@regle/core';\n\nexport const { useRegle: useCustomRegle } = defineRegleConfig({\n rules: () => ({\n strongPassword: withMessage(() => true, 'test')\n }),\n shortcuts: {\n fields: {\n $test: () => true\n }\n }\n});\n```\n\n```vue twoslash [MyPassword.vue]\n\n```\n\n:::"
|
|
308
|
+
},
|
|
309
|
+
{
|
|
310
|
+
"id": "typescript-typing-rules",
|
|
311
|
+
"title": "Typing rules",
|
|
312
|
+
"category": "typescript",
|
|
313
|
+
"path": "typescript/typing-rules.md",
|
|
314
|
+
"content": "# Typing rules schema\n\n## Computed rules\n\nWhen defining your rules schema in a separate computed property, you may lose autocompletion and detailed type checking for your rules. While useRegle will still perform type checks, the feedback will be less informative.\n\nTo avoid this, it is recommended to use the `inferRules` utility. This ensures that the rules schema aligns perfectly with the state, preventing extra or mismatched properties.\n\n### Example: Using inferRules\n\nThe inferRules utility requires that your state be declared independently of useRegle.\n\n```ts twoslash\n\nimport { computed, ref } from 'vue';\n// ---cut---\nimport { inferRules, useRegle } from '@regle/core';\n\nconst form = ref({ name: '' });\n\nconst rules = computed(() =>\n inferRules(form, {\n n\n\n })\n);\n\nconst { r$ } = useRegle(form, rules);\n```\n<br/>\n\n### Example: Typing Without inferRules\n\nIf you prefer not to use inferRules, you can type your rules explicitly using RegleComputedRules. To ensure type safety while retaining type inference, use the satisfies operator.\n\n```ts twoslash\n\nimport { computed, ref } from 'vue';\n// ---cut---\nimport { useRegle, type RegleComputedRules } from '@regle/core';\n\nconst form = ref({ name: '' });\n\nconst rules = computed(() => ({\n name: {\n required: () => true\n }\n} satisfies RegleComputedRules<typeof form>))\n\nconst { r$ } = useRegle(form, rules);\n```\n\n:::tip\nIf you don’t want to write your rules in a separate computed property, you can still define them inline.\n\n```ts\nconst min = ref(1);\nuseRegle({ name: '' }, () => ({\n name: { minLength: minLength(min.value) }\n}))\n```\n:::\n\n## Typing external nested properties\n\n`inferRules` can be used for any nested properties and chained inside the same schema.\n\n```ts twoslash\n\nimport { inferRules, useRegle } from '@regle/core';\nimport { required } from '@regle/rules';\nimport { computed, ref } from 'vue';\n\nconst form = ref({ name: { firstName: '', lastName: '' } });\n\nconst nameRules = computed(() =>\n inferRules(form.value.name, {\n firstName: { required },\n lastName: { r },\n \n })\n);\n\nconst rules = computed(() =>\n inferRules(form, {\n name: nameRules.value,\n })\n);\n\nconst { r$ } = useRegle(form, rules);\n```\n\n:::tip\n`Zod` and `Valibot` also have their own `inferSchema` exported.\n:::\n\n## Typing external nested field\n\n`inferRules` can also accept plain values to autocomplete the available rules.\n\n```ts twoslash\nimport { inferRules, useRegle } from '@regle/core';\nimport { required } from '@regle/rules';\nimport { computed, ref } from 'vue';\n\nconst form = ref({ name: { firstName: '', lastName: '' } });\n\nconst firstNameRules = computed(() =>\n inferRules(form.value.name.firstName, {\n required,\n })\n);\n\nconst rules = computed(() =>\n inferRules(form, {\n name: {\n firstName: firstNameRules.value,\n },\n })\n);\n\nconst { r$ } = useRegle(form, rules);\n```"
|
|
315
|
+
}
|
|
316
|
+
],
|
|
317
|
+
api: {
|
|
318
|
+
"@regle/core": [
|
|
319
|
+
{
|
|
320
|
+
"name": "createRule",
|
|
321
|
+
"kind": "function",
|
|
322
|
+
"description": "Create a typed custom rule that can be used like built-in rules.\nThe created rule can be declared in global options or used directly.\n\nFeatures:\n- Automatically detects if the rule is async\n- Supports parameters for configurable rules\n- Full TypeScript type inference\n- Custom metadata support",
|
|
323
|
+
"parameters": [{
|
|
324
|
+
"name": "definition",
|
|
325
|
+
"type": "RegleRuleInit<TValue, TParams, TReturn, TMetadata, TAsync>",
|
|
326
|
+
"description": "- The rule definition object containing:\n- `validator`: The validation function\n- `message`: Error message (string or function)\n- `type`: Optional rule type identifier\n- `active`: Optional function to conditionally activate the rule\n- `tooltip`: Optional tooltip message",
|
|
327
|
+
"optional": false
|
|
328
|
+
}],
|
|
329
|
+
"returnType": "InferRegleRule<TValue, TParams, TAsync, TMetadata>",
|
|
330
|
+
"example": "import { createRule } from '@regle/core';\nimport { isFilled } from '@regle/rules';\n\n// Simple rule without params\nexport const isFoo = createRule({\n validator(value: Maybe<string>) {\n if (isFilled(value)) {\n return value === 'foo';\n }\n return true;\n },\n message: \"The value should be 'foo'\"\n});\n\n// Rule with parameters\nexport const minCustom = createRule({\n validator(value: Maybe<number>, min: number) {\n if (isFilled(value)) {\n return value >= min;\n }\n return true;\n },\n message: ({ $params: [min] }) => `Value must be at least ${min}`\n});\n\n// Usage\nuseRegle({ name: '' }, { name: { isFoo } });\nuseRegle({ count: 0 }, { count: { minCustom: minCustom(5) } });",
|
|
331
|
+
"tags": { "see": "://reglejs.dev/core-concepts/rules/reusable-rules Documentation" }
|
|
332
|
+
},
|
|
333
|
+
{
|
|
334
|
+
"name": "createScopedUseRegle",
|
|
335
|
+
"kind": "function",
|
|
336
|
+
"description": "Create a scoped validation system for collecting and validating multiple form instances.\nUseful for dynamic forms, multi-step wizards, or component-based form architectures.",
|
|
337
|
+
"parameters": [{
|
|
338
|
+
"name": "options",
|
|
339
|
+
"type": "CreateScopedUseRegleOptions<TCustomRegle, TAsRecord>",
|
|
340
|
+
"description": "- Configuration options",
|
|
341
|
+
"optional": true
|
|
342
|
+
}],
|
|
343
|
+
"returnType": "{\n useScopedRegle: TReturnedRegle;\n useCollectScope: useCollectScopeFn<TAsRecord>;\n}",
|
|
344
|
+
"example": "// scoped-config.ts\nimport { createScopedUseRegle } from '@regle/core';\n\nexport const { useScopedRegle, useCollectScope } = createScopedUseRegle();\n\n// ChildComponent.vue\nconst { r$ } = useScopedRegle(state, rules, {\n namespace: 'myForm'\n});\n\n// ParentComponent.vue\nconst { r$: collectedR$ } = useCollectScope('myForm');\nawait collectedR$.$validate(); // Validates all child forms",
|
|
345
|
+
"tags": { "see": "://reglejs.dev/advanced-usage/scoped-validation Documentation" }
|
|
346
|
+
},
|
|
347
|
+
{
|
|
348
|
+
"name": "createVariant",
|
|
349
|
+
"kind": "function",
|
|
350
|
+
"description": "Create variant-based validation rules that depend on a discriminant field value.\nUseful for union types where different fields are required based on a type discriminant.\n\nNote: Autocomplete may not fully work due to TypeScript limitations.",
|
|
351
|
+
"parameters": [
|
|
352
|
+
{
|
|
353
|
+
"name": "root",
|
|
354
|
+
"type": "MaybeRefOrGetter<TForm> | DeepReactiveState<TForm>",
|
|
355
|
+
"description": "- The reactive state object",
|
|
356
|
+
"optional": false
|
|
357
|
+
},
|
|
358
|
+
{
|
|
359
|
+
"name": "discriminantKey",
|
|
360
|
+
"type": "TDiscriminant",
|
|
361
|
+
"description": "- The key used to discriminate between variants",
|
|
362
|
+
"optional": false
|
|
363
|
+
},
|
|
364
|
+
{
|
|
365
|
+
"name": "variants",
|
|
366
|
+
"type": "[...TVariants]",
|
|
367
|
+
"description": "- Array of variant rule definitions using `literal` for type matching",
|
|
368
|
+
"optional": false
|
|
369
|
+
}
|
|
370
|
+
],
|
|
371
|
+
"returnType": "Ref<TVariants[number]>",
|
|
372
|
+
"example": "import { useRegle, createVariant } from '@regle/core';\nimport { required, email, literal } from '@regle/rules';\n\nconst state = ref({\n type: 'EMAIL' as 'EMAIL' | 'GITHUB',\n email: '',\n username: ''\n});\n\n// ⚠️ Use getter syntax for your rules\nconst { r$ } = useRegle(state, () => {\n const variant = createVariant(state, 'type', [\n { type: { literal: literal('EMAIL') }, email: { required, email } },\n { type: { literal: literal('GITHUB') }, username: { required } },\n { type: { required } }, // Default case\n ]);\n\n return {\n ...variant.value,\n };\n});",
|
|
373
|
+
"tags": { "see": "://reglejs.dev/advanced-usage/variants Documentation" }
|
|
374
|
+
},
|
|
375
|
+
{
|
|
376
|
+
"name": "defineRegleConfig",
|
|
377
|
+
"kind": "function",
|
|
378
|
+
"description": "Define a global Regle configuration to customize the validation behavior across your application.\n\nFeatures:\n- Customize built-in rules messages\n- Add your custom rules with full type inference\n- Define global modifiers (lazy, rewardEarly, etc.)\n- Define shortcuts for common validation patterns",
|
|
379
|
+
"parameters": [],
|
|
380
|
+
"returnType": "{\n useRegle: useRegleFn<TCustomRules, TShortcuts>;\n inferRules: inferRulesFn<TCustomRules>;\n useRules: useRulesFn<TCustomRules, TShortcuts>;\n}",
|
|
381
|
+
"example": "import { defineRegleConfig } from '@regle/core';\nimport { required, withMessage } from '@regle/rules';\n\nexport const { useRegle, inferRules, useRules } = defineRegleConfig({\n rules: () => ({\n // Override default required message\n required: withMessage(required, 'This field cannot be empty'),\n // Add custom rule\n myCustomRule: createRule({\n validator: (value) => value === 'valid',\n message: 'Invalid value'\n })\n }),\n modifiers: {\n lazy: true,\n rewardEarly: true\n }\n});",
|
|
382
|
+
"tags": { "see": "://reglejs.dev/advanced-usage/global-config Documentation" }
|
|
383
|
+
},
|
|
384
|
+
{
|
|
385
|
+
"name": "defineRules",
|
|
386
|
+
"kind": "function",
|
|
387
|
+
"description": "Helper method to wrap a raw rules object with type inference.\nProvides autocomplete and type-checking without processing.",
|
|
388
|
+
"parameters": [{
|
|
389
|
+
"name": "rules",
|
|
390
|
+
"type": "TRules",
|
|
391
|
+
"description": "- The rules object to wrap",
|
|
392
|
+
"optional": false
|
|
393
|
+
}],
|
|
394
|
+
"returnType": "TRules",
|
|
395
|
+
"example": "import { defineRules } from '@regle/core';\nimport { required, string } from '@regle/rules';\n\n// defineRules helps catch structure errors\nconst rules = defineRules({\n firstName: { required, string },\n lastName: { required, string }\n});",
|
|
396
|
+
"tags": { "see": "://reglejs.dev/common-usage/standard-schema Documentation" }
|
|
397
|
+
},
|
|
398
|
+
{
|
|
399
|
+
"name": "extendRegleConfig",
|
|
400
|
+
"kind": "function",
|
|
401
|
+
"description": "Extend an already created custom `useRegle` configuration with additional rules, modifiers, or shortcuts.",
|
|
402
|
+
"parameters": [{
|
|
403
|
+
"name": "regle",
|
|
404
|
+
"type": "useRegleFn<TRootCustomRules, TRootShortcuts, {}, {}>",
|
|
405
|
+
"description": "- The existing useRegle function to extend",
|
|
406
|
+
"optional": false
|
|
407
|
+
}],
|
|
408
|
+
"returnType": "{\n useRegle: useRegleFn<Merge<TRootCustomRules, TCustomRules>, TRootShortcuts & TShortcuts>;\n inferRules: inferRulesFn<Merge<TRootCustomRules, TCustomRules>>;\n}",
|
|
409
|
+
"example": "import { extendRegleConfig } from '@regle/core';\nimport { baseUseRegle } from './base-config';\n\nexport const { useRegle, inferRules } = extendRegleConfig(baseUseRegle, {\n rules: () => ({\n additionalRule: myNewRule\n }),\n modifiers: {\n rewardEarly: true\n }\n});",
|
|
410
|
+
"tags": { "see": "://reglejs.dev/advanced-usage/global-config Documentation" }
|
|
411
|
+
},
|
|
412
|
+
{
|
|
413
|
+
"name": "flatErrors",
|
|
414
|
+
"kind": "function",
|
|
415
|
+
"description": "Converts a nested $errors object to a flat array of string errors\n\nCan also flatten to an array containing the path of each error with the options.includePath",
|
|
416
|
+
"parameters": [{
|
|
417
|
+
"name": "errors",
|
|
418
|
+
"type": "$InternalRegleErrors",
|
|
419
|
+
"description": "",
|
|
420
|
+
"optional": false
|
|
421
|
+
}, {
|
|
422
|
+
"name": "options",
|
|
423
|
+
"type": "{ includePath: true; }",
|
|
424
|
+
"description": "",
|
|
425
|
+
"optional": false
|
|
426
|
+
}],
|
|
427
|
+
"returnType": "StandardSchemaV1.Issue[]",
|
|
428
|
+
"example": "",
|
|
429
|
+
"tags": {}
|
|
430
|
+
},
|
|
431
|
+
{
|
|
432
|
+
"name": "inferRules",
|
|
433
|
+
"kind": "const",
|
|
434
|
+
"description": "Type helper to provide autocomplete and type-checking for your form rules.\nIt returns the rules without any processing - useful with computed rules.",
|
|
435
|
+
"parameters": [{
|
|
436
|
+
"name": "state",
|
|
437
|
+
"type": "MaybeRef<TState> | DeepReactiveState<TState>",
|
|
438
|
+
"description": "- The state reference",
|
|
439
|
+
"optional": false
|
|
440
|
+
}, {
|
|
441
|
+
"name": "rulesFactory",
|
|
442
|
+
"type": "TState extends PrimitiveTypes ? TDecl : TState extends Record<string, any> ? TRules : {}",
|
|
443
|
+
"description": "",
|
|
444
|
+
"optional": false
|
|
445
|
+
}],
|
|
446
|
+
"returnType": "NonNullable<TState> extends PrimitiveTypes ? TDecl : TRules",
|
|
447
|
+
"example": "import { inferRules, useRegle } from '@regle/core';\nimport { required, minLength } from '@regle/rules';\n\nconst state = ref({ name: '' });\n\n// inferRules preserves TypeScript autocompletion\nconst rules = computed(() => {\n return inferRules(state, {\n name: { required, minLength: minLength(2) }\n })\n});\n\nconst { r$ } = useRegle(state, rules);",
|
|
448
|
+
"tags": { "see": "://reglejs.dev/core-concepts/#dynamic-rules-object Documentation" }
|
|
449
|
+
},
|
|
450
|
+
{
|
|
451
|
+
"name": "InternalRuleType",
|
|
452
|
+
"kind": "const",
|
|
453
|
+
"description": "",
|
|
454
|
+
"parameters": [],
|
|
455
|
+
"returnType": "",
|
|
456
|
+
"example": "",
|
|
457
|
+
"tags": {}
|
|
458
|
+
},
|
|
459
|
+
{
|
|
460
|
+
"name": "markStatic",
|
|
461
|
+
"kind": "function",
|
|
462
|
+
"description": "Marks a value as static and treats the constructor as a regular raw Field.",
|
|
463
|
+
"parameters": [{
|
|
464
|
+
"name": "value",
|
|
465
|
+
"type": "T",
|
|
466
|
+
"description": "- The value to mark as static.",
|
|
467
|
+
"optional": false
|
|
468
|
+
}],
|
|
469
|
+
"returnType": "T extends RegleStaticImpl<unknown> ? T : RegleStatic<T>",
|
|
470
|
+
"example": "const StaticDecimal = markStatic(Decimal);\nconst StaticBigWrapper = markStatic(BigWrapper);",
|
|
471
|
+
"tags": {}
|
|
472
|
+
},
|
|
473
|
+
{
|
|
474
|
+
"name": "mergeRegles",
|
|
475
|
+
"kind": "function",
|
|
476
|
+
"description": "Merge multiple Regle instances into a single validation state.\nUseful for combining multiple forms or validation scopes.",
|
|
477
|
+
"parameters": [{
|
|
478
|
+
"name": "regles",
|
|
479
|
+
"type": "TRegles",
|
|
480
|
+
"description": "- An object containing named Regle instances to merge",
|
|
481
|
+
"optional": false
|
|
482
|
+
}, {
|
|
483
|
+
"name": "_scoped",
|
|
484
|
+
"type": "TScoped",
|
|
485
|
+
"description": "- Internal flag for scoped validation (default: false)",
|
|
486
|
+
"optional": true
|
|
487
|
+
}],
|
|
488
|
+
"returnType": "TScoped extends false ? MergedRegles<TRegles> : MergedScopedRegles",
|
|
489
|
+
"example": "import { useRegle, mergeRegles } from '@regle/core';\nimport { required } from '@regle/rules';\n\n// Create separate validation instances\nconst { r$: personalInfo } = useRegle(\n { name: '', email: '' },\n { name: { required }, email: { required } }\n);\n\nconst { r$: address } = useRegle(\n { street: '', city: '' },\n { street: { required }, city: { required } }\n);\n\n// Merge them together\nconst merged$ = mergeRegles({\n personalInfo,\n address\n});\n\n// Access combined state\nmerged$.$valid // true when ALL forms are valid\nmerged$.$errors // { personalInfo: {...}, address: {...} }\nawait merged$.$validate() // Validates all forms",
|
|
490
|
+
"tags": { "see": "://reglejs.dev/advanced-usage/merge-regles Documentation" }
|
|
491
|
+
},
|
|
492
|
+
{
|
|
493
|
+
"name": "narrowVariant",
|
|
494
|
+
"kind": "function",
|
|
495
|
+
"description": "Type guard to narrow a variant field to a specific discriminated value.\nEnables type-safe access to variant-specific fields.",
|
|
496
|
+
"parameters": [
|
|
497
|
+
{
|
|
498
|
+
"name": "root",
|
|
499
|
+
"type": "TRoot",
|
|
500
|
+
"description": "- The Regle status object",
|
|
501
|
+
"optional": false
|
|
502
|
+
},
|
|
503
|
+
{
|
|
504
|
+
"name": "discriminantKey",
|
|
505
|
+
"type": "TKey",
|
|
506
|
+
"description": "- The key used to discriminate between variants",
|
|
507
|
+
"optional": false
|
|
508
|
+
},
|
|
509
|
+
{
|
|
510
|
+
"name": "discriminantValue",
|
|
511
|
+
"type": "TValue",
|
|
512
|
+
"description": "- The specific value to narrow to",
|
|
513
|
+
"optional": false
|
|
514
|
+
}
|
|
515
|
+
],
|
|
516
|
+
"returnType": "root is NarrowVariant<TRoot, TKey, TValue>",
|
|
517
|
+
"example": "import { narrowVariant } from '@regle/core';\n\nif (narrowVariant(r$, 'type', 'EMAIL')) {\n // TypeScript knows r$.email exists here\n r$.email.$value = 'user@example.com';\n}",
|
|
518
|
+
"tags": { "see": "://reglejs.dev/advanced-usage/variants Documentation" }
|
|
519
|
+
},
|
|
520
|
+
{
|
|
521
|
+
"name": "refineRules",
|
|
522
|
+
"kind": "function",
|
|
523
|
+
"description": "Refine a rules object to add rules that depend on the state values.\nInspired by Zod's `refine`, this allows writing dynamic rules while maintaining type safety.",
|
|
524
|
+
"parameters": [{
|
|
525
|
+
"name": "rules",
|
|
526
|
+
"type": "TRules",
|
|
527
|
+
"description": "- The base rules object",
|
|
528
|
+
"optional": false
|
|
529
|
+
}, {
|
|
530
|
+
"name": "refinement",
|
|
531
|
+
"type": "(state: Ref<InferInput<TRules>, InferInput<TRules>>) => TRefinement",
|
|
532
|
+
"description": "- A function that receives the typed state and returns additional rules",
|
|
533
|
+
"optional": false
|
|
534
|
+
}],
|
|
535
|
+
"returnType": "(state: Ref<InferInput<TRules>>) => Merge<TRules, TRefinement>",
|
|
536
|
+
"example": "import { refineRules, type InferInput } from '@regle/core';\nimport { required, string, sameAs } from '@regle/rules';\n\nconst rules = refineRules({\n password: { required, string },\n}, (state) => ({\n // state is typed based on the base rules\n confirmPassword: {\n required,\n sameAs: sameAs(() => state.value.password)\n }\n}));\n\ntype State = InferInput<typeof rules>;\n// { password: string; confirmPassword: string }",
|
|
537
|
+
"tags": { "see": "://reglejs.dev/common-usage/standard-schema#refinerules Documentation" }
|
|
538
|
+
},
|
|
539
|
+
{
|
|
540
|
+
"name": "RegleVuePlugin",
|
|
541
|
+
"kind": "const",
|
|
542
|
+
"description": "",
|
|
543
|
+
"parameters": [],
|
|
544
|
+
"returnType": "",
|
|
545
|
+
"example": "",
|
|
546
|
+
"tags": {}
|
|
547
|
+
},
|
|
548
|
+
{
|
|
549
|
+
"name": "unwrapRuleParameters",
|
|
550
|
+
"kind": "function",
|
|
551
|
+
"description": "Returns a clean list of parameters\nRemoving Ref and executing function to return the unwrapped value",
|
|
552
|
+
"parameters": [{
|
|
553
|
+
"name": "params",
|
|
554
|
+
"type": "any[]",
|
|
555
|
+
"description": "",
|
|
556
|
+
"optional": false
|
|
557
|
+
}],
|
|
558
|
+
"returnType": "TParams",
|
|
559
|
+
"example": "",
|
|
560
|
+
"tags": {}
|
|
561
|
+
},
|
|
562
|
+
{
|
|
563
|
+
"name": "useCollectScope",
|
|
564
|
+
"kind": "function",
|
|
565
|
+
"description": "",
|
|
566
|
+
"parameters": [{
|
|
567
|
+
"name": "namespace",
|
|
568
|
+
"type": "MaybeRefOrGetter<string | string[]>",
|
|
569
|
+
"description": "",
|
|
570
|
+
"optional": true
|
|
571
|
+
}],
|
|
572
|
+
"returnType": "{\n r$: MergedScopedRegles<Record<string, unknown>[]> | MergedRegles<Record<string, SuperCompatibleRegleRoot>>;\n }",
|
|
573
|
+
"example": "",
|
|
574
|
+
"tags": {}
|
|
575
|
+
},
|
|
576
|
+
{
|
|
577
|
+
"name": "useRegle",
|
|
578
|
+
"kind": "const",
|
|
579
|
+
"description": "`useRegle` serves as the foundation for validation logic.\nIt transforms your data and validation rules into a powerful, reactive validation system.",
|
|
580
|
+
"parameters": [{
|
|
581
|
+
"name": "params",
|
|
582
|
+
"type": "[state: MaybeRef<TState> | DeepReactiveState<TState>, rulesFactory: TState extends PrimitiveTypes ? MaybeRefOrGetter<TDecl> : TState extends Record<...> ? MaybeRefOrComputedRef<...> | ((...args: any[]...",
|
|
583
|
+
"description": "",
|
|
584
|
+
"optional": false
|
|
585
|
+
}],
|
|
586
|
+
"returnType": "NonNullable<TState> extends PrimitiveTypes ? { r$: Raw<RegleFieldStatus<PrimitiveTypes & TState & {}, TDecl, RegleShortcutDefinition<any>>>; } : { ...; }",
|
|
587
|
+
"example": "import { useRegle } from '@regle/core';\nimport { required, email, minLength } from '@regle/rules';\n\nconst { r$ } = useRegle(\n { name: '', email: '' },\n {\n name: { required, minLength: minLength(2) },\n email: { required, email }\n }\n);\n\n// Access validation state\nr$.$valid // Whether all validations pass\nr$.$value // The current form values\nr$.name.$errors // Errors for the name field\n\n// Trigger validation\nconst result = await r$.$validate();",
|
|
588
|
+
"tags": { "see": "://reglejs.dev/core-concepts/ Documentation" }
|
|
589
|
+
},
|
|
590
|
+
{
|
|
591
|
+
"name": "useRootStorage",
|
|
592
|
+
"kind": "function",
|
|
593
|
+
"description": "",
|
|
594
|
+
"parameters": [],
|
|
595
|
+
"returnType": "{ regle: $InternalRegleStatusType; }",
|
|
596
|
+
"example": "",
|
|
597
|
+
"tags": {}
|
|
598
|
+
},
|
|
599
|
+
{
|
|
600
|
+
"name": "useRules",
|
|
601
|
+
"kind": "const",
|
|
602
|
+
"description": "`useRules` is a variant of `useRegle` that doesn't require you to provide initial state.\nIt creates an empty state based on your rules structure and implements the Standard Schema spec.\n\nThis is useful when you want to define validation rules first and infer the state type from them.",
|
|
603
|
+
"parameters": [{
|
|
604
|
+
"name": "rulesFactory",
|
|
605
|
+
"type": "TState extends Record<string, any> ? MaybeRef<TRules> | ((...args: any[]) => TRules) : {}",
|
|
606
|
+
"description": "",
|
|
607
|
+
"optional": false
|
|
608
|
+
}, {
|
|
609
|
+
"name": "options",
|
|
610
|
+
"type": "useRulesFnOptions<TRules, TValidationGroups, TState>",
|
|
611
|
+
"description": "",
|
|
612
|
+
"optional": true
|
|
613
|
+
}],
|
|
614
|
+
"returnType": "NonNullable<TState> extends PrimitiveTypes ? Omit<RegleCommonStatus<PrimitiveTypes & TState & {}, TDecl>, \"$value\" | ... 2 more ... | \"$originalValue\"> & { ...; } & { ...; } & { ...; } & StandardSchem...",
|
|
615
|
+
"example": "import { useRules, type InferInput } from '@regle/core';\nimport { required, string, email } from '@regle/rules';\n\nconst r$ = useRules({\n name: { required, string },\n email: { required, email }\n});\n\n// State is automatically created and typed\nr$.$value.name // string | null\nr$.$value.email // string | null\n\n// Can be used with Standard Schema compatible libraries\nconst result = await r$['~standard'].validate({ name: '', email: '' });",
|
|
616
|
+
"tags": { "see": "://reglejs.dev/common-usage/standard-schema#userules Documentation" }
|
|
617
|
+
},
|
|
618
|
+
{
|
|
619
|
+
"name": "useScopedRegle",
|
|
620
|
+
"kind": "const",
|
|
621
|
+
"description": "",
|
|
622
|
+
"parameters": [{
|
|
623
|
+
"name": "params",
|
|
624
|
+
"type": "[state: MaybeRef<TState> | DeepReactiveState<TState>, rulesFactory: TState extends PrimitiveTypes ? MaybeRefOrGetter<TDecl> : TState extends Record<...> ? MaybeRefOrComputedRef<...> | ((...args: any[]...",
|
|
625
|
+
"description": "",
|
|
626
|
+
"optional": false
|
|
627
|
+
}],
|
|
628
|
+
"returnType": "NonNullable<TState> extends PrimitiveTypes ? { r$: Raw<RegleFieldStatus<PrimitiveTypes & TState & {}, TDecl, any>>; } : { ...; }",
|
|
629
|
+
"example": "",
|
|
630
|
+
"tags": {}
|
|
631
|
+
},
|
|
632
|
+
{
|
|
633
|
+
"name": "variantToRef",
|
|
634
|
+
"kind": "function",
|
|
635
|
+
"description": "Create a reactive reference to a narrowed variant.\nUseful in templates or when you need a stable ref to the narrowed type.",
|
|
636
|
+
"parameters": [
|
|
637
|
+
{
|
|
638
|
+
"name": "root",
|
|
639
|
+
"type": "MaybeRef<TRoot>",
|
|
640
|
+
"description": "- The Regle status object (can be a ref)",
|
|
641
|
+
"optional": false
|
|
642
|
+
},
|
|
643
|
+
{
|
|
644
|
+
"name": "discriminantKey",
|
|
645
|
+
"type": "TKey",
|
|
646
|
+
"description": "- The key used to discriminate between variants",
|
|
647
|
+
"optional": false
|
|
648
|
+
},
|
|
649
|
+
{
|
|
650
|
+
"name": "discriminantValue",
|
|
651
|
+
"type": "TValue",
|
|
652
|
+
"description": "- The specific value to narrow to",
|
|
653
|
+
"optional": false
|
|
654
|
+
},
|
|
655
|
+
{
|
|
656
|
+
"name": "options",
|
|
657
|
+
"type": "{ unsafeAssertion: true; }",
|
|
658
|
+
"description": "- Optional `{ unsafeAssertion: true }` to assert the variant always exists",
|
|
659
|
+
"optional": false
|
|
660
|
+
}
|
|
661
|
+
],
|
|
662
|
+
"returnType": "Ref<NarrowVariant<TRoot, TKey, TValue>>",
|
|
663
|
+
"example": "<script setup lang=\"ts\">\nimport { variantToRef } from '@regle/core';\n\nconst emailR$ = variantToRef(r$, 'type', 'EMAIL');\n\n// In template:\n// <input v-if=\"emailR$\" v-model=\"emailR$.$value.email\" />\n<\/script>",
|
|
664
|
+
"tags": { "see": "://reglejs.dev/advanced-usage/variants Documentation" }
|
|
665
|
+
}
|
|
666
|
+
],
|
|
667
|
+
"@regle/rules": [
|
|
668
|
+
{
|
|
669
|
+
"name": "alpha",
|
|
670
|
+
"kind": "const",
|
|
671
|
+
"description": "Allows only alphabetic characters.",
|
|
672
|
+
"parameters": [{
|
|
673
|
+
"name": "params",
|
|
674
|
+
"type": "[options?: MaybeRefOrGetter<CommonAlphaOptions>]",
|
|
675
|
+
"description": "",
|
|
676
|
+
"optional": false
|
|
677
|
+
}],
|
|
678
|
+
"returnType": "RegleRuleDefinition<string, [options?: CommonAlphaOptions], false, boolean, string, string>",
|
|
679
|
+
"example": "import { alpha } from '@regle/rules';\n\nconst { r$ } = useRegle({ name: '' }, {\n name: {\n alpha,\n // or with symbols allowed\n alpha: alpha({ allowSymbols: true }),\n },\n})",
|
|
680
|
+
"tags": { "see": "://reglejs.dev/core-concepts/rules/built-in-rules#alpha Documentation" }
|
|
681
|
+
},
|
|
682
|
+
{
|
|
683
|
+
"name": "alphaNum",
|
|
684
|
+
"kind": "const",
|
|
685
|
+
"description": "Allows only alphanumeric characters.",
|
|
686
|
+
"parameters": [{
|
|
687
|
+
"name": "params",
|
|
688
|
+
"type": "[options?: MaybeRefOrGetter<CommonAlphaOptions>]",
|
|
689
|
+
"description": "",
|
|
690
|
+
"optional": false
|
|
691
|
+
}],
|
|
692
|
+
"returnType": "RegleRuleDefinition<string | number, [options?: CommonAlphaOptions], false, boolean, string, string | number>",
|
|
693
|
+
"example": "import { alphaNum } from '@regle/rules';\n\nconst { r$ } = useRegle({ name: '' }, {\n name: {\n alphaNum,\n // or with symbols allowed\n alphaNum: alphaNum({ allowSymbols: true }),\n },\n})",
|
|
694
|
+
"tags": { "see": "://reglejs.dev/core-concepts/rules/built-in-rules#alphanum Documentation" }
|
|
695
|
+
},
|
|
696
|
+
{
|
|
697
|
+
"name": "and",
|
|
698
|
+
"kind": "function",
|
|
699
|
+
"description": "The `and` operator combines multiple rules and validates successfully only if **all** provided rules are valid.",
|
|
700
|
+
"parameters": [{
|
|
701
|
+
"name": "rules",
|
|
702
|
+
"type": "[...TRules]",
|
|
703
|
+
"description": "- Two or more rules to combine",
|
|
704
|
+
"optional": false
|
|
705
|
+
}],
|
|
706
|
+
"returnType": "RegleRuleDefinition<\n ExtractValueFromRules<TRules>[number],\n UnwrapTuples<ExtractParamsFromRules<TRules>>,\n GuessAsyncFromRules<TRules>,\n GuessMetadataFromRules<TRules>\n>",
|
|
707
|
+
"example": "import { useRegle } from '@regle/core';\nimport { and, startsWith, endsWith, withMessage } from '@regle/rules';\n\nconst { r$ } = useRegle(\n { regex: '' },\n {\n regex: {\n myError: withMessage(\n and(startsWith('^'), endsWith('$')),\n ({ $params: [start, end] }) =>\n `Regex should start with \"${start}\" and end with \"${end}\"`\n ),\n },\n }\n);",
|
|
708
|
+
"tags": { "see": "://reglejs.dev/core-concepts/rules/rules-operators#and Documentation" }
|
|
709
|
+
},
|
|
710
|
+
{
|
|
711
|
+
"name": "applyIf",
|
|
712
|
+
"kind": "function",
|
|
713
|
+
"description": "The `applyIf` operator is similar to `requiredIf`, but it can be used with **any rule**.\nIt simplifies conditional rule declarations.",
|
|
714
|
+
"parameters": [{
|
|
715
|
+
"name": "_condition",
|
|
716
|
+
"type": "MaybeRefOrGetter<boolean>",
|
|
717
|
+
"description": "- The condition to check (ref, getter, or value)",
|
|
718
|
+
"optional": false
|
|
719
|
+
}, {
|
|
720
|
+
"name": "rule",
|
|
721
|
+
"type": "TRule",
|
|
722
|
+
"description": "- The rule to apply conditionally",
|
|
723
|
+
"optional": false
|
|
724
|
+
}],
|
|
725
|
+
"returnType": "TRule extends InlineRuleDeclaration<infer TValue, infer TParams, infer TReturn>\n ? RegleRuleDefinition<\n TValue,\n [...TParams, condition: boolean],\n TReturn extends Promise<any> ? true...",
|
|
726
|
+
"example": "import { minLength, applyIf } from '@regle/rules';\n\nconst condition = ref(false);\n\nconst { r$ } = useRegle({ name: '' }, {\n name: {\n minLength: applyIf(condition, minLength(6))\n },\n});",
|
|
727
|
+
"tags": { "see": "://reglejs.dev/core-concepts/rules/rules-operators#applyif Documentation" }
|
|
728
|
+
},
|
|
729
|
+
{
|
|
730
|
+
"name": "assignIf",
|
|
731
|
+
"kind": "function",
|
|
732
|
+
"description": "The `assignIf` is a shorthand for conditional destructuring assignment.\nIt allows applying **multiple rules** to a field conditionally.",
|
|
733
|
+
"parameters": [
|
|
734
|
+
{
|
|
735
|
+
"name": "_condition",
|
|
736
|
+
"type": "MaybeRefOrGetter<boolean>",
|
|
737
|
+
"description": "- The condition to check (ref, getter, or value)",
|
|
738
|
+
"optional": false
|
|
739
|
+
},
|
|
740
|
+
{
|
|
741
|
+
"name": "rules",
|
|
742
|
+
"type": "MaybeRefOrGetter<TRulesDelc>",
|
|
743
|
+
"description": "- An object of rules to apply conditionally",
|
|
744
|
+
"optional": false
|
|
745
|
+
},
|
|
746
|
+
{
|
|
747
|
+
"name": "otherwiseRules",
|
|
748
|
+
"type": "MaybeRefOrGetter<TRulesDelc>",
|
|
749
|
+
"description": "",
|
|
750
|
+
"optional": true
|
|
751
|
+
}
|
|
752
|
+
],
|
|
753
|
+
"returnType": "ComputedRef<TRulesDelc>",
|
|
754
|
+
"example": "import { required, email, minLength, assignIf } from '@regle/rules';\n\nconst condition = ref(false);\n\nconst { r$ } = useRegle(ref({ name: '', email: '' }), {\n name: assignIf(condition, { required, minLength: minLength(4) }),\n email: { email },\n});",
|
|
755
|
+
"tags": { "see": "://reglejs.dev/core-concepts/rules/rules-operators#assignif Documentation" }
|
|
756
|
+
},
|
|
757
|
+
{
|
|
758
|
+
"name": "between",
|
|
759
|
+
"kind": "const",
|
|
760
|
+
"description": "Checks if a number is in specified bounds. `min` and `max` are both inclusive by default.",
|
|
761
|
+
"parameters": [{
|
|
762
|
+
"name": "params",
|
|
763
|
+
"type": "[min: MaybeRefOrGetter<number>, max: MaybeRefOrGetter<number>, options?: MaybeRefOrGetter<CommonComparisonOptions>]",
|
|
764
|
+
"description": "",
|
|
765
|
+
"optional": false
|
|
766
|
+
}],
|
|
767
|
+
"returnType": "RegleRuleDefinition<number, [min: number, max: number, options?: CommonComparisonOptions], false, boolean, number, number>",
|
|
768
|
+
"example": "import { between } from '@regle/rules';\n\nconst maxCount = ref(6);\n\nconst { r$ } = useRegle({ count: 0 }, {\n count: {\n between: between(1, 6),\n // or with reactive max\n between: between(1, maxCount, { allowEqual: false }),\n // or with getter\n between: between(() => maxCount.value, 10)\n },\n})",
|
|
769
|
+
"tags": { "see": "://reglejs.dev/core-concepts/rules/built-in-rules#between Documentation" }
|
|
770
|
+
},
|
|
771
|
+
{
|
|
772
|
+
"name": "boolean",
|
|
773
|
+
"kind": "const",
|
|
774
|
+
"description": "Requires a value to be a native boolean type.\n\nMainly used for typing with `InferInput`.",
|
|
775
|
+
"parameters": [],
|
|
776
|
+
"returnType": "",
|
|
777
|
+
"example": "import { type InferInput } from '@regle/core';\nimport { boolean } from '@regle/rules';\n\nconst rules = {\n checkbox: { boolean },\n}\n\nconst state = ref<InferInput<typeof rules>>({});",
|
|
778
|
+
"tags": { "see": "://reglejs.dev/core-concepts/rules/built-in-rules#boolean Documentation" }
|
|
779
|
+
},
|
|
780
|
+
{
|
|
781
|
+
"name": "checked",
|
|
782
|
+
"kind": "const",
|
|
783
|
+
"description": "Requires a boolean value to be `true`. This is useful for checkbox inputs like \"accept terms\".",
|
|
784
|
+
"parameters": [],
|
|
785
|
+
"returnType": "",
|
|
786
|
+
"example": "import { checked } from '@regle/rules';\n\nconst { r$ } = useRegle({ confirm: false }, {\n confirm: { checked },\n})",
|
|
787
|
+
"tags": { "see": "://reglejs.dev/core-concepts/rules/built-in-rules#checked Documentation" }
|
|
788
|
+
},
|
|
789
|
+
{
|
|
790
|
+
"name": "contains",
|
|
791
|
+
"kind": "const",
|
|
792
|
+
"description": "Checks if the string contains the specified substring.",
|
|
793
|
+
"parameters": [{
|
|
794
|
+
"name": "params",
|
|
795
|
+
"type": "[part: MaybeRefOrGetter<string>]",
|
|
796
|
+
"description": "",
|
|
797
|
+
"optional": false
|
|
798
|
+
}],
|
|
799
|
+
"returnType": "RegleRuleDefinition<string, [part: string], false, boolean, string, string>",
|
|
800
|
+
"example": "import { contains } from '@regle/rules';\n\nconst { r$ } = useRegle({ bestLib: '' }, {\n bestLib: {\n contains: contains('regle')\n },\n})",
|
|
801
|
+
"tags": { "see": "://reglejs.dev/core-concepts/rules/built-in-rules#contains Documentation" }
|
|
802
|
+
},
|
|
803
|
+
{
|
|
804
|
+
"name": "date",
|
|
805
|
+
"kind": "const",
|
|
806
|
+
"description": "Requires a value to be a native `Date` constructor.\n\nMainly used for typing with `InferInput`.",
|
|
807
|
+
"parameters": [],
|
|
808
|
+
"returnType": "",
|
|
809
|
+
"example": "import { type InferInput } from '@regle/core';\nimport { date } from '@regle/rules';\n\nconst rules = {\n birthday: { date },\n}\n\nconst state = ref<InferInput<typeof rules>>({});",
|
|
810
|
+
"tags": { "see": "://reglejs.dev/core-concepts/rules/built-in-rules#date Documentation" }
|
|
811
|
+
},
|
|
812
|
+
{
|
|
813
|
+
"name": "dateAfter",
|
|
814
|
+
"kind": "const",
|
|
815
|
+
"description": "Checks if the date is after the given parameter.",
|
|
816
|
+
"parameters": [{
|
|
817
|
+
"name": "params",
|
|
818
|
+
"type": "[after: MaybeRefOrGetter<string | Date>, options?: MaybeRefOrGetter<CommonComparisonOptions>]",
|
|
819
|
+
"description": "",
|
|
820
|
+
"optional": false
|
|
821
|
+
}],
|
|
822
|
+
"returnType": "RegleRuleDefinition<string | Date, [after: string | Date, options?: CommonComparisonOptions], false, true | { $valid: false; error: \"date-not-after\"; } | { $valid: false; error: \"value-or-parameter-no...",
|
|
823
|
+
"example": "import { dateAfter } from '@regle/rules';\n\nconst { r$ } = useRegle({ birthday: null as Date | null }, {\n birthday: {\n dateAfter: dateAfter(new Date()),\n // or with options\n dateAfter: dateAfter(new Date(), { allowEqual: false }),\n },\n})",
|
|
824
|
+
"tags": { "see": "://reglejs.dev/core-concepts/rules/built-in-rules#dateafter Documentation" }
|
|
825
|
+
},
|
|
826
|
+
{
|
|
827
|
+
"name": "dateBefore",
|
|
828
|
+
"kind": "const",
|
|
829
|
+
"description": "Checks if the date is before the given parameter.",
|
|
830
|
+
"parameters": [{
|
|
831
|
+
"name": "params",
|
|
832
|
+
"type": "[before: MaybeRefOrGetter<string | Date>, options?: MaybeRefOrGetter<CommonComparisonOptions>]",
|
|
833
|
+
"description": "",
|
|
834
|
+
"optional": false
|
|
835
|
+
}],
|
|
836
|
+
"returnType": "RegleRuleDefinition<string | Date, [before: string | Date, options?: CommonComparisonOptions], false, true | { $valid: false; error: \"date-not-before\"; } | { $valid: false; error: \"value-or-parameter-...",
|
|
837
|
+
"example": "import { dateBefore } from '@regle/rules';\n\nconst { r$ } = useRegle({ birthday: null as Date | null }, {\n birthday: {\n dateBefore: dateBefore(new Date()),\n // or with options\n dateBefore: dateBefore(new Date(), { allowEqual: false }),\n },\n})",
|
|
838
|
+
"tags": { "see": "://reglejs.dev/core-concepts/rules/built-in-rules#datebefore Documentation" }
|
|
839
|
+
},
|
|
840
|
+
{
|
|
841
|
+
"name": "dateBetween",
|
|
842
|
+
"kind": "const",
|
|
843
|
+
"description": "Checks if the date falls between the specified bounds.",
|
|
844
|
+
"parameters": [{
|
|
845
|
+
"name": "params",
|
|
846
|
+
"type": "[before: MaybeRefOrGetter<string | Date>, after: MaybeRefOrGetter<string | Date>, options?: MaybeRefOrGetter<CommonComparisonOptions>]",
|
|
847
|
+
"description": "",
|
|
848
|
+
"optional": false
|
|
849
|
+
}],
|
|
850
|
+
"returnType": "RegleRuleDefinition<string | Date, [before: string | Date, after: string | Date, options?: CommonComparisonOptions], false, boolean, string | Date, string | Date>",
|
|
851
|
+
"example": "import { dateBetween } from '@regle/rules';\n\nconst { r$ } = useRegle({ birthday: null as Date | null }, {\n birthday: {\n dateBetween: dateBetween(new Date(), new Date(2030, 3, 1)),\n // or with options\n dateBetween: dateBetween(new Date(), new Date(2030, 3, 1), { allowEqual: false }),\n },\n})",
|
|
852
|
+
"tags": { "see": "://reglejs.dev/core-concepts/rules/built-in-rules#datebetweeen Documentation" }
|
|
853
|
+
},
|
|
854
|
+
{
|
|
855
|
+
"name": "decimal",
|
|
856
|
+
"kind": "const",
|
|
857
|
+
"description": "Allows positive and negative decimal numbers.",
|
|
858
|
+
"parameters": [],
|
|
859
|
+
"returnType": "",
|
|
860
|
+
"example": "import { decimal } from '@regle/rules';\n\nconst { r$ } = useRegle({ price: 0 }, {\n price: { decimal },\n})",
|
|
861
|
+
"tags": { "see": "://reglejs.dev/core-concepts/rules/built-in-rules#decimal Documentation" }
|
|
862
|
+
},
|
|
863
|
+
{
|
|
864
|
+
"name": "email",
|
|
865
|
+
"kind": "const",
|
|
866
|
+
"description": "Validates email addresses. Always verify on the server to ensure the address is real and not already in use.",
|
|
867
|
+
"parameters": [],
|
|
868
|
+
"returnType": "",
|
|
869
|
+
"example": "import { email } from '@regle/rules';\n\nconst { r$ } = useRegle({ email: '' }, {\n email: { email },\n})",
|
|
870
|
+
"tags": { "see": "://reglejs.dev/core-concepts/rules/built-in-rules#email Documentation" }
|
|
871
|
+
},
|
|
872
|
+
{
|
|
873
|
+
"name": "endsWith",
|
|
874
|
+
"kind": "const",
|
|
875
|
+
"description": "Checks if the string ends with the specified substring.",
|
|
876
|
+
"parameters": [{
|
|
877
|
+
"name": "params",
|
|
878
|
+
"type": "[part: MaybeRefOrGetter<string>]",
|
|
879
|
+
"description": "",
|
|
880
|
+
"optional": false
|
|
881
|
+
}],
|
|
882
|
+
"returnType": "RegleRuleDefinition<string, [part: string], false, boolean, string, string>",
|
|
883
|
+
"example": "import { endsWith } from '@regle/rules';\n\nconst { r$ } = useRegle({ firstName: '' }, {\n firstName: { endsWith: endsWith('foo') },\n})",
|
|
884
|
+
"tags": { "see": "://reglejs.dev/core-concepts/rules/built-in-rules#endswith Documentation" }
|
|
885
|
+
},
|
|
886
|
+
{
|
|
887
|
+
"name": "exactLength",
|
|
888
|
+
"kind": "const",
|
|
889
|
+
"description": "Requires the input value to have a strict specified length. Works with arrays, objects and strings.",
|
|
890
|
+
"parameters": [{
|
|
891
|
+
"name": "params",
|
|
892
|
+
"type": "[count: MaybeRefOrGetter<number>]",
|
|
893
|
+
"description": "",
|
|
894
|
+
"optional": false
|
|
895
|
+
}],
|
|
896
|
+
"returnType": "RegleRuleDefinition<string | any[] | Record<PropertyKey, any>, [count: number], false, boolean, unknown, string | any[] | Record<PropertyKey, any>>",
|
|
897
|
+
"example": "import { exactLength } from '@regle/rules';\n\nconst exactValue = ref(6);\n\nconst { r$ } = useRegle({ name: '' }, {\n name: {\n exactLength: exactLength(6),\n // or with reactive value\n exactLength: exactLength(exactValue),\n // or with getter\n exactLength: exactLength(() => exactValue.value)\n },\n})",
|
|
898
|
+
"tags": { "see": "://reglejs.dev/core-concepts/rules/built-in-rules#exactlength Documentation" }
|
|
899
|
+
},
|
|
900
|
+
{
|
|
901
|
+
"name": "exactValue",
|
|
902
|
+
"kind": "const",
|
|
903
|
+
"description": "Requires a field to have a strict numeric value.",
|
|
904
|
+
"parameters": [{
|
|
905
|
+
"name": "params",
|
|
906
|
+
"type": "[count: MaybeRefOrGetter<number>]",
|
|
907
|
+
"description": "",
|
|
908
|
+
"optional": false
|
|
909
|
+
}],
|
|
910
|
+
"returnType": "RegleRuleDefinition<number, [count: number], false, boolean, number, number>",
|
|
911
|
+
"example": "import { exactValue } from '@regle/rules';\n\nconst exactCount = ref(6);\n\nconst { r$ } = useRegle({ count: 0 }, {\n count: {\n exactValue: exactValue(6),\n // or with reactive value\n exactValue: exactValue(exactCount),\n // or with getter\n exactValue: exactValue(() => exactCount.value)\n },\n})",
|
|
912
|
+
"tags": { "see": "://reglejs.dev/core-concepts/rules/built-in-rules#exactvalue Documentation" }
|
|
913
|
+
},
|
|
914
|
+
{
|
|
915
|
+
"name": "getSize",
|
|
916
|
+
"kind": "function",
|
|
917
|
+
"description": "Returns the length/size of any data type. Works with strings, arrays, objects and numbers.",
|
|
918
|
+
"parameters": [{
|
|
919
|
+
"name": "value",
|
|
920
|
+
"type": "MaybeRef<string | number | any[] | Record<string, any>>",
|
|
921
|
+
"description": "- The value to get the size of",
|
|
922
|
+
"optional": false
|
|
923
|
+
}],
|
|
924
|
+
"returnType": "number",
|
|
925
|
+
"example": "import { 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})",
|
|
926
|
+
"tags": { "see": "://reglejs.dev/core-concepts/rules/validations-helpers#getsize Documentation" }
|
|
927
|
+
},
|
|
928
|
+
{
|
|
929
|
+
"name": "hexadecimal",
|
|
930
|
+
"kind": "const",
|
|
931
|
+
"description": "Validates hexadecimal values.",
|
|
932
|
+
"parameters": [],
|
|
933
|
+
"returnType": "",
|
|
934
|
+
"example": "import { hexadecimal } from '@regle/rules';\n\nconst { r$ } = useRegle({ hexadecimal: '' }, {\n hexadecimal: { hexadecimal },\n})",
|
|
935
|
+
"tags": { "see": "://reglejs.dev/core-concepts/rules/built-in-rules#hexadecimal Documentation" }
|
|
936
|
+
},
|
|
937
|
+
{
|
|
938
|
+
"name": "integer",
|
|
939
|
+
"kind": "const",
|
|
940
|
+
"description": "Allows only integers (positive and negative).",
|
|
941
|
+
"parameters": [],
|
|
942
|
+
"returnType": "",
|
|
943
|
+
"example": "import { integer } from '@regle/rules';\n\nconst { r$ } = useRegle({ count: 0 }, {\n count: { integer },\n})",
|
|
944
|
+
"tags": { "see": "://reglejs.dev/core-concepts/rules/built-in-rules#integer Documentation" }
|
|
945
|
+
},
|
|
946
|
+
{
|
|
947
|
+
"name": "ipv4Address",
|
|
948
|
+
"kind": "const",
|
|
949
|
+
"description": "Validates IPv4 addresses in dotted decimal notation (e.g., `127.0.0.1`).",
|
|
950
|
+
"parameters": [],
|
|
951
|
+
"returnType": "",
|
|
952
|
+
"example": "import { ipv4Address } from '@regle/rules';\n\nconst { r$ } = useRegle({ address: '' }, {\n address: { ipv4Address },\n})",
|
|
953
|
+
"tags": { "see": "://reglejs.dev/core-concepts/rules/built-in-rules#ipv4address Documentation" }
|
|
954
|
+
},
|
|
955
|
+
{
|
|
956
|
+
"name": "isDate",
|
|
957
|
+
"kind": "function",
|
|
958
|
+
"description": "Checks if the provided value is a valid Date. Used internally for date rules.\nCan also validate date strings.",
|
|
959
|
+
"parameters": [{
|
|
960
|
+
"name": "value",
|
|
961
|
+
"type": "unknown",
|
|
962
|
+
"description": "- The value to check",
|
|
963
|
+
"optional": false
|
|
964
|
+
}],
|
|
965
|
+
"returnType": "value is Date | string",
|
|
966
|
+
"example": "import { 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})",
|
|
967
|
+
"tags": { "see": "://reglejs.dev/core-concepts/rules/validations-helpers#isdate Documentation" }
|
|
968
|
+
},
|
|
969
|
+
{
|
|
970
|
+
"name": "isEmpty",
|
|
971
|
+
"kind": "function",
|
|
972
|
+
"description": "Checks if a value is empty in any way (including arrays and objects).\nThis is the inverse of `isFilled`.\n\n`isEmpty` also acts as a type guard.\n\nBy default, it considers an empty array as `true`. You can override this behavior with `considerEmptyArrayInvalid`.",
|
|
973
|
+
"parameters": [{
|
|
974
|
+
"name": "value",
|
|
975
|
+
"type": "unknown",
|
|
976
|
+
"description": "- The target value to check",
|
|
977
|
+
"optional": false
|
|
978
|
+
}, {
|
|
979
|
+
"name": "considerEmptyArrayInvalid",
|
|
980
|
+
"type": "boolean",
|
|
981
|
+
"description": "- When `false`, empty arrays are not considered empty (default: `true`)",
|
|
982
|
+
"optional": false
|
|
983
|
+
}],
|
|
984
|
+
"returnType": "value is null | undefined | [] | EmptyObject",
|
|
985
|
+
"example": "import { 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})",
|
|
986
|
+
"tags": { "see": "://reglejs.dev/core-concepts/rules/validations-helpers#isempty Documentation" }
|
|
987
|
+
},
|
|
988
|
+
{
|
|
989
|
+
"name": "isFilled",
|
|
990
|
+
"kind": "function",
|
|
991
|
+
"description": "Checks if any value you provide is defined (including arrays and objects).\nThis is almost a must-have for optional fields when writing custom rules.\n\n`isFilled` also acts as a type guard.\n\nBy default, it considers an empty array as `false`. You can override this behavior with `considerEmptyArrayInvalid`.",
|
|
992
|
+
"parameters": [{
|
|
993
|
+
"name": "value",
|
|
994
|
+
"type": "T",
|
|
995
|
+
"description": "- The target value to check",
|
|
996
|
+
"optional": false
|
|
997
|
+
}, {
|
|
998
|
+
"name": "considerEmptyArrayInvalid",
|
|
999
|
+
"type": "boolean",
|
|
1000
|
+
"description": "- When `false`, empty arrays are considered filled (default: `true`)",
|
|
1001
|
+
"optional": false
|
|
1002
|
+
}],
|
|
1003
|
+
"returnType": "value is NonNullable<T>",
|
|
1004
|
+
"example": "import { 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})",
|
|
1005
|
+
"tags": { "see": "://reglejs.dev/core-concepts/rules/validations-helpers#isfilled Documentation" }
|
|
1006
|
+
},
|
|
1007
|
+
{
|
|
1008
|
+
"name": "isNumber",
|
|
1009
|
+
"kind": "function",
|
|
1010
|
+
"description": "Type guard that checks if the passed value is a real `Number`.\nReturns `false` for `NaN`, making it safer than `typeof value === \"number\"`.",
|
|
1011
|
+
"parameters": [{
|
|
1012
|
+
"name": "value",
|
|
1013
|
+
"type": "unknown",
|
|
1014
|
+
"description": "- The value to check",
|
|
1015
|
+
"optional": false
|
|
1016
|
+
}],
|
|
1017
|
+
"returnType": "value is number",
|
|
1018
|
+
"example": "import { 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})",
|
|
1019
|
+
"tags": { "see": "://reglejs.dev/core-concepts/rules/validations-helpers#isnumber Documentation" }
|
|
1020
|
+
},
|
|
1021
|
+
{
|
|
1022
|
+
"name": "literal",
|
|
1023
|
+
"kind": "function",
|
|
1024
|
+
"description": "Allow only one possible literal value.",
|
|
1025
|
+
"parameters": [{
|
|
1026
|
+
"name": "literal",
|
|
1027
|
+
"type": "MaybeRefOrGetter<TValue>",
|
|
1028
|
+
"description": "- The literal value to match",
|
|
1029
|
+
"optional": false
|
|
1030
|
+
}],
|
|
1031
|
+
"returnType": "RegleRuleDefinition<TValue, [literal: TValue], false, boolean, MaybeInput<TValue>, string | number>",
|
|
1032
|
+
"example": "import { literal } from '@regle/rules';\n\nconst { r$ } = useRegle({ status: '' }, {\n status: { literal: literal('active') },\n})",
|
|
1033
|
+
"tags": { "see": "://reglejs.dev/core-concepts/rules/built-in-rules#literal Documentation" }
|
|
1034
|
+
},
|
|
1035
|
+
{
|
|
1036
|
+
"name": "macAddress",
|
|
1037
|
+
"kind": "const",
|
|
1038
|
+
"description": "Validates MAC addresses. Call as a function to specify a custom separator (e.g., `':'` or an empty string for `00ff1122334455`).",
|
|
1039
|
+
"parameters": [{
|
|
1040
|
+
"name": "params",
|
|
1041
|
+
"type": "[separator?: MaybeRefOrGetter<string>]",
|
|
1042
|
+
"description": "",
|
|
1043
|
+
"optional": false
|
|
1044
|
+
}],
|
|
1045
|
+
"returnType": "RegleRuleDefinition<string, [separator?: string], false, boolean, string, string>",
|
|
1046
|
+
"example": "import { macAddress } from '@regle/rules';\n\nconst { r$ } = useRegle({ address: '' }, {\n address: {\n macAddress,\n // or with custom separator\n macAddress: macAddress('-')\n },\n})",
|
|
1047
|
+
"tags": { "see": "://reglejs.dev/core-concepts/rules/built-in-rules#macaddress Documentation" }
|
|
1048
|
+
},
|
|
1049
|
+
{
|
|
1050
|
+
"name": "matchRegex",
|
|
1051
|
+
"kind": "function",
|
|
1052
|
+
"description": "Tests a value against one or more regular expressions.\nReturns `true` if the value is empty or matches **all** provided patterns.",
|
|
1053
|
+
"parameters": [{
|
|
1054
|
+
"name": "_value",
|
|
1055
|
+
"type": "string | number",
|
|
1056
|
+
"description": "- The value to test",
|
|
1057
|
+
"optional": false
|
|
1058
|
+
}, {
|
|
1059
|
+
"name": "expr",
|
|
1060
|
+
"type": "RegExp[]",
|
|
1061
|
+
"description": "- One or more RegExp patterns to match against",
|
|
1062
|
+
"optional": false
|
|
1063
|
+
}],
|
|
1064
|
+
"returnType": "boolean",
|
|
1065
|
+
"example": "import { 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})",
|
|
1066
|
+
"tags": { "see": "://reglejs.dev/core-concepts/rules/validations-helpers#matchregex Documentation" }
|
|
1067
|
+
},
|
|
1068
|
+
{
|
|
1069
|
+
"name": "maxLength",
|
|
1070
|
+
"kind": "const",
|
|
1071
|
+
"description": "Requires the input value to have a maximum specified length, inclusive. Works with arrays, objects and strings.",
|
|
1072
|
+
"parameters": [{
|
|
1073
|
+
"name": "params",
|
|
1074
|
+
"type": "[max: MaybeRefOrGetter<number>, options?: MaybeRefOrGetter<CommonComparisonOptions>]",
|
|
1075
|
+
"description": "",
|
|
1076
|
+
"optional": false
|
|
1077
|
+
}],
|
|
1078
|
+
"returnType": "RegleRuleDefinition<string | any[] | Record<PropertyKey, any>, [max: number, options?: CommonComparisonOptions], false, boolean, unknown, string | any[] | Record<PropertyKey, any>>",
|
|
1079
|
+
"example": "import { maxLength } from '@regle/rules';\n\nconst maxValue = ref(6);\n\nconst { r$ } = useRegle({ name: '' }, {\n name: {\n maxLength: maxLength(6),\n // or with reactive value\n maxLength: maxLength(maxValue),\n // or with getter\n maxLength: maxLength(() => maxValue.value)\n },\n})",
|
|
1080
|
+
"tags": { "see": "://reglejs.dev/core-concepts/rules/built-in-rules#maxlength Documentation" }
|
|
1081
|
+
},
|
|
1082
|
+
{
|
|
1083
|
+
"name": "maxValue",
|
|
1084
|
+
"kind": "const",
|
|
1085
|
+
"description": "Requires a field to have a specified maximum numeric value.",
|
|
1086
|
+
"parameters": [{
|
|
1087
|
+
"name": "params",
|
|
1088
|
+
"type": "[max: MaybeRefOrGetter<string | number>, options?: MaybeRefOrGetter<CommonComparisonOptions>]",
|
|
1089
|
+
"description": "",
|
|
1090
|
+
"optional": false
|
|
1091
|
+
}],
|
|
1092
|
+
"returnType": "RegleRuleDefinition<string | number, [max: string | number, options?: CommonComparisonOptions], false, boolean, string | number, string | number>",
|
|
1093
|
+
"example": "import { maxValue } from '@regle/rules';\n\nconst maxCount = ref(6);\n\nconst { r$ } = useRegle({ count: 0 }, {\n count: {\n maxValue: maxValue(6),\n // or with options\n maxValue: maxValue(maxCount, { allowEqual: false }),\n // or with getter\n maxValue: maxValue(() => maxCount.value)\n },\n})",
|
|
1094
|
+
"tags": { "see": "://reglejs.dev/core-concepts/rules/built-in-rules#maxvalue Documentation" }
|
|
1095
|
+
},
|
|
1096
|
+
{
|
|
1097
|
+
"name": "minLength",
|
|
1098
|
+
"kind": "const",
|
|
1099
|
+
"description": "Requires the input value to have a minimum specified length, inclusive. Works with arrays, objects and strings.",
|
|
1100
|
+
"parameters": [{
|
|
1101
|
+
"name": "params",
|
|
1102
|
+
"type": "[min: MaybeRefOrGetter<number>, options?: MaybeRefOrGetter<CommonComparisonOptions>]",
|
|
1103
|
+
"description": "",
|
|
1104
|
+
"optional": false
|
|
1105
|
+
}],
|
|
1106
|
+
"returnType": "RegleRuleDefinition<string | any[] | Record<PropertyKey, any>, [min: number, options?: CommonComparisonOptions], false, boolean, unknown, string | any[] | Record<PropertyKey, any>>",
|
|
1107
|
+
"example": "import { minLength } from '@regle/rules';\n\nconst minValue = ref(6);\n\nconst { r$ } = useRegle({ name: '' }, {\n name: {\n minLength: minLength(6),\n // or with reactive value\n minLength: minLength(minValue),\n // or with getter\n minLength: minLength(() => minValue.value)\n },\n})",
|
|
1108
|
+
"tags": { "see": "://reglejs.dev/core-concepts/rules/built-in-rules#minlength Documentation" }
|
|
1109
|
+
},
|
|
1110
|
+
{
|
|
1111
|
+
"name": "minValue",
|
|
1112
|
+
"kind": "const",
|
|
1113
|
+
"description": "Requires a field to have a specified minimum numeric value.",
|
|
1114
|
+
"parameters": [{
|
|
1115
|
+
"name": "params",
|
|
1116
|
+
"type": "[min: MaybeRefOrGetter<string | number>, options?: MaybeRefOrGetter<CommonComparisonOptions>]",
|
|
1117
|
+
"description": "",
|
|
1118
|
+
"optional": false
|
|
1119
|
+
}],
|
|
1120
|
+
"returnType": "RegleRuleDefinition<string | number, [min: string | number, options?: CommonComparisonOptions], false, boolean, string | number, string | number>",
|
|
1121
|
+
"example": "import { minValue } from '@regle/rules';\n\nconst minCount = ref(6);\n\nconst { r$ } = useRegle({ count: 0 }, {\n count: {\n minValue: minValue(6),\n // or with options\n minValue: minValue(minCount, { allowEqual: false }),\n // or with getter\n minValue: minValue(() => minCount.value)\n },\n})",
|
|
1122
|
+
"tags": { "see": "://reglejs.dev/core-concepts/rules/built-in-rules#minvalue Documentation" }
|
|
1123
|
+
},
|
|
1124
|
+
{
|
|
1125
|
+
"name": "nativeEnum",
|
|
1126
|
+
"kind": "function",
|
|
1127
|
+
"description": "Validate against a native TypeScript enum value. Similar to Zod's `nativeEnum`.",
|
|
1128
|
+
"parameters": [{
|
|
1129
|
+
"name": "enumLike",
|
|
1130
|
+
"type": "T",
|
|
1131
|
+
"description": "- The TypeScript enum to validate against",
|
|
1132
|
+
"optional": false
|
|
1133
|
+
}],
|
|
1134
|
+
"returnType": "RegleRuleDefinition<MaybeInput<T[keyof T]>, [enumLike: T], false, boolean, MaybeInput<T[keyof T]>, string | number>",
|
|
1135
|
+
"example": "import { nativeEnum } from '@regle/rules';\n\nenum Foo {\n Bar, Baz\n}\n\nconst { r$ } = useRegle({ type: '' }, {\n type: { nativeEnum: nativeEnum(Foo) },\n})",
|
|
1136
|
+
"tags": { "see": "://reglejs.dev/core-concepts/rules/built-in-rules#nativeenum Documentation" }
|
|
1137
|
+
},
|
|
1138
|
+
{
|
|
1139
|
+
"name": "not",
|
|
1140
|
+
"kind": "function",
|
|
1141
|
+
"description": "The `not` operator passes when the provided rule **fails** and fails when the rule **passes**.\nIt can be combined with other rules.",
|
|
1142
|
+
"parameters": [{
|
|
1143
|
+
"name": "rule",
|
|
1144
|
+
"type": "RegleRuleDefinition<TValue, TParams, TAsync, TMetadata, unknown, TValue extends Date & File & infer M ? M : TValue> | InlineRuleDeclaration<...>",
|
|
1145
|
+
"description": "- The rule to negate",
|
|
1146
|
+
"optional": false
|
|
1147
|
+
}, {
|
|
1148
|
+
"name": "message",
|
|
1149
|
+
"type": "RegleRuleDefinitionWithMetadataProcessor<TValue, RegleRuleMetadataConsumer<TValue, TParams, TMetadata>, string | string[]>",
|
|
1150
|
+
"description": "- Optional custom error message",
|
|
1151
|
+
"optional": true
|
|
1152
|
+
}],
|
|
1153
|
+
"returnType": "RegleRuleDefinition<TValue, TParams, TAsync, TMetadata>",
|
|
1154
|
+
"example": "import { useRegle } from '@regle/core';\nimport { not, required, sameAs, withMessage } from '@regle/rules';\nimport { ref } from 'vue';\n\nconst form = ref({ oldPassword: '', newPassword: '' });\n\nconst { r$ } = useRegle(form, {\n oldPassword: { required },\n newPassword: {\n notEqual: withMessage(\n not(sameAs(() => form.value.oldPassword)),\n 'Your new password must not be the same as your old password'\n ),\n },\n});",
|
|
1155
|
+
"tags": { "see": "://reglejs.dev/core-concepts/rules/rules-operators#not Documentation" }
|
|
1156
|
+
},
|
|
1157
|
+
{
|
|
1158
|
+
"name": "number",
|
|
1159
|
+
"kind": "const",
|
|
1160
|
+
"description": "Requires a value to be a native number type.\n\nMainly used for typing with `InferInput`.",
|
|
1161
|
+
"parameters": [],
|
|
1162
|
+
"returnType": "",
|
|
1163
|
+
"example": "import { type InferInput } from '@regle/core';\nimport { number } from '@regle/rules';\n\nconst rules = {\n count: { number },\n}\n\nconst state = ref<InferInput<typeof rules>>({});",
|
|
1164
|
+
"tags": { "see": "://reglejs.dev/core-concepts/rules/built-in-rules#number Documentation" }
|
|
1165
|
+
},
|
|
1166
|
+
{
|
|
1167
|
+
"name": "numeric",
|
|
1168
|
+
"kind": "const",
|
|
1169
|
+
"description": "Allows only numeric values (including numeric strings).",
|
|
1170
|
+
"parameters": [],
|
|
1171
|
+
"returnType": "",
|
|
1172
|
+
"example": "import { numeric } from '@regle/rules';\n\nconst { r$ } = useRegle({ count: 0 }, {\n count: { numeric },\n})",
|
|
1173
|
+
"tags": { "see": "://reglejs.dev/core-concepts/rules/built-in-rules#numeric Documentation" }
|
|
1174
|
+
},
|
|
1175
|
+
{
|
|
1176
|
+
"name": "oneOf",
|
|
1177
|
+
"kind": "const",
|
|
1178
|
+
"description": "Allow only one of the values from a fixed Array of possible entries.",
|
|
1179
|
+
"parameters": [{
|
|
1180
|
+
"name": "options",
|
|
1181
|
+
"type": "MaybeReadonly<MaybeRefOrGetter<[...TValues]>>",
|
|
1182
|
+
"description": "- Array of allowed values",
|
|
1183
|
+
"optional": false
|
|
1184
|
+
}],
|
|
1185
|
+
"returnType": "RegleRuleDefinition<TValues[number], [options: TValues], false, boolean, TValues[number], string | number>",
|
|
1186
|
+
"example": "import { oneOf } from '@regle/rules';\n\nconst { r$ } = useRegle({ aliment: 'Fish' }, {\n aliment: {\n oneOf: oneOf(['Fish', 'Meat', 'Bone'])\n },\n})",
|
|
1187
|
+
"tags": { "see": "://reglejs.dev/core-concepts/rules/built-in-rules#oneof Documentation" }
|
|
1188
|
+
},
|
|
1189
|
+
{
|
|
1190
|
+
"name": "or",
|
|
1191
|
+
"kind": "function",
|
|
1192
|
+
"description": "The `or` operator validates successfully if **at least one** of the provided rules is valid.",
|
|
1193
|
+
"parameters": [{
|
|
1194
|
+
"name": "rules",
|
|
1195
|
+
"type": "[...TRules]",
|
|
1196
|
+
"description": "- Two or more rules to combine",
|
|
1197
|
+
"optional": false
|
|
1198
|
+
}],
|
|
1199
|
+
"returnType": "RegleRuleDefinition<\n ExtractValueFromRules<TRules>[number],\n UnwrapTuples<ExtractParamsFromRules<TRules>>,\n GuessAsyncFromRules<TRules>,\n GuessMetadataFromRules<TRules>\n>",
|
|
1200
|
+
"example": "import { useRegle } from '@regle/core';\nimport { or, startsWith, endsWith, withMessage } from '@regle/rules';\n\nconst { r$ } = useRegle(\n { regex: '' },\n {\n regex: {\n myError: withMessage(\n or(startsWith('^'), endsWith('$')),\n ({ $params: [start, end] }) =>\n `Field should start with \"${start}\" or end with \"${end}\"`\n ),\n },\n }\n);",
|
|
1201
|
+
"tags": { "see": "://reglejs.dev/core-concepts/rules/rules-operators#or Documentation" }
|
|
1202
|
+
},
|
|
1203
|
+
{
|
|
1204
|
+
"name": "regex",
|
|
1205
|
+
"kind": "const",
|
|
1206
|
+
"description": "Checks if the value matches one or more regular expressions.",
|
|
1207
|
+
"parameters": [{
|
|
1208
|
+
"name": "params",
|
|
1209
|
+
"type": "[regexp: MaybeRefOrGetter<RegExp | RegExp[]>]",
|
|
1210
|
+
"description": "",
|
|
1211
|
+
"optional": false
|
|
1212
|
+
}],
|
|
1213
|
+
"returnType": "RegleRuleDefinition<string | number, [regexp: RegExp | RegExp[]], false, boolean, string | number, string | number>",
|
|
1214
|
+
"example": "import { regex } from '@regle/rules';\n\nconst { r$ } = useRegle({ name: '' }, {\n name: {\n regex: regex(/^foo/),\n // or with multiple patterns\n regex: regex([/^bar/, /baz$/]),\n },\n})",
|
|
1215
|
+
"tags": { "see": "://reglejs.dev/core-concepts/rules/built-in-rules#regex Documentation" }
|
|
1216
|
+
},
|
|
1217
|
+
{
|
|
1218
|
+
"name": "required",
|
|
1219
|
+
"kind": "const",
|
|
1220
|
+
"description": "Requires non-empty data. Checks for empty arrays and strings containing only whitespaces.",
|
|
1221
|
+
"parameters": [],
|
|
1222
|
+
"returnType": "",
|
|
1223
|
+
"example": "import { required } from '@regle/rules';\n\nconst { r$ } = useRegle({ name: '' }, {\n name: { required },\n})",
|
|
1224
|
+
"tags": { "see": "://reglejs.dev/core-concepts/rules/built-in-rules#required Documentation" }
|
|
1225
|
+
},
|
|
1226
|
+
{
|
|
1227
|
+
"name": "requiredIf",
|
|
1228
|
+
"kind": "const",
|
|
1229
|
+
"description": "Requires non-empty data, only if provided data property, ref, or a function resolves to `true`.",
|
|
1230
|
+
"parameters": [{
|
|
1231
|
+
"name": "params",
|
|
1232
|
+
"type": "[condition: MaybeRefOrGetter<boolean>]",
|
|
1233
|
+
"description": "",
|
|
1234
|
+
"optional": false
|
|
1235
|
+
}],
|
|
1236
|
+
"returnType": "RegleRuleDefinition<unknown, [condition: boolean], false, boolean, unknown, unknown>",
|
|
1237
|
+
"example": "import { requiredIf } from '@regle/rules';\n\nconst form = ref({ name: '', condition: false });\nconst conditionRef = ref(false);\n\nconst { r$ } = useRegle(form, {\n name: {\n required: requiredIf(() => form.value.condition),\n // or with a ref\n required: requiredIf(conditionRef),\n },\n})",
|
|
1238
|
+
"tags": { "see": "://reglejs.dev/core-concepts/rules/built-in-rules#requiredif Documentation" }
|
|
1239
|
+
},
|
|
1240
|
+
{
|
|
1241
|
+
"name": "requiredUnless",
|
|
1242
|
+
"kind": "const",
|
|
1243
|
+
"description": "Requires non-empty data, only if provided data property, ref, or a function resolves to `false`.",
|
|
1244
|
+
"parameters": [{
|
|
1245
|
+
"name": "params",
|
|
1246
|
+
"type": "[condition: MaybeRefOrGetter<boolean>]",
|
|
1247
|
+
"description": "",
|
|
1248
|
+
"optional": false
|
|
1249
|
+
}],
|
|
1250
|
+
"returnType": "RegleRuleDefinition<unknown, [condition: boolean], false, boolean, unknown, unknown>",
|
|
1251
|
+
"example": "import { requiredUnless } from '@regle/rules';\n\nconst form = ref({ name: '', condition: false });\nconst conditionRef = ref(false);\n\nconst { r$ } = useRegle(form, {\n name: {\n required: requiredUnless(() => form.value.condition),\n // or with a ref\n required: requiredUnless(conditionRef)\n },\n})",
|
|
1252
|
+
"tags": { "see": "://reglejs.dev/core-concepts/rules/built-in-rules#requiredunless Documentation" }
|
|
1253
|
+
},
|
|
1254
|
+
{
|
|
1255
|
+
"name": "sameAs",
|
|
1256
|
+
"kind": "const",
|
|
1257
|
+
"description": "Checks if the value matches the specified property or ref. Useful for password confirmation fields.",
|
|
1258
|
+
"parameters": [{
|
|
1259
|
+
"name": "target",
|
|
1260
|
+
"type": "MaybeRefOrGetter<TTarget>",
|
|
1261
|
+
"description": "- The target value to compare against (can be a ref or getter)",
|
|
1262
|
+
"optional": false
|
|
1263
|
+
}, {
|
|
1264
|
+
"name": "otherName",
|
|
1265
|
+
"type": "MaybeRefOrGetter<string>",
|
|
1266
|
+
"description": "- Optional name for the other field (used in error message)",
|
|
1267
|
+
"optional": true
|
|
1268
|
+
}],
|
|
1269
|
+
"returnType": "RegleRuleDefinition<TTarget, [target: TTarget, otherName?: string], false, boolean, TTarget extends infer M ? M : TTarget, TTarget extends Date & File & infer M ? M : TTarget>",
|
|
1270
|
+
"example": "import { 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})",
|
|
1271
|
+
"tags": { "see": "://reglejs.dev/core-concepts/rules/built-in-rules#sameas Documentation" }
|
|
1272
|
+
},
|
|
1273
|
+
{
|
|
1274
|
+
"name": "startsWith",
|
|
1275
|
+
"kind": "const",
|
|
1276
|
+
"description": "Checks if the string starts with the specified substring.",
|
|
1277
|
+
"parameters": [{
|
|
1278
|
+
"name": "params",
|
|
1279
|
+
"type": "[part: MaybeRefOrGetter<string>]",
|
|
1280
|
+
"description": "",
|
|
1281
|
+
"optional": false
|
|
1282
|
+
}],
|
|
1283
|
+
"returnType": "RegleRuleDefinition<string, [part: string], false, boolean, string, string>",
|
|
1284
|
+
"example": "import { startsWith } from '@regle/rules';\n\nconst { r$ } = useRegle({ bestLib: '' }, {\n bestLib: {\n startsWith: startsWith('regle')\n },\n})",
|
|
1285
|
+
"tags": { "see": "://reglejs.dev/core-concepts/rules/built-in-rules#startswith Documentation" }
|
|
1286
|
+
},
|
|
1287
|
+
{
|
|
1288
|
+
"name": "string",
|
|
1289
|
+
"kind": "const",
|
|
1290
|
+
"description": "Requires a value to be a native string type.\n\nMainly used for typing with `InferInput`.",
|
|
1291
|
+
"parameters": [],
|
|
1292
|
+
"returnType": "",
|
|
1293
|
+
"example": "import { type InferInput } from '@regle/core';\nimport { string } from '@regle/rules';\n\nconst rules = {\n firstName: { string },\n}\n\nconst state = ref<InferInput<typeof rules>>({});",
|
|
1294
|
+
"tags": { "see": "://reglejs.dev/core-concepts/rules/built-in-rules#string Documentation" }
|
|
1295
|
+
},
|
|
1296
|
+
{
|
|
1297
|
+
"name": "toDate",
|
|
1298
|
+
"kind": "function",
|
|
1299
|
+
"description": "Coerces any string, number, or Date value into a `Date` using the `Date` constructor.",
|
|
1300
|
+
"parameters": [{
|
|
1301
|
+
"name": "argument",
|
|
1302
|
+
"type": "string | number | Date",
|
|
1303
|
+
"description": "- The value to convert to a Date",
|
|
1304
|
+
"optional": false
|
|
1305
|
+
}],
|
|
1306
|
+
"returnType": "Date",
|
|
1307
|
+
"example": "import { toDate } from '@regle/rules';\n\nconst date1 = toDate('2024-01-15'); // Date object\nconst date2 = toDate(1705276800000); // Date from timestamp\nconst date3 = toDate(new Date()); // Clone of Date",
|
|
1308
|
+
"tags": { "see": "://reglejs.dev/core-concepts/rules/validations-helpers#todate Documentation" }
|
|
1309
|
+
},
|
|
1310
|
+
{
|
|
1311
|
+
"name": "toNumber",
|
|
1312
|
+
"kind": "function",
|
|
1313
|
+
"description": "Converts any string (or number) into a number using the `Number` constructor.",
|
|
1314
|
+
"parameters": [{
|
|
1315
|
+
"name": "argument",
|
|
1316
|
+
"type": "T",
|
|
1317
|
+
"description": "- The value to convert",
|
|
1318
|
+
"optional": false
|
|
1319
|
+
}],
|
|
1320
|
+
"returnType": "number",
|
|
1321
|
+
"example": "import { toNumber, isNumber } from '@regle/rules';\n\nconst num = toNumber('42'); // 42\nconst invalid = toNumber('abc'); // NaN\n\n// Always check for NaN when using toNumber\nif (!isNaN(toNumber(value))) {\n // Safe to use as number\n}",
|
|
1322
|
+
"tags": { "see": "://reglejs.dev/core-concepts/rules/validations-helpers#tonumber Documentation" }
|
|
1323
|
+
},
|
|
1324
|
+
{
|
|
1325
|
+
"name": "type",
|
|
1326
|
+
"kind": "function",
|
|
1327
|
+
"description": "Define the input type of a rule. No runtime validation.\n\nOverride any input type set by other rules.",
|
|
1328
|
+
"parameters": [],
|
|
1329
|
+
"returnType": "RegleRuleDefinition<unknown, [], false, boolean, MaybeInput<T>>",
|
|
1330
|
+
"example": "import { type InferInput } from '@regle/core';\nimport { type } from '@regle/rules';\n\nconst rules = {\n firstName: { type: type<string>() },\n status: { type: type<'active' | 'inactive'>() },\n}\n\nconst state = ref<InferInput<typeof rules>>({});",
|
|
1331
|
+
"tags": { "see": "://reglejs.dev/core-concepts/rules/built-in-rules#type Documentation" }
|
|
1332
|
+
},
|
|
1333
|
+
{
|
|
1334
|
+
"name": "url",
|
|
1335
|
+
"kind": "const",
|
|
1336
|
+
"description": "Validates URLs.",
|
|
1337
|
+
"parameters": [],
|
|
1338
|
+
"returnType": "",
|
|
1339
|
+
"example": "import { url } from '@regle/rules';\n\nconst { r$ } = useRegle({ bestUrl: '' }, {\n bestUrl: { url },\n})",
|
|
1340
|
+
"tags": { "see": "://reglejs.dev/core-concepts/rules/built-in-rules#url Documentation" }
|
|
1341
|
+
},
|
|
1342
|
+
{
|
|
1343
|
+
"name": "withAsync",
|
|
1344
|
+
"kind": "function",
|
|
1345
|
+
"description": "`withAsync` works like `withParams`, but is specifically designed for async rules that depend on external values.",
|
|
1346
|
+
"parameters": [{
|
|
1347
|
+
"name": "rule",
|
|
1348
|
+
"type": "InlineRuleDeclaration<TValue, TParams, TReturn>",
|
|
1349
|
+
"description": "- The async rule function",
|
|
1350
|
+
"optional": false
|
|
1351
|
+
}, {
|
|
1352
|
+
"name": "depsArray",
|
|
1353
|
+
"type": "[...TParams]",
|
|
1354
|
+
"description": "- Array of reactive dependencies (refs or getters)",
|
|
1355
|
+
"optional": true
|
|
1356
|
+
}],
|
|
1357
|
+
"returnType": "RegleRuleDefinition<TValue, UnwrapRegleUniversalParams<TParams>, true, TMetadata>",
|
|
1358
|
+
"example": "import { 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})",
|
|
1359
|
+
"tags": { "see": "://reglejs.dev/core-concepts/rules/rule-wrappers#withasync Documentation" }
|
|
1360
|
+
},
|
|
1361
|
+
{
|
|
1362
|
+
"name": "withMessage",
|
|
1363
|
+
"kind": "function",
|
|
1364
|
+
"description": "The `withMessage` wrapper lets you associate an error message with a rule.\nPass your rule as the first argument and the error message as the second.",
|
|
1365
|
+
"parameters": [{
|
|
1366
|
+
"name": "rule",
|
|
1367
|
+
"type": "RegleRuleWithParamsDefinition<TValue, TParams, TAsync, TMetadata>",
|
|
1368
|
+
"description": "- The rule to wrap (can be inline function or rule definition)",
|
|
1369
|
+
"optional": false
|
|
1370
|
+
}, {
|
|
1371
|
+
"name": "newMessage",
|
|
1372
|
+
"type": "RegleRuleDefinitionWithMetadataProcessor<TValue, RegleRuleMetadataConsumer<TValue, TParams, TMetadata>, string | string[]>",
|
|
1373
|
+
"description": "- The error message (string or function returning a string)",
|
|
1374
|
+
"optional": false
|
|
1375
|
+
}],
|
|
1376
|
+
"returnType": "RegleRuleWithParamsDefinition<TValue, TParams, TAsync, TMetadata>",
|
|
1377
|
+
"example": "import { withMessage } from '@regle/rules';\n\nconst { r$ } = useRegle({ name: '' }, {\n name: {\n // With a static message\n customRule1: withMessage((value) => !!value, \"Custom Error\"),\n // With dynamic message using metadata\n customRule2: withMessage(\n customRuleInlineWithMetaData,\n ({ $value, foo }) => `Custom Error: ${$value} ${foo}`\n ),\n }\n})",
|
|
1378
|
+
"tags": { "see": "://reglejs.dev/core-concepts/rules/rule-wrappers#withmessage Documentation" }
|
|
1379
|
+
},
|
|
1380
|
+
{
|
|
1381
|
+
"name": "withParams",
|
|
1382
|
+
"kind": "function",
|
|
1383
|
+
"description": "The `withParams` wrapper allows your rule to depend on external parameters,\nsuch 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.\nHowever, sometimes dependencies cannot be tracked automatically; use `withParams` to manually define them.",
|
|
1384
|
+
"parameters": [{
|
|
1385
|
+
"name": "rule",
|
|
1386
|
+
"type": "InlineRuleDeclaration<TValue, TParams, TReturn> | RegleRuleDefinition<TValue, any[], TAsync, TMetadata, unknown, TValue extends Date & ... 1 more ... & infer M ? M : TValue>",
|
|
1387
|
+
"description": "- The rule function or definition",
|
|
1388
|
+
"optional": false
|
|
1389
|
+
}, {
|
|
1390
|
+
"name": "depsArray",
|
|
1391
|
+
"type": "[...TParams]",
|
|
1392
|
+
"description": "- Array of reactive dependencies (refs or getters)",
|
|
1393
|
+
"optional": false
|
|
1394
|
+
}],
|
|
1395
|
+
"returnType": "RegleRuleDefinition<TValue, UnwrapRegleUniversalParams<TParams>, TAsync, TMetadata>",
|
|
1396
|
+
"example": "import { 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 with getter\n customRule: withParams((value, param) => value === param, [() => base.value]),\n }\n})",
|
|
1397
|
+
"tags": { "see": "://reglejs.dev/core-concepts/rules/rule-wrappers#withparams Documentation" }
|
|
1398
|
+
},
|
|
1399
|
+
{
|
|
1400
|
+
"name": "withTooltip",
|
|
1401
|
+
"kind": "function",
|
|
1402
|
+
"description": "The `withTooltip` wrapper allows you to display additional messages for your field that aren't necessarily errors.\nTooltips are aggregated and accessible via `$tooltips` property.",
|
|
1403
|
+
"parameters": [{
|
|
1404
|
+
"name": "rule",
|
|
1405
|
+
"type": "RegleRuleWithParamsDefinition<TValue, TParams, TAsync, TMetadata>",
|
|
1406
|
+
"description": "- The rule to wrap (can be inline function or rule definition)",
|
|
1407
|
+
"optional": false
|
|
1408
|
+
}, {
|
|
1409
|
+
"name": "newTooltip",
|
|
1410
|
+
"type": "RegleRuleDefinitionWithMetadataProcessor<TValue, RegleRuleMetadataConsumer<TValue, TParams, TMetadata>, string | string[]>",
|
|
1411
|
+
"description": "- The tooltip message (string or function returning a string)",
|
|
1412
|
+
"optional": false
|
|
1413
|
+
}],
|
|
1414
|
+
"returnType": "RegleRuleWithParamsDefinition<TValue, TParams, TAsync, TMetadata>",
|
|
1415
|
+
"example": "import { withTooltip, minLength } from '@regle/rules';\n\nconst { r$ } = useRegle({ password: '' }, {\n password: {\n minLength: withTooltip(\n minLength(8),\n 'Password should be at least 8 characters for better security'\n ),\n }\n})\n\n// Access tooltips via:\n// r$.password.$tooltips",
|
|
1416
|
+
"tags": { "see": "://reglejs.dev/core-concepts/rules/rule-wrappers#withtooltip Documentation" }
|
|
1417
|
+
}
|
|
1418
|
+
],
|
|
1419
|
+
"@regle/schemas": [
|
|
1420
|
+
{
|
|
1421
|
+
"name": "createScopedUseRegleSchema",
|
|
1422
|
+
"kind": "const",
|
|
1423
|
+
"description": "Create a scoped validation system for schema-based validation.\nSimilar to `createScopedUseRegle` but for use with Standard Schema libraries.",
|
|
1424
|
+
"parameters": [{
|
|
1425
|
+
"name": "options",
|
|
1426
|
+
"type": "CreateScopedUseRegleSchemaOptions<TCustomRegle, TAsRecord>",
|
|
1427
|
+
"description": "- Configuration options",
|
|
1428
|
+
"optional": true
|
|
1429
|
+
}],
|
|
1430
|
+
"returnType": "{ useScopedRegle: TReturnedRegle; useCollectScope: useCollectScopeFn<TAsRecord>; }",
|
|
1431
|
+
"example": "import { createScopedUseRegleSchema, defineRegleSchemaConfig } from '@regle/schemas';\n\nconst { useRegleSchema } = defineRegleSchemaConfig({...});\n\nexport const { useScopedRegle, useCollectScope } = createScopedUseRegleSchema({\n customUseRegle: useRegleSchema\n});",
|
|
1432
|
+
"tags": { "see": "://reglejs.dev/advanced-usage/scoped-validation Documentation" }
|
|
1433
|
+
},
|
|
1434
|
+
{
|
|
1435
|
+
"name": "defineRegleSchemaConfig",
|
|
1436
|
+
"kind": "function",
|
|
1437
|
+
"description": "Define a global configuration for `useRegleSchema`.\n\nFeatures:\n- Define global modifiers (lazy, rewardEarly, etc.)\n- Define shortcuts for common validation patterns",
|
|
1438
|
+
"parameters": [],
|
|
1439
|
+
"returnType": "{\n useRegleSchema: useRegleSchemaFn<TShortcuts>;\n inferSchema: inferSchemaFn;\n}",
|
|
1440
|
+
"example": "import { defineRegleSchemaConfig } from '@regle/schemas';\n\nexport const { useRegleSchema, inferSchema } = defineRegleSchemaConfig({\n modifiers: {\n lazy: true,\n rewardEarly: true\n }\n});",
|
|
1441
|
+
"tags": { "see": "://reglejs.dev/integrations/schemas-libraries Documentation" }
|
|
1442
|
+
},
|
|
1443
|
+
{
|
|
1444
|
+
"name": "inferSchema",
|
|
1445
|
+
"kind": "const",
|
|
1446
|
+
"description": "Type helper to provide autocomplete and type-checking for your schema.\nReturns the schema without any processing - useful with computed schemas.",
|
|
1447
|
+
"parameters": [{
|
|
1448
|
+
"name": "state",
|
|
1449
|
+
"type": "MaybeRef<DeepPartial<TState>> | DeepReactiveState<DeepPartial<TState>>",
|
|
1450
|
+
"description": "- The state reference",
|
|
1451
|
+
"optional": false
|
|
1452
|
+
}, {
|
|
1453
|
+
"name": "rulesFactory",
|
|
1454
|
+
"type": "MaybeRef<TSchema>",
|
|
1455
|
+
"description": "",
|
|
1456
|
+
"optional": false
|
|
1457
|
+
}],
|
|
1458
|
+
"returnType": "NoInferLegacy<TSchema>",
|
|
1459
|
+
"example": "import { inferSchema, useRegleSchema } from '@regle/schemas';\nimport { z } from 'zod';\n\nconst state = ref({ name: '' });\n\n// inferSchema preserves TypeScript autocompletion\nconst schema = computed(() => {\n return inferSchema(state, z.object({\n name: z.string().min(2)\n }));\n});\n\nconst { r$ } = useRegleSchema(state, schema);",
|
|
1460
|
+
"tags": { "see": "://reglejs.dev/integrations/schemas-libraries Documentation" }
|
|
1461
|
+
},
|
|
1462
|
+
{
|
|
1463
|
+
"name": "useRegleSchema",
|
|
1464
|
+
"kind": "const",
|
|
1465
|
+
"description": "`useRegleSchema` enables validation using Standard Schema compatible libraries\nlike Zod, Valibot, or ArkType.",
|
|
1466
|
+
"parameters": [{
|
|
1467
|
+
"name": "params",
|
|
1468
|
+
"type": "[state: MaybeRef<DeepPartial<NoInferLegacy<TState>>> | DeepReactiveState<DeepPartial<NoInferLegacy<TState>>>, rulesFactory: MaybeRef<...>, options: useRegleSchemaFnOptions<...>]",
|
|
1469
|
+
"description": "",
|
|
1470
|
+
"optional": false
|
|
1471
|
+
}],
|
|
1472
|
+
"returnType": "NonNullable<TState> extends PrimitiveTypes ? { r$: Raw<Omit<RegleCommonStatus<PrimitiveTypes & TState & {}, never>, \"$pending\" | ... 3 more ... | \"$originalValue\"> & { ...; } & { ...; } & { ...; }>; }...",
|
|
1473
|
+
"example": "import { useRegleSchema } from '@regle/schemas';\nimport * as v from 'valibot';\n\n// With Valibot\nconst { r$ } = useRegleSchema(\n { name: '', email: '' },\n v.object({\n name: v.pipe(v.string(), v.minLength(3)),\n email: v.pipe(v.string(), v.email())\n })\n);\n\n// With Zod\nimport { z } from 'zod';\n\nconst { r$ } = useRegleSchema(\n { name: '' },\n z.object({\n name: z.string().min(3)\n })\n);\n\n// Access validation state\nr$.$valid // Whether all validations pass\nr$.$value // The current form values\nr$.name.$errors // Errors for the name field",
|
|
1474
|
+
"tags": { "see": "://reglejs.dev/integrations/schemas-libraries Documentation" }
|
|
1475
|
+
},
|
|
1476
|
+
{
|
|
1477
|
+
"name": "withDeps",
|
|
1478
|
+
"kind": "function",
|
|
1479
|
+
"description": "Force a schema validation to re-run when specified dependencies change.\nUseful when your schema depends on reactive values that aren't automatically tracked.",
|
|
1480
|
+
"parameters": [{
|
|
1481
|
+
"name": "schema",
|
|
1482
|
+
"type": "TSchema",
|
|
1483
|
+
"description": "- The Standard Schema to wrap",
|
|
1484
|
+
"optional": false
|
|
1485
|
+
}, {
|
|
1486
|
+
"name": "_depsArray",
|
|
1487
|
+
"type": "[...TParams]",
|
|
1488
|
+
"description": "- Array of reactive dependencies (their values will trigger re-validation)",
|
|
1489
|
+
"optional": false
|
|
1490
|
+
}],
|
|
1491
|
+
"returnType": "TSchema",
|
|
1492
|
+
"example": "import { withDeps, useRegleSchema } from '@regle/schemas';\nimport * as v from 'valibot';\n\nconst compareValue = ref('');\n\nconst schema = computed(() => v.object({\n name: withDeps(\n v.pipe(\n v.string(),\n v.check((value) => value === compareValue.value)\n ),\n [compareValue.value] // Re-validate when this changes\n )\n}));\n\nconst { r$ } = useRegleSchema({ name: '' }, schema);",
|
|
1493
|
+
"tags": { "see": "://reglejs.dev/integrations/schemas-libraries Documentation" }
|
|
1494
|
+
}
|
|
1495
|
+
],
|
|
1496
|
+
"@regle/nuxt": []
|
|
315
1497
|
}
|
|
316
|
-
|
|
1498
|
+
};
|
|
317
1499
|
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
1500
|
+
const rawData = docs_data_default;
|
|
1501
|
+
const data = rawData && typeof rawData === "object" && "docs" in rawData ? rawData : {
|
|
1502
|
+
docs: rawData,
|
|
1503
|
+
api: {}
|
|
1504
|
+
};
|
|
1505
|
+
const docs = data.docs;
|
|
1506
|
+
const api = data.api || {};
|
|
321
1507
|
/**
|
|
322
1508
|
* Get all unique categories from docs
|
|
323
1509
|
*/
|
|
@@ -435,52 +1621,115 @@ function getHelpersFromDocs() {
|
|
|
435
1621
|
}
|
|
436
1622
|
return helpers;
|
|
437
1623
|
}
|
|
1624
|
+
/**
|
|
1625
|
+
* Get all available package names in the API
|
|
1626
|
+
*/
|
|
1627
|
+
function getApiPackages() {
|
|
1628
|
+
return Object.keys(api);
|
|
1629
|
+
}
|
|
1630
|
+
/**
|
|
1631
|
+
* Get API metadata for a specific package
|
|
1632
|
+
*/
|
|
1633
|
+
function getApiByPackage(packageName) {
|
|
1634
|
+
return api[packageName] || [];
|
|
1635
|
+
}
|
|
1636
|
+
/**
|
|
1637
|
+
* Get API metadata for a specific function/export by name
|
|
1638
|
+
*/
|
|
1639
|
+
function getApiByName(name, packageName) {
|
|
1640
|
+
if (packageName) return api[packageName]?.find((a) => a.name === name);
|
|
1641
|
+
for (const apis of Object.values(api)) {
|
|
1642
|
+
const found = apis.find((a) => a.name === name);
|
|
1643
|
+
if (found) return found;
|
|
1644
|
+
}
|
|
1645
|
+
}
|
|
1646
|
+
/**
|
|
1647
|
+
* Search API metadata by query string
|
|
1648
|
+
*/
|
|
1649
|
+
function searchApi(query) {
|
|
1650
|
+
const lowerQuery = query.toLowerCase();
|
|
1651
|
+
const results = [];
|
|
1652
|
+
for (const [packageName, apis] of Object.entries(api)) for (const apiItem of apis) if (apiItem.name.toLowerCase().includes(lowerQuery) || apiItem.description.toLowerCase().includes(lowerQuery)) results.push({
|
|
1653
|
+
...apiItem,
|
|
1654
|
+
package: packageName
|
|
1655
|
+
});
|
|
1656
|
+
return results;
|
|
1657
|
+
}
|
|
1658
|
+
|
|
1659
|
+
var version = "1.14.0";
|
|
438
1660
|
|
|
439
|
-
//#endregion
|
|
440
|
-
//#region src/index.ts
|
|
441
1661
|
const categories = getCategories();
|
|
442
1662
|
const server = new McpServer({
|
|
443
1663
|
name: "regle-mcp-server",
|
|
444
|
-
version
|
|
1664
|
+
version,
|
|
1665
|
+
icons: [{ src: "https://reglejs.dev/logo_main.png" }],
|
|
1666
|
+
title: "Regle MCP Server",
|
|
1667
|
+
websiteUrl: "https://reglejs.dev"
|
|
445
1668
|
});
|
|
446
|
-
server.
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
path: doc.path
|
|
452
|
-
}));
|
|
1669
|
+
server.registerTool("regle-list-docs", {
|
|
1670
|
+
title: "List all available Regle documentation pages",
|
|
1671
|
+
inputSchema: z.object({ category: z.string().optional().describe("Filter by category (e.g., \"rules\", \"core-concepts\", \"introduction\")") })
|
|
1672
|
+
}, async ({ category }) => {
|
|
1673
|
+
const filteredDocs = category ? getDocsByCategory(category) : docs;
|
|
453
1674
|
return { content: [{
|
|
454
1675
|
type: "text",
|
|
455
1676
|
text: JSON.stringify({
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
1677
|
+
message: "Available Regle documentation pages",
|
|
1678
|
+
category: category || "all",
|
|
1679
|
+
count: filteredDocs.length,
|
|
1680
|
+
docs: filteredDocs.map((doc) => ({
|
|
1681
|
+
id: doc.id,
|
|
1682
|
+
title: doc.title
|
|
1683
|
+
}))
|
|
459
1684
|
}, null, 2)
|
|
460
1685
|
}] };
|
|
461
1686
|
});
|
|
462
|
-
server.
|
|
1687
|
+
server.registerTool("regle-get-doc", {
|
|
1688
|
+
title: "Get the full content of a specific Regle documentation page",
|
|
1689
|
+
inputSchema: z.object({ id: z.string().describe("The documentation page ID (e.g., \"core-concepts-rules-built-in-rules\")") })
|
|
1690
|
+
}, async ({ id }) => {
|
|
463
1691
|
const doc = getDocById(id);
|
|
464
|
-
if (!doc)
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
1692
|
+
if (!doc) {
|
|
1693
|
+
const availableIds = docs.map((d) => d.id);
|
|
1694
|
+
return {
|
|
1695
|
+
content: [{
|
|
1696
|
+
type: "text",
|
|
1697
|
+
text: JSON.stringify({
|
|
1698
|
+
error: "Documentation page not found",
|
|
1699
|
+
requestedId: id,
|
|
1700
|
+
availableIds
|
|
1701
|
+
}, null, 2)
|
|
1702
|
+
}],
|
|
1703
|
+
isError: true
|
|
1704
|
+
};
|
|
1705
|
+
}
|
|
471
1706
|
return { content: [{
|
|
472
1707
|
type: "text",
|
|
473
|
-
text:
|
|
1708
|
+
text: JSON.stringify({
|
|
1709
|
+
id: doc.id,
|
|
1710
|
+
title: doc.title,
|
|
1711
|
+
category: doc.category,
|
|
1712
|
+
path: doc.path,
|
|
1713
|
+
content: doc.content
|
|
1714
|
+
}, null, 2)
|
|
474
1715
|
}] };
|
|
475
1716
|
});
|
|
476
|
-
server.
|
|
477
|
-
|
|
478
|
-
|
|
1717
|
+
server.registerTool("regle-search-docs", {
|
|
1718
|
+
title: "Search Regle documentation for specific topics, rules, or concepts",
|
|
1719
|
+
inputSchema: z.object({
|
|
1720
|
+
query: z.string().describe("Search query (e.g., \"required\", \"async validation\", \"useRegle\")"),
|
|
1721
|
+
limit: z.number().optional().default(5).describe("Maximum number of results to return")
|
|
1722
|
+
})
|
|
479
1723
|
}, async ({ query, limit }) => {
|
|
480
1724
|
const results = searchDocs(query).slice(0, limit);
|
|
481
1725
|
if (results.length === 0) return { content: [{
|
|
482
1726
|
type: "text",
|
|
483
|
-
text:
|
|
1727
|
+
text: JSON.stringify({
|
|
1728
|
+
query,
|
|
1729
|
+
resultCount: 0,
|
|
1730
|
+
results: [],
|
|
1731
|
+
suggestions: categories
|
|
1732
|
+
}, null, 2)
|
|
484
1733
|
}] };
|
|
485
1734
|
const formattedResults = results.map((doc) => ({
|
|
486
1735
|
id: doc.id,
|
|
@@ -497,30 +1746,29 @@ server.tool("search-docs", "Search Regle documentation for specific topics, rule
|
|
|
497
1746
|
}, null, 2)
|
|
498
1747
|
}] };
|
|
499
1748
|
});
|
|
500
|
-
server.
|
|
1749
|
+
server.registerTool("regle-get-rules-reference", {
|
|
1750
|
+
title: "Get a quick reference of all built-in validation rules in Regle",
|
|
1751
|
+
inputSchema: z.object({})
|
|
1752
|
+
}, async () => {
|
|
501
1753
|
const rules = getRulesFromDocs();
|
|
502
1754
|
if (rules.length === 0) return {
|
|
503
1755
|
content: [{
|
|
504
1756
|
type: "text",
|
|
505
|
-
text: "Built-in rules documentation not found"
|
|
1757
|
+
text: JSON.stringify({ error: "Built-in rules documentation not found" }, null, 2)
|
|
506
1758
|
}],
|
|
507
1759
|
isError: true
|
|
508
1760
|
};
|
|
509
|
-
const rulesList = rules.map((r) => `- \`${r.name}\`: ${r.description}`).join("\n");
|
|
510
1761
|
return { content: [{
|
|
511
1762
|
type: "text",
|
|
512
|
-
text:
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
\`\`\`typescript
|
|
523
|
-
import { useRegle } from '@regle/core';
|
|
1763
|
+
text: JSON.stringify({
|
|
1764
|
+
title: "Built-in Rules",
|
|
1765
|
+
package: "@regle/rules",
|
|
1766
|
+
count: rules.length,
|
|
1767
|
+
rules: rules.map((r) => ({
|
|
1768
|
+
name: r.name,
|
|
1769
|
+
description: r.description
|
|
1770
|
+
})),
|
|
1771
|
+
usageExample: `import { useRegle } from '@regle/core';
|
|
524
1772
|
import { required, email, minLength } from '@regle/rules';
|
|
525
1773
|
|
|
526
1774
|
const { r$ } = useRegle(
|
|
@@ -529,89 +1777,198 @@ const { r$ } = useRegle(
|
|
|
529
1777
|
email: { required, email },
|
|
530
1778
|
password: { required, minLength: minLength(8) }
|
|
531
1779
|
}
|
|
532
|
-
)
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
Use \`get-doc\` with id \`core-concepts-rules-built-in-rules\` for full documentation.`
|
|
1780
|
+
);`,
|
|
1781
|
+
fullDocId: "core-concepts-rules-built-in-rules"
|
|
1782
|
+
}, null, 2)
|
|
536
1783
|
}] };
|
|
537
1784
|
});
|
|
538
|
-
server.
|
|
1785
|
+
server.registerTool("regle-get-validation-properties", {
|
|
1786
|
+
title: "Get documentation on all validation properties available on r$ and field objects",
|
|
1787
|
+
inputSchema: z.object({})
|
|
1788
|
+
}, async () => {
|
|
539
1789
|
const doc = getDocById("core-concepts-validation-properties");
|
|
540
1790
|
if (!doc) return {
|
|
541
1791
|
content: [{
|
|
542
1792
|
type: "text",
|
|
543
|
-
text: "Validation properties documentation not found"
|
|
1793
|
+
text: JSON.stringify({ error: "Validation properties documentation not found" }, null, 2)
|
|
544
1794
|
}],
|
|
545
1795
|
isError: true
|
|
546
1796
|
};
|
|
547
1797
|
return { content: [{
|
|
548
1798
|
type: "text",
|
|
549
|
-
text:
|
|
1799
|
+
text: JSON.stringify({
|
|
1800
|
+
id: doc.id,
|
|
1801
|
+
title: doc.title,
|
|
1802
|
+
category: doc.category,
|
|
1803
|
+
content: doc.content
|
|
1804
|
+
}, null, 2)
|
|
550
1805
|
}] };
|
|
551
1806
|
});
|
|
552
|
-
server.
|
|
1807
|
+
server.registerTool("regle-get-helpers-reference", {
|
|
1808
|
+
title: "Get a reference of all validation helper utilities available in Regle",
|
|
1809
|
+
inputSchema: z.object({})
|
|
1810
|
+
}, async () => {
|
|
553
1811
|
const helpers = getHelpersFromDocs();
|
|
554
1812
|
if (helpers.length === 0) return {
|
|
555
1813
|
content: [{
|
|
556
1814
|
type: "text",
|
|
557
|
-
text: "Validation helpers documentation not found"
|
|
1815
|
+
text: JSON.stringify({ error: "Validation helpers documentation not found" }, null, 2)
|
|
558
1816
|
}],
|
|
559
1817
|
isError: true
|
|
560
1818
|
};
|
|
561
1819
|
const guards = helpers.filter((h) => h.category === "guard");
|
|
562
1820
|
const operations = helpers.filter((h) => h.category === "operation");
|
|
563
1821
|
const coerces = helpers.filter((h) => h.category === "coerce");
|
|
564
|
-
const formatHelpers = (
|
|
1822
|
+
const formatHelpers = (list) => list.map((h) => ({
|
|
1823
|
+
name: h.name,
|
|
1824
|
+
description: h.description
|
|
1825
|
+
}));
|
|
565
1826
|
return { content: [{
|
|
566
1827
|
type: "text",
|
|
567
|
-
text:
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
import { createRule } from '@regle/core';
|
|
1828
|
+
text: JSON.stringify({
|
|
1829
|
+
title: "Validation Helpers",
|
|
1830
|
+
package: "@regle/rules",
|
|
1831
|
+
totalCount: helpers.length,
|
|
1832
|
+
categories: {
|
|
1833
|
+
guards: {
|
|
1834
|
+
description: "Runtime and Type Guards",
|
|
1835
|
+
helpers: formatHelpers(guards)
|
|
1836
|
+
},
|
|
1837
|
+
operations: {
|
|
1838
|
+
description: "Operations Utils",
|
|
1839
|
+
helpers: formatHelpers(operations)
|
|
1840
|
+
},
|
|
1841
|
+
coerces: {
|
|
1842
|
+
description: "Coerce Utils",
|
|
1843
|
+
helpers: formatHelpers(coerces)
|
|
1844
|
+
}
|
|
1845
|
+
},
|
|
1846
|
+
usageExample: `import { createRule, type Maybe } from '@regle/core';
|
|
587
1847
|
import { isFilled, getSize } from '@regle/rules';
|
|
588
1848
|
|
|
589
1849
|
const rule = createRule({
|
|
590
|
-
validator(value:
|
|
1850
|
+
validator(value: Maybe<string>) {
|
|
591
1851
|
if (isFilled(value)) {
|
|
592
1852
|
return getSize(value) > 6;
|
|
593
1853
|
}
|
|
594
1854
|
return true;
|
|
595
1855
|
},
|
|
596
1856
|
message: 'Error'
|
|
597
|
-
})
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
Use \`get-doc\` with id \`core-concepts-rules-validations-helpers\` for full documentation.`
|
|
1857
|
+
});`,
|
|
1858
|
+
fullDocId: "core-concepts-rules-validations-helpers"
|
|
1859
|
+
}, null, 2)
|
|
601
1860
|
}] };
|
|
602
1861
|
});
|
|
603
|
-
server.
|
|
1862
|
+
server.registerTool("regle-get-useregle-guide", {
|
|
1863
|
+
title: "Get a comprehensive guide on how to use the useRegle composable",
|
|
1864
|
+
inputSchema: z.object({})
|
|
1865
|
+
}, async () => {
|
|
604
1866
|
const doc = getDocById("core-concepts-index");
|
|
605
1867
|
if (!doc) return {
|
|
606
1868
|
content: [{
|
|
607
1869
|
type: "text",
|
|
608
|
-
text: "useRegle guide not found"
|
|
1870
|
+
text: JSON.stringify({ error: "useRegle guide not found" }, null, 2)
|
|
609
1871
|
}],
|
|
610
1872
|
isError: true
|
|
611
1873
|
};
|
|
612
1874
|
return { content: [{
|
|
613
1875
|
type: "text",
|
|
614
|
-
text:
|
|
1876
|
+
text: JSON.stringify({
|
|
1877
|
+
id: doc.id,
|
|
1878
|
+
title: doc.title,
|
|
1879
|
+
category: doc.category,
|
|
1880
|
+
content: doc.content
|
|
1881
|
+
}, null, 2)
|
|
1882
|
+
}] };
|
|
1883
|
+
});
|
|
1884
|
+
const apiPackages = getApiPackages();
|
|
1885
|
+
server.registerTool("regle-get-api-reference", {
|
|
1886
|
+
title: "Get API reference for Regle packages with full metadata (parameters, return types, examples)",
|
|
1887
|
+
inputSchema: z.object({
|
|
1888
|
+
package: z.string().optional().describe("Package name (e.g., \"@regle/core\", \"@regle/rules\", \"@regle/schemas\", \"@regle/nuxt\")"),
|
|
1889
|
+
name: z.string().optional().describe("Specific function/export name to look up (e.g., \"useRegle\", \"required\")"),
|
|
1890
|
+
search: z.string().optional().describe("Search query to find exports by name or description")
|
|
1891
|
+
})
|
|
1892
|
+
}, async ({ package: packageName, name, search }) => {
|
|
1893
|
+
if (name) {
|
|
1894
|
+
const apiItem = getApiByName(name, packageName);
|
|
1895
|
+
if (!apiItem) return {
|
|
1896
|
+
content: [{
|
|
1897
|
+
type: "text",
|
|
1898
|
+
text: JSON.stringify({
|
|
1899
|
+
error: `API export "${name}" not found`,
|
|
1900
|
+
availablePackages: apiPackages
|
|
1901
|
+
}, null, 2)
|
|
1902
|
+
}],
|
|
1903
|
+
isError: true
|
|
1904
|
+
};
|
|
1905
|
+
return { content: [{
|
|
1906
|
+
type: "text",
|
|
1907
|
+
text: JSON.stringify({
|
|
1908
|
+
name: apiItem.name,
|
|
1909
|
+
kind: apiItem.kind,
|
|
1910
|
+
description: apiItem.description,
|
|
1911
|
+
parameters: apiItem.parameters,
|
|
1912
|
+
returnType: apiItem.returnType,
|
|
1913
|
+
example: apiItem.example,
|
|
1914
|
+
tags: apiItem.tags
|
|
1915
|
+
}, null, 2)
|
|
1916
|
+
}] };
|
|
1917
|
+
}
|
|
1918
|
+
if (search) {
|
|
1919
|
+
const results = searchApi(search);
|
|
1920
|
+
return { content: [{
|
|
1921
|
+
type: "text",
|
|
1922
|
+
text: JSON.stringify({
|
|
1923
|
+
query: search,
|
|
1924
|
+
resultCount: results.length,
|
|
1925
|
+
results: results.map((r) => ({
|
|
1926
|
+
name: r.name,
|
|
1927
|
+
package: r.package,
|
|
1928
|
+
kind: r.kind,
|
|
1929
|
+
description: r.description.substring(0, 200) + (r.description.length > 200 ? "..." : "")
|
|
1930
|
+
}))
|
|
1931
|
+
}, null, 2)
|
|
1932
|
+
}] };
|
|
1933
|
+
}
|
|
1934
|
+
if (packageName) {
|
|
1935
|
+
const apis = getApiByPackage(packageName);
|
|
1936
|
+
if (apis.length === 0) return {
|
|
1937
|
+
content: [{
|
|
1938
|
+
type: "text",
|
|
1939
|
+
text: JSON.stringify({
|
|
1940
|
+
error: `Package "${packageName}" not found or has no exports`,
|
|
1941
|
+
availablePackages: apiPackages
|
|
1942
|
+
}, null, 2)
|
|
1943
|
+
}],
|
|
1944
|
+
isError: true
|
|
1945
|
+
};
|
|
1946
|
+
return { content: [{
|
|
1947
|
+
type: "text",
|
|
1948
|
+
text: JSON.stringify({
|
|
1949
|
+
package: packageName,
|
|
1950
|
+
exportCount: apis.length,
|
|
1951
|
+
exports: apis.map((a) => ({
|
|
1952
|
+
name: a.name,
|
|
1953
|
+
kind: a.kind,
|
|
1954
|
+
description: a.description.substring(0, 150) + (a.description.length > 150 ? "..." : ""),
|
|
1955
|
+
hasExample: !!a.example,
|
|
1956
|
+
parameterCount: a.parameters.length
|
|
1957
|
+
}))
|
|
1958
|
+
}, null, 2)
|
|
1959
|
+
}] };
|
|
1960
|
+
}
|
|
1961
|
+
const packageSummary = apiPackages.map((pkg) => ({
|
|
1962
|
+
package: pkg,
|
|
1963
|
+
exportCount: getApiByPackage(pkg).length
|
|
1964
|
+
}));
|
|
1965
|
+
return { content: [{
|
|
1966
|
+
type: "text",
|
|
1967
|
+
text: JSON.stringify({
|
|
1968
|
+
message: "Available Regle API packages",
|
|
1969
|
+
packages: packageSummary,
|
|
1970
|
+
usage: "Use \"package\" to list exports, \"name\" to get specific export details, or \"search\" to find exports"
|
|
1971
|
+
}, null, 2)
|
|
615
1972
|
}] };
|
|
616
1973
|
});
|
|
617
1974
|
async function main() {
|
|
@@ -624,5 +1981,4 @@ main().catch((error) => {
|
|
|
624
1981
|
process.exit(1);
|
|
625
1982
|
});
|
|
626
1983
|
|
|
627
|
-
//#endregion
|
|
628
1984
|
export { };
|