@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 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;
@@ -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": "19.5.0",
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": {