@ezez/utils 4.6.0 → 4.7.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.
Files changed (137) hide show
  1. package/.github/workflows/docs.yml +4 -3
  2. package/CHANGELOG.md +11 -0
  3. package/README.md +21 -6
  4. package/dist/assertProps.d.ts +6 -0
  5. package/dist/assertProps.d.ts.map +1 -0
  6. package/dist/assertProps.js +11 -0
  7. package/dist/assertProps.js.map +1 -0
  8. package/dist/hasProps.d.ts +3 -0
  9. package/dist/hasProps.d.ts.map +1 -0
  10. package/dist/hasProps.js +16 -0
  11. package/dist/hasProps.js.map +1 -0
  12. package/dist/index.d.ts +2 -0
  13. package/dist/index.d.ts.map +1 -1
  14. package/dist/index.js +2 -0
  15. package/dist/index.js.map +1 -1
  16. package/dist/match.d.ts.map +1 -1
  17. package/dist/match.js.map +1 -1
  18. package/docs/assets/navigation.js +1 -1
  19. package/docs/assets/search.js +1 -1
  20. package/docs/documents/Changelog.html +93 -83
  21. package/docs/functions/index.assertProps.html +6 -0
  22. package/docs/functions/index.cap.html +2 -2
  23. package/docs/functions/index.capitalize.html +2 -2
  24. package/docs/functions/index.coalesce.html +2 -2
  25. package/docs/functions/index.compareArrays.html +2 -2
  26. package/docs/functions/index.compareProps.html +2 -2
  27. package/docs/functions/index.deserialize.html +2 -2
  28. package/docs/functions/index.ensureArray.html +2 -2
  29. package/docs/functions/index.ensureDate.html +2 -2
  30. package/docs/functions/index.ensureError.html +2 -2
  31. package/docs/functions/index.ensurePrefix.html +2 -2
  32. package/docs/functions/index.ensureSuffix.html +2 -2
  33. package/docs/functions/index.ensureTimestamp.html +2 -2
  34. package/docs/functions/index.escapeRegExp.html +2 -2
  35. package/docs/functions/index.formatDate.html +2 -2
  36. package/docs/functions/index.get.html +2 -2
  37. package/docs/functions/index.getMultiple.html +2 -2
  38. package/docs/functions/index.hasProps.html +6 -0
  39. package/docs/functions/index.ignore.html +2 -2
  40. package/docs/functions/index.insertSeparator.html +2 -2
  41. package/docs/functions/index.isEmpty.html +2 -2
  42. package/docs/functions/index.isNumericString.html +2 -2
  43. package/docs/functions/index.isPlainObject.html +2 -2
  44. package/docs/functions/index.last.html +2 -2
  45. package/docs/functions/index.later-1.html +2 -2
  46. package/docs/functions/index.mapAsync.html +2 -2
  47. package/docs/functions/index.mapValues.html +2 -2
  48. package/docs/functions/index.match.html +6 -3
  49. package/docs/functions/index.memoize.html +2 -2
  50. package/docs/functions/index.merge.html +2 -2
  51. package/docs/functions/index.mostFrequent.html +2 -2
  52. package/docs/functions/index.noop.html +2 -2
  53. package/docs/functions/index.occurrences.html +2 -2
  54. package/docs/functions/index.omit.html +2 -2
  55. package/docs/functions/index.pick.html +2 -2
  56. package/docs/functions/index.pull.html +2 -2
  57. package/docs/functions/index.race.html +2 -2
  58. package/docs/functions/index.remove.html +2 -2
  59. package/docs/functions/index.removeCommonProperties.html +2 -2
  60. package/docs/functions/index.replace.html +2 -2
  61. package/docs/functions/index.replaceDeep.html +2 -2
  62. package/docs/functions/index.rethrow.html +2 -2
  63. package/docs/functions/index.retry.html +2 -2
  64. package/docs/functions/index.round.html +2 -2
  65. package/docs/functions/index.safe.html +2 -2
  66. package/docs/functions/index.sample.html +2 -2
  67. package/docs/functions/index.samples.html +2 -2
  68. package/docs/functions/index.scale.html +2 -2
  69. package/docs/functions/index.seq.html +2 -2
  70. package/docs/functions/index.seqEarlyBreak.html +2 -2
  71. package/docs/functions/index.serialize.html +2 -2
  72. package/docs/functions/index.serializeToBuffer.html +2 -2
  73. package/docs/functions/index.set.html +2 -2
  74. package/docs/functions/index.setImmutable.html +2 -2
  75. package/docs/functions/index.shuffle.html +2 -2
  76. package/docs/functions/index.sortBy.html +2 -2
  77. package/docs/functions/index.sortByMultiple.html +2 -2
  78. package/docs/functions/index.sortProps.html +2 -2
  79. package/docs/functions/index.stripPrefix.html +2 -2
  80. package/docs/functions/index.stripSuffix.html +2 -2
  81. package/docs/functions/index.throttle.html +2 -2
  82. package/docs/functions/index.toggle.html +2 -2
  83. package/docs/functions/index.trim.html +2 -2
  84. package/docs/functions/index.trimEnd.html +2 -2
  85. package/docs/functions/index.trimStart.html +2 -2
  86. package/docs/functions/index.truthy.html +2 -2
  87. package/docs/functions/index.unique.html +2 -2
  88. package/docs/functions/index.unserializeFromBuffer.html +2 -2
  89. package/docs/functions/index.wait.html +2 -2
  90. package/docs/functions/index.waitFor.html +2 -2
  91. package/docs/functions/index.waitSync.html +2 -2
  92. package/docs/index.html +23 -8
  93. package/docs/interfaces/index.ComparePropsOptions.html +3 -3
  94. package/docs/interfaces/index.GetMultipleSource.html +2 -2
  95. package/docs/interfaces/index.GetSource.html +2 -2
  96. package/docs/interfaces/index.IsNumericStringOptions.html +2 -2
  97. package/docs/interfaces/index.OccurencesOptions.html +2 -2
  98. package/docs/interfaces/index.SetImmutableSource.html +2 -2
  99. package/docs/interfaces/index.SetSource.html +2 -2
  100. package/docs/interfaces/index.ThrottleOptions.html +3 -3
  101. package/docs/interfaces/index.ThrottledFunctionExtras.html +3 -3
  102. package/docs/modules/index.html +1 -1
  103. package/docs/modules.html +1 -1
  104. package/docs/types/index.CustomDeserializers.html +1 -1
  105. package/docs/types/index.CustomSerializers.html +1 -1
  106. package/docs/types/index.Later.html +2 -2
  107. package/docs/types/index.MapValuesFn.html +2 -2
  108. package/docs/types/index.MatchCallback.html +1 -1
  109. package/docs/types/index.MergeTwo.html +2 -2
  110. package/docs/types/index.SeqEarlyBreaker.html +2 -2
  111. package/docs/types/index.SeqFn.html +2 -2
  112. package/docs/types/index.SeqFunctions.html +2 -2
  113. package/docs/types/index.SetImmutablePath.html +2 -2
  114. package/docs/types/index.ThrottledFunction.html +1 -1
  115. package/docs/variables/index.mapValuesUNSET.html +2 -2
  116. package/docs/variables/index.mergeUNSET.html +2 -2
  117. package/esm/assertProps.d.ts +6 -0
  118. package/esm/assertProps.d.ts.map +1 -0
  119. package/esm/assertProps.js +8 -0
  120. package/esm/assertProps.js.map +1 -0
  121. package/esm/hasProps.d.ts +3 -0
  122. package/esm/hasProps.d.ts.map +1 -0
  123. package/esm/hasProps.js +13 -0
  124. package/esm/hasProps.js.map +1 -0
  125. package/esm/index.d.ts +2 -0
  126. package/esm/index.d.ts.map +1 -1
  127. package/esm/index.js +2 -0
  128. package/esm/index.js.map +1 -1
  129. package/esm/match.d.ts.map +1 -1
  130. package/esm/match.js.map +1 -1
  131. package/package.json +1 -1
  132. package/src/assertProps.spec.ts +128 -0
  133. package/src/assertProps.ts +37 -0
  134. package/src/hasProps.spec.ts +122 -0
  135. package/src/hasProps.ts +36 -0
  136. package/src/index.ts +2 -0
  137. package/src/match.ts +1 -0
@@ -0,0 +1,122 @@
1
+ import must from "must"; // eslint-disable-line @typescript-eslint/no-shadow
2
+
3
+ import { hasProps } from "./hasProps";
4
+
5
+ describe("hasProps", () => {
6
+ it("should return true for object with valid string properties", () => {
7
+ const obj = { id: "123", name: "John" };
8
+ const result = hasProps(obj, { id: "string", name: "string" });
9
+ must(result).be.true();
10
+ });
11
+
12
+ it("should return true for object with valid number properties", () => {
13
+ const obj = { age: 25, count: 100 };
14
+ const result = hasProps(obj, { age: "number", count: "number" });
15
+ must(result).be.true();
16
+ });
17
+
18
+ it("should return true for object with valid boolean properties", () => {
19
+ const obj = { isActive: true, isDeleted: false };
20
+ const result = hasProps(obj, { isActive: "boolean", isDeleted: "boolean" });
21
+ must(result).be.true();
22
+ });
23
+
24
+ it("should return true for object with mixed property types", () => {
25
+ const obj = { id: "123", age: 30, isActive: true };
26
+ const result = hasProps(obj, { id: "string", age: "number", isActive: "boolean" });
27
+ must(result).be.true();
28
+ });
29
+
30
+ it("should return false for object with wrong property type", () => {
31
+ const obj = { id: 123, name: "John" };
32
+ const result = hasProps(obj, { id: "string", name: "string" });
33
+ must(result).be.false();
34
+ });
35
+
36
+ it("should return false for object missing required property", () => {
37
+ const obj = { id: "123" };
38
+ const result = hasProps(obj, { id: "string", name: "string" });
39
+ must(result).be.false();
40
+ });
41
+
42
+ it("should return false for null", () => {
43
+ const result = hasProps(null, { id: "string" });
44
+ must(result).be.false();
45
+ });
46
+
47
+ it("should return false for undefined", () => {
48
+ const result = hasProps(undefined, { id: "string" });
49
+ must(result).be.false();
50
+ });
51
+
52
+ it("should return false for array", () => {
53
+ const arr = [{ id: "123" }];
54
+ const result = hasProps(arr, { id: "string" });
55
+ must(result).be.false();
56
+ });
57
+
58
+ it("should return false for primitive string", () => {
59
+ const result = hasProps("test", { id: "string" });
60
+ must(result).be.false();
61
+ });
62
+
63
+ it("should return false for primitive number", () => {
64
+ const result = hasProps(123, { id: "string" });
65
+ must(result).be.false();
66
+ });
67
+
68
+ it("should return false for primitive boolean", () => {
69
+ const result = hasProps(true, { id: "string" });
70
+ must(result).be.false();
71
+ });
72
+
73
+ it("should return true for empty schema", () => {
74
+ const obj = { id: "123", name: "John" };
75
+ const result = hasProps(obj, {});
76
+ must(result).be.true();
77
+ });
78
+
79
+ it("should allow extra properties on the object", () => {
80
+ const obj = { id: "123", name: "John", extra: "value" };
81
+ const result = hasProps(obj, { id: "string", name: "string" });
82
+ must(result).be.true();
83
+ });
84
+
85
+ it("should handle objects with null property values", () => {
86
+ const obj = { id: null, name: "John" };
87
+ const result = hasProps(obj, { id: "string", name: "string" });
88
+ must(result).be.false();
89
+ });
90
+
91
+ it("should handle objects with undefined property values", () => {
92
+ const obj = { id: undefined, name: "John" };
93
+ const result = hasProps(obj, { id: "string", name: "string" });
94
+ must(result).be.false();
95
+ });
96
+
97
+ it("should correctly validate boolean false value", () => {
98
+ const obj = { isActive: false };
99
+ const result = hasProps(obj, { isActive: "boolean" });
100
+ must(result).be.true();
101
+ });
102
+
103
+ it("should correctly validate number zero value", () => {
104
+ const obj = { count: 0 };
105
+ const result = hasProps(obj, { count: "number" });
106
+ must(result).be.true();
107
+ });
108
+
109
+ it("should correctly validate empty string value", () => {
110
+ const obj = { name: "" };
111
+ const result = hasProps(obj, { name: "string" });
112
+ must(result).be.true();
113
+ });
114
+
115
+ it("should work with objects source only", async () => {
116
+ const source = () => null;
117
+ must(source).have.property("name", "source");
118
+
119
+ const result = hasProps(source, { name: "string" });
120
+ must(result).be.false();
121
+ });
122
+ });
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Check if an object has the specified properties with the specified types. Ideal for quick runtime type checking of
3
+ * API responses if you don't need a full schema validation.
4
+ *
5
+ * @example
6
+ * ```typescript
7
+ * const validated = hasProps(obj, { id: "string", age: "number" });
8
+ * if (validated) {
9
+ * // obj is runtime validated and type is now narrowed down to { id: string; age: number }
10
+ * console.log(obj.id, obj.age);
11
+ * }
12
+ * ```
13
+ */
14
+ const hasProps = <
15
+ T extends Record<string, "string" | "number" | "boolean">,
16
+ >(
17
+ obj: unknown,
18
+ schema: T,
19
+ ): obj is {
20
+ [K in keyof T]:
21
+ T[K] extends "string" ? string
22
+ : T[K] extends "number" ? number
23
+ : T[K] extends "boolean" ? boolean
24
+ : never
25
+ } => {
26
+ if (typeof obj !== "object" || obj === null || Array.isArray(obj)) { return false; }
27
+ for (const [key, type] of Object.entries(schema)) {
28
+ // eslint-disable-next-line valid-typeof
29
+ if (typeof (obj as { [key]: unknown })[key] !== type) { return false; }
30
+ }
31
+ return true;
32
+ };
33
+
34
+ export {
35
+ hasProps,
36
+ };
package/src/index.ts CHANGED
@@ -3,6 +3,7 @@ export * from "./serializeToBuffer/serializeToBuffer.js";
3
3
  export * from "./serializeToBuffer/unserializeFromBuffer.js";
4
4
  export * from "./ensurePrefix.js";
5
5
  export * from "./ensureSuffix.js";
6
+ export * from "./assertProps.js";
6
7
  export * from "./cap.js";
7
8
  export * from "./capitalize.js";
8
9
  export * from "./coalesce.js";
@@ -17,6 +18,7 @@ export * from "./escapeRegExp.js";
17
18
  export * from "./formatDate.js";
18
19
  export * from "./get.js";
19
20
  export * from "./getMultiple.js";
21
+ export * from "./hasProps.js";
20
22
  export * from "./ignore.js";
21
23
  export * from "./insertSeparator.js";
22
24
  export * from "./isEmpty.js";
package/src/match.ts CHANGED
@@ -4,6 +4,7 @@ type MatchCallback<T> = (value: T) => boolean;
4
4
  * A `Array.prototype.filter`-like function that splits the results into two groups - matched and unmatched
5
5
  * @param {Array} list - original array
6
6
  * @param {function} fn - function matching elements
7
+ * @deprecated - use `Map.groupBy` instead for even more robustness
7
8
  */
8
9
  const match = <T>(list: T[], fn: MatchCallback<T>): { matched: T[]; unmatched: T[] } => {
9
10
  const matched: T[] = [];