@regle/core 1.13.0 → 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.
@@ -9,12 +9,34 @@ function isFile(value) {
9
9
  }
10
10
 
11
11
  /**
12
- * This is the inverse of isFilled. It will check if the value is in any way empty (including arrays and objects)
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
- * @param value - the target value
17
- * @param [considerEmptyArrayInvalid=true] - will return false if set to `false`. (default: `true`)
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
- * This utility will coerce any string, number or Date value into a Date using the Date constructor.
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);
@@ -282,20 +318,6 @@ const InternalRuleType = {
282
318
  Async: "__async"
283
319
  };
284
320
 
285
- function mergeBooleanGroupProperties(entries, property) {
286
- return entries.some((entry) => {
287
- if (!property) return false;
288
- return entry?.[property];
289
- });
290
- }
291
- function mergeArrayGroupProperties(entries, property) {
292
- if (!property) return [];
293
- return entries.reduce((all, entry) => {
294
- const fetchedProperty = entry?.[property] || [];
295
- return all.concat(fetchedProperty);
296
- }, []);
297
- }
298
-
299
321
  /**
300
322
  * Returns a clean list of parameters
301
323
  * Removing Ref and executing function to return the unwrapped value
@@ -384,35 +406,57 @@ function defineRuleProcessors(definition, ...params) {
384
406
  }
385
407
 
386
408
  /**
387
- * Create a typed custom rule that can be used like default rules.
388
- * It can also be declared in the global options
389
- *
390
- * It will automatically detect if the rule is async
391
- *
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.
392
411
  *
393
- * @param definition - The rule processors object
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
394
417
  *
395
- * @returns A rule definition that can be callable depending on params presence
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
396
424
  *
397
- * @exemple
425
+ * @returns A rule definition that is callable if it accepts parameters
398
426
  *
427
+ * @example
399
428
  * ```ts
400
- * // Create a simple rule with no params
401
- * import {isFilled} from '@regle/rules';
429
+ * import { createRule } from '@regle/core';
430
+ * import { isFilled } from '@regle/rules';
402
431
  *
432
+ * // Simple rule without params
403
433
  * export const isFoo = createRule({
404
434
  * validator(value: Maybe<string>) {
405
- * if (isFilled(value)) {
406
- * return value === 'foo';
407
- * }
408
- * return true
435
+ * if (isFilled(value)) {
436
+ * return value === 'foo';
437
+ * }
438
+ * return true;
409
439
  * },
410
440
  * message: "The value should be 'foo'"
411
- * })
441
+ * });
412
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
+ * });
453
+ *
454
+ * // Usage
455
+ * useRegle({ name: '' }, { name: { isFoo } });
456
+ * useRegle({ count: 0 }, { count: { minCustom: minCustom(5) } });
413
457
  * ```
414
458
  *
415
- * Docs: {@link https://reglejs.dev/core-concepts/rules/reusable-rules}
459
+ * @see {@link https://reglejs.dev/core-concepts/rules/reusable-rules Documentation}
416
460
  */
417
461
  function createRule(definition) {
418
462
  if (typeof definition.validator === "function") {
@@ -538,6 +582,20 @@ function createGlobalState(stateFactory) {
538
582
  });
539
583
  }
540
584
 
585
+ function mergeBooleanGroupProperties(entries, property) {
586
+ return entries.some((entry) => {
587
+ if (!property) return false;
588
+ return entry?.[property];
589
+ });
590
+ }
591
+ function mergeArrayGroupProperties(entries, property) {
592
+ if (!property) return [];
593
+ return entries.reduce((all, entry) => {
594
+ const fetchedProperty = entry?.[property] || [];
595
+ return all.concat(fetchedProperty);
596
+ }, []);
597
+ }
598
+
541
599
  /**
542
600
  * Inspired by Vuelidate storage
543
601
  */
@@ -1140,8 +1198,7 @@ function createReactiveFieldStatus({ state, rulesDef, customMessages, path, cach
1140
1198
  return extractRulesTooltips({ field: { $rules: $rules.value } });
1141
1199
  });
1142
1200
  const $ready = computed(() => {
1143
- if ($silent.value) return !($invalid.value || $pending.value);
1144
- return $anyDirty.value && !($invalid.value || $pending.value);
1201
+ return !($invalid.value || $pending.value);
1145
1202
  });
1146
1203
  const $pending = computed(() => {
1147
1204
  if (triggerPunishment.value || !$rewardEarly$1.value) return Object.entries($rules.value).some(([_, ruleResult]) => {
@@ -2083,8 +2140,7 @@ function createReactiveNestedStatus({ rulesDef, state, path = "", cachePath, roo
2083
2140
  return true;
2084
2141
  });
2085
2142
  const $ready = computed(() => {
2086
- if ($silent.value) return !($invalid.value || $pending.value);
2087
- return $anyDirty.value && !($invalid.value || $pending.value);
2143
+ return !($invalid.value || $pending.value);
2088
2144
  });
2089
2145
  const $localPending = ref(false);
2090
2146
  const $pending = computed(() => {
@@ -2504,6 +2560,7 @@ function useRegleDevtoolsRegistry() {
2504
2560
  watchers.value.set(id, stopHandle);
2505
2561
  }
2506
2562
  return {
2563
+ devtoolsApi,
2507
2564
  register,
2508
2565
  unregister,
2509
2566
  getAll,
@@ -2521,7 +2578,7 @@ const regleDevtoolsRegistry = useRegleDevtoolsRegistry();
2521
2578
  */
2522
2579
  function registerRegleInstance(r$, options) {
2523
2580
  if (typeof window === "undefined") return;
2524
- if (!inject(regleSymbol) && !regleDevtoolsRegistry.loggedWarning.value) {
2581
+ if (!inject(regleSymbol) && !regleDevtoolsRegistry.loggedWarning.value && !!regleDevtoolsRegistry.devtoolsApi) {
2525
2582
  regleDevtoolsRegistry.loggedWarning.value = true;
2526
2583
  console.warn(`📏 Regle Devtools are not available. Install Regle plugin in your app to enable them. https://reglejs.dev/introduction/devtools`);
2527
2584
  return;
@@ -2985,7 +3042,7 @@ function filterInspectorTree(nodes, filter) {
2985
3042
  return filtered;
2986
3043
  }
2987
3044
 
2988
- var version$1 = "1.13.0";
3045
+ var version$1 = "1.14.0-beta.1";
2989
3046
 
2990
3047
  function createDevtools(app) {
2991
3048
  setupDevtoolsPlugin({
@@ -3188,23 +3245,37 @@ function createUseRegleComposable(customRules, options, shortcuts) {
3188
3245
  return useRegle$1;
3189
3246
  }
3190
3247
  /**
3191
- * 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.
3192
3250
  *
3193
- * It accepts the following inputs:
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
3194
3255
  *
3195
- * @param state - This can be a plain object, a ref, a reactive object, or a structure containing nested refs.
3196
- * @param rules - These should align with the structure of your state.
3197
- * @param modifiers - Customize regle behaviour
3198
- *
3256
+ * @example
3199
3257
  * ```ts
3200
3258
  * import { useRegle } from '@regle/core';
3201
- import { required } from '@regle/rules';
3202
-
3203
- const { r$ } = useRegle({ email: '' }, {
3204
- email: { required }
3205
- })
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();
3206
3276
  * ```
3207
- * Docs: {@link https://reglejs.dev/core-concepts/}
3277
+ *
3278
+ * @see {@link https://reglejs.dev/core-concepts/ Documentation}
3208
3279
  */
3209
3280
  const useRegle = createUseRegleComposable();
3210
3281
 
@@ -3215,11 +3286,31 @@ function createInferRuleHelper() {
3215
3286
  return inferRules$1;
3216
3287
  }
3217
3288
  /**
3218
- * Rule type helper to provide autocomplete and typecheck to your form rules or part of your form rules
3219
- * It will just return the rules without any processing.
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.
3220
3291
  *
3221
3292
  * @param state - The state reference
3222
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}
3223
3314
  */
3224
3315
  const inferRules = createInferRuleHelper();
3225
3316
 
@@ -3256,21 +3347,34 @@ function createUseRulesComposable(customRules, options, shortcuts) {
3256
3347
  return useRules$1;
3257
3348
  }
3258
3349
  /**
3259
- * useRules is a clone of useRegle, without the need to provide a state.
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.
3260
3352
  *
3261
- * It accepts the following inputs:
3353
+ * This is useful when you want to define validation rules first and infer the state type from them.
3262
3354
  *
3263
- * @param rules - Your rules object
3264
- * @param modifiers - Customize regle behaviour
3265
- *
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
3266
3360
  * ```ts
3267
- * import { useRules } from '@regle/core';
3268
- import { required } from '@regle/rules';
3269
-
3270
- const { r$ } = useRules({
3271
- email: { required }
3272
- })
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: '' });
3273
3375
  * ```
3376
+ *
3377
+ * @see {@link https://reglejs.dev/common-usage/standard-schema#userules Documentation}
3274
3378
  */
3275
3379
  const useRules = createUseRulesComposable();
3276
3380
 
@@ -3304,16 +3408,43 @@ function applyMarkStatic(value) {
3304
3408
  }
3305
3409
 
3306
3410
  /**
3307
- * Define a global regle configuration, where you can:
3411
+ * Define a global Regle configuration to customize the validation behavior across your application.
3412
+ *
3413
+ * Features:
3308
3414
  * - Customize built-in rules messages
3309
- * - Add your custom rules
3310
- * - Define global modifiers
3311
- * - 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';
3312
3429
  *
3313
- * It will return:
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
+ * ```
3314
3446
  *
3315
- * - a `useRegle` composable that can typecheck your custom rules
3316
- * - an `inferRules` helper that can typecheck your custom rules
3447
+ * @see {@link https://reglejs.dev/advanced-usage/global-config Documentation}
3317
3448
  */
3318
3449
  function defineRegleConfig({ rules, modifiers, shortcuts }) {
3319
3450
  const useRegle$1 = createUseRegleComposable(rules, modifiers, shortcuts);
@@ -3335,12 +3466,31 @@ function defineRegleConfig({ rules, modifiers, shortcuts }) {
3335
3466
  };
3336
3467
  }
3337
3468
  /**
3338
- * Extend an already created custom `useRegle` (as the first parameter)
3469
+ * Extend an already created custom `useRegle` configuration with additional rules, modifiers, or shortcuts.
3339
3470
  *
3340
- * It will return:
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
3341
3477
  *
3342
- * - a `useRegle` composable that can typecheck your custom rules
3343
- * - an `inferRules` helper that can typecheck your custom rules
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}
3344
3494
  */
3345
3495
  function extendRegleConfig(regle, { rules, modifiers, shortcuts }) {
3346
3496
  const rootConfig = regle.__config ?? {};
@@ -3362,6 +3512,44 @@ function extendRegleConfig(regle, { rules, modifiers, shortcuts }) {
3362
3512
  };
3363
3513
  }
3364
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
+ */
3365
3553
  function mergeRegles(regles, _scoped) {
3366
3554
  const scoped = _scoped == null ? false : _scoped;
3367
3555
  const $value = computed({
@@ -3629,6 +3817,35 @@ function createUseScopedRegleComposable(instances, customUseRegle) {
3629
3817
  return { useScopedRegle: useScopedRegle$1 };
3630
3818
  }
3631
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
+ */
3632
3849
  function createScopedUseRegle(options) {
3633
3850
  const instances = (options?.customStore ? () => {
3634
3851
  if (options.customStore) {
@@ -3649,24 +3866,42 @@ function createScopedUseRegle(options) {
3649
3866
  const { useCollectScope, useScopedRegle } = createScopedUseRegle();
3650
3867
 
3651
3868
  /**
3652
- * Declare variations of state that depends on one value
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.
3653
3871
  *
3654
- * Autocomplete may not work here because of https://github.com/microsoft/TypeScript/issues/49547
3872
+ * Note: Autocomplete may not fully work due to TypeScript limitations.
3655
3873
  *
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
3878
+ *
3879
+ * @example
3656
3880
  * ```ts
3657
- * // ⚠️ Use getter syntax for your rules () => {} or a computed one
3658
- * const {r$} = useRegle(state, () => {
3659
- * const variant = createVariant(state, 'type', [
3660
- * {type: { literal: literal('EMAIL')}, email: { required, email }},
3661
- * {type: { literal: literal('GITHUB')}, username: { required }},
3662
- * {type: { required }},
3663
- * ]);
3664
- *
3665
- * return {
3666
- * ...variant.value,
3667
- * };
3668
- * })
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
+ * });
3669
3902
  * ```
3903
+ *
3904
+ * @see {@link https://reglejs.dev/advanced-usage/variants Documentation}
3670
3905
  */
3671
3906
  function createVariant(root, discriminantKey, variants) {
3672
3907
  const watchableRoot = computed(() => toValue(root)[discriminantKey]);
@@ -3686,13 +3921,25 @@ function createVariant(root, discriminantKey, variants) {
3686
3921
  });
3687
3922
  }
3688
3923
  /**
3689
- * Narrow a nested variant field to a discriminated value
3924
+ * Type guard to narrow a variant field to a specific discriminated value.
3925
+ * Enables type-safe access to variant-specific fields.
3690
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
3931
+ *
3932
+ * @example
3691
3933
  * ```ts
3934
+ * import { narrowVariant } from '@regle/core';
3935
+ *
3692
3936
  * if (narrowVariant(r$, 'type', 'EMAIL')) {
3693
- * r$.email.$value = 'foo';
3937
+ * // TypeScript knows r$.email exists here
3938
+ * r$.email.$value = 'user@example.com';
3694
3939
  * }
3695
3940
  * ```
3941
+ *
3942
+ * @see {@link https://reglejs.dev/advanced-usage/variants Documentation}
3696
3943
  */
3697
3944
  function narrowVariant(root, discriminantKey, discriminantValue) {
3698
3945
  return !!root && isObject(root[discriminantKey]) && "$value" in root[discriminantKey] && root[discriminantKey]?.$value === discriminantValue;
@@ -3712,31 +3959,57 @@ function variantToRef(root, discriminantKey, discriminantValue, _options) {
3712
3959
  }
3713
3960
 
3714
3961
  /**
3715
- * Helper method to wrap an raw rules object
3962
+ * Helper method to wrap a raw rules object with type inference.
3963
+ * Provides autocomplete and type-checking without processing.
3716
3964
  *
3717
- * Similar to:
3965
+ * @param rules - The rules object to wrap
3966
+ * @returns The same rules object (passthrough)
3718
3967
  *
3968
+ * @example
3719
3969
  * ```ts
3720
- * const rules = {...} satisfies RegleUnknownRulesTree
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
+ * });
3721
3978
  * ```
3979
+ *
3980
+ * @see {@link https://reglejs.dev/common-usage/standard-schema Documentation}
3722
3981
  */
3723
3982
  function defineRules(rules) {
3724
3983
  return rules;
3725
3984
  }
3726
3985
  /**
3727
- * Refine a raw rules object to set rules that depends on the state values.
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.
3728
3988
  *
3729
- * @example
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
3730
3992
  *
3993
+ * @example
3731
3994
  * ```ts
3995
+ * import { refineRules, type InferInput } from '@regle/core';
3996
+ * import { required, string, sameAs } from '@regle/rules';
3997
+ *
3732
3998
  * const rules = refineRules({
3733
- * password: { required, type: type<string>() },
3734
- * }, (state) => {
3735
- * return {
3736
- * confirmPassword: { required, sameAs: sameAs(() => state.value.password)}
3737
- * }
3738
- * })
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 }
3739
4010
  * ```
4011
+ *
4012
+ * @see {@link https://reglejs.dev/common-usage/standard-schema#refinerules Documentation}
3740
4013
  */
3741
4014
  function refineRules(rules, refinement) {
3742
4015
  return (state) => merge({ ...rules }, refinement(state));