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.
- package/README.md +2 -7
- package/dist/cjs/minifyObject.development.js +217 -0
- package/dist/cjs/minifyObject.development.js.map +1 -0
- package/dist/cjs/minifyObject.js +6 -0
- package/dist/cjs/minifyObject.production.js +2 -0
- package/dist/cjs/minifyObject.production.js.map +1 -0
- package/dist/cjs/package.json +1 -0
- package/dist/cjs/vest-utils.development.js +378 -0
- package/dist/cjs/vest-utils.development.js.map +1 -0
- package/dist/cjs/vest-utils.js +6 -0
- package/dist/cjs/vest-utils.production.js +2 -0
- package/dist/cjs/vest-utils.production.js.map +1 -0
- package/dist/es/minifyObject.development.js +214 -0
- package/dist/es/minifyObject.development.js.map +1 -0
- package/dist/es/minifyObject.production.js +2 -0
- package/dist/es/minifyObject.production.js.map +1 -0
- package/dist/es/package.json +1 -0
- package/dist/es/vest-utils.development.js +330 -0
- package/dist/es/vest-utils.development.js.map +1 -0
- package/dist/es/vest-utils.production.js +2 -0
- package/dist/es/vest-utils.production.js.map +1 -0
- package/dist/umd/minifyObject.development.js +223 -0
- package/dist/umd/minifyObject.development.js.map +1 -0
- package/dist/umd/minifyObject.production.js +2 -0
- package/dist/umd/minifyObject.production.js.map +1 -0
- package/dist/umd/vest-utils.development.js +384 -0
- package/dist/umd/vest-utils.development.js.map +1 -0
- package/dist/umd/vest-utils.production.js +2 -0
- package/dist/umd/vest-utils.production.js.map +1 -0
- package/minifyObject/package.json +8 -12
- package/package.json +58 -43
- package/types/{exports/minifyObject.d.cts → minifyObject.d.ts} +2 -4
- package/types/minifyObject.d.ts.map +1 -0
- package/types/vest-utils.d.ts +143 -245
- package/types/vest-utils.d.ts.map +1 -0
- package/vitest.config.ts +45 -9
- package/dist/chunk-CLMFDpHK.mjs +0 -18
- package/dist/exports/minifyObject.cjs +0 -114
- package/dist/exports/minifyObject.cjs.map +0 -1
- package/dist/exports/minifyObject.mjs +0 -113
- package/dist/exports/minifyObject.mjs.map +0 -1
- package/dist/exports/standardSchemaSpec.cjs +0 -0
- package/dist/exports/standardSchemaSpec.mjs +0 -1
- package/dist/isEmpty-BBxAFjjm.mjs +0 -103
- package/dist/isEmpty-BBxAFjjm.mjs.map +0 -1
- package/dist/isEmpty-BuEa-96Q.cjs +0 -235
- package/dist/isEmpty-BuEa-96Q.cjs.map +0 -1
- package/dist/vest-utils.cjs +0 -510
- package/dist/vest-utils.cjs.map +0 -1
- package/dist/vest-utils.mjs +0 -421
- package/dist/vest-utils.mjs.map +0 -1
- package/src/Brand.ts +0 -9
- package/src/IO.ts +0 -2
- package/src/Predicates.ts +0 -13
- package/src/Result.ts +0 -121
- package/src/SimpleStateMachine.ts +0 -157
- package/src/StringObject.ts +0 -6
- package/src/__tests__/Architecture.test.ts +0 -69
- package/src/__tests__/Predicates.test.ts +0 -118
- package/src/__tests__/Result.test.ts +0 -284
- package/src/__tests__/SimpleStateMachine.test.ts +0 -425
- package/src/__tests__/StringObject.test.ts +0 -18
- package/src/__tests__/asArray.test.ts +0 -14
- package/src/__tests__/bindNot.test.ts +0 -39
- package/src/__tests__/bus.test.ts +0 -135
- package/src/__tests__/cache.test.ts +0 -139
- package/src/__tests__/callEach.test.ts +0 -20
- package/src/__tests__/defaultTo.test.ts +0 -52
- package/src/__tests__/deferThrow.test.ts +0 -26
- package/src/__tests__/either.test.ts +0 -17
- package/src/__tests__/freezeAssign.test.ts +0 -24
- package/src/__tests__/greaterThan.test.ts +0 -68
- package/src/__tests__/invariant.test.ts +0 -47
- package/src/__tests__/isArray.test.ts +0 -16
- package/src/__tests__/isBoolean.test.ts +0 -16
- package/src/__tests__/isEmpty.test.ts +0 -55
- package/src/__tests__/isEmptySet.test.ts +0 -22
- package/src/__tests__/isNull.test.ts +0 -26
- package/src/__tests__/isNumeric.test.ts +0 -27
- package/src/__tests__/isPositive.test.ts +0 -38
- package/src/__tests__/isPromise.test.ts +0 -17
- package/src/__tests__/isString.test.ts +0 -13
- package/src/__tests__/isUndefined.test.ts +0 -27
- package/src/__tests__/isUnsafeKey.test.ts +0 -22
- package/src/__tests__/lengthEquals.test.ts +0 -58
- package/src/__tests__/longerThan.test.ts +0 -58
- package/src/__tests__/mapFirst.test.ts +0 -31
- package/src/__tests__/nonnullish.test.ts +0 -25
- package/src/__tests__/noop.test.ts +0 -12
- package/src/__tests__/numberEquals.test.ts +0 -67
- package/src/__tests__/optionalFunctionValue.test.ts +0 -29
- package/src/__tests__/seq.test.ts +0 -29
- package/src/__tests__/text.test.ts +0 -41
- package/src/__tests__/tinyState.test.ts +0 -68
- package/src/__tests__/toNumber.test.ts +0 -39
- package/src/__tests__/vest-utils.test.ts +0 -13
- package/src/__tests__/withCatch.test.ts +0 -17
- package/src/__tests__/withResolvers.test.ts +0 -45
- package/src/asArray.ts +0 -3
- package/src/assign.ts +0 -1
- package/src/bindNot.ts +0 -3
- package/src/bus.ts +0 -52
- package/src/cache.ts +0 -68
- package/src/callEach.ts +0 -5
- package/src/defaultTo.ts +0 -9
- package/src/deferThrow.ts +0 -7
- package/src/dynamicValue.ts +0 -9
- package/src/either.ts +0 -3
- package/src/exports/__tests__/minifyObject.security.test.ts +0 -65
- package/src/exports/__tests__/minifyObject.test.ts +0 -281
- package/src/exports/minifyObject.ts +0 -198
- package/src/exports/standardSchemaSpec.ts +0 -70
- package/src/freezeAssign.ts +0 -5
- package/src/globals.d.ts +0 -3
- package/src/greaterThan.ts +0 -8
- package/src/hasOwnProperty.ts +0 -9
- package/src/invariant.ts +0 -19
- package/src/isArrayValue.ts +0 -11
- package/src/isBooleanValue.ts +0 -3
- package/src/isEmpty.ts +0 -18
- package/src/isEmptySet.ts +0 -15
- package/src/isFunction.ts +0 -5
- package/src/isNull.ts +0 -7
- package/src/isNullish.ts +0 -10
- package/src/isNumeric.ts +0 -11
- package/src/isPositive.ts +0 -5
- package/src/isPromise.ts +0 -5
- package/src/isStringValue.ts +0 -3
- package/src/isUndefined.ts +0 -7
- package/src/isUnsafeKey.ts +0 -3
- package/src/lengthEquals.ts +0 -11
- package/src/longerThan.ts +0 -8
- package/src/mapFirst.ts +0 -25
- package/src/nonnullish.ts +0 -9
- package/src/noop.ts +0 -1
- package/src/numberEquals.ts +0 -11
- package/src/seq.ts +0 -16
- package/src/text.ts +0 -20
- package/src/tinyState.ts +0 -28
- package/src/toNumber.ts +0 -11
- package/src/utilityTypes.ts +0 -25
- package/src/valueIsObject.ts +0 -5
- package/src/vest-utils.ts +0 -73
- package/src/withCatch.ts +0 -11
- package/src/withResolvers.ts +0 -33
- package/standardSchemaSpec/package.json +0 -14
- package/types/exports/minifyObject.d.cts.map +0 -1
- package/types/exports/minifyObject.d.mts +0 -7
- package/types/exports/minifyObject.d.mts.map +0 -1
- package/types/exports/standardSchemaSpec.d.cts +0 -59
- package/types/exports/standardSchemaSpec.d.cts.map +0 -1
- package/types/exports/standardSchemaSpec.d.mts +0 -59
- package/types/exports/standardSchemaSpec.d.mts.map +0 -1
- package/types/vest-utils.d.cts +0 -296
- package/types/vest-utils.d.cts.map +0 -1
- package/types/vest-utils.d.mts +0 -295
- 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
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
package/src/dynamicValue.ts
DELETED
package/src/either.ts
DELETED
|
@@ -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;
|