utilitish 0.0.7 → 0.0.8

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.
@@ -1,4 +1,3 @@
1
- export {};
2
1
  declare global {
3
2
  interface ArrayConstructor {
4
3
  /**
@@ -97,3 +96,4 @@ declare global {
97
96
  }
98
97
  type MultiDimensionalArray<T, Dims extends number[]> = Dims extends [number, ...infer Rest extends number[]] ? MultiDimensionalArray<T[], Rest> : T;
99
98
  type WidenLiterals<T> = T extends string ? string : T extends number ? number : T extends boolean ? boolean : T;
99
+ export {};
@@ -1,5 +1,4 @@
1
1
  import { Selector } from '../utils/core.utils';
2
- export {};
3
2
  declare global {
4
3
  interface Array<T> {
5
4
  /**
@@ -1,4 +1,3 @@
1
- export {};
2
1
  declare global {
3
2
  interface Map<K, V> {
4
3
  /**
@@ -83,3 +82,4 @@ declare global {
83
82
  ensureArray<L extends Array<any>>(this: Map<K, L>, key: K): L;
84
83
  }
85
84
  }
85
+ export {};
@@ -1,4 +1,3 @@
1
- export {};
2
1
  declare global {
3
2
  interface Object {
4
3
  /**
@@ -71,5 +70,44 @@ declare global {
71
70
  * - null and undefined are handled correctly
72
71
  */
73
72
  deepEquals(other: unknown): boolean;
73
+ /**
74
+ * Converts the object to a stable string representation with sorted keys.
75
+ * The resulting string is deterministic: the same object will always produce the same string.
76
+ *
77
+ * @returns {string} A stable string representation of the object
78
+ *
79
+ * @example
80
+ * const obj = { b: 2, a: 1 };
81
+ * obj.stableStringify(); // '{"a":1,"b":2}'
82
+ *
83
+ * const obj2 = { a: 1, b: 2 };
84
+ * obj2.stableStringify(); // '{"a":1,"b":2}' (same result, different key order)
85
+ *
86
+ * @remarks
87
+ * - Object keys are sorted alphabetically to ensure stable output
88
+ * - Arrays maintain their element order
89
+ * - Null and primitives are handled using JSON.stringify
90
+ */
91
+ stableStringify(): string;
92
+ /**
93
+ * Generates a stable hash of the object using FNV-1a algorithm.
94
+ * The hash is deterministic: the same object will always produce the same hash.
95
+ *
96
+ * @returns {string} A hexadecimal string representing the hash of the object
97
+ *
98
+ * @example
99
+ * const obj = { b: 2, a: 1 };
100
+ * obj.stableHash(); // '7a8c9f2b'
101
+ *
102
+ * const obj2 = { a: 1, b: 2 };
103
+ * obj2.stableHash(); // '7a8c9f2b' (same hash, different key order)
104
+ *
105
+ * @remarks
106
+ * - Uses FNV-1a (Fowler–Noll–Vo) hashing algorithm
107
+ * - The object is first converted to a stable string using stableStringify()
108
+ * - The hash is returned as a hexadecimal string
109
+ */
110
+ stableHash(): string;
74
111
  }
75
112
  }
113
+ export {};
@@ -1,26 +1,16 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- const defineIfNotExists = (name, fn) => {
4
- const descriptor = Object.getOwnPropertyDescriptor(Object.prototype, name);
5
- if (!descriptor || descriptor.writable || descriptor.configurable) {
6
- Object.defineProperty(Object.prototype, name, {
7
- value: fn,
8
- enumerable: false,
9
- configurable: true,
10
- writable: true,
11
- });
12
- }
13
- };
3
+ const core_utils_1 = require("../utils/core.utils");
14
4
  /**
15
5
  * @see Object.prototype.deepClone
16
6
  */
17
- defineIfNotExists('deepClone', function () {
7
+ (0, core_utils_1.defineIfNotExists)(Object.prototype, 'deepClone', function () {
18
8
  return structuredClone(this);
19
9
  });
20
10
  /**
21
11
  * @see Object.prototype.deepMerge
22
12
  */
23
- defineIfNotExists('deepMerge', function (source) {
13
+ (0, core_utils_1.defineIfNotExists)(Object.prototype, 'deepMerge', function (source) {
24
14
  if (typeof source !== 'object' || source === null) {
25
15
  throw new TypeError('Source must be a non-null object');
26
16
  }
@@ -43,7 +33,7 @@ defineIfNotExists('deepMerge', function (source) {
43
33
  /**
44
34
  * @see Object.prototype.deepEquals
45
35
  */
46
- defineIfNotExists('deepEquals', function (other) {
36
+ (0, core_utils_1.defineIfNotExists)(Object.prototype, 'deepEquals', function (other) {
47
37
  function eq(a, b) {
48
38
  // Functions: always false (even if code is the same)
49
39
  if (typeof a === 'function' || typeof b === 'function') {
@@ -95,3 +85,33 @@ defineIfNotExists('deepEquals', function (other) {
95
85
  }
96
86
  return eq(this, other);
97
87
  });
88
+ /**
89
+ * @see Object.prototype.stableStringify
90
+ */
91
+ (0, core_utils_1.defineIfNotExists)(Object.prototype, 'stableStringify', function () {
92
+ const stringify = (v) => {
93
+ if (v === null || typeof v !== 'object') {
94
+ return JSON.stringify(v);
95
+ }
96
+ if (Array.isArray(v)) {
97
+ return '[' + v.map(stringify).join(',') + ']';
98
+ }
99
+ const obj = v;
100
+ const keys = Object.keys(obj).sort();
101
+ const entries = keys.map((key) => JSON.stringify(key) + ':' + stringify(obj[key]));
102
+ return '{' + entries.join(',') + '}';
103
+ };
104
+ return stringify(this);
105
+ });
106
+ /**
107
+ * @see Object.prototype.stableHash
108
+ */
109
+ (0, core_utils_1.defineIfNotExists)(Object.prototype, 'stableHash', function () {
110
+ const str = this.stableStringify();
111
+ let hash = 2166136261;
112
+ for (let i = 0; i < str.length; i++) {
113
+ hash ^= str.charCodeAt(i);
114
+ hash = Math.imul(hash, 16777619);
115
+ }
116
+ return (hash >>> 0).toString(16);
117
+ });
@@ -107,4 +107,78 @@ describe('Object.prototype', () => {
107
107
  });
108
108
  });
109
109
  });
110
+ describe('stableStringify()', () => {
111
+ it('should stringify plain objects with sorted keys', () => {
112
+ const obj = { b: 2, a: 1 };
113
+ expect(obj.stableStringify()).toBe('{"a":1,"b":2}');
114
+ });
115
+ it('should produce same string regardless of key order', () => {
116
+ const obj1 = { b: 2, a: 1, c: 3 };
117
+ const obj2 = { c: 3, a: 1, b: 2 };
118
+ expect(obj1.stableStringify()).toBe(obj2.stableStringify());
119
+ });
120
+ it('should stringify arrays with element order preserved', () => {
121
+ const arr = [1, 2, 3];
122
+ expect(arr.stableStringify()).toBe('[1,2,3]');
123
+ });
124
+ it('should handle nested objects', () => {
125
+ const obj = { b: { d: 4, c: 3 }, a: 1 };
126
+ expect(obj.stableStringify()).toBe('{"a":1,"b":{"c":3,"d":4}}');
127
+ });
128
+ it('should handle arrays within objects', () => {
129
+ const obj = { a: [1, 2], b: 2 };
130
+ expect(obj.stableStringify()).toBe('{"a":[1,2],"b":2}');
131
+ });
132
+ it('should handle empty objects and arrays', () => {
133
+ expect({}.stableStringify()).toBe('{}');
134
+ expect([].stableStringify()).toBe('[]');
135
+ });
136
+ it('should handle undefined values in objects', () => {
137
+ const obj = { a: undefined, b: 1 };
138
+ expect(obj.stableStringify()).toBe('{"a":undefined,"b":1}');
139
+ });
140
+ });
141
+ describe('stableHash()', () => {
142
+ it('should generate same hash for objects with different key order', () => {
143
+ const obj1 = { b: 2, a: 1 };
144
+ const obj2 = { a: 1, b: 2 };
145
+ expect(obj1.stableHash()).toBe(obj2.stableHash());
146
+ });
147
+ it('should generate different hashes for different objects', () => {
148
+ const obj1 = { a: 1, b: 2 };
149
+ const obj2 = { a: 1, b: 3 };
150
+ expect(obj1.stableHash()).not.toBe(obj2.stableHash());
151
+ });
152
+ it('should generate consistent hashes', () => {
153
+ const obj = { a: 1, b: 2 };
154
+ const hash1 = obj.stableHash();
155
+ const hash2 = obj.stableHash();
156
+ expect(hash1).toBe(hash2);
157
+ });
158
+ it('should generate hex string', () => {
159
+ const obj = { a: 1 };
160
+ const hash = obj.stableHash();
161
+ expect(/^[0-9a-f]+$/.test(hash)).toBe(true);
162
+ });
163
+ it('should handle arrays', () => {
164
+ const arr1 = [1, 2, 3];
165
+ const arr2 = [1, 2, 3];
166
+ expect(arr1.stableHash()).toBe(arr2.stableHash());
167
+ });
168
+ it('should handle different arrays', () => {
169
+ const arr1 = [1, 2, 3];
170
+ const arr2 = [3, 2, 1];
171
+ expect(arr1.stableHash()).not.toBe(arr2.stableHash());
172
+ });
173
+ it('should handle nested structures', () => {
174
+ const obj1 = { a: [1, { b: 2 }], c: 3 };
175
+ const obj2 = { c: 3, a: [1, { b: 2 }] };
176
+ expect(obj1.stableHash()).toBe(obj2.stableHash());
177
+ });
178
+ it('should handle empty objects', () => {
179
+ const obj1 = {};
180
+ const obj2 = {};
181
+ expect(obj1.stableHash()).toBe(obj2.stableHash());
182
+ });
183
+ });
110
184
  });
@@ -1,4 +1,3 @@
1
- export {};
2
1
  declare global {
3
2
  interface Set<T> {
4
3
  /**
@@ -103,3 +102,4 @@ declare global {
103
102
  intersection(...others: Set<T>[]): Set<T>;
104
103
  }
105
104
  }
105
+ export {};
@@ -1,4 +1,3 @@
1
- export {};
2
1
  declare global {
3
2
  interface String {
4
3
  /**
@@ -179,3 +178,4 @@ declare global {
179
178
  replaceRange(start: number, end: number, replaceString?: string): string;
180
179
  }
181
180
  }
181
+ export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "utilitish",
3
- "version": "0.0.7",
3
+ "version": "0.0.8",
4
4
  "description": "",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",