vest-utils 1.4.0 → 1.5.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 (157) hide show
  1. package/README.md +2 -7
  2. package/dist/cjs/minifyObject.development.js +217 -0
  3. package/dist/cjs/minifyObject.development.js.map +1 -0
  4. package/dist/cjs/minifyObject.js +6 -0
  5. package/dist/cjs/minifyObject.production.js +2 -0
  6. package/dist/cjs/minifyObject.production.js.map +1 -0
  7. package/dist/cjs/package.json +1 -0
  8. package/dist/cjs/vest-utils.development.js +378 -0
  9. package/dist/cjs/vest-utils.development.js.map +1 -0
  10. package/dist/cjs/vest-utils.js +6 -0
  11. package/dist/cjs/vest-utils.production.js +2 -0
  12. package/dist/cjs/vest-utils.production.js.map +1 -0
  13. package/dist/es/minifyObject.development.js +214 -0
  14. package/dist/es/minifyObject.development.js.map +1 -0
  15. package/dist/es/minifyObject.production.js +2 -0
  16. package/dist/es/minifyObject.production.js.map +1 -0
  17. package/dist/es/package.json +1 -0
  18. package/dist/es/vest-utils.development.js +330 -0
  19. package/dist/es/vest-utils.development.js.map +1 -0
  20. package/dist/es/vest-utils.production.js +2 -0
  21. package/dist/es/vest-utils.production.js.map +1 -0
  22. package/dist/umd/minifyObject.development.js +223 -0
  23. package/dist/umd/minifyObject.development.js.map +1 -0
  24. package/dist/umd/minifyObject.production.js +2 -0
  25. package/dist/umd/minifyObject.production.js.map +1 -0
  26. package/dist/umd/vest-utils.development.js +384 -0
  27. package/dist/umd/vest-utils.development.js.map +1 -0
  28. package/dist/umd/vest-utils.production.js +2 -0
  29. package/dist/umd/vest-utils.production.js.map +1 -0
  30. package/minifyObject/package.json +8 -12
  31. package/package.json +58 -43
  32. package/types/{exports/minifyObject.d.cts → minifyObject.d.ts} +2 -4
  33. package/types/minifyObject.d.ts.map +1 -0
  34. package/types/vest-utils.d.ts +143 -245
  35. package/types/vest-utils.d.ts.map +1 -0
  36. package/vitest.config.ts +45 -9
  37. package/dist/chunk-CLMFDpHK.mjs +0 -18
  38. package/dist/exports/minifyObject.cjs +0 -114
  39. package/dist/exports/minifyObject.cjs.map +0 -1
  40. package/dist/exports/minifyObject.mjs +0 -113
  41. package/dist/exports/minifyObject.mjs.map +0 -1
  42. package/dist/exports/standardSchemaSpec.cjs +0 -0
  43. package/dist/exports/standardSchemaSpec.mjs +0 -1
  44. package/dist/isEmpty-BBxAFjjm.mjs +0 -103
  45. package/dist/isEmpty-BBxAFjjm.mjs.map +0 -1
  46. package/dist/isEmpty-BuEa-96Q.cjs +0 -235
  47. package/dist/isEmpty-BuEa-96Q.cjs.map +0 -1
  48. package/dist/vest-utils.cjs +0 -510
  49. package/dist/vest-utils.cjs.map +0 -1
  50. package/dist/vest-utils.mjs +0 -421
  51. package/dist/vest-utils.mjs.map +0 -1
  52. package/src/Brand.ts +0 -9
  53. package/src/IO.ts +0 -2
  54. package/src/Predicates.ts +0 -13
  55. package/src/Result.ts +0 -121
  56. package/src/SimpleStateMachine.ts +0 -157
  57. package/src/StringObject.ts +0 -6
  58. package/src/__tests__/Architecture.test.ts +0 -69
  59. package/src/__tests__/Predicates.test.ts +0 -118
  60. package/src/__tests__/Result.test.ts +0 -284
  61. package/src/__tests__/SimpleStateMachine.test.ts +0 -425
  62. package/src/__tests__/StringObject.test.ts +0 -18
  63. package/src/__tests__/asArray.test.ts +0 -14
  64. package/src/__tests__/bindNot.test.ts +0 -39
  65. package/src/__tests__/bus.test.ts +0 -135
  66. package/src/__tests__/cache.test.ts +0 -139
  67. package/src/__tests__/callEach.test.ts +0 -20
  68. package/src/__tests__/defaultTo.test.ts +0 -52
  69. package/src/__tests__/deferThrow.test.ts +0 -26
  70. package/src/__tests__/either.test.ts +0 -17
  71. package/src/__tests__/freezeAssign.test.ts +0 -24
  72. package/src/__tests__/greaterThan.test.ts +0 -68
  73. package/src/__tests__/invariant.test.ts +0 -47
  74. package/src/__tests__/isArray.test.ts +0 -16
  75. package/src/__tests__/isBoolean.test.ts +0 -16
  76. package/src/__tests__/isEmpty.test.ts +0 -55
  77. package/src/__tests__/isEmptySet.test.ts +0 -22
  78. package/src/__tests__/isNull.test.ts +0 -26
  79. package/src/__tests__/isNumeric.test.ts +0 -27
  80. package/src/__tests__/isPositive.test.ts +0 -38
  81. package/src/__tests__/isPromise.test.ts +0 -17
  82. package/src/__tests__/isString.test.ts +0 -13
  83. package/src/__tests__/isUndefined.test.ts +0 -27
  84. package/src/__tests__/isUnsafeKey.test.ts +0 -22
  85. package/src/__tests__/lengthEquals.test.ts +0 -58
  86. package/src/__tests__/longerThan.test.ts +0 -58
  87. package/src/__tests__/mapFirst.test.ts +0 -31
  88. package/src/__tests__/nonnullish.test.ts +0 -25
  89. package/src/__tests__/noop.test.ts +0 -12
  90. package/src/__tests__/numberEquals.test.ts +0 -67
  91. package/src/__tests__/optionalFunctionValue.test.ts +0 -29
  92. package/src/__tests__/seq.test.ts +0 -29
  93. package/src/__tests__/text.test.ts +0 -41
  94. package/src/__tests__/tinyState.test.ts +0 -68
  95. package/src/__tests__/toNumber.test.ts +0 -39
  96. package/src/__tests__/vest-utils.test.ts +0 -13
  97. package/src/__tests__/withCatch.test.ts +0 -17
  98. package/src/__tests__/withResolvers.test.ts +0 -45
  99. package/src/asArray.ts +0 -3
  100. package/src/assign.ts +0 -1
  101. package/src/bindNot.ts +0 -3
  102. package/src/bus.ts +0 -52
  103. package/src/cache.ts +0 -68
  104. package/src/callEach.ts +0 -5
  105. package/src/defaultTo.ts +0 -9
  106. package/src/deferThrow.ts +0 -7
  107. package/src/dynamicValue.ts +0 -9
  108. package/src/either.ts +0 -3
  109. package/src/exports/__tests__/minifyObject.security.test.ts +0 -65
  110. package/src/exports/__tests__/minifyObject.test.ts +0 -281
  111. package/src/exports/minifyObject.ts +0 -198
  112. package/src/exports/standardSchemaSpec.ts +0 -70
  113. package/src/freezeAssign.ts +0 -5
  114. package/src/globals.d.ts +0 -3
  115. package/src/greaterThan.ts +0 -8
  116. package/src/hasOwnProperty.ts +0 -9
  117. package/src/invariant.ts +0 -19
  118. package/src/isArrayValue.ts +0 -11
  119. package/src/isBooleanValue.ts +0 -3
  120. package/src/isEmpty.ts +0 -18
  121. package/src/isEmptySet.ts +0 -15
  122. package/src/isFunction.ts +0 -5
  123. package/src/isNull.ts +0 -7
  124. package/src/isNullish.ts +0 -10
  125. package/src/isNumeric.ts +0 -11
  126. package/src/isPositive.ts +0 -5
  127. package/src/isPromise.ts +0 -5
  128. package/src/isStringValue.ts +0 -3
  129. package/src/isUndefined.ts +0 -7
  130. package/src/isUnsafeKey.ts +0 -3
  131. package/src/lengthEquals.ts +0 -11
  132. package/src/longerThan.ts +0 -8
  133. package/src/mapFirst.ts +0 -25
  134. package/src/nonnullish.ts +0 -9
  135. package/src/noop.ts +0 -1
  136. package/src/numberEquals.ts +0 -11
  137. package/src/seq.ts +0 -16
  138. package/src/text.ts +0 -20
  139. package/src/tinyState.ts +0 -28
  140. package/src/toNumber.ts +0 -11
  141. package/src/utilityTypes.ts +0 -25
  142. package/src/valueIsObject.ts +0 -5
  143. package/src/vest-utils.ts +0 -73
  144. package/src/withCatch.ts +0 -11
  145. package/src/withResolvers.ts +0 -33
  146. package/standardSchemaSpec/package.json +0 -14
  147. package/types/exports/minifyObject.d.cts.map +0 -1
  148. package/types/exports/minifyObject.d.mts +0 -7
  149. package/types/exports/minifyObject.d.mts.map +0 -1
  150. package/types/exports/standardSchemaSpec.d.cts +0 -59
  151. package/types/exports/standardSchemaSpec.d.cts.map +0 -1
  152. package/types/exports/standardSchemaSpec.d.mts +0 -59
  153. package/types/exports/standardSchemaSpec.d.mts.map +0 -1
  154. package/types/vest-utils.d.cts +0 -296
  155. package/types/vest-utils.d.cts.map +0 -1
  156. package/types/vest-utils.d.mts +0 -295
  157. package/types/vest-utils.d.mts.map +0 -1
package/src/cache.ts DELETED
@@ -1,68 +0,0 @@
1
- import { lengthEquals } from './lengthEquals';
2
- import { longerThan } from './longerThan';
3
- import { DynamicValue, Nullable } from './utilityTypes';
4
- import { dynamicValue } from './vest-utils';
5
-
6
- /**
7
- * Creates a cache function
8
- */
9
- export default function createCache<T = unknown>(maxSize = 1): CacheApi<T> {
10
- const cacheStorage: Array<[unknown[], T]> = [];
11
-
12
- const cache = (deps: unknown[], cacheAction: DynamicValue<T>): T => {
13
- const cacheHit = cache.get(deps);
14
- // cache hit is not null
15
- if (cacheHit) return cacheHit[1];
16
-
17
- const result = dynamicValue(cacheAction);
18
- cacheStorage.unshift([deps.concat(), result]);
19
-
20
- trimToSize();
21
-
22
- return result;
23
- };
24
-
25
- // invalidate an item in the cache by its dependencies
26
- cache.invalidate = (deps: any[]): void => {
27
- const index = findIndex(deps);
28
- if (index > -1) cacheStorage.splice(index, 1);
29
- };
30
-
31
- // Retrieves an item from the cache.
32
- cache.get = (deps: unknown[]): Nullable<[unknown[], T]> =>
33
- cacheStorage[findIndex(deps)] || null;
34
-
35
- // sets a value to the cache by its dependencies, updating if a hit is found.
36
- cache.set = (deps: unknown[], value: T): void => {
37
- const index = findIndex(deps);
38
- if (index > -1) {
39
- cacheStorage[index] = [deps, value];
40
- } else {
41
- cacheStorage.unshift([deps, value]);
42
- }
43
- trimToSize();
44
- };
45
-
46
- return cache;
47
-
48
- function trimToSize() {
49
- if (longerThan(cacheStorage, maxSize)) {
50
- cacheStorage.length = maxSize;
51
- }
52
- }
53
-
54
- function findIndex(deps: unknown[]): number {
55
- return cacheStorage.findIndex(
56
- ([cachedDeps]) =>
57
- lengthEquals(deps, cachedDeps.length) &&
58
- deps.every((dep, i) => dep === cachedDeps[i]),
59
- );
60
- }
61
- }
62
-
63
- export type CacheApi<T = unknown> = {
64
- (deps: unknown[], cacheAction: DynamicValue<T>): T;
65
- get(deps: unknown[]): Nullable<[unknown[], T]>;
66
- set(deps: unknown[], value: T): void;
67
- invalidate(item: any): void;
68
- };
package/src/callEach.ts DELETED
@@ -1,5 +0,0 @@
1
- import type { CB } from './utilityTypes';
2
-
3
- export default function callEach(arr: CB[]): void {
4
- return arr.forEach(fn => fn());
5
- }
package/src/defaultTo.ts DELETED
@@ -1,9 +0,0 @@
1
- import dynamicValue from './dynamicValue';
2
- import { DynamicValue, Nullish } from './utilityTypes';
3
-
4
- export default function defaultTo<T>(
5
- value: DynamicValue<Nullish<T>>,
6
- defaultValue: DynamicValue<T>,
7
- ): T {
8
- return dynamicValue(value) ?? dynamicValue(defaultValue);
9
- }
package/src/deferThrow.ts DELETED
@@ -1,7 +0,0 @@
1
- export default function deferThrow(message?: string): void {
2
- setTimeout(() => {
3
- throw new Error(message);
4
- }, 0);
5
- }
6
-
7
- export type TDeferThrow = typeof deferThrow;
@@ -1,9 +0,0 @@
1
- import isFunction from './isFunction';
2
- import { DynamicValue } from './utilityTypes';
3
-
4
- export default function dynamicValue<T>(
5
- value: DynamicValue<T>,
6
- ...args: unknown[]
7
- ): T {
8
- return isFunction(value) ? value(...args) : value;
9
- }
package/src/either.ts DELETED
@@ -1,3 +0,0 @@
1
- export default function either(a: unknown, b: unknown): boolean {
2
- return !!a !== !!b;
3
- }
@@ -1,65 +0,0 @@
1
- import { describe, it, expect } from 'vitest';
2
- import { expandObject } from '../minifyObject';
3
-
4
- describe('minifyObject Security', () => {
5
- describe('Prototype Pollution', () => {
6
- it('Should not allow pollution via "__proto__" key', () => {
7
- const maliciousPayload = '{"__proto__":{"polluted":true}}';
8
- const parsed = JSON.parse(maliciousPayload);
9
-
10
- // Simulate expansion (minifyObject's expandObject handles the reconstruction)
11
- // We pass an empty map as we are testing raw key assignment
12
- const expanded = expandObject(parsed, {});
13
-
14
- // @ts-ignore
15
- expect({}.polluted).toBeUndefined();
16
- expect(expanded.polluted).toBeUndefined();
17
- // @ts-ignore
18
- expect(expanded.__proto__).not.toEqual({ polluted: true });
19
- });
20
-
21
- it('Should not allow pollution via "constructor" key', () => {
22
- const maliciousPayload =
23
- '{"constructor":{"prototype":{"polluted":true}}}';
24
- const parsed = JSON.parse(maliciousPayload);
25
-
26
- const expanded = expandObject(parsed, {});
27
-
28
- // @ts-ignore
29
- expect({}.polluted).toBeUndefined();
30
- expect(expanded.constructor?.prototype?.polluted).toBeUndefined();
31
- });
32
-
33
- it('Should not allow pollution via "prototype" key', () => {
34
- const maliciousPayload = '{"prototype":{"polluted":true}}';
35
- const parsed = JSON.parse(maliciousPayload);
36
-
37
- expandObject(parsed, {});
38
-
39
- // @ts-ignore
40
- expect({}.polluted).toBeUndefined();
41
- });
42
- });
43
-
44
- describe('Sanity: Unprotected JSON.parse behavior', () => {
45
- it('Should preserve "__proto__" as a plain key when using standard JSON.parse', () => {
46
- const json = '{"__proto__":{"polluted":true}}';
47
- const parsed = JSON.parse(json);
48
-
49
- // This confirms that the environment allows parsing this key
50
- // which justifies the need for our protections.
51
- expect(Object.prototype.hasOwnProperty.call(parsed, '__proto__')).toBe(
52
- true,
53
- );
54
- expect(parsed.__proto__).toEqual({ polluted: true });
55
- });
56
-
57
- it('Should preserve "constructor" as a plain key when using standard JSON.parse', () => {
58
- const json = '{"constructor":{"prototype":{"polluted":true}}}';
59
- const parsed = JSON.parse(json);
60
-
61
- expect(parsed).toHaveProperty('constructor');
62
- expect(parsed.constructor).toEqual({ prototype: { polluted: true } });
63
- });
64
- });
65
- });
@@ -1,281 +0,0 @@
1
- import { describe, it, expect, vi } from 'vitest';
2
-
3
- import { expandObject, minifyObject } from '../minifyObject';
4
-
5
- describe('minifyObject', () => {
6
- it('should be a function', () => {
7
- expect(typeof minifyObject).toBe('function');
8
- });
9
-
10
- it('should return an array', () => {
11
- expect(Array.isArray(minifyObject({}))).toBe(true);
12
- });
13
-
14
- it('should return an array with two items', () => {
15
- expect(minifyObject({})).toHaveLength(2);
16
- });
17
-
18
- it('should return an array with two items, the first being an object', () => {
19
- expect(typeof minifyObject({})[0]).toBe('object');
20
- });
21
-
22
- it('should return an array with two items, the second being an object', () => {
23
- expect(typeof minifyObject({})[1]).toBe('object');
24
- });
25
-
26
- it('Should return a minified object', () => {
27
- // we can't really test this because the minified object is random
28
- // but we can test that the object is minified
29
- // by checking that it is smaller than the original
30
-
31
- const obj = {
32
- lorem: 'ipsum',
33
- dolor: 'sit',
34
- amet: 'consectetur',
35
- adipiscing: 'elit',
36
- sed: [
37
- 'lorem',
38
- 'ipsum',
39
- 'dolor',
40
- 'sit',
41
- 'amet',
42
- 'consectetur',
43
- 'adipiscing',
44
- 'elit',
45
- ],
46
- sed2: [
47
- 'lorem',
48
- 'ipsum',
49
- 'dolor',
50
- 'sit',
51
- 'amet',
52
- 'consectetur',
53
- 'adipiscing',
54
- 'elit',
55
- ],
56
- };
57
-
58
- const [minified] = minifyObject(obj);
59
- expect(JSON.stringify(minified).length).toBeLessThan(
60
- JSON.stringify(obj).length,
61
- );
62
- });
63
-
64
- it('Should return a minified object with a reverse map', () => {
65
- // check that all the keys in the reverse map are in the minified object
66
- // either as a key or a value
67
- const obj = {
68
- lorem: 'ipsum',
69
- dolor: 'sit',
70
- amet: 'consectetur',
71
- adipiscing: 'elit',
72
- };
73
-
74
- const [minified, reverseMap] = minifyObject(obj);
75
-
76
- for (const key in reverseMap) {
77
- expect(minified[key] || Object.values(minified).includes(key)).toBe(true);
78
- }
79
- });
80
-
81
- describe('When a key only appears once', () => {
82
- it('Should remain in place', () => {
83
- const obj = {
84
- lorem: 'ipsum',
85
- dolor: false,
86
- amet: true,
87
- adipiscing: 1,
88
- };
89
-
90
- const [minified] = minifyObject(obj);
91
- expect(minified).toEqual(obj);
92
- });
93
-
94
- it('Should not appear in the mapping', () => {
95
- const obj = {
96
- lorem: 'ipsum',
97
- dolor: false,
98
- amet: true,
99
- adipiscing: 1,
100
- };
101
-
102
- const [, reverseMap] = minifyObject(obj);
103
- expect(reverseMap).toEqual({});
104
- });
105
- });
106
-
107
- it('Should omit nullish values', () => {
108
- const obj = {
109
- lorem: 'ipsum',
110
- dolor: null,
111
- amet: undefined,
112
- adipiscing: '',
113
- };
114
-
115
- const [minified] = minifyObject(obj);
116
- expect(minified).toEqual({ lorem: 'ipsum', adipiscing: '' });
117
- });
118
-
119
- it('Should omit functions', () => {
120
- const obj = {
121
- lorem: 'ipsum',
122
- dolor: () => {},
123
- amet() {},
124
- adipiscing: '',
125
- };
126
-
127
- const [minified] = minifyObject(obj);
128
- expect(minified).toEqual({ lorem: 'ipsum', adipiscing: '' });
129
- });
130
-
131
- it('Should omit symbols', () => {
132
- const obj = {
133
- lorem: 'ipsum',
134
- dolor: Symbol('dolor'),
135
- amet: Symbol('amet'),
136
- adipiscing: '',
137
- };
138
-
139
- const [minified] = minifyObject(obj);
140
- expect(minified).toEqual({ lorem: 'ipsum', adipiscing: '' });
141
- });
142
-
143
- it('Should give the most occuring values or keys the first keys', () => {
144
- const obj = {
145
- consectetur: ['consectetur'],
146
- amet: ['amet', 'amet'],
147
- sit: ['sit', 'sit', 'sit'],
148
- dolor: ['dolor', 'dolor', 'dolor', 'dolor'],
149
- ipsum: ['ipsum', 'ipsum', 'ipsum', 'ipsum', 'ipsum'],
150
- lorem: ['lorem', 'lorem', 'lorem', 'lorem', 'lorem', 'lorem'],
151
- };
152
-
153
- const [_m, map] = minifyObject(obj);
154
-
155
- expect(map).toEqual({
156
- 0: 'lorem',
157
- 1: 'ipsum',
158
- 2: 'dolor',
159
- 3: 'sit',
160
- 4: 'amet',
161
- 5: 'consectetur',
162
- });
163
- });
164
-
165
- it('Should only minify values that are longer than the next key', () => {
166
- const obj = {
167
- a: 1,
168
- b: 2,
169
- c: 3,
170
- lorem: 'lorem',
171
- };
172
- expect(minifyObject(obj)).toEqual([
173
- {
174
- a: 1,
175
- b: 2,
176
- c: 3,
177
- 0: '0',
178
- },
179
- { 0: 'lorem' },
180
- ]);
181
- });
182
-
183
- describe('replacer function', () => {
184
- it('Should be a function receiving the value and key being iterated', () => {
185
- const replacer = vi.fn((value: any, _key: string) => value);
186
- const obj = {
187
- lorem: 'hi!',
188
- a: 1,
189
- };
190
- minifyObject(obj, replacer);
191
-
192
- expect(replacer.mock.calls[0]).toEqual(['hi!', 'lorem']);
193
- expect(replacer.mock.calls[1]).toEqual([1, 'a']);
194
- });
195
-
196
- it('Should replace the object value when a different value is being returned', () => {
197
- const obj = {
198
- lorem: 'hi!',
199
- a: 1,
200
- };
201
-
202
- expect(minifyObject(obj, v => v + 1)).toEqual([
203
- {
204
- a: 2,
205
- lorem: 'hi!1',
206
- },
207
- {},
208
- ]);
209
- });
210
-
211
- it('Should omit values when returning undefined', () => {
212
- const obj = {
213
- lorem: 'hi!',
214
- a: 1,
215
- };
216
-
217
- expect(
218
- minifyObject(obj, (v, k) => (k === 'lorem' ? undefined : v)),
219
- ).toEqual([
220
- {
221
- a: 1,
222
- },
223
- {},
224
- ]);
225
- });
226
- });
227
-
228
- describe('Auto removal of empty objects', () => {
229
- it('Should remove empty objects', () => {
230
- expect(
231
- minifyObject({
232
- a: 1,
233
- data: {},
234
- }),
235
- ).toEqual([{ a: 1 }, {}]);
236
- });
237
-
238
- it('Should remove empty arrays', () => {
239
- expect(
240
- minifyObject({
241
- a: 1,
242
- data: [],
243
- }),
244
- ).toEqual([{ a: 1 }, {}]);
245
- });
246
- });
247
- });
248
-
249
- describe('expandObject', () => {
250
- it('should be a function', () => {
251
- expect(typeof expandObject).toBe('function');
252
- });
253
- it('should take the output of minifyObject and return a clone original object', () => {
254
- const obj = {
255
- lorem: 'ipsum',
256
- dolor: ['lorem', 'ipsum'],
257
- amet: {
258
- lorem: 'ipsum',
259
- dolor: ['sit', 'amet'],
260
- },
261
- adipiscing: 'elit',
262
- number: 1,
263
- boolean: true,
264
- someValues: [
265
- 'lorem',
266
- 'ipsum',
267
- 'dolor',
268
- 'sit',
269
- 'amet',
270
- {
271
- lorem: 'ipsum',
272
- dolor: ['sit', 'amet'],
273
- },
274
- ],
275
- };
276
-
277
- const [minified, reverseMap] = minifyObject(obj);
278
-
279
- expect(expandObject(minified, reverseMap)).toEqual(obj);
280
- });
281
- });
@@ -1,198 +0,0 @@
1
- import isUnsafeKey from '../isUnsafeKey';
2
- import { isArray } from '../isArrayValue';
3
- import { isEmpty } from '../isEmpty';
4
- import isFunction from '../isFunction';
5
- import { isNullish } from '../isNullish';
6
- import isStringValue from '../isStringValue';
7
- import { isObject } from '../valueIsObject';
8
-
9
- // Basic key generator that generates keys like a, b, c, ... aa, ab, ac, ...
10
- function genMinifiedKey() {
11
- const chars =
12
- '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!@#$%^&*';
13
- let index = 0;
14
-
15
- return function next(): string {
16
- let code = '';
17
- let x = index;
18
-
19
- do {
20
- code = code + chars[x % chars.length];
21
- x = Math.floor(x / chars.length);
22
- } while (x > 0);
23
- index++;
24
- return code;
25
- };
26
- }
27
-
28
- export function minifyObject(
29
- obj: any,
30
- replacer: MinifyObjectReplacer = v => v,
31
- ): [any, any] {
32
- const countMap = new Map<any, number>();
33
- countOccurrences(obj, countMap, replacer);
34
- const maps = genMap(countMap);
35
- const o = minifyObjectImpl(obj, maps.map, replacer);
36
- // need to reverse the map so that the minified keys are the keys and the original keys are the values
37
- // and turn it into an object
38
-
39
- return [o, maps.reverseMap];
40
- }
41
-
42
- function genMap(countMap: Map<any, number>) {
43
- const counts = [];
44
-
45
- for (const [value, count] of countMap) {
46
- if (count > 1) {
47
- counts.push({ value, count });
48
- }
49
- }
50
-
51
- const sorted = counts.sort((a, z) => z.count - a.count);
52
-
53
- const getKey = genMinifiedKey();
54
- return sorted.reduce(
55
- (maps, { value }) => {
56
- if (!shouldAddToMap(value, maps.keyLength)) {
57
- return maps;
58
- }
59
-
60
- let key;
61
- do {
62
- key = getKey();
63
- } while (countMap.has(key));
64
- maps.map.set(value, key);
65
- maps.reverseMap[key] = value;
66
- maps.keyLength = key.length;
67
- return maps;
68
- },
69
- {
70
- map: new Map(),
71
- reverseMap: {},
72
- keyLength: 1,
73
- } as {
74
- map: Map<any, string>;
75
- reverseMap: Record<string, any>;
76
- keyLength: number;
77
- },
78
- );
79
- }
80
-
81
- // This avoids minification if the original key is shorter than or equals the minified key
82
- function shouldAddToMap(value: any, keyLength: number) {
83
- return value.toString().length >= keyLength;
84
- }
85
-
86
- function addCount(value: any, countMap: Map<any, number>) {
87
- countMap.set(value, (countMap.get(value) || 0) + 1);
88
- }
89
-
90
- function countOccurrences(
91
- obj: any,
92
- countMap: Map<any, number>,
93
- replacer: MinifyObjectReplacer,
94
- ) {
95
- for (const key in obj) {
96
- const value = replacer(obj[key], key);
97
- if (!shouldMinify(value)) continue;
98
-
99
- if (!Array.isArray(obj)) {
100
- addCount(key, countMap);
101
- }
102
-
103
- if (isObject(value)) {
104
- countOccurrences(value, countMap, replacer);
105
- } else {
106
- addCount(value, countMap);
107
- }
108
- }
109
- }
110
-
111
- function isNonSerializable(value: any): boolean {
112
- return isNullish(value) || isFunction(value) || typeof value === 'symbol';
113
- }
114
-
115
- function shouldMinify(value: any): boolean {
116
- if (isObject(value) && isEmpty(value)) {
117
- return false;
118
- }
119
-
120
- if (isNonSerializable(value)) {
121
- return false;
122
- }
123
-
124
- return true;
125
- }
126
-
127
- function minifyObjectImpl(
128
- obj: any,
129
- map: Map<any, string>,
130
- replacer: MinifyObjectReplacer,
131
- ): any {
132
- const minifiedObject: any = getRootNode(obj);
133
-
134
- for (const key in obj) {
135
- const value = replacer(obj[key], key);
136
- if (!shouldMinify(value)) continue;
137
-
138
- let minifiedValue;
139
- if (isObject(value)) {
140
- minifiedValue = minifyObjectImpl(value, map, replacer);
141
- } else {
142
- minifiedValue = minifyValue(value, map);
143
- }
144
-
145
- setValue(minifiedObject, minifiedValue, minifyValue(key, map));
146
- }
147
-
148
- return minifiedObject;
149
- }
150
-
151
- function minifyValue(value: any, map: Map<any, string>) {
152
- return map.get(value) ?? value;
153
- }
154
-
155
- function expandSingle(value: any, map: Record<string, any>): any {
156
- if (isStringValue(value)) {
157
- return map[value] ?? value;
158
- }
159
-
160
- return value;
161
- }
162
-
163
- export function expandObject(minifiedObj: any, map: Record<string, any>): any {
164
- const expandedObject: any = getRootNode(minifiedObj);
165
-
166
- for (const key in minifiedObj) {
167
- let expandedValue;
168
- const value = minifiedObj[key];
169
- if (isObject(value)) {
170
- expandedValue = expandObject(value, map);
171
- } else {
172
- expandedValue = expandSingle(value, map);
173
- }
174
-
175
- const expandedKey = expandSingle(key, map);
176
- setValue(expandedObject, expandedValue, expandedKey);
177
- }
178
-
179
- return expandedObject;
180
- }
181
-
182
- function setValue(container: any, value: any, key: string) {
183
- if (isUnsafeKey(key)) {
184
- return;
185
- }
186
-
187
- if (isArray(container)) {
188
- container.push(value);
189
- } else {
190
- container[key] = value;
191
- }
192
- }
193
-
194
- function getRootNode(node: any) {
195
- return isArray(node) ? [] : {};
196
- }
197
-
198
- export type MinifyObjectReplacer = (value: any, key: string) => any;