n4s 6.1.12 → 6.2.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.
Files changed (42) hide show
  1. package/dist/exports/date.cjs +1 -1
  2. package/dist/exports/date.mjs +1 -1
  3. package/dist/exports/email.cjs +1 -1
  4. package/dist/exports/email.mjs +1 -1
  5. package/dist/exports/isURL.cjs +1 -1
  6. package/dist/exports/isURL.mjs +1 -1
  7. package/dist/{n4s-CoDF5Fg6.cjs → n4s-BTHEz-bJ.cjs} +215 -5
  8. package/dist/n4s-BTHEz-bJ.cjs.map +1 -0
  9. package/dist/{n4s-KWquSyTb.mjs → n4s-BxrvnvKp.mjs} +218 -8
  10. package/dist/n4s-BxrvnvKp.mjs.map +1 -0
  11. package/dist/n4s.cjs +1 -1
  12. package/dist/n4s.mjs +1 -1
  13. package/package.json +5 -1
  14. package/src/eager/eagerTypes.ts +14 -0
  15. package/src/lazy.ts +24 -0
  16. package/src/n4s.ts +5 -0
  17. package/src/rules/schemaRules/__tests__/integrationSchemaRules.types.test.ts +92 -0
  18. package/src/rules/schemaRules/__tests__/isArrayOf.test.ts +37 -1
  19. package/src/rules/schemaRules/__tests__/lazy.test.ts +312 -0
  20. package/src/rules/schemaRules/__tests__/record.test.ts +205 -0
  21. package/src/rules/schemaRules/__tests__/schema.parse.integration.test.ts +79 -0
  22. package/src/rules/schemaRules/__tests__/tuple.test.ts +256 -0
  23. package/src/rules/schemaRules/lazy.ts +48 -0
  24. package/src/rules/schemaRules/record.ts +126 -0
  25. package/src/rules/schemaRules/schemaRules.ts +8 -1
  26. package/src/rules/schemaRules/schemaRulesLazyTypes.ts +21 -0
  27. package/src/rules/schemaRules/tuple.ts +157 -0
  28. package/types/exports/date.d.cts +1 -1
  29. package/types/exports/date.d.mts +1 -1
  30. package/types/n4s.d.cts +21 -2
  31. package/types/n4s.d.cts.map +1 -1
  32. package/types/n4s.d.mts +78 -69
  33. package/types/n4s.d.mts.map +1 -1
  34. package/types/n4s.d.ts +21 -2
  35. package/types/{n4sTypes-Bb1zNxyv.d.mts → n4sTypes-3THfSmAQ.d.mts} +79 -5
  36. package/types/n4sTypes-3THfSmAQ.d.mts.map +1 -0
  37. package/types/{n4sTypes-ChCugpFQ.d.cts → n4sTypes-BSTzXRsU.d.cts} +66 -2
  38. package/types/n4sTypes-BSTzXRsU.d.cts.map +1 -0
  39. package/dist/n4s-CoDF5Fg6.cjs.map +0 -1
  40. package/dist/n4s-KWquSyTb.mjs.map +0 -1
  41. package/types/n4sTypes-Bb1zNxyv.d.mts.map +0 -1
  42. package/types/n4sTypes-ChCugpFQ.d.cts.map +0 -1
@@ -1,4 +1,4 @@
1
- import { StringObject, asArray, assign, bindNot, dynamicValue, greaterThan, greaterThan as greaterThan$1, hasOwnProperty, invariant, isBoolean, isEmpty, isEmpty as isEmpty$2, isFailure, isNotEmpty, isNotEmpty as isNotEmpty$2, isNotNull, isNotNullish, isNotUndefined, isNull, isNullish, isNumeric, isObject, isStringValue, isUndefined, isUnsafeKey, lengthEquals, mapFirst, numberEquals, numberEquals as numberEquals$1, numberNotEquals, toNumber } from "vest-utils";
1
+ import { StringObject, asArray, assign, bindNot, dynamicValue, greaterThan, greaterThan as greaterThan$1, hasOwnProperty, invariant, isBoolean, isEmpty, isEmpty as isEmpty$2, isFailure, isFunction, isNotEmpty, isNotEmpty as isNotEmpty$2, isNotNull, isNotNullish, isNotUndefined, isNull, isNullish, isNumeric, isObject, isStringValue, isUndefined, isUnsafeKey, lengthEquals, longerThan, mapFirst, numberEquals, numberEquals as numberEquals$1, numberNotEquals, toNumber } from "vest-utils";
2
2
  import { createCascade } from "context";
3
3
 
4
4
  //#region rolldown:runtime
@@ -274,7 +274,7 @@ function notInside(value, container) {
274
274
  var commonLength_exports = /* @__PURE__ */ __export({
275
275
  lengthEquals: () => lengthEquals$1,
276
276
  lengthNotEquals: () => lengthNotEquals,
277
- longerThan: () => longerThan,
277
+ longerThan: () => longerThan$1,
278
278
  longerThanOrEquals: () => longerThanOrEquals,
279
279
  max: () => max,
280
280
  maxLength: () => maxLength,
@@ -297,7 +297,7 @@ function lengthEquals$1(value, n) {
297
297
  function lengthNotEquals(value, n) {
298
298
  return value.length !== n;
299
299
  }
300
- function longerThan(value, n) {
300
+ function longerThan$1(value, n) {
301
301
  return value.length > n;
302
302
  }
303
303
  function longerThanOrEquals(value, n) {
@@ -342,7 +342,7 @@ var arrayRules_exports = /* @__PURE__ */ __export({
342
342
  isNotEmpty: () => isNotEmpty$2,
343
343
  lengthEquals: () => lengthEquals$1,
344
344
  lengthNotEquals: () => lengthNotEquals,
345
- longerThan: () => longerThan,
345
+ longerThan: () => longerThan$1,
346
346
  longerThanOrEquals: () => longerThanOrEquals,
347
347
  maxLength: () => maxLength,
348
348
  minLength: () => minLength,
@@ -1575,16 +1575,177 @@ function shape(value, schema) {
1575
1575
  return RuleRunReturn.Passing(baseRes.type);
1576
1576
  }
1577
1577
 
1578
+ //#endregion
1579
+ //#region src/rules/schemaRules/record.ts
1580
+ /**
1581
+ * Validates that an object's dynamic keys and/or values match provided rules.
1582
+ * Like TypeScript's Record<K, V>, it checks elements against shape rules.
1583
+ *
1584
+ * @param value - The object to validate
1585
+ * @param arg1 - Either the key rule (if arg2 is present) or the value rule
1586
+ * @param arg2 - The value rule (if arg1 is the key rule)
1587
+ * @returns RuleRunReturn indicating success or failure
1588
+ */
1589
+ function record(value, arg1, arg2) {
1590
+ if (!isObject(value) || Array.isArray(value)) return RuleRunReturn.Failing(value);
1591
+ const rules = parseRules(arg1, arg2);
1592
+ const dangerousKey = findDangerousOwnKey(value);
1593
+ if (dangerousKey) return createRecordFailure(value, dangerousKey, RuleRunReturn.Failing(value));
1594
+ const parsedValue = safeShallowCopy(value);
1595
+ return mapFirst(ownKeys(value), (key, breakout) => {
1596
+ const errorRes = evaluateRecordEntry(key, value, rules, parsedValue);
1597
+ if (errorRes) breakout(true, errorRes);
1598
+ }) || RuleRunReturn.Passing(parsedValue);
1599
+ }
1600
+ function parseRules(arg1, arg2) {
1601
+ if (arg2 !== void 0) return {
1602
+ keyRule: arg1,
1603
+ valueRule: arg2
1604
+ };
1605
+ return {
1606
+ keyRule: void 0,
1607
+ valueRule: arg1
1608
+ };
1609
+ }
1610
+ function validateKey(key, keyRule) {
1611
+ return ctx.run({
1612
+ value: key,
1613
+ set: true
1614
+ }, () => keyRule.run(key));
1615
+ }
1616
+ function evaluateRecordEntry(key, value, rules, parsedValue) {
1617
+ if (rules.keyRule) {
1618
+ const keyRes = validateKey(key, rules.keyRule);
1619
+ if (!keyRes.pass) return createRecordFailure(value, key, keyRes);
1620
+ if (keyRes.type !== key) {
1621
+ delete parsedValue[key];
1622
+ key = keyRes.type;
1623
+ }
1624
+ }
1625
+ const valRes = ctx.run({
1626
+ value: value[key],
1627
+ set: true,
1628
+ meta: { key }
1629
+ }, () => rules.valueRule.run(value[key]));
1630
+ if (!valRes.pass) return createRecordFailure(value, key, valRes);
1631
+ parsedValue[key] = valRes.type;
1632
+ }
1633
+ function createRecordFailure(value, key, ruleRes) {
1634
+ const currentPath = ruleRes.path || [];
1635
+ const newRes = RuleRunReturn.Failing(value, ruleRes.message);
1636
+ newRes.path = [key, ...currentPath];
1637
+ return newRes;
1638
+ }
1639
+
1640
+ //#endregion
1641
+ //#region src/rules/schemaRules/tuple.ts
1642
+ /**
1643
+ * Validates that a value is a fixed-length array (tuple) where each position
1644
+ * matches the corresponding rule. Enforces exact length unless trailing
1645
+ * elements use enforce.optional().
1646
+ *
1647
+ * Parsed values are propagated: if a rule transforms its input (e.g. toNumber),
1648
+ * the parsed tuple returned via `.parse()` carries the transformed values.
1649
+ *
1650
+ * @param value - The array to validate
1651
+ * @param rules - One RuleInstance per tuple position
1652
+ * @returns RuleRunReturn indicating success or failure, with `.type` holding
1653
+ * the parsed tuple on success
1654
+ *
1655
+ * @example
1656
+ * ```typescript
1657
+ * // Eager API
1658
+ * enforce(['hello', 42]).tuple(enforce.isString(), enforce.isNumber());
1659
+ *
1660
+ * // Lazy API
1661
+ * const coordSchema = enforce.tuple(enforce.isNumber(), enforce.isNumber());
1662
+ * coordSchema.test([40.7, -74.0]); // true
1663
+ * coordSchema.test([40.7]); // false — too few
1664
+ * coordSchema.test([40.7, -74, 0]);// false — too many
1665
+ * ```
1666
+ */
1667
+ function tuple(value, ...rules) {
1668
+ if (!Array.isArray(value)) return RuleRunReturn.Failing(value);
1669
+ if (greaterThan(countRequired(rules), value.length) || longerThan(value, rules.length)) return RuleRunReturn.Failing(value);
1670
+ return validateElements(value, rules);
1671
+ }
1672
+ /**
1673
+ * Counts the number of required (non-optional) leading positions by scanning
1674
+ * backwards from the end of the rules array. Stops at the first non-optional
1675
+ * rule, so only *trailing* optionals reduce the required count.
1676
+ */
1677
+ function countRequired(rules) {
1678
+ let count = rules.length;
1679
+ for (let i = rules.length - 1; i >= 0; i--) if (isOptionalRule(rules[i])) count = i;
1680
+ else break;
1681
+ return count;
1682
+ }
1683
+ /**
1684
+ * Iterates over each rule position, validates the corresponding array element,
1685
+ * and collects parsed output values. Returns early on the first failing element
1686
+ * with an index-based error path.
1687
+ */
1688
+ function validateElements(value, rules) {
1689
+ const parsedTuple = [];
1690
+ for (let i = 0; i < rules.length; i++) {
1691
+ if (isBeyondArrayEnd(value, i, rules[i])) continue;
1692
+ const res = runElementRule(value[i], rules[i], i);
1693
+ if (!res.pass) return elementFailure(value, res, i);
1694
+ parsedTuple.push(res.type ?? value[i]);
1695
+ }
1696
+ return RuleRunReturn.Passing(parsedTuple);
1697
+ }
1698
+ /**
1699
+ * Checks whether the given index is past the array's actual length
1700
+ * and the corresponding rule is optional, meaning it can be skipped.
1701
+ */
1702
+ function isBeyondArrayEnd(value, index, rule) {
1703
+ return index >= value.length && isOptionalRule(rule);
1704
+ }
1705
+ /**
1706
+ * Runs a single element's rule within an enforce context that carries
1707
+ * the element value and its positional index as metadata.
1708
+ */
1709
+ function runElementRule(item, rule, index) {
1710
+ return ctx.run({
1711
+ value: item,
1712
+ set: true,
1713
+ meta: { index }
1714
+ }, () => rule.run(item));
1715
+ }
1716
+ /**
1717
+ * Builds a failing RuleRunReturn with an error path that includes the
1718
+ * tuple index, prepended to any nested path from the inner rule failure.
1719
+ * For example, a shape failure at index 1 on key "id" yields path ["1", "id"].
1720
+ */
1721
+ function elementFailure(value, res, index) {
1722
+ const failure = RuleRunReturn.Failing(value, res.message);
1723
+ failure.path = [index.toString(), ...res.path || []];
1724
+ return failure;
1725
+ }
1726
+ /**
1727
+ * Determines whether a rule is optional by testing if it passes with undefined.
1728
+ * This mirrors how shape/loose detect optional fields — a rule wrapping
1729
+ * enforce.optional() will pass for undefined, while required rules will not.
1730
+ */
1731
+ function isOptionalRule(rule) {
1732
+ if (!rule || !isFunction(rule.test)) return false;
1733
+ return rule.test(void 0);
1734
+ }
1735
+
1578
1736
  //#endregion
1579
1737
  //#region src/rules/schemaRules/schemaRules.ts
1580
1738
  var schemaRules_exports = /* @__PURE__ */ __export({
1581
1739
  isArrayOf: () => isArrayOf,
1740
+ list: () => isArrayOf,
1582
1741
  loose: () => loose,
1583
1742
  omit: () => omit,
1584
1743
  optional: () => optional,
1585
1744
  partial: () => partial,
1586
1745
  pick: () => pick,
1587
- shape: () => shape
1746
+ record: () => record,
1747
+ shape: () => shape,
1748
+ tuple: () => tuple
1588
1749
  });
1589
1750
 
1590
1751
  //#endregion
@@ -1685,7 +1846,7 @@ var stringRules_exports = /* @__PURE__ */ __export({
1685
1846
  isString: () => isString,
1686
1847
  lengthEquals: () => lengthEquals$1,
1687
1848
  lengthNotEquals: () => lengthNotEquals,
1688
- longerThan: () => longerThan,
1849
+ longerThan: () => longerThan$1,
1689
1850
  longerThanOrEquals: () => longerThanOrEquals,
1690
1851
  matches: () => matches,
1691
1852
  maxLength: () => maxLength,
@@ -2229,6 +2390,44 @@ const typeRules = {
2229
2390
  isUndefined: () => addToChain({}, isUndefined$1)
2230
2391
  };
2231
2392
 
2393
+ //#endregion
2394
+ //#region src/rules/schemaRules/lazy.ts
2395
+ /**
2396
+ * Creates a lazy schema wrapper for recursive/self-referencing schemas.
2397
+ * The factory function is called on first validation and cached.
2398
+ *
2399
+ * @param factory - A function that returns the RuleInstance to delegate to
2400
+ * @returns A RuleInstance that defers schema resolution to validation time
2401
+ *
2402
+ * @example
2403
+ * ```typescript
2404
+ * type Category = { name: string; children: Category[] };
2405
+ *
2406
+ * const categorySchema = enforce.shape({
2407
+ * name: enforce.isString(),
2408
+ * children: enforce.isArrayOf(enforce.lazy(() => categorySchema)),
2409
+ * });
2410
+ *
2411
+ * categorySchema.test({
2412
+ * name: 'Root',
2413
+ * children: [
2414
+ * { name: 'Child', children: [] },
2415
+ * ],
2416
+ * }); // true
2417
+ * ```
2418
+ */
2419
+ function lazy(factory) {
2420
+ let cached = null;
2421
+ const resolve = () => {
2422
+ if (!cached) cached = factory();
2423
+ return cached;
2424
+ };
2425
+ return addToChain({}, (value) => {
2426
+ const result = ctx.run({ value }, () => resolve().run(value));
2427
+ return RuleRunReturn.create(result, value);
2428
+ });
2429
+ }
2430
+
2232
2431
  //#endregion
2233
2432
  //#region src/lazy.ts
2234
2433
  const schemaModifiers = adaptDynamicRules({
@@ -2241,6 +2440,7 @@ const schemaEvaluators = adaptDynamicRules({
2241
2440
  shape,
2242
2441
  loose
2243
2442
  });
2443
+ const recordEvaluators = adaptDynamicRules({ record });
2244
2444
  /**
2245
2445
  * Wraps a lazy schema evaluator so the resulting RuleInstance carries
2246
2446
  * a `__schema` reference to the original schema definition.
@@ -2258,8 +2458,18 @@ const schemaRulesWithArrayChaining = {
2258
2458
  const result = ctx.run({ value }, () => isArrayOf(value, ...rules));
2259
2459
  return RuleRunReturn.create(result, value);
2260
2460
  }),
2461
+ lazy,
2462
+ list: (...rules) => addToChain(arrayRules_exports, (value) => {
2463
+ const result = ctx.run({ value }, () => isArrayOf(value, ...rules));
2464
+ return RuleRunReturn.create(result, value);
2465
+ }),
2261
2466
  loose: schemaAttacher(schemaEvaluators.loose),
2262
- shape: schemaAttacher(schemaEvaluators.shape)
2467
+ record: recordEvaluators.record,
2468
+ shape: schemaAttacher(schemaEvaluators.shape),
2469
+ tuple: (...rules) => addToChain(arrayRules_exports, (value) => {
2470
+ const result = ctx.run({ value }, () => tuple(value, ...rules));
2471
+ return RuleRunReturn.create(result, value);
2472
+ })
2263
2473
  };
2264
2474
  const baseEnforceLazy = {
2265
2475
  ...adaptDynamicRules(compoundRules_exports),
@@ -2450,4 +2660,4 @@ enforce.extend = function extend(rules) {
2450
2660
 
2451
2661
  //#endregion
2452
2662
  export { compose as n, ctx as r, enforce as t };
2453
- //# sourceMappingURL=n4s-KWquSyTb.mjs.map
2663
+ //# sourceMappingURL=n4s-BxrvnvKp.mjs.map