@regle/core 1.13.1 → 1.14.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/regle-core.d.ts +333 -80
- package/dist/regle-core.js +359 -85
- package/dist/regle-core.min.js +1 -1
- package/package.json +1 -1
package/dist/regle-core.js
CHANGED
|
@@ -9,12 +9,34 @@ function isFile(value) {
|
|
|
9
9
|
}
|
|
10
10
|
|
|
11
11
|
/**
|
|
12
|
-
*
|
|
12
|
+
* Checks if a value is empty in any way (including arrays and objects).
|
|
13
|
+
* This is the inverse of `isFilled`.
|
|
13
14
|
*
|
|
14
|
-
* isEmpty also acts as a type guard.
|
|
15
|
+
* `isEmpty` also acts as a type guard.
|
|
15
16
|
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
17
|
+
* By default, it considers an empty array as `true`. You can override this behavior with `considerEmptyArrayInvalid`.
|
|
18
|
+
*
|
|
19
|
+
* @param value - The target value to check
|
|
20
|
+
* @param considerEmptyArrayInvalid - When `false`, empty arrays are not considered empty (default: `true`)
|
|
21
|
+
* @returns `true` if the value is empty, `false` otherwise
|
|
22
|
+
*
|
|
23
|
+
* @example
|
|
24
|
+
* ```ts
|
|
25
|
+
* import { createRule, type Maybe } from '@regle/core';
|
|
26
|
+
* import { isEmpty } from '@regle/rules';
|
|
27
|
+
*
|
|
28
|
+
* const rule = createRule({
|
|
29
|
+
* validator(value: Maybe<string>) {
|
|
30
|
+
* if (isEmpty(value)) {
|
|
31
|
+
* return true;
|
|
32
|
+
* }
|
|
33
|
+
* return check(value);
|
|
34
|
+
* },
|
|
35
|
+
* message: 'Error'
|
|
36
|
+
* })
|
|
37
|
+
* ```
|
|
38
|
+
*
|
|
39
|
+
* @see {@link https://reglejs.dev/core-concepts/rules/validations-helpers#isempty Documentation}
|
|
18
40
|
*/
|
|
19
41
|
function isEmpty(value, considerEmptyArrayInvalid = true) {
|
|
20
42
|
if (value === void 0 || value === null) return true;
|
|
@@ -153,7 +175,21 @@ function isConstructor(value) {
|
|
|
153
175
|
}
|
|
154
176
|
|
|
155
177
|
/**
|
|
156
|
-
*
|
|
178
|
+
* Coerces any string, number, or Date value into a `Date` using the `Date` constructor.
|
|
179
|
+
*
|
|
180
|
+
* @param argument - The value to convert to a Date
|
|
181
|
+
* @returns A new Date object (may be invalid if input cannot be parsed)
|
|
182
|
+
*
|
|
183
|
+
* @example
|
|
184
|
+
* ```ts
|
|
185
|
+
* import { toDate } from '@regle/rules';
|
|
186
|
+
*
|
|
187
|
+
* const date1 = toDate('2024-01-15'); // Date object
|
|
188
|
+
* const date2 = toDate(1705276800000); // Date from timestamp
|
|
189
|
+
* const date3 = toDate(new Date()); // Clone of Date
|
|
190
|
+
* ```
|
|
191
|
+
*
|
|
192
|
+
* @see {@link https://reglejs.dev/core-concepts/rules/validations-helpers#todate Documentation}
|
|
157
193
|
*/
|
|
158
194
|
function toDate(argument) {
|
|
159
195
|
const argStr = Object.prototype.toString.call(argument);
|
|
@@ -370,35 +406,57 @@ function defineRuleProcessors(definition, ...params) {
|
|
|
370
406
|
}
|
|
371
407
|
|
|
372
408
|
/**
|
|
373
|
-
* Create a typed custom rule that can be used like
|
|
374
|
-
*
|
|
409
|
+
* Create a typed custom rule that can be used like built-in rules.
|
|
410
|
+
* The created rule can be declared in global options or used directly.
|
|
375
411
|
*
|
|
376
|
-
*
|
|
412
|
+
* Features:
|
|
413
|
+
* - Automatically detects if the rule is async
|
|
414
|
+
* - Supports parameters for configurable rules
|
|
415
|
+
* - Full TypeScript type inference
|
|
416
|
+
* - Custom metadata support
|
|
377
417
|
*
|
|
418
|
+
* @param definition - The rule definition object containing:
|
|
419
|
+
* - `validator`: The validation function
|
|
420
|
+
* - `message`: Error message (string or function)
|
|
421
|
+
* - `type`: Optional rule type identifier
|
|
422
|
+
* - `active`: Optional function to conditionally activate the rule
|
|
423
|
+
* - `tooltip`: Optional tooltip message
|
|
378
424
|
*
|
|
379
|
-
* @
|
|
380
|
-
*
|
|
381
|
-
* @returns A rule definition that can be callable depending on params presence
|
|
382
|
-
*
|
|
383
|
-
* @exemple
|
|
425
|
+
* @returns A rule definition that is callable if it accepts parameters
|
|
384
426
|
*
|
|
427
|
+
* @example
|
|
385
428
|
* ```ts
|
|
386
|
-
*
|
|
387
|
-
* import {isFilled} from '@regle/rules';
|
|
429
|
+
* import { createRule } from '@regle/core';
|
|
430
|
+
* import { isFilled } from '@regle/rules';
|
|
388
431
|
*
|
|
432
|
+
* // Simple rule without params
|
|
389
433
|
* export const isFoo = createRule({
|
|
390
434
|
* validator(value: Maybe<string>) {
|
|
391
|
-
*
|
|
392
|
-
*
|
|
393
|
-
*
|
|
394
|
-
*
|
|
435
|
+
* if (isFilled(value)) {
|
|
436
|
+
* return value === 'foo';
|
|
437
|
+
* }
|
|
438
|
+
* return true;
|
|
395
439
|
* },
|
|
396
440
|
* message: "The value should be 'foo'"
|
|
397
|
-
* })
|
|
441
|
+
* });
|
|
442
|
+
*
|
|
443
|
+
* // Rule with parameters
|
|
444
|
+
* export const minCustom = createRule({
|
|
445
|
+
* validator(value: Maybe<number>, min: number) {
|
|
446
|
+
* if (isFilled(value)) {
|
|
447
|
+
* return value >= min;
|
|
448
|
+
* }
|
|
449
|
+
* return true;
|
|
450
|
+
* },
|
|
451
|
+
* message: ({ $params: [min] }) => `Value must be at least ${min}`
|
|
452
|
+
* });
|
|
398
453
|
*
|
|
454
|
+
* // Usage
|
|
455
|
+
* useRegle({ name: '' }, { name: { isFoo } });
|
|
456
|
+
* useRegle({ count: 0 }, { count: { minCustom: minCustom(5) } });
|
|
399
457
|
* ```
|
|
400
458
|
*
|
|
401
|
-
*
|
|
459
|
+
* @see {@link https://reglejs.dev/core-concepts/rules/reusable-rules Documentation}
|
|
402
460
|
*/
|
|
403
461
|
function createRule(definition) {
|
|
404
462
|
if (typeof definition.validator === "function") {
|
|
@@ -2984,7 +3042,7 @@ function filterInspectorTree(nodes, filter) {
|
|
|
2984
3042
|
return filtered;
|
|
2985
3043
|
}
|
|
2986
3044
|
|
|
2987
|
-
var version$1 = "1.
|
|
3045
|
+
var version$1 = "1.14.0-beta.1";
|
|
2988
3046
|
|
|
2989
3047
|
function createDevtools(app) {
|
|
2990
3048
|
setupDevtoolsPlugin({
|
|
@@ -3187,23 +3245,37 @@ function createUseRegleComposable(customRules, options, shortcuts) {
|
|
|
3187
3245
|
return useRegle$1;
|
|
3188
3246
|
}
|
|
3189
3247
|
/**
|
|
3190
|
-
* useRegle serves as the foundation for validation logic.
|
|
3248
|
+
* `useRegle` serves as the foundation for validation logic.
|
|
3249
|
+
* It transforms your data and validation rules into a powerful, reactive validation system.
|
|
3191
3250
|
*
|
|
3192
|
-
*
|
|
3251
|
+
* @param state - Your form data (plain object, ref, reactive object, or structure with nested refs)
|
|
3252
|
+
* @param rules - Validation rules that should align with the structure of your state
|
|
3253
|
+
* @param modifiers - Optional configuration to customize regle behavior
|
|
3254
|
+
* @returns An object containing `r$` - the reactive validation state
|
|
3193
3255
|
*
|
|
3194
|
-
* @
|
|
3195
|
-
* @param rules - These should align with the structure of your state.
|
|
3196
|
-
* @param modifiers - Customize regle behaviour
|
|
3197
|
-
*
|
|
3256
|
+
* @example
|
|
3198
3257
|
* ```ts
|
|
3199
3258
|
* import { useRegle } from '@regle/core';
|
|
3200
|
-
import { required } from '@regle/rules';
|
|
3201
|
-
|
|
3202
|
-
const { r$ } = useRegle(
|
|
3203
|
-
email:
|
|
3204
|
-
|
|
3259
|
+
* import { required, email, minLength } from '@regle/rules';
|
|
3260
|
+
*
|
|
3261
|
+
* const { r$ } = useRegle(
|
|
3262
|
+
* { name: '', email: '' },
|
|
3263
|
+
* {
|
|
3264
|
+
* name: { required, minLength: minLength(2) },
|
|
3265
|
+
* email: { required, email }
|
|
3266
|
+
* }
|
|
3267
|
+
* );
|
|
3268
|
+
*
|
|
3269
|
+
* // Access validation state
|
|
3270
|
+
* r$.$valid // Whether all validations pass
|
|
3271
|
+
* r$.$value // The current form values
|
|
3272
|
+
* r$.name.$errors // Errors for the name field
|
|
3273
|
+
*
|
|
3274
|
+
* // Trigger validation
|
|
3275
|
+
* const result = await r$.$validate();
|
|
3205
3276
|
* ```
|
|
3206
|
-
*
|
|
3277
|
+
*
|
|
3278
|
+
* @see {@link https://reglejs.dev/core-concepts/ Documentation}
|
|
3207
3279
|
*/
|
|
3208
3280
|
const useRegle = createUseRegleComposable();
|
|
3209
3281
|
|
|
@@ -3214,11 +3286,31 @@ function createInferRuleHelper() {
|
|
|
3214
3286
|
return inferRules$1;
|
|
3215
3287
|
}
|
|
3216
3288
|
/**
|
|
3217
|
-
*
|
|
3218
|
-
* It
|
|
3289
|
+
* Type helper to provide autocomplete and type-checking for your form rules.
|
|
3290
|
+
* It returns the rules without any processing - useful with computed rules.
|
|
3219
3291
|
*
|
|
3220
3292
|
* @param state - The state reference
|
|
3221
3293
|
* @param rules - Your rule tree
|
|
3294
|
+
* @returns The rules object (passthrough)
|
|
3295
|
+
*
|
|
3296
|
+
* @example
|
|
3297
|
+
* ```ts
|
|
3298
|
+
* import { inferRules, useRegle } from '@regle/core';
|
|
3299
|
+
* import { required, minLength } from '@regle/rules';
|
|
3300
|
+
*
|
|
3301
|
+
* const state = ref({ name: '' });
|
|
3302
|
+
*
|
|
3303
|
+
* // inferRules preserves TypeScript autocompletion
|
|
3304
|
+
* const rules = computed(() => {
|
|
3305
|
+
* return inferRules(state, {
|
|
3306
|
+
* name: { required, minLength: minLength(2) }
|
|
3307
|
+
* })
|
|
3308
|
+
* });
|
|
3309
|
+
*
|
|
3310
|
+
* const { r$ } = useRegle(state, rules);
|
|
3311
|
+
* ```
|
|
3312
|
+
*
|
|
3313
|
+
* @see {@link https://reglejs.dev/core-concepts/#dynamic-rules-object Documentation}
|
|
3222
3314
|
*/
|
|
3223
3315
|
const inferRules = createInferRuleHelper();
|
|
3224
3316
|
|
|
@@ -3255,21 +3347,34 @@ function createUseRulesComposable(customRules, options, shortcuts) {
|
|
|
3255
3347
|
return useRules$1;
|
|
3256
3348
|
}
|
|
3257
3349
|
/**
|
|
3258
|
-
* useRules is a
|
|
3350
|
+
* `useRules` is a variant of `useRegle` that doesn't require you to provide initial state.
|
|
3351
|
+
* It creates an empty state based on your rules structure and implements the Standard Schema spec.
|
|
3259
3352
|
*
|
|
3260
|
-
*
|
|
3353
|
+
* This is useful when you want to define validation rules first and infer the state type from them.
|
|
3261
3354
|
*
|
|
3262
|
-
* @param rules - Your rules object
|
|
3263
|
-
* @param modifiers -
|
|
3264
|
-
*
|
|
3355
|
+
* @param rules - Your validation rules object
|
|
3356
|
+
* @param modifiers - Optional configuration to customize regle behavior
|
|
3357
|
+
* @returns The reactive validation state (implements StandardSchemaV1)
|
|
3358
|
+
*
|
|
3359
|
+
* @example
|
|
3265
3360
|
* ```ts
|
|
3266
|
-
* import { useRules } from '@regle/core';
|
|
3267
|
-
import { required } from '@regle/rules';
|
|
3268
|
-
|
|
3269
|
-
const
|
|
3270
|
-
|
|
3271
|
-
}
|
|
3361
|
+
* import { useRules, type InferInput } from '@regle/core';
|
|
3362
|
+
* import { required, string, email } from '@regle/rules';
|
|
3363
|
+
*
|
|
3364
|
+
* const r$ = useRules({
|
|
3365
|
+
* name: { required, string },
|
|
3366
|
+
* email: { required, email }
|
|
3367
|
+
* });
|
|
3368
|
+
*
|
|
3369
|
+
* // State is automatically created and typed
|
|
3370
|
+
* r$.$value.name // string | null
|
|
3371
|
+
* r$.$value.email // string | null
|
|
3372
|
+
*
|
|
3373
|
+
* // Can be used with Standard Schema compatible libraries
|
|
3374
|
+
* const result = await r$['~standard'].validate({ name: '', email: '' });
|
|
3272
3375
|
* ```
|
|
3376
|
+
*
|
|
3377
|
+
* @see {@link https://reglejs.dev/common-usage/standard-schema#userules Documentation}
|
|
3273
3378
|
*/
|
|
3274
3379
|
const useRules = createUseRulesComposable();
|
|
3275
3380
|
|
|
@@ -3303,16 +3408,43 @@ function applyMarkStatic(value) {
|
|
|
3303
3408
|
}
|
|
3304
3409
|
|
|
3305
3410
|
/**
|
|
3306
|
-
* Define a global
|
|
3411
|
+
* Define a global Regle configuration to customize the validation behavior across your application.
|
|
3412
|
+
*
|
|
3413
|
+
* Features:
|
|
3307
3414
|
* - Customize built-in rules messages
|
|
3308
|
-
* - Add your custom rules
|
|
3309
|
-
* - Define global modifiers
|
|
3310
|
-
* - Define shortcuts
|
|
3415
|
+
* - Add your custom rules with full type inference
|
|
3416
|
+
* - Define global modifiers (lazy, rewardEarly, etc.)
|
|
3417
|
+
* - Define shortcuts for common validation patterns
|
|
3418
|
+
*
|
|
3419
|
+
* @param options - Configuration options
|
|
3420
|
+
* @param options.rules - Factory function returning custom rules
|
|
3421
|
+
* @param options.modifiers - Global behavior modifiers
|
|
3422
|
+
* @param options.shortcuts - Reusable validation shortcuts
|
|
3423
|
+
* @returns Object containing typed `useRegle`, `inferRules`, and `useRules` functions
|
|
3424
|
+
*
|
|
3425
|
+
* @example
|
|
3426
|
+
* ```ts
|
|
3427
|
+
* import { defineRegleConfig } from '@regle/core';
|
|
3428
|
+
* import { required, withMessage } from '@regle/rules';
|
|
3311
3429
|
*
|
|
3312
|
-
*
|
|
3430
|
+
* export const { useRegle, inferRules, useRules } = defineRegleConfig({
|
|
3431
|
+
* rules: () => ({
|
|
3432
|
+
* // Override default required message
|
|
3433
|
+
* required: withMessage(required, 'This field cannot be empty'),
|
|
3434
|
+
* // Add custom rule
|
|
3435
|
+
* myCustomRule: createRule({
|
|
3436
|
+
* validator: (value) => value === 'valid',
|
|
3437
|
+
* message: 'Invalid value'
|
|
3438
|
+
* })
|
|
3439
|
+
* }),
|
|
3440
|
+
* modifiers: {
|
|
3441
|
+
* lazy: true,
|
|
3442
|
+
* rewardEarly: true
|
|
3443
|
+
* }
|
|
3444
|
+
* });
|
|
3445
|
+
* ```
|
|
3313
3446
|
*
|
|
3314
|
-
*
|
|
3315
|
-
* - an `inferRules` helper that can typecheck your custom rules
|
|
3447
|
+
* @see {@link https://reglejs.dev/advanced-usage/global-config Documentation}
|
|
3316
3448
|
*/
|
|
3317
3449
|
function defineRegleConfig({ rules, modifiers, shortcuts }) {
|
|
3318
3450
|
const useRegle$1 = createUseRegleComposable(rules, modifiers, shortcuts);
|
|
@@ -3334,12 +3466,31 @@ function defineRegleConfig({ rules, modifiers, shortcuts }) {
|
|
|
3334
3466
|
};
|
|
3335
3467
|
}
|
|
3336
3468
|
/**
|
|
3337
|
-
* Extend an already created custom `useRegle`
|
|
3469
|
+
* Extend an already created custom `useRegle` configuration with additional rules, modifiers, or shortcuts.
|
|
3338
3470
|
*
|
|
3339
|
-
*
|
|
3471
|
+
* @param regle - The existing useRegle function to extend
|
|
3472
|
+
* @param options - Additional configuration to merge
|
|
3473
|
+
* @param options.rules - Additional custom rules
|
|
3474
|
+
* @param options.modifiers - Additional modifiers to merge
|
|
3475
|
+
* @param options.shortcuts - Additional shortcuts to merge
|
|
3476
|
+
* @returns Object containing the extended `useRegle` and `inferRules` functions
|
|
3340
3477
|
*
|
|
3341
|
-
*
|
|
3342
|
-
*
|
|
3478
|
+
* @example
|
|
3479
|
+
* ```ts
|
|
3480
|
+
* import { extendRegleConfig } from '@regle/core';
|
|
3481
|
+
* import { baseUseRegle } from './base-config';
|
|
3482
|
+
*
|
|
3483
|
+
* export const { useRegle, inferRules } = extendRegleConfig(baseUseRegle, {
|
|
3484
|
+
* rules: () => ({
|
|
3485
|
+
* additionalRule: myNewRule
|
|
3486
|
+
* }),
|
|
3487
|
+
* modifiers: {
|
|
3488
|
+
* rewardEarly: true
|
|
3489
|
+
* }
|
|
3490
|
+
* });
|
|
3491
|
+
* ```
|
|
3492
|
+
*
|
|
3493
|
+
* @see {@link https://reglejs.dev/advanced-usage/global-config Documentation}
|
|
3343
3494
|
*/
|
|
3344
3495
|
function extendRegleConfig(regle, { rules, modifiers, shortcuts }) {
|
|
3345
3496
|
const rootConfig = regle.__config ?? {};
|
|
@@ -3361,6 +3512,44 @@ function extendRegleConfig(regle, { rules, modifiers, shortcuts }) {
|
|
|
3361
3512
|
};
|
|
3362
3513
|
}
|
|
3363
3514
|
|
|
3515
|
+
/**
|
|
3516
|
+
* Merge multiple Regle instances into a single validation state.
|
|
3517
|
+
* Useful for combining multiple forms or validation scopes.
|
|
3518
|
+
*
|
|
3519
|
+
* @param regles - An object containing named Regle instances to merge
|
|
3520
|
+
* @param _scoped - Internal flag for scoped validation (default: false)
|
|
3521
|
+
* @returns A merged validation state with all instances' properties combined
|
|
3522
|
+
*
|
|
3523
|
+
* @example
|
|
3524
|
+
* ```ts
|
|
3525
|
+
* import { useRegle, mergeRegles } from '@regle/core';
|
|
3526
|
+
* import { required } from '@regle/rules';
|
|
3527
|
+
*
|
|
3528
|
+
* // Create separate validation instances
|
|
3529
|
+
* const { r$: personalInfo } = useRegle(
|
|
3530
|
+
* { name: '', email: '' },
|
|
3531
|
+
* { name: { required }, email: { required } }
|
|
3532
|
+
* );
|
|
3533
|
+
*
|
|
3534
|
+
* const { r$: address } = useRegle(
|
|
3535
|
+
* { street: '', city: '' },
|
|
3536
|
+
* { street: { required }, city: { required } }
|
|
3537
|
+
* );
|
|
3538
|
+
*
|
|
3539
|
+
* // Merge them together
|
|
3540
|
+
* const merged$ = mergeRegles({
|
|
3541
|
+
* personalInfo,
|
|
3542
|
+
* address
|
|
3543
|
+
* });
|
|
3544
|
+
*
|
|
3545
|
+
* // Access combined state
|
|
3546
|
+
* merged$.$valid // true when ALL forms are valid
|
|
3547
|
+
* merged$.$errors // { personalInfo: {...}, address: {...} }
|
|
3548
|
+
* await merged$.$validate() // Validates all forms
|
|
3549
|
+
* ```
|
|
3550
|
+
*
|
|
3551
|
+
* @see {@link https://reglejs.dev/advanced-usage/merge-regles Documentation}
|
|
3552
|
+
*/
|
|
3364
3553
|
function mergeRegles(regles, _scoped) {
|
|
3365
3554
|
const scoped = _scoped == null ? false : _scoped;
|
|
3366
3555
|
const $value = computed({
|
|
@@ -3628,6 +3817,35 @@ function createUseScopedRegleComposable(instances, customUseRegle) {
|
|
|
3628
3817
|
return { useScopedRegle: useScopedRegle$1 };
|
|
3629
3818
|
}
|
|
3630
3819
|
|
|
3820
|
+
/**
|
|
3821
|
+
* Create a scoped validation system for collecting and validating multiple form instances.
|
|
3822
|
+
* Useful for dynamic forms, multi-step wizards, or component-based form architectures.
|
|
3823
|
+
*
|
|
3824
|
+
* @param options - Configuration options
|
|
3825
|
+
* @param options.customUseRegle - Custom useRegle instance with your global config
|
|
3826
|
+
* @param options.customStore - External ref to store collected instances
|
|
3827
|
+
* @param options.asRecord - If true, collect instances in a Record (requires `id` param in useScopedRegle)
|
|
3828
|
+
* @returns Object containing `useScopedRegle` and `useCollectScope` functions
|
|
3829
|
+
*
|
|
3830
|
+
* @example
|
|
3831
|
+
* ```ts
|
|
3832
|
+
* // scoped-config.ts
|
|
3833
|
+
* import { createScopedUseRegle } from '@regle/core';
|
|
3834
|
+
*
|
|
3835
|
+
* export const { useScopedRegle, useCollectScope } = createScopedUseRegle();
|
|
3836
|
+
*
|
|
3837
|
+
* // ChildComponent.vue
|
|
3838
|
+
* const { r$ } = useScopedRegle(state, rules, {
|
|
3839
|
+
* namespace: 'myForm'
|
|
3840
|
+
* });
|
|
3841
|
+
*
|
|
3842
|
+
* // ParentComponent.vue
|
|
3843
|
+
* const { r$: collectedR$ } = useCollectScope('myForm');
|
|
3844
|
+
* await collectedR$.$validate(); // Validates all child forms
|
|
3845
|
+
* ```
|
|
3846
|
+
*
|
|
3847
|
+
* @see {@link https://reglejs.dev/advanced-usage/scoped-validation Documentation}
|
|
3848
|
+
*/
|
|
3631
3849
|
function createScopedUseRegle(options) {
|
|
3632
3850
|
const instances = (options?.customStore ? () => {
|
|
3633
3851
|
if (options.customStore) {
|
|
@@ -3648,24 +3866,42 @@ function createScopedUseRegle(options) {
|
|
|
3648
3866
|
const { useCollectScope, useScopedRegle } = createScopedUseRegle();
|
|
3649
3867
|
|
|
3650
3868
|
/**
|
|
3651
|
-
*
|
|
3869
|
+
* Create variant-based validation rules that depend on a discriminant field value.
|
|
3870
|
+
* Useful for union types where different fields are required based on a type discriminant.
|
|
3871
|
+
*
|
|
3872
|
+
* Note: Autocomplete may not fully work due to TypeScript limitations.
|
|
3652
3873
|
*
|
|
3653
|
-
*
|
|
3874
|
+
* @param root - The reactive state object
|
|
3875
|
+
* @param discriminantKey - The key used to discriminate between variants
|
|
3876
|
+
* @param variants - Array of variant rule definitions using `literal` for type matching
|
|
3877
|
+
* @returns A computed ref containing the currently active variant rules
|
|
3654
3878
|
*
|
|
3879
|
+
* @example
|
|
3655
3880
|
* ```ts
|
|
3656
|
-
*
|
|
3657
|
-
*
|
|
3658
|
-
*
|
|
3659
|
-
*
|
|
3660
|
-
*
|
|
3661
|
-
*
|
|
3662
|
-
*
|
|
3663
|
-
*
|
|
3664
|
-
*
|
|
3665
|
-
*
|
|
3666
|
-
*
|
|
3667
|
-
*
|
|
3881
|
+
* import { useRegle, createVariant } from '@regle/core';
|
|
3882
|
+
* import { required, email, literal } from '@regle/rules';
|
|
3883
|
+
*
|
|
3884
|
+
* const state = ref({
|
|
3885
|
+
* type: 'EMAIL' as 'EMAIL' | 'GITHUB',
|
|
3886
|
+
* email: '',
|
|
3887
|
+
* username: ''
|
|
3888
|
+
* });
|
|
3889
|
+
*
|
|
3890
|
+
* // ⚠️ Use getter syntax for your rules
|
|
3891
|
+
* const { r$ } = useRegle(state, () => {
|
|
3892
|
+
* const variant = createVariant(state, 'type', [
|
|
3893
|
+
* { type: { literal: literal('EMAIL') }, email: { required, email } },
|
|
3894
|
+
* { type: { literal: literal('GITHUB') }, username: { required } },
|
|
3895
|
+
* { type: { required } }, // Default case
|
|
3896
|
+
* ]);
|
|
3897
|
+
*
|
|
3898
|
+
* return {
|
|
3899
|
+
* ...variant.value,
|
|
3900
|
+
* };
|
|
3901
|
+
* });
|
|
3668
3902
|
* ```
|
|
3903
|
+
*
|
|
3904
|
+
* @see {@link https://reglejs.dev/advanced-usage/variants Documentation}
|
|
3669
3905
|
*/
|
|
3670
3906
|
function createVariant(root, discriminantKey, variants) {
|
|
3671
3907
|
const watchableRoot = computed(() => toValue(root)[discriminantKey]);
|
|
@@ -3685,13 +3921,25 @@ function createVariant(root, discriminantKey, variants) {
|
|
|
3685
3921
|
});
|
|
3686
3922
|
}
|
|
3687
3923
|
/**
|
|
3688
|
-
*
|
|
3924
|
+
* Type guard to narrow a variant field to a specific discriminated value.
|
|
3925
|
+
* Enables type-safe access to variant-specific fields.
|
|
3926
|
+
*
|
|
3927
|
+
* @param root - The Regle status object
|
|
3928
|
+
* @param discriminantKey - The key used to discriminate between variants
|
|
3929
|
+
* @param discriminantValue - The specific value to narrow to
|
|
3930
|
+
* @returns `true` if the discriminant matches, with TypeScript narrowing the type
|
|
3689
3931
|
*
|
|
3932
|
+
* @example
|
|
3690
3933
|
* ```ts
|
|
3934
|
+
* import { narrowVariant } from '@regle/core';
|
|
3935
|
+
*
|
|
3691
3936
|
* if (narrowVariant(r$, 'type', 'EMAIL')) {
|
|
3692
|
-
*
|
|
3937
|
+
* // TypeScript knows r$.email exists here
|
|
3938
|
+
* r$.email.$value = 'user@example.com';
|
|
3693
3939
|
* }
|
|
3694
3940
|
* ```
|
|
3941
|
+
*
|
|
3942
|
+
* @see {@link https://reglejs.dev/advanced-usage/variants Documentation}
|
|
3695
3943
|
*/
|
|
3696
3944
|
function narrowVariant(root, discriminantKey, discriminantValue) {
|
|
3697
3945
|
return !!root && isObject(root[discriminantKey]) && "$value" in root[discriminantKey] && root[discriminantKey]?.$value === discriminantValue;
|
|
@@ -3711,31 +3959,57 @@ function variantToRef(root, discriminantKey, discriminantValue, _options) {
|
|
|
3711
3959
|
}
|
|
3712
3960
|
|
|
3713
3961
|
/**
|
|
3714
|
-
* Helper method to wrap
|
|
3962
|
+
* Helper method to wrap a raw rules object with type inference.
|
|
3963
|
+
* Provides autocomplete and type-checking without processing.
|
|
3715
3964
|
*
|
|
3716
|
-
*
|
|
3965
|
+
* @param rules - The rules object to wrap
|
|
3966
|
+
* @returns The same rules object (passthrough)
|
|
3717
3967
|
*
|
|
3968
|
+
* @example
|
|
3718
3969
|
* ```ts
|
|
3719
|
-
*
|
|
3970
|
+
* import { defineRules } from '@regle/core';
|
|
3971
|
+
* import { required, string } from '@regle/rules';
|
|
3972
|
+
*
|
|
3973
|
+
* // defineRules helps catch structure errors
|
|
3974
|
+
* const rules = defineRules({
|
|
3975
|
+
* firstName: { required, string },
|
|
3976
|
+
* lastName: { required, string }
|
|
3977
|
+
* });
|
|
3720
3978
|
* ```
|
|
3979
|
+
*
|
|
3980
|
+
* @see {@link https://reglejs.dev/common-usage/standard-schema Documentation}
|
|
3721
3981
|
*/
|
|
3722
3982
|
function defineRules(rules) {
|
|
3723
3983
|
return rules;
|
|
3724
3984
|
}
|
|
3725
3985
|
/**
|
|
3726
|
-
* Refine a
|
|
3986
|
+
* Refine a rules object to add rules that depend on the state values.
|
|
3987
|
+
* Inspired by Zod's `refine`, this allows writing dynamic rules while maintaining type safety.
|
|
3727
3988
|
*
|
|
3728
|
-
* @
|
|
3989
|
+
* @param rules - The base rules object
|
|
3990
|
+
* @param refinement - A function that receives the typed state and returns additional rules
|
|
3991
|
+
* @returns A function that, given the state ref, returns the merged rules
|
|
3729
3992
|
*
|
|
3993
|
+
* @example
|
|
3730
3994
|
* ```ts
|
|
3995
|
+
* import { refineRules, type InferInput } from '@regle/core';
|
|
3996
|
+
* import { required, string, sameAs } from '@regle/rules';
|
|
3997
|
+
*
|
|
3731
3998
|
* const rules = refineRules({
|
|
3732
|
-
*
|
|
3733
|
-
* }, (state) => {
|
|
3734
|
-
*
|
|
3735
|
-
*
|
|
3736
|
-
*
|
|
3737
|
-
*
|
|
3999
|
+
* password: { required, string },
|
|
4000
|
+
* }, (state) => ({
|
|
4001
|
+
* // state is typed based on the base rules
|
|
4002
|
+
* confirmPassword: {
|
|
4003
|
+
* required,
|
|
4004
|
+
* sameAs: sameAs(() => state.value.password)
|
|
4005
|
+
* }
|
|
4006
|
+
* }));
|
|
4007
|
+
*
|
|
4008
|
+
* type State = InferInput<typeof rules>;
|
|
4009
|
+
* // { password: string; confirmPassword: string }
|
|
3738
4010
|
* ```
|
|
4011
|
+
*
|
|
4012
|
+
* @see {@link https://reglejs.dev/common-usage/standard-schema#refinerules Documentation}
|
|
3739
4013
|
*/
|
|
3740
4014
|
function refineRules(rules, refinement) {
|
|
3741
4015
|
return (state) => merge({ ...rules }, refinement(state));
|