@naman_deep_singh/js-extensions 1.3.3 → 1.4.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 (38) hide show
  1. package/README.md +174 -425
  2. package/dist/cjs/array/array-extensions.js +84 -50
  3. package/dist/cjs/core/performance.d.ts +1 -0
  4. package/dist/cjs/core/performance.js +6 -0
  5. package/dist/cjs/core/version.d.ts +1 -0
  6. package/dist/cjs/core/version.js +9 -0
  7. package/dist/cjs/index.d.ts +1 -0
  8. package/dist/cjs/index.js +1 -0
  9. package/dist/cjs/number/number-extensions.js +85 -97
  10. package/dist/cjs/object/object-extensions.js +102 -103
  11. package/dist/cjs/string/string-extensions.js +66 -43
  12. package/dist/cjs/types/global-augmentations.d.ts +1 -0
  13. package/dist/cjs/utils/defineExtension.d.ts +1 -0
  14. package/dist/cjs/utils/defineExtension.js +13 -0
  15. package/dist/cjs/utils/index.d.ts +1 -0
  16. package/dist/cjs/utils/index.js +1 -0
  17. package/dist/esm/array/array-extensions.js +84 -50
  18. package/dist/esm/core/performance.d.ts +1 -0
  19. package/dist/esm/core/performance.js +5 -0
  20. package/dist/esm/core/version.d.ts +1 -0
  21. package/dist/esm/core/version.js +5 -0
  22. package/dist/esm/index.d.ts +1 -0
  23. package/dist/esm/index.js +1 -0
  24. package/dist/esm/number/number-extensions.js +86 -98
  25. package/dist/esm/object/object-extensions.js +102 -103
  26. package/dist/esm/string/string-extensions.js +66 -43
  27. package/dist/esm/types/global-augmentations.d.ts +1 -0
  28. package/dist/esm/utils/defineExtension.d.ts +1 -0
  29. package/dist/esm/utils/defineExtension.js +10 -0
  30. package/dist/esm/utils/index.d.ts +1 -0
  31. package/dist/esm/utils/index.js +1 -0
  32. package/dist/types/core/performance.d.ts +1 -0
  33. package/dist/types/core/version.d.ts +1 -0
  34. package/dist/types/index.d.ts +1 -0
  35. package/dist/types/types/global-augmentations.d.ts +1 -0
  36. package/dist/types/utils/defineExtension.d.ts +1 -0
  37. package/dist/types/utils/index.d.ts +1 -0
  38. package/package.json +8 -4
@@ -1,72 +1,43 @@
1
1
  "use strict";
2
- // Object prototype extensions
3
2
  Object.defineProperty(exports, "__esModule", { value: true });
4
3
  exports.extendObject = extendObject;
4
+ const utils_1 = require("src/utils");
5
+ let objectExtended = false;
5
6
  function extendObject() {
6
- Object.prototype.isEmpty = function () {
7
+ if (objectExtended)
8
+ return;
9
+ objectExtended = true;
10
+ (0, utils_1.defineExtension)(Object.prototype, 'isEmpty', function () {
7
11
  return Object.keys(this).length === 0;
8
- };
9
- Object.prototype.pick = function (keys) {
10
- if (this === null || this === undefined) {
11
- throw new TypeError('pick: cannot be called on null or undefined');
12
- }
13
- if (!Array.isArray(keys)) {
14
- throw new TypeError(`pick: keys must be an array, got ${typeof keys}`);
15
- }
16
- if (keys.length === 0) {
12
+ });
13
+ (0, utils_1.defineExtension)(Object.prototype, 'pick', function (keys) {
14
+ if (!Array.isArray(keys))
15
+ throw new TypeError('pick: keys must be an array');
16
+ if (!keys.length)
17
17
  throw new TypeError('pick: keys array cannot be empty');
18
- }
19
18
  const result = {};
20
- const obj = this;
21
19
  keys.forEach((key) => {
22
- if (key in obj) {
23
- result[key] = obj[key];
24
- }
20
+ if (key in this)
21
+ result[key] = this[key];
25
22
  });
26
23
  return result;
27
- };
28
- Object.prototype.omit = function (keys) {
29
- if (this === null || this === undefined) {
30
- throw new TypeError('omit: cannot be called on null or undefined');
31
- }
32
- if (!Array.isArray(keys)) {
33
- throw new TypeError(`omit: keys must be an array, got ${typeof keys}`);
34
- }
35
- if (keys.length === 0) {
24
+ });
25
+ (0, utils_1.defineExtension)(Object.prototype, 'omit', function (keys) {
26
+ if (!Array.isArray(keys))
27
+ throw new TypeError('omit: keys must be an array');
28
+ if (!keys.length)
36
29
  throw new TypeError('omit: keys array cannot be empty');
37
- }
38
30
  const result = { ...this };
39
- keys.forEach((key) => {
40
- delete result[key];
41
- });
31
+ keys.forEach((key) => delete result[key]);
42
32
  return result;
43
- };
44
- Object.prototype.deepClone = function () {
45
- // Simple cycle detection without caching key generation
46
- if (this === null || typeof this !== 'object')
47
- return this;
48
- // Handle Date objects
49
- if (this instanceof Date)
50
- return new Date(this.getTime());
51
- // Handle Array objects
52
- if (Array.isArray(this)) {
53
- return this.map((item) => {
54
- if (item &&
55
- typeof item === 'object' &&
56
- typeof item.deepClone === 'function') {
57
- return item.deepClone();
58
- }
59
- return item;
60
- });
61
- }
62
- // Handle regular objects with better cycle detection
33
+ });
34
+ (0, utils_1.defineExtension)(Object.prototype, 'deepClone', function () {
63
35
  const visited = new WeakSet();
64
36
  function deepCloneSafe(obj) {
65
37
  if (obj === null || typeof obj !== 'object')
66
38
  return obj;
67
- if (visited.has(obj)) {
39
+ if (visited.has(obj))
68
40
  throw new Error('Circular reference detected in deepClone');
69
- }
70
41
  visited.add(obj);
71
42
  if (obj instanceof Date)
72
43
  return new Date(obj.getTime());
@@ -79,69 +50,97 @@ function extendObject() {
79
50
  return cloned;
80
51
  }
81
52
  return deepCloneSafe(this);
82
- };
83
- Object.prototype.merge = function (other) {
53
+ });
54
+ (0, utils_1.defineExtension)(Object.prototype, 'merge', function (other) {
84
55
  return { ...this, ...other };
85
- };
86
- Object.prototype.deepFreeze = function () {
87
- const propNames = Object.getOwnPropertyNames(this);
88
- for (const name of propNames) {
56
+ });
57
+ (0, utils_1.defineExtension)(Object.prototype, 'deepFreeze', function () {
58
+ Object.getOwnPropertyNames(this).forEach((name) => {
89
59
  const value = this[name];
90
- if (value && typeof value === 'object') {
91
- value.deepFreeze();
92
- }
93
- }
60
+ if (value && typeof value === 'object')
61
+ value.deepFreeze?.();
62
+ });
94
63
  return Object.freeze(this);
95
- };
96
- Object.prototype.hasPath = function (path) {
97
- if (typeof path !== 'string') {
98
- throw new TypeError(`hasPath: path must be a string, got ${typeof path}`);
99
- }
100
- if (path.trim() === '') {
101
- throw new TypeError('hasPath: path cannot be empty or whitespace');
102
- }
64
+ });
65
+ (0, utils_1.defineExtension)(Object.prototype, 'hasPath', function (path) {
66
+ if (!path.trim())
67
+ throw new TypeError('hasPath: path cannot be empty');
68
+ return path.split('.').every((key) => {
69
+ if (this == null || !(key in this))
70
+ return false;
71
+ // @ts-ignore
72
+ this = this[key];
73
+ return true;
74
+ });
75
+ });
76
+ (0, utils_1.defineExtension)(Object.prototype, 'getPath', function (path, defaultValue) {
77
+ if (!path.trim())
78
+ throw new TypeError('getPath: path cannot be empty');
79
+ return path.split('.').reduce((acc, key) => (acc && key in acc ? acc[key] : defaultValue), this);
80
+ });
81
+ (0, utils_1.defineExtension)(Object.prototype, 'setPath', function (path, value) {
82
+ if (!path.trim())
83
+ throw new TypeError('setPath: path cannot be empty');
103
84
  const keys = path.split('.');
104
85
  let current = this;
105
- for (const key of keys) {
106
- if (current == null || !(key in current))
107
- return false;
108
- current = current[key];
86
+ for (let i = 0; i < keys.length - 1; i++) {
87
+ if (!(keys[i] in current) || typeof current[keys[i]] !== 'object')
88
+ current[keys[i]] = {};
89
+ current = current[keys[i]];
109
90
  }
110
- return true;
111
- };
112
- Object.prototype.getPath = function (path, defaultValue) {
113
- if (typeof path !== 'string') {
114
- throw new TypeError(`getPath: path must be a string, got ${typeof path}`);
91
+ current[keys[keys.length - 1]] = value;
92
+ return this;
93
+ });
94
+ (0, utils_1.defineExtension)(Object.prototype, 'mapValues', function (fn) {
95
+ if (typeof fn !== 'function') {
96
+ throw new TypeError(`mapValues: fn must be a function, got ${typeof fn}`);
115
97
  }
116
- if (path.trim() === '') {
117
- throw new TypeError('getPath: path cannot be empty or whitespace');
98
+ const result = {};
99
+ for (const key in this) {
100
+ if (Object.prototype.hasOwnProperty.call(this, key)) {
101
+ result[key] = fn(this[key], key);
102
+ }
118
103
  }
119
- const keys = path.split('.');
120
- let current = this;
121
- for (const key of keys) {
122
- if (current == null || !(key in current))
123
- return defaultValue;
124
- current = current[key];
104
+ return result;
105
+ });
106
+ (0, utils_1.defineExtension)(Object.prototype, 'mapKeys', function (fn) {
107
+ if (typeof fn !== 'function') {
108
+ throw new TypeError(`mapKeys: fn must be a function, got ${typeof fn}`);
125
109
  }
126
- return current;
127
- };
128
- Object.prototype.setPath = function (path, value) {
129
- if (typeof path !== 'string') {
130
- throw new TypeError(`setPath: path must be a string, got ${typeof path}`);
110
+ const result = {};
111
+ for (const key in this) {
112
+ if (Object.prototype.hasOwnProperty.call(this, key)) {
113
+ const newKey = fn(key);
114
+ result[newKey] = this[key];
115
+ }
131
116
  }
132
- if (path.trim() === '') {
133
- throw new TypeError('setPath: path cannot be empty or whitespace');
117
+ return result;
118
+ });
119
+ (0, utils_1.defineExtension)(Object.prototype, 'filterKeys', function (keys) {
120
+ if (!Array.isArray(keys)) {
121
+ throw new TypeError(`filterKeys: keys must be an array, got ${typeof keys}`);
134
122
  }
135
- const keys = path.split('.');
136
- let current = this;
137
- for (let i = 0; i < keys.length - 1; i++) {
138
- const key = keys[i];
139
- if (!(key in current) || typeof current[key] !== 'object') {
140
- current[key] = {};
123
+ const result = {};
124
+ for (const key of keys) {
125
+ if (key in this) {
126
+ result[key] = this[key];
141
127
  }
142
- current = current[key];
143
128
  }
144
- current[keys[keys.length - 1]] = value;
145
- return this;
146
- };
129
+ return result;
130
+ });
131
+ (0, utils_1.defineExtension)(Object.prototype, 'filterValues', function (fn) {
132
+ if (typeof fn !== 'function') {
133
+ throw new TypeError(`filterValues: fn must be a function, got ${typeof fn}`);
134
+ }
135
+ const result = {};
136
+ for (const key in this) {
137
+ if (Object.prototype.hasOwnProperty.call(this, key)) {
138
+ const val = this[key];
139
+ if (fn(val, key)) {
140
+ result[key] = val;
141
+ }
142
+ }
143
+ }
144
+ return result;
145
+ });
147
146
  }
@@ -1,37 +1,39 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.extendString = extendString;
4
- // String prototype extensions
4
+ const utils_1 = require("src/utils");
5
+ let stringExtended = false;
5
6
  function extendString() {
6
- String.prototype.toCapitalize = function () {
7
+ if (stringExtended)
8
+ return;
9
+ stringExtended = true;
10
+ (0, utils_1.defineExtension)(String.prototype, 'toCapitalize', function () {
7
11
  return this.charAt(0).toUpperCase() + this.slice(1).toLowerCase();
8
- };
9
- String.prototype.toCamelCase = function () {
10
- return this.replace(/[-_\s]+(.)?/g, (_, char) => char ? char.toUpperCase() : '');
11
- };
12
- String.prototype.toKebabCase = function () {
12
+ });
13
+ (0, utils_1.defineExtension)(String.prototype, 'toCamelCase', function () {
14
+ return this.replace(/[-_\s]+(.)?/g, (_, char) => (char ? char.toUpperCase() : ''));
15
+ });
16
+ (0, utils_1.defineExtension)(String.prototype, 'toKebabCase', function () {
13
17
  return this.replace(/([a-z])([A-Z])/g, '$1-$2')
14
18
  .replace(/[\s_]+/g, '-')
15
19
  .toLowerCase();
16
- };
17
- String.prototype.toSnakeCase = function () {
20
+ });
21
+ (0, utils_1.defineExtension)(String.prototype, 'toSnakeCase', function () {
18
22
  return this.replace(/([a-z])([A-Z])/g, '$1_$2')
19
23
  .replace(/[\s-]+/g, '_')
20
24
  .toLowerCase();
21
- };
22
- String.prototype.truncate = function (length, suffix = '...') {
25
+ });
26
+ (0, utils_1.defineExtension)(String.prototype, 'truncate', function (length, suffix = '...') {
23
27
  if (!Number.isInteger(length) || length < 0) {
24
28
  throw new TypeError(`truncate: length must be a non-negative integer, got ${length}`);
25
29
  }
26
- return this.length > length
27
- ? this.substring(0, length) + suffix
28
- : this.toString();
29
- };
30
- String.prototype.isEmail = function () {
30
+ return this.length > length ? this.substring(0, length) + suffix : this.toString();
31
+ });
32
+ (0, utils_1.defineExtension)(String.prototype, 'isEmail', function () {
31
33
  const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
32
34
  return emailRegex.test(this.toString());
33
- };
34
- String.prototype.isUrl = function () {
35
+ });
36
+ (0, utils_1.defineExtension)(String.prototype, 'isUrl', function () {
35
37
  try {
36
38
  new URL(this.toString());
37
39
  return true;
@@ -39,24 +41,24 @@ function extendString() {
39
41
  catch {
40
42
  return false;
41
43
  }
42
- };
43
- String.prototype.removeWhitespace = function () {
44
+ });
45
+ (0, utils_1.defineExtension)(String.prototype, 'removeWhitespace', function () {
44
46
  return this.replace(/\s+/g, '');
45
- };
46
- String.prototype.reverse = function () {
47
+ });
48
+ (0, utils_1.defineExtension)(String.prototype, 'reverse', function () {
47
49
  return this.split('').reverse().join('');
48
- };
49
- String.prototype.isPalindrome = function () {
50
+ });
51
+ (0, utils_1.defineExtension)(String.prototype, 'isPalindrome', function () {
50
52
  const cleaned = this.toLowerCase().replace(/[^a-z0-9]/g, '');
51
53
  return cleaned === cleaned.split('').reverse().join('');
52
- };
53
- String.prototype.toTitleCase = function () {
54
+ });
55
+ (0, utils_1.defineExtension)(String.prototype, 'toTitleCase', function () {
54
56
  return this.replace(/\w\S*/g, (txt) => txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase());
55
- };
56
- String.prototype.stripHtml = function () {
57
+ });
58
+ (0, utils_1.defineExtension)(String.prototype, 'stripHtml', function () {
57
59
  return this.replace(/<[^>]*>/g, '');
58
- };
59
- String.prototype.padStart = function (targetLength, padString = ' ') {
60
+ });
61
+ (0, utils_1.defineExtension)(String.prototype, 'padStart', function (targetLength, padString = ' ') {
60
62
  if (!Number.isInteger(targetLength) || targetLength < 0) {
61
63
  throw new TypeError(`padStart: targetLength must be a non-negative integer, got ${targetLength}`);
62
64
  }
@@ -67,8 +69,8 @@ function extendString() {
67
69
  throw new TypeError('padStart: padString cannot be empty');
68
70
  }
69
71
  return this.toString().padStart(targetLength, padString);
70
- };
71
- String.prototype.padEnd = function (targetLength, padString = ' ') {
72
+ });
73
+ (0, utils_1.defineExtension)(String.prototype, 'padEnd', function (targetLength, padString = ' ') {
72
74
  if (!Number.isInteger(targetLength) || targetLength < 0) {
73
75
  throw new TypeError(`padEnd: targetLength must be a non-negative integer, got ${targetLength}`);
74
76
  }
@@ -79,22 +81,43 @@ function extendString() {
79
81
  throw new TypeError('padEnd: padString cannot be empty');
80
82
  }
81
83
  return this.toString().padEnd(targetLength, padString);
82
- };
83
- String.prototype.count = function (substring) {
84
+ });
85
+ (0, utils_1.defineExtension)(String.prototype, 'count', function (substring) {
84
86
  if (typeof substring !== 'string') {
85
87
  throw new TypeError(`count: substring must be a string, got ${typeof substring}`);
86
88
  }
87
89
  if (substring === '') {
88
90
  throw new TypeError('count: substring cannot be empty');
89
91
  }
90
- return (this.match(new RegExp(substring, 'g')) || []).length;
91
- };
92
- String.prototype.words = function () {
93
- return this.trim()
94
- .split(/\s+/)
95
- .filter((word) => word.length > 0);
96
- };
97
- String.prototype.lines = function () {
92
+ const escaped = substring.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
93
+ return (this.match(new RegExp(escaped, 'g')) || []).length;
94
+ });
95
+ (0, utils_1.defineExtension)(String.prototype, 'words', function () {
96
+ return this.trim().split(/\s+/).filter((word) => word.length > 0);
97
+ });
98
+ (0, utils_1.defineExtension)(String.prototype, 'lines', function () {
98
99
  return this.split(/\r?\n/);
99
- };
100
+ });
101
+ (0, utils_1.defineExtension)(String.prototype, 'capitalizeWords', function () {
102
+ return this.toString().replace(/\b\w/g, (char) => char.toUpperCase());
103
+ });
104
+ (0, utils_1.defineExtension)(String.prototype, 'reverseWords', function () {
105
+ return this.toString().split(/\s+/).reverse().join(' ');
106
+ });
107
+ (0, utils_1.defineExtension)(String.prototype, 'truncateWords', function (count, suffix = '...') {
108
+ if (!Number.isInteger(count) || count < 0) {
109
+ throw new TypeError(`truncateWords: count must be a non-negative integer, got ${count}`);
110
+ }
111
+ const words = this.toString().split(/\s+/);
112
+ if (words.length <= count)
113
+ return this.toString();
114
+ return words.slice(0, count).join(' ') + suffix;
115
+ });
116
+ (0, utils_1.defineExtension)(String.prototype, 'slugify', function () {
117
+ return this.toString()
118
+ .toLowerCase()
119
+ .replace(/[^\w\s-]/g, '')
120
+ .trim()
121
+ .replace(/[\s_-]+/g, '-');
122
+ });
100
123
  }
@@ -26,6 +26,7 @@ declare global {
26
26
  sum(): number;
27
27
  average(): number;
28
28
  compact(): T[];
29
+ compactTruthy(): T[];
29
30
  pluck<K extends keyof T>(key: K): T[K][];
30
31
  findLast(predicate: (item: T) => boolean): T | undefined;
31
32
  partition(predicate: (item: T) => boolean): [T[], T[]];
@@ -0,0 +1 @@
1
+ export declare function defineExtension<T extends object>(prototype: T, name: string, fn: Function): void;
@@ -0,0 +1,13 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.defineExtension = defineExtension;
4
+ function defineExtension(prototype, name, fn) {
5
+ if (!Object.prototype.hasOwnProperty.call(prototype, name)) {
6
+ Object.defineProperty(prototype, name, {
7
+ value: fn,
8
+ writable: false,
9
+ configurable: false,
10
+ enumerable: false,
11
+ });
12
+ }
13
+ }
@@ -1,2 +1,3 @@
1
1
  export * from './config';
2
2
  export * from './helpers';
3
+ export * from './defineExtension';
@@ -17,3 +17,4 @@ Object.defineProperty(exports, "__esModule", { value: true });
17
17
  // Re-export all utilities
18
18
  __exportStar(require("./config"), exports);
19
19
  __exportStar(require("./helpers"), exports);
20
+ __exportStar(require("./defineExtension"), exports);
@@ -1,17 +1,21 @@
1
- // Array prototype extensions
1
+ import { defineExtension } from "src/utils";
2
+ let arrayExtended = false;
2
3
  export function extendArray() {
3
- Array.prototype.unique = function () {
4
+ if (arrayExtended)
5
+ return;
6
+ arrayExtended = true;
7
+ defineExtension(Array.prototype, 'unique', function () {
4
8
  return [...new Set(this)];
5
- };
6
- Array.prototype.shuffle = function () {
9
+ });
10
+ defineExtension(Array.prototype, 'shuffle', function () {
7
11
  const arr = [...this];
8
12
  for (let i = arr.length - 1; i > 0; i--) {
9
13
  const j = Math.floor(Math.random() * (i + 1));
10
14
  [arr[i], arr[j]] = [arr[j], arr[i]];
11
15
  }
12
16
  return arr;
13
- };
14
- Array.prototype.chunk = function (size) {
17
+ });
18
+ defineExtension(Array.prototype, 'chunk', function (size) {
15
19
  if (!Number.isInteger(size) || size <= 0) {
16
20
  throw new TypeError(`chunk: size must be a positive integer, got ${size}`);
17
21
  }
@@ -20,8 +24,8 @@ export function extendArray() {
20
24
  chunks.push(this.slice(i, i + size));
21
25
  }
22
26
  return chunks;
23
- };
24
- Array.prototype.groupBy = function (keyFn) {
27
+ });
28
+ defineExtension(Array.prototype, 'groupBy', function (keyFn) {
25
29
  if (typeof keyFn !== 'function') {
26
30
  throw new TypeError(`groupBy: keyFn must be a function, got ${typeof keyFn}`);
27
31
  }
@@ -32,33 +36,31 @@ export function extendArray() {
32
36
  groups[key].push(item);
33
37
  return groups;
34
38
  }, {});
35
- };
36
- Array.prototype.sum = function () {
37
- const numbers = this.filter((item) => typeof item === 'number');
38
- if (numbers.length === 0) {
39
+ });
40
+ defineExtension(Array.prototype, 'sum', function () {
41
+ if (this.length === 0) {
39
42
  throw new TypeError('sum: array must contain at least one number');
40
43
  }
41
- return numbers.reduce((sum, num) => sum + num, 0);
42
- };
43
- Array.prototype.average = function () {
44
- const numbers = this.filter((item) => typeof item === 'number');
45
- if (numbers.length === 0) {
44
+ return this.reduce((sum, num) => sum + num, 0);
45
+ });
46
+ defineExtension(Array.prototype, 'average', function () {
47
+ if (this.length === 0) {
46
48
  throw new TypeError('average: array must contain at least one number');
47
49
  }
48
- return numbers.reduce((sum, num) => sum + num, 0) / numbers.length;
49
- };
50
- Array.prototype.compact = function () {
51
- return this.filter((item) => item != null && item !== '' && item !== false);
52
- };
53
- Array.prototype.pluck = function (key) {
54
- if (typeof key !== 'string' &&
55
- typeof key !== 'number' &&
56
- typeof key !== 'symbol') {
57
- throw new TypeError(`pluck: key must be a string, number, or symbol, got ${typeof key}`);
58
- }
59
- return this.map((item) => item && typeof item === 'object' ? item[key] : undefined).filter((val) => val !== undefined);
60
- };
61
- Array.prototype.findLast = function (predicate) {
50
+ return this.reduce((sum, num) => sum + num, 0) / this.length;
51
+ });
52
+ defineExtension(Array.prototype, 'compact', function () {
53
+ return this.filter((item) => item !== null && item !== undefined && item !== '');
54
+ });
55
+ defineExtension(Array.prototype, 'compactTruthy', function () {
56
+ return this.filter(Boolean);
57
+ });
58
+ defineExtension(Array.prototype, 'pluck', function (key) {
59
+ return this
60
+ .map((item) => item[key])
61
+ .filter((val) => val !== undefined);
62
+ });
63
+ defineExtension(Array.prototype, 'findLast', function (predicate) {
62
64
  if (typeof predicate !== 'function') {
63
65
  throw new TypeError(`findLast: predicate must be a function, got ${typeof predicate}`);
64
66
  }
@@ -67,8 +69,8 @@ export function extendArray() {
67
69
  return this[i];
68
70
  }
69
71
  return undefined;
70
- };
71
- Array.prototype.partition = function (predicate) {
72
+ });
73
+ defineExtension(Array.prototype, 'partition', function (predicate) {
72
74
  if (typeof predicate !== 'function') {
73
75
  throw new TypeError(`partition: predicate must be a function, got ${typeof predicate}`);
74
76
  }
@@ -76,42 +78,74 @@ export function extendArray() {
76
78
  const falsy = [];
77
79
  this.forEach((item) => predicate(item) ? truthy.push(item) : falsy.push(item));
78
80
  return [truthy, falsy];
79
- };
80
- Array.prototype.flatten = function (depth = 1) {
81
+ });
82
+ defineExtension(Array.prototype, 'flatten', function (depth = 1) {
81
83
  return depth > 0
82
84
  ? this.reduce((acc, val) => acc.concat(Array.isArray(val) ? val.flatten(depth - 1) : val), [])
83
85
  : this.slice();
84
- };
85
- Array.prototype.deepFlatten = function () {
86
+ });
87
+ defineExtension(Array.prototype, 'deepFlatten', function () {
86
88
  return this.reduce((acc, val) => acc.concat(Array.isArray(val) ? val.deepFlatten() : val), []);
87
- };
88
- Array.prototype.difference = function (other) {
89
+ });
90
+ defineExtension(Array.prototype, 'difference', function (other) {
89
91
  if (!Array.isArray(other)) {
90
92
  throw new TypeError(`difference: other must be an array, got ${typeof other}`);
91
93
  }
92
94
  return this.filter((item) => !other.includes(item));
93
- };
94
- Array.prototype.intersection = function (other) {
95
+ });
96
+ defineExtension(Array.prototype, 'intersection', function (other) {
95
97
  if (!Array.isArray(other)) {
96
98
  throw new TypeError(`intersection: other must be an array, got ${typeof other}`);
97
99
  }
98
100
  return this.filter((item) => other.includes(item));
99
- };
100
- Array.prototype.union = function (other) {
101
+ });
102
+ defineExtension(Array.prototype, 'union', function (other) {
101
103
  if (!Array.isArray(other)) {
102
104
  throw new TypeError(`union: other must be an array, got ${typeof other}`);
103
105
  }
104
106
  return [...new Set([...this, ...other])];
105
- };
106
- Array.prototype.sample = function () {
107
+ });
108
+ defineExtension(Array.prototype, 'sample', function () {
107
109
  return this.length > 0
108
110
  ? this[Math.floor(Math.random() * this.length)]
109
111
  : undefined;
110
- };
111
- Array.prototype.take = function (count) {
112
+ });
113
+ defineExtension(Array.prototype, 'take', function (count) {
112
114
  return this.slice(0, Math.max(0, count));
113
- };
114
- Array.prototype.drop = function (count) {
115
+ });
116
+ defineExtension(Array.prototype, 'drop', function (count) {
115
117
  return this.slice(Math.max(0, count));
116
- };
118
+ });
119
+ defineExtension(Array.prototype, 'uniqueBy', function (keyFn) {
120
+ if (typeof keyFn !== 'function') {
121
+ throw new TypeError(`uniqueBy: keyFn must be a function, got ${typeof keyFn}`);
122
+ }
123
+ const seen = new Set();
124
+ const result = [];
125
+ for (const item of this) {
126
+ const key = keyFn(item);
127
+ if (!seen.has(key)) {
128
+ seen.add(key);
129
+ result.push(item);
130
+ }
131
+ }
132
+ return result;
133
+ });
134
+ defineExtension(Array.prototype, 'sortBy', function (keyFn) {
135
+ if (typeof keyFn !== 'function') {
136
+ throw new TypeError(`sortBy: keyFn must be a function, got ${typeof keyFn}`);
137
+ }
138
+ return [...this].sort((a, b) => {
139
+ const aVal = keyFn(a);
140
+ const bVal = keyFn(b);
141
+ if (aVal < bVal)
142
+ return -1;
143
+ if (aVal > bVal)
144
+ return 1;
145
+ return 0;
146
+ });
147
+ });
148
+ defineExtension(Array.prototype, 'last', function () {
149
+ return this.length > 0 ? this[this.length - 1] : undefined;
150
+ });
117
151
  }
@@ -13,4 +13,5 @@ export declare class LRUCache<K, V> {
13
13
  set(key: K, value: V): void;
14
14
  clear(): void;
15
15
  }
16
+ export declare function makeInternalCacheKey(domain: string, key: string | number): string;
16
17
  export declare function withCache<T>(key: string, fn: () => T): T;