@augment-vir/common 19.5.0 → 20.0.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 -0
- package/dist/cjs/augments/object/nested-keys.js +24 -1
- package/dist/cjs/augments/random.js +83 -0
- package/dist/cjs/index.js +1 -0
- package/dist/esm/augments/object/nested-keys.js +22 -0
- package/dist/esm/augments/random.js +76 -0
- package/dist/esm/index.js +1 -0
- package/dist/types/augments/object/nested-keys.d.ts +1 -0
- package/dist/types/augments/random.d.ts +39 -0
- package/dist/types/index.d.ts +1 -0
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -3,3 +3,5 @@
|
|
|
3
3
|
`augment-vir` is a collection of small helper functions that I constantly use across all my JavaScript and TypeScript repos. I call these functions `augments`. These are functions, constants, and types typically placed within a "util", or "helpers", etc. directory.
|
|
4
4
|
|
|
5
5
|
This `common` package is for environment-agnostic augments. Everything in here will work in Node.js or the browser with identical results.
|
|
6
|
+
|
|
7
|
+
Note that the `random*` functions exported by this package will not work in Node.js < v19.0.0.
|
|
@@ -1,7 +1,30 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.getValueFromNestedKeys = void 0;
|
|
3
|
+
exports.getValueFromNestedKeys = exports.setValueWithNestedKeys = void 0;
|
|
4
|
+
const object_1 = require("./object");
|
|
4
5
|
const typed_has_property_1 = require("./typed-has-property");
|
|
6
|
+
function setValueWithNestedKeys(inputObject, nestedKeys, value) {
|
|
7
|
+
/**
|
|
8
|
+
* Lots of as any casts in here because these types are, under the hood, pretty complex. Since
|
|
9
|
+
* the inputs and outputs of this function are well typed, these internal as any casts do not
|
|
10
|
+
* affect the external API of this function.
|
|
11
|
+
*/
|
|
12
|
+
const nextKey = nestedKeys[0];
|
|
13
|
+
if (!(nextKey in inputObject)) {
|
|
14
|
+
inputObject[nextKey] = {};
|
|
15
|
+
}
|
|
16
|
+
else if (!(0, object_1.isObject)(inputObject[nextKey])) {
|
|
17
|
+
throw new Error(`Cannot set value at key '${String(nextKey)}' as its not an object.`);
|
|
18
|
+
}
|
|
19
|
+
const nextParent = inputObject[nextKey];
|
|
20
|
+
if (nestedKeys.length > 2) {
|
|
21
|
+
setValueWithNestedKeys(nextParent, nestedKeys.slice(1), value);
|
|
22
|
+
}
|
|
23
|
+
else {
|
|
24
|
+
nextParent[nestedKeys[1]] = value;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
exports.setValueWithNestedKeys = setValueWithNestedKeys;
|
|
5
28
|
function getValueFromNestedKeys(inputObject, nestedKeys) {
|
|
6
29
|
/**
|
|
7
30
|
* Lots of as any casts in here because these types are, under the hood, pretty complex. Since
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.randomString = exports.createUuid = exports.randomBoolean = exports.randomInteger = void 0;
|
|
4
|
+
const common_number_1 = require("./common-number");
|
|
5
|
+
const crypto = globalThis.crypto;
|
|
6
|
+
// can't get this coverage to work
|
|
7
|
+
/* c8 ignore start */
|
|
8
|
+
/**
|
|
9
|
+
* Creates a random integer (decimal points are all cut off) between the given min and max
|
|
10
|
+
* (inclusive).
|
|
11
|
+
*
|
|
12
|
+
* This function uses cryptographically secure randomness.
|
|
13
|
+
*/
|
|
14
|
+
function randomInteger({ min: rawMin, max: rawMax }) {
|
|
15
|
+
const { min, max } = (0, common_number_1.ensureMinAndMax)({ min: Math.floor(rawMin), max: Math.floor(rawMax) });
|
|
16
|
+
const range = max - min + 1;
|
|
17
|
+
const neededByteCount = Math.ceil(Math.log2(range) / 8);
|
|
18
|
+
const cutoff = Math.floor(256 ** neededByteCount / range) * range;
|
|
19
|
+
const currentBytes = new Uint8Array(neededByteCount);
|
|
20
|
+
let value;
|
|
21
|
+
do {
|
|
22
|
+
crypto.getRandomValues(currentBytes);
|
|
23
|
+
value = currentBytes.reduce((accum, byte, index) => {
|
|
24
|
+
return accum + byte * 256 ** index;
|
|
25
|
+
}, 0);
|
|
26
|
+
} while (value >= cutoff);
|
|
27
|
+
return min + (value % range);
|
|
28
|
+
}
|
|
29
|
+
exports.randomInteger = randomInteger;
|
|
30
|
+
/**
|
|
31
|
+
* Returns true at rate of the percentLikelyToBeTrue input. Inputs should be whole numbers which
|
|
32
|
+
* will be treated like percents. Anything outside of 0-100 inclusively will be clamped. An input 0
|
|
33
|
+
* will always return true. An input of 100 will always return true. Decimals on the input will be
|
|
34
|
+
* chopped off, use whole numbers.
|
|
35
|
+
*
|
|
36
|
+
* This function uses cryptographically secure randomness.
|
|
37
|
+
*
|
|
38
|
+
* @example
|
|
39
|
+
* randomBoolean(50); // 50% chance to return true
|
|
40
|
+
*
|
|
41
|
+
* @example
|
|
42
|
+
* randomBoolean(0); // always false, 0% chance of being true
|
|
43
|
+
*
|
|
44
|
+
* @example
|
|
45
|
+
* randomBoolean(100); // always true, 100% chance of being true
|
|
46
|
+
*
|
|
47
|
+
* @example
|
|
48
|
+
* randomBoolean(59.67; // 59% chance of being true
|
|
49
|
+
*/
|
|
50
|
+
function randomBoolean(percentLikelyToBeTrue = 50) {
|
|
51
|
+
return (randomInteger({ min: 0, max: 99 }) <
|
|
52
|
+
(0, common_number_1.clamp)({
|
|
53
|
+
value: Math.floor(percentLikelyToBeTrue),
|
|
54
|
+
min: 0,
|
|
55
|
+
max: 100,
|
|
56
|
+
}));
|
|
57
|
+
}
|
|
58
|
+
exports.randomBoolean = randomBoolean;
|
|
59
|
+
/** Creates a cryptographically secure uuid. */
|
|
60
|
+
function createUuid() {
|
|
61
|
+
return crypto.randomUUID();
|
|
62
|
+
}
|
|
63
|
+
exports.createUuid = createUuid;
|
|
64
|
+
/**
|
|
65
|
+
* Creates a random string (including letters and numbers) of a given length.
|
|
66
|
+
*
|
|
67
|
+
* This function uses cryptographically secure randomness.
|
|
68
|
+
*/
|
|
69
|
+
function randomString(inputLength = 16) {
|
|
70
|
+
const arrayLength = Math.ceil(inputLength / 2);
|
|
71
|
+
const uintArray = new Uint8Array(arrayLength);
|
|
72
|
+
crypto.getRandomValues(uintArray);
|
|
73
|
+
return (Array.from(uintArray)
|
|
74
|
+
.map((value) => value.toString(16).padStart(2, '0'))
|
|
75
|
+
.join('')
|
|
76
|
+
/**
|
|
77
|
+
* Because getRandomValues works with even numbers only, we must then chop off extra
|
|
78
|
+
* characters if they exist in the even that inputLength was odd.
|
|
79
|
+
*/
|
|
80
|
+
.substring(0, inputLength));
|
|
81
|
+
}
|
|
82
|
+
exports.randomString = randomString;
|
|
83
|
+
/* c8 ignore stop */
|
package/dist/cjs/index.js
CHANGED
|
@@ -41,6 +41,7 @@ __exportStar(require("./augments/object/old-union-to-intersection"), exports);
|
|
|
41
41
|
__exportStar(require("./augments/object/pick-deep"), exports);
|
|
42
42
|
__exportStar(require("./augments/object/typed-has-property"), exports);
|
|
43
43
|
__exportStar(require("./augments/promise"), exports);
|
|
44
|
+
__exportStar(require("./augments/random"), exports);
|
|
44
45
|
__exportStar(require("./augments/regexp"), exports);
|
|
45
46
|
__exportStar(require("./augments/runtime-type-of"), exports);
|
|
46
47
|
__exportStar(require("./augments/string/prefixes"), exports);
|
|
@@ -1,4 +1,26 @@
|
|
|
1
|
+
import { isObject } from './object';
|
|
1
2
|
import { typedHasProperty } from './typed-has-property';
|
|
3
|
+
export function setValueWithNestedKeys(inputObject, nestedKeys, value) {
|
|
4
|
+
/**
|
|
5
|
+
* Lots of as any casts in here because these types are, under the hood, pretty complex. Since
|
|
6
|
+
* the inputs and outputs of this function are well typed, these internal as any casts do not
|
|
7
|
+
* affect the external API of this function.
|
|
8
|
+
*/
|
|
9
|
+
const nextKey = nestedKeys[0];
|
|
10
|
+
if (!(nextKey in inputObject)) {
|
|
11
|
+
inputObject[nextKey] = {};
|
|
12
|
+
}
|
|
13
|
+
else if (!isObject(inputObject[nextKey])) {
|
|
14
|
+
throw new Error(`Cannot set value at key '${String(nextKey)}' as its not an object.`);
|
|
15
|
+
}
|
|
16
|
+
const nextParent = inputObject[nextKey];
|
|
17
|
+
if (nestedKeys.length > 2) {
|
|
18
|
+
setValueWithNestedKeys(nextParent, nestedKeys.slice(1), value);
|
|
19
|
+
}
|
|
20
|
+
else {
|
|
21
|
+
nextParent[nestedKeys[1]] = value;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
2
24
|
export function getValueFromNestedKeys(inputObject, nestedKeys) {
|
|
3
25
|
/**
|
|
4
26
|
* Lots of as any casts in here because these types are, under the hood, pretty complex. Since
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { clamp, ensureMinAndMax } from './common-number';
|
|
2
|
+
const crypto = globalThis.crypto;
|
|
3
|
+
// can't get this coverage to work
|
|
4
|
+
/* c8 ignore start */
|
|
5
|
+
/**
|
|
6
|
+
* Creates a random integer (decimal points are all cut off) between the given min and max
|
|
7
|
+
* (inclusive).
|
|
8
|
+
*
|
|
9
|
+
* This function uses cryptographically secure randomness.
|
|
10
|
+
*/
|
|
11
|
+
export function randomInteger({ min: rawMin, max: rawMax }) {
|
|
12
|
+
const { min, max } = ensureMinAndMax({ min: Math.floor(rawMin), max: Math.floor(rawMax) });
|
|
13
|
+
const range = max - min + 1;
|
|
14
|
+
const neededByteCount = Math.ceil(Math.log2(range) / 8);
|
|
15
|
+
const cutoff = Math.floor(256 ** neededByteCount / range) * range;
|
|
16
|
+
const currentBytes = new Uint8Array(neededByteCount);
|
|
17
|
+
let value;
|
|
18
|
+
do {
|
|
19
|
+
crypto.getRandomValues(currentBytes);
|
|
20
|
+
value = currentBytes.reduce((accum, byte, index) => {
|
|
21
|
+
return accum + byte * 256 ** index;
|
|
22
|
+
}, 0);
|
|
23
|
+
} while (value >= cutoff);
|
|
24
|
+
return min + (value % range);
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Returns true at rate of the percentLikelyToBeTrue input. Inputs should be whole numbers which
|
|
28
|
+
* will be treated like percents. Anything outside of 0-100 inclusively will be clamped. An input 0
|
|
29
|
+
* will always return true. An input of 100 will always return true. Decimals on the input will be
|
|
30
|
+
* chopped off, use whole numbers.
|
|
31
|
+
*
|
|
32
|
+
* This function uses cryptographically secure randomness.
|
|
33
|
+
*
|
|
34
|
+
* @example
|
|
35
|
+
* randomBoolean(50); // 50% chance to return true
|
|
36
|
+
*
|
|
37
|
+
* @example
|
|
38
|
+
* randomBoolean(0); // always false, 0% chance of being true
|
|
39
|
+
*
|
|
40
|
+
* @example
|
|
41
|
+
* randomBoolean(100); // always true, 100% chance of being true
|
|
42
|
+
*
|
|
43
|
+
* @example
|
|
44
|
+
* randomBoolean(59.67; // 59% chance of being true
|
|
45
|
+
*/
|
|
46
|
+
export function randomBoolean(percentLikelyToBeTrue = 50) {
|
|
47
|
+
return (randomInteger({ min: 0, max: 99 }) <
|
|
48
|
+
clamp({
|
|
49
|
+
value: Math.floor(percentLikelyToBeTrue),
|
|
50
|
+
min: 0,
|
|
51
|
+
max: 100,
|
|
52
|
+
}));
|
|
53
|
+
}
|
|
54
|
+
/** Creates a cryptographically secure uuid. */
|
|
55
|
+
export function createUuid() {
|
|
56
|
+
return crypto.randomUUID();
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Creates a random string (including letters and numbers) of a given length.
|
|
60
|
+
*
|
|
61
|
+
* This function uses cryptographically secure randomness.
|
|
62
|
+
*/
|
|
63
|
+
export function randomString(inputLength = 16) {
|
|
64
|
+
const arrayLength = Math.ceil(inputLength / 2);
|
|
65
|
+
const uintArray = new Uint8Array(arrayLength);
|
|
66
|
+
crypto.getRandomValues(uintArray);
|
|
67
|
+
return (Array.from(uintArray)
|
|
68
|
+
.map((value) => value.toString(16).padStart(2, '0'))
|
|
69
|
+
.join('')
|
|
70
|
+
/**
|
|
71
|
+
* Because getRandomValues works with even numbers only, we must then chop off extra
|
|
72
|
+
* characters if they exist in the even that inputLength was odd.
|
|
73
|
+
*/
|
|
74
|
+
.substring(0, inputLength));
|
|
75
|
+
}
|
|
76
|
+
/* c8 ignore stop */
|
package/dist/esm/index.js
CHANGED
|
@@ -25,6 +25,7 @@ export * from './augments/object/old-union-to-intersection';
|
|
|
25
25
|
export * from './augments/object/pick-deep';
|
|
26
26
|
export * from './augments/object/typed-has-property';
|
|
27
27
|
export * from './augments/promise';
|
|
28
|
+
export * from './augments/random';
|
|
28
29
|
export * from './augments/regexp';
|
|
29
30
|
export * from './augments/runtime-type-of';
|
|
30
31
|
export * from './augments/string/prefixes';
|
|
@@ -8,4 +8,5 @@ export type NestedKeys<ObjectGeneric extends object> = UnionToIntersection<Extra
|
|
|
8
8
|
...(NestedKeys<UnionToIntersection<Extract<PropertyValueType<ObjectGeneric>, object>>> | [])
|
|
9
9
|
] : [keyof ObjectGeneric];
|
|
10
10
|
export type NestedValue<ObjectGeneric extends object, NestedKeysGeneric extends NestedSequentialKeys<ObjectGeneric>> = NestedKeysGeneric extends readonly [infer FirstEntry, ...infer FollowingEntries] ? FirstEntry extends keyof ObjectGeneric ? FollowingEntries extends never[] ? ObjectGeneric[FirstEntry] : ObjectGeneric[FirstEntry] extends object ? FollowingEntries extends NestedSequentialKeys<ObjectGeneric[FirstEntry]> ? NestedValue<ObjectGeneric[FirstEntry], FollowingEntries> : never : never : never : never;
|
|
11
|
+
export declare function setValueWithNestedKeys<const ObjectGeneric extends object, const KeysGeneric extends NestedSequentialKeys<ObjectGeneric>>(inputObject: ObjectGeneric, nestedKeys: KeysGeneric, value: NestedValue<ObjectGeneric, KeysGeneric>): void;
|
|
11
12
|
export declare function getValueFromNestedKeys<const ObjectGeneric extends object, const KeysGeneric extends NestedSequentialKeys<ObjectGeneric>>(inputObject: ObjectGeneric, nestedKeys: KeysGeneric): NestedValue<ObjectGeneric, KeysGeneric> | undefined;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Creates a random integer (decimal points are all cut off) between the given min and max
|
|
3
|
+
* (inclusive).
|
|
4
|
+
*
|
|
5
|
+
* This function uses cryptographically secure randomness.
|
|
6
|
+
*/
|
|
7
|
+
export declare function randomInteger({ min: rawMin, max: rawMax }: {
|
|
8
|
+
min: number;
|
|
9
|
+
max: number;
|
|
10
|
+
}): number;
|
|
11
|
+
/**
|
|
12
|
+
* Returns true at rate of the percentLikelyToBeTrue input. Inputs should be whole numbers which
|
|
13
|
+
* will be treated like percents. Anything outside of 0-100 inclusively will be clamped. An input 0
|
|
14
|
+
* will always return true. An input of 100 will always return true. Decimals on the input will be
|
|
15
|
+
* chopped off, use whole numbers.
|
|
16
|
+
*
|
|
17
|
+
* This function uses cryptographically secure randomness.
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* randomBoolean(50); // 50% chance to return true
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* randomBoolean(0); // always false, 0% chance of being true
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* randomBoolean(100); // always true, 100% chance of being true
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* randomBoolean(59.67; // 59% chance of being true
|
|
30
|
+
*/
|
|
31
|
+
export declare function randomBoolean(percentLikelyToBeTrue?: number): boolean;
|
|
32
|
+
/** Creates a cryptographically secure uuid. */
|
|
33
|
+
export declare function createUuid(): `${string}-${string}-${string}-${string}-${string}`;
|
|
34
|
+
/**
|
|
35
|
+
* Creates a random string (including letters and numbers) of a given length.
|
|
36
|
+
*
|
|
37
|
+
* This function uses cryptographically secure randomness.
|
|
38
|
+
*/
|
|
39
|
+
export declare function randomString(inputLength?: number): string;
|
package/dist/types/index.d.ts
CHANGED
|
@@ -25,6 +25,7 @@ export * from './augments/object/old-union-to-intersection';
|
|
|
25
25
|
export * from './augments/object/pick-deep';
|
|
26
26
|
export * from './augments/object/typed-has-property';
|
|
27
27
|
export * from './augments/promise';
|
|
28
|
+
export * from './augments/random';
|
|
28
29
|
export * from './augments/regexp';
|
|
29
30
|
export * from './augments/runtime-type-of';
|
|
30
31
|
export * from './augments/string/prefixes';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@augment-vir/common",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "20.0.0",
|
|
4
4
|
"homepage": "https://github.com/electrovir/augment-vir/tree/main/packages/common",
|
|
5
5
|
"bugs": {
|
|
6
6
|
"url": "https://github.com/electrovir/augment-vir/issues"
|
|
@@ -24,6 +24,7 @@
|
|
|
24
24
|
"test:types": "tsc --noEmit"
|
|
25
25
|
},
|
|
26
26
|
"dependencies": {
|
|
27
|
+
"browser-or-node": "^2.1.1",
|
|
27
28
|
"type-fest": "^4.4.0"
|
|
28
29
|
},
|
|
29
30
|
"devDependencies": {
|