senaev-utils 1.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.
Files changed (166) hide show
  1. package/README.md +11 -0
  2. package/package.json +66 -0
  3. package/src/reactHooks/setDeepEqualState.ts +19 -0
  4. package/src/reactHooks/useLocationParameter.ts +46 -0
  5. package/src/reactHooks/useObjectDeepChangesCounter.ts +22 -0
  6. package/src/reactHooks/useObservableMap/useObservableMap.ts +21 -0
  7. package/src/reactHooks/useRecentChangeTimer.ts +42 -0
  8. package/src/reactHooks/useReload.ts +17 -0
  9. package/src/reactHooks/useVisualizedProcessesTree.ts +202 -0
  10. package/src/types/Boolean.ts +9 -0
  11. package/src/types/DeepPartial.ts +1 -0
  12. package/src/types/GlobalContext.ts +8 -0
  13. package/src/types/HexColorString.ts +9 -0
  14. package/src/types/ISOLang.ts +5 -0
  15. package/src/types/IsEqualUnion.ts +1 -0
  16. package/src/types/IterableWithIndexAccess.ts +4 -0
  17. package/src/types/Kilobits.ts +4 -0
  18. package/src/types/Network/Bits.ts +4 -0
  19. package/src/types/Network/Bps.ts +4 -0
  20. package/src/types/Network/Kbps.ts +4 -0
  21. package/src/types/Network/Mbps.ts +4 -0
  22. package/src/types/Number/AngleInDegrees.ts +3 -0
  23. package/src/types/Number/FiniteNumber.ts +11 -0
  24. package/src/types/Number/Integer.ts +14 -0
  25. package/src/types/Number/IntegerInString.ts +13 -0
  26. package/src/types/Number/Number.ts +15 -0
  27. package/src/types/Number/Portion.ts +4 -0
  28. package/src/types/Number/PositiveInteger.ts +30 -0
  29. package/src/types/Number/PositiveNumber.ts +4 -0
  30. package/src/types/Number/StringUnsignedInteger.ts +4 -0
  31. package/src/types/Number/UnsignedFiniteNumber.ts +13 -0
  32. package/src/types/Number/UnsignedInteger.ts +28 -0
  33. package/src/types/Object/Object.ts +20 -0
  34. package/src/types/Percent.ts +4 -0
  35. package/src/types/PixelPosition.ts +9 -0
  36. package/src/types/PixelSize.ts +9 -0
  37. package/src/types/Pixels.ts +4 -0
  38. package/src/types/RangeTuple.ts +1 -0
  39. package/src/types/Size.ts +8 -0
  40. package/src/types/Time/DaysCount.ts +6 -0
  41. package/src/types/Time/Hours.ts +4 -0
  42. package/src/types/Time/ISODateString/ISODateString.ts +16 -0
  43. package/src/types/Time/Milliseconds.ts +4 -0
  44. package/src/types/Time/Minutes.ts +4 -0
  45. package/src/types/Time/Seconds.ts +5 -0
  46. package/src/types/Time/Time.ts +13 -0
  47. package/src/types/Time/Timestamp.ts +6 -0
  48. package/src/types/Time/UnixTimeMs.ts +6 -0
  49. package/src/types/Time/UnixTimeSec.ts +11 -0
  50. package/src/types/Time/const.ts +24 -0
  51. package/src/types/any/AnyAsyncFunction.ts +2 -0
  52. package/src/types/any/AnyFunction.ts +2 -0
  53. package/src/types/assertions/ANY_VALUE.ts +2 -0
  54. package/src/types/assertions/isNever/isNever.ts +34 -0
  55. package/src/types/assertions/isTrue/isTrue.ts +13 -0
  56. package/src/utils/AbortableProcess.ts +160 -0
  57. package/src/utils/Array/createArray/createArray.ts +8 -0
  58. package/src/utils/Array/createIntegerSequenceArray/createIntegerSequenceArray.ts +3 -0
  59. package/src/utils/Array/getUniqueValues/getUniqueValues.ts +3 -0
  60. package/src/utils/Array/separateToArrays/separateToArrays.ts +24 -0
  61. package/src/utils/Array/shuffleArray/shuffleArray.ts +18 -0
  62. package/src/utils/CircularBuffer/CircularBuffer.ts +117 -0
  63. package/src/utils/DOM/blurDocument.ts +3 -0
  64. package/src/utils/DOM/unfocusDocument.ts +3 -0
  65. package/src/utils/Date/dateToUnixTimeSec/dateToUnixTimeSec.ts +6 -0
  66. package/src/utils/Date/isoDateStringToUnixTimeMs/isoDateStringToUnixTimeMs.ts +6 -0
  67. package/src/utils/Date/isoDateStringToUnixTimeSec/isoDateStringToUnixTimeSec.ts +7 -0
  68. package/src/utils/Date/unixTimeSecToIsoDateString/unixTimeSecToIsoDateString.ts +6 -0
  69. package/src/utils/Deque/Deque.ts +530 -0
  70. package/src/utils/DeviceDetection/isTouchScreenDevice/isTouchScreenDevice.ts +16 -0
  71. package/src/utils/Error/rethrowError/rethrowError.ts +8 -0
  72. package/src/utils/Error/wrapError.ts +32 -0
  73. package/src/utils/Function/callFirstThenOther/callFirstThenOther.ts +24 -0
  74. package/src/utils/Function/callFunctions/callFunctions.ts +11 -0
  75. package/src/utils/Function/callTimes/callTimes.ts +5 -0
  76. package/src/utils/Function/isFunction.ts +3 -0
  77. package/src/utils/Function/noop/index.ts +4 -0
  78. package/src/utils/Function/once/once.ts +32 -0
  79. package/src/utils/Function/subscribeUpdatesWithTimeout/index.ts +44 -0
  80. package/src/utils/Function/throttleFunction/throttleFunction.ts +18 -0
  81. package/src/utils/Function/throttleWithPromiseFunction/throttleWithPromiseFunction.ts +19 -0
  82. package/src/utils/Function/tryCatch/tryCatch.ts +13 -0
  83. package/src/utils/Latch/Latch.ts +63 -0
  84. package/src/utils/Map/ObservableMap/ObservableMap.ts +55 -0
  85. package/src/utils/Map/findMapsDifferences/findMapsDifferences.ts +35 -0
  86. package/src/utils/Map/mapGetOrSet/mapGetOrSet.ts +7 -0
  87. package/src/utils/Map/mapMapToArray/mapMapToArray.ts +6 -0
  88. package/src/utils/Map/mapMapValues/mapMapValues.ts +9 -0
  89. package/src/utils/Math/createSimpleMovingAverageSeries/createSimpleMovingAverageSeries.ts +22 -0
  90. package/src/utils/Math/mean/mean.ts +14 -0
  91. package/src/utils/Math/simpleMovingAverageUsingAccFabric/simpleMovingAverageUsingAccFabric.ts +29 -0
  92. package/src/utils/Math/sum/sum.ts +45 -0
  93. package/src/utils/MinHeap/MinHeap.ts +88 -0
  94. package/src/utils/MinHeapUniqueNumber/MinHeapUniqueNumber.ts +105 -0
  95. package/src/utils/MinMaxHeap/MinMaxHeap.ts +91 -0
  96. package/src/utils/Number/collectIntegerSequences/collectIntegerSequences.ts +36 -0
  97. package/src/utils/Number/numberToFixedPrecision/numberToFixedPrecision.ts +13 -0
  98. package/src/utils/Object/cloneObject/cloneObject.ts +3 -0
  99. package/src/utils/Object/deepEqual/deepEqual.ts +45 -0
  100. package/src/utils/Object/deleteUndefinedProperties/index.ts +13 -0
  101. package/src/utils/Object/entriesToObject/entriesToObject.ts +12 -0
  102. package/src/utils/Object/filterObject/filterObject.ts +18 -0
  103. package/src/utils/Object/forOwn/forOwn.ts +12 -0
  104. package/src/utils/Object/getObjectEntries/getObjectEntries.ts +3 -0
  105. package/src/utils/Object/getObjectKeys/getObjectKeys.ts +3 -0
  106. package/src/utils/Object/getObjectValues/index.ts +3 -0
  107. package/src/utils/Object/hasOwnProperty/hasOwnProperty.ts +3 -0
  108. package/src/utils/Object/invertObject/InvertedObject.ts +3 -0
  109. package/src/utils/Object/invertObject/invertObject.ts +15 -0
  110. package/src/utils/Object/isEmptyObject/index.ts +5 -0
  111. package/src/utils/Object/isObject/index.ts +8 -0
  112. package/src/utils/Object/mapObjectKeys/mapObjectKeys.ts +25 -0
  113. package/src/utils/Object/mapObjectValues/mapObjectValues.ts +16 -0
  114. package/src/utils/Object/mirrorObject/ObjectKeysMirror.ts +1 -0
  115. package/src/utils/Object/mirrorObject/mirrorObject.ts +7 -0
  116. package/src/utils/Pagination/DEFAULT_PAGE.ts +1 -0
  117. package/src/utils/Pagination/calculatePaginationParameters.ts +29 -0
  118. package/src/utils/Promise/promiseAll/promiseAll.ts +6 -0
  119. package/src/utils/Promise/restrictParallelCalls/restrictParallelCalls.ts +18 -0
  120. package/src/utils/Ranges/checkRangesAdjacent/checkRangesAdjacent.ts +23 -0
  121. package/src/utils/Ranges/checkRangesIntersection/checkRangesIntersection.ts +8 -0
  122. package/src/utils/Ranges/excludeRanges/excludeRanges.ts +42 -0
  123. package/src/utils/Ranges/getDistanceToRange/getDistanceToRange.ts +14 -0
  124. package/src/utils/Ranges/isRangeWithinRanges.ts +14 -0
  125. package/src/utils/Ranges/mapDateRangeToISODateStringRange/mapDateRangeToISODateStringRange.ts +7 -0
  126. package/src/utils/Ranges/mapISODateStringRangeToUnixTimeMsRange/mapISODateStringRangeToUnixTimeMsRange.ts +8 -0
  127. package/src/utils/Ranges/mapRange/mapRange.ts +13 -0
  128. package/src/utils/Ranges/mapUnixTimeMsRangeToISODateStringRange/mapUnixTimeMsRangeToISODateStringRange.ts +8 -0
  129. package/src/utils/Ranges/mergeRanges/mergeRanges.ts +34 -0
  130. package/src/utils/Signal/Signal.ts +47 -0
  131. package/src/utils/Signal/SignalCallback.ts +1 -0
  132. package/src/utils/Signal/combineSignalsIntoNewOne/combineSignalsIntoNewOne.ts +75 -0
  133. package/src/utils/Signal/subscribeSignalAndCallWithCurrentValue/subscribeSignalAndCallWithCurrentValue.ts +7 -0
  134. package/src/utils/Signal/useSignal.ts +10 -0
  135. package/src/utils/String/HexString/HEX_SYMBOLS.ts +1 -0
  136. package/src/utils/String/HexString/HexString.ts +2 -0
  137. package/src/utils/String/HexString/isHexString.ts +16 -0
  138. package/src/utils/String/NonEmptyString/NonEmptyString.ts +22 -0
  139. package/src/utils/String/endsWith/index.ts +6 -0
  140. package/src/utils/String/isString.ts +3 -0
  141. package/src/utils/binarySearch/binarySearch.ts +20 -0
  142. package/src/utils/binarySearchClosest/binarySearchClosest.ts +43 -0
  143. package/src/utils/createIncrementalIntegerGenerator.ts +7 -0
  144. package/src/utils/createRemoteDataProcessingWindow/createRemoteDataProcessingWindow.ts +157 -0
  145. package/src/utils/createSimultaneousOperationsHandler/createSimultaneousOperationsHandler.ts +65 -0
  146. package/src/utils/encryptDecryptString/encryptDecryptString.ts +34 -0
  147. package/src/utils/getPrimeNumbersWithEratosthenesSieve/getPrimeNumbersWithEratosthenesSieve.ts +21 -0
  148. package/src/utils/isPromise/isPromise.ts +30 -0
  149. package/src/utils/makeAsyncFunctionCallsSimultaneous/makeAsyncFunctionCallsSimultaneous.ts +64 -0
  150. package/src/utils/parallelsLimitPromise/index.ts +28 -0
  151. package/src/utils/parallelsLimitQueue/parallelsLimitQueue.ts +69 -0
  152. package/src/utils/prettyStringify/index.ts +3 -0
  153. package/src/utils/random/fnv1a/fnv1a.ts +87 -0
  154. package/src/utils/random/generateHashedFloats/generateHashedFloats.ts +28 -0
  155. package/src/utils/random/getRandomIntegerInARange/getRandomIntegerInARange.ts +3 -0
  156. package/src/utils/random/getSampleIndexByStringHash/getSampleIndexByStringHash.ts +10 -0
  157. package/src/utils/random/randomBoolean/randomBoolean.ts +3 -0
  158. package/src/utils/random/randomHexString/randomHexString.ts +12 -0
  159. package/src/utils/requestAnimationFrame/requestAnimationFramePromise/requestAnimationFramePromise.ts +7 -0
  160. package/src/utils/requestAnimationFrame/requestAnimationFrameThrottle/requestAnimationFrameThrottle.ts +6 -0
  161. package/src/utils/retryFunctionWrapper/retryFunctionWrapper.ts +40 -0
  162. package/src/utils/runParallelTimescalesProcessing/runParallelTimescalesProcessing.ts +113 -0
  163. package/src/utils/stringifyError.ts +3 -0
  164. package/src/utils/timers/promiseTimeout/promiseTimeout.ts +26 -0
  165. package/src/utils/timers/subscribeSyntheticInterval/subscribeSyntheticInterval.ts +47 -0
  166. package/src/utils/waitForFunction/waitForFunction.ts +50 -0
package/README.md ADDED
@@ -0,0 +1,11 @@
1
+ # senaev-utils
2
+
3
+ Personal library with utils for `@senaev`.
4
+
5
+ Published as raw source files without a root entrypoint.
6
+
7
+ Import utilities directly by path, for example:
8
+
9
+ ```ts
10
+ import { deepEqual } from 'senaev-utils/src/utils/Object/deepEqual/deepEqual';
11
+ ```
package/package.json ADDED
@@ -0,0 +1,66 @@
1
+ {
2
+ "name": "senaev-utils",
3
+ "version": "1.0.0",
4
+ "description": "Personal library with utils for @senaev",
5
+ "files": [
6
+ "src",
7
+ "README.md"
8
+ ],
9
+ "scripts": {
10
+ "lint": "eslint .",
11
+ "lint:fix": "npx eslint . --fix",
12
+ "test": "vitest",
13
+ "test:ci": "vitest --run",
14
+ "typecheck": "tsc --noEmit"
15
+ },
16
+ "repository": {
17
+ "type": "git",
18
+ "url": "git+https://github.com/senaev/senaev-utils.git"
19
+ },
20
+ "author": "Andrei Senaev <andrei.senaev@gmail.com>",
21
+ "license": "ISC",
22
+ "bugs": {
23
+ "url": "https://github.com/senaev/senaev-utils/issues"
24
+ },
25
+ "homepage": "https://github.com/senaev/senaev-utils#readme",
26
+ "engines": {
27
+ "node": ">=18"
28
+ },
29
+ "devDependencies": {
30
+ "@eslint/js": "9.17.0",
31
+ "@stylistic/eslint-plugin": "2.12.1",
32
+ "@testing-library/react": "^16.3.0",
33
+ "@types/async": "^3.2.24",
34
+ "@types/react": "^19.1.3",
35
+ "eslint": "9.17.0",
36
+ "eslint-config-prettier": "9.1.0",
37
+ "eslint-plugin-import-x": "^4.6.1",
38
+ "eslint-plugin-no-only-tests": "^3.3.0",
39
+ "eslint-plugin-prettier": "5.2.1",
40
+ "eslint-plugin-react": "7.37.3",
41
+ "jsdom": "^27.0.0",
42
+ "lefthook": "^1.10.10",
43
+ "react": "^19.1.1",
44
+ "react-router": "^7.9.1",
45
+ "typescript": "^5.8.3",
46
+ "typescript-eslint": "^8.32.0",
47
+ "vitest": "^3.1.3"
48
+ },
49
+ "dependencies": {
50
+ "@types/node": "^24.0.0",
51
+ "jose": "^6.0.11",
52
+ "rxjs": "^7.8.2"
53
+ },
54
+ "peerDependencies": {
55
+ "react": ">=18",
56
+ "react-router": ">=7"
57
+ },
58
+ "peerDependenciesMeta": {
59
+ "react": {
60
+ "optional": true
61
+ },
62
+ "react-router": {
63
+ "optional": true
64
+ }
65
+ }
66
+ }
@@ -0,0 +1,19 @@
1
+ import { useState } from 'react';
2
+
3
+ import { deepEqual } from '../utils/Object/deepEqual/deepEqual';
4
+
5
+ export function setDeepEqualState<T>(defaultValue: T): [T, (nextValue: T) => void] {
6
+ const [
7
+ value,
8
+ setValue,
9
+ ] = useState(defaultValue);
10
+
11
+ return [
12
+ value,
13
+ (nextValue) => {
14
+ if (!deepEqual(value, nextValue)) {
15
+ setValue(nextValue);
16
+ }
17
+ },
18
+ ];
19
+ }
@@ -0,0 +1,46 @@
1
+ import { useEffect, useState } from 'react';
2
+ import { useLocation, useNavigate } from 'react-router';
3
+
4
+ export function useLocationParameter({
5
+ parameterName,
6
+ defaultValue,
7
+ }: {
8
+ parameterName: string;
9
+ defaultValue: string;
10
+ }): [value: string, setValue: (nextValue: string) => void] {
11
+ const location = useLocation();
12
+ const navigate = useNavigate();
13
+
14
+ // Extract the query parameter from the URL
15
+ const queryParams = new URLSearchParams(location.search);
16
+
17
+ const initialValue = queryParams.get(parameterName) || defaultValue;
18
+
19
+ // Set the query parameter as state
20
+ const [
21
+ stateValue,
22
+ setStateValue,
23
+ ] = useState(initialValue);
24
+
25
+ // Update the query parameter in the URL when the state changes
26
+ useEffect(() => {
27
+ const params = new URLSearchParams(location.search);
28
+
29
+ if (stateValue === defaultValue) {
30
+ params.delete(parameterName);
31
+ } else {
32
+ params.set(parameterName, String(stateValue));
33
+ }
34
+
35
+ navigate(`?${params.toString()}`, { replace: true });
36
+ }, [
37
+ stateValue,
38
+ location.search,
39
+ navigate,
40
+ ]);
41
+
42
+ return [
43
+ stateValue,
44
+ setStateValue,
45
+ ];
46
+ }
@@ -0,0 +1,22 @@
1
+ import { useRef } from 'react';
2
+
3
+ import { PositiveInteger } from '../types/Number/PositiveInteger';
4
+ import { deepEqual } from '../utils/Object/deepEqual/deepEqual';
5
+
6
+ import { useReload } from './useReload';
7
+
8
+ export function useObjectDeepChangesCounter(object: unknown): PositiveInteger {
9
+ const [
10
+ reload,
11
+ reloadIndex,
12
+ ] = useReload();
13
+
14
+ const objectRef = useRef(object);
15
+
16
+ if (!deepEqual(object, objectRef.current)) {
17
+ objectRef.current = object;
18
+ reload();
19
+ }
20
+
21
+ return reloadIndex;
22
+ }
@@ -0,0 +1,21 @@
1
+ import { useEffect, useRef } from 'react';
2
+
3
+ import { UnsignedInteger } from '../../types/Number/UnsignedInteger';
4
+ import { ObservableMap } from '../../utils/Map/ObservableMap/ObservableMap';
5
+ import { useReload } from '../useReload';
6
+
7
+ export function useObservableMap<K, V>(initialValue?: Iterable<readonly [K, V]> | null): [ObservableMap<K, V>, UnsignedInteger] {
8
+ const [
9
+ reload,
10
+ reloadIndex,
11
+ ] = useReload();
12
+
13
+ const mapRef = useRef<ObservableMap<K, V>>(new ObservableMap(initialValue));
14
+
15
+ useEffect(() => mapRef.current.subscribe(reload), []);
16
+
17
+ return [
18
+ mapRef.current,
19
+ reloadIndex,
20
+ ];
21
+ }
@@ -0,0 +1,42 @@
1
+ import {
2
+ useEffect,
3
+ useRef,
4
+ useState,
5
+ } from 'react';
6
+
7
+ import { Milliseconds } from '../types/Time/Milliseconds';
8
+ import { AbortableProcess } from '../utils/AbortableProcess';
9
+ import { promiseTimeout } from '../utils/timers/promiseTimeout/promiseTimeout';
10
+
11
+ export function useRecentChangeTimer<T>(value: T, timeoutMs: Milliseconds): boolean {
12
+ const [
13
+ isRecentlyChanged,
14
+ setIsRecentlyChanged,
15
+ ] = useState(true);
16
+
17
+ const lastValueRef = useRef<T>(value);
18
+
19
+ useEffect(() => {
20
+ const abortable = new AbortableProcess('useRecentChangeTimer');
21
+
22
+ setIsRecentlyChanged(true);
23
+ promiseTimeout(timeoutMs, { abortable }).then(() => {
24
+ setIsRecentlyChanged(false);
25
+ });
26
+
27
+ return () => {
28
+ abortable.abort('useEffect aborted');
29
+ };
30
+ }, [
31
+ value,
32
+ timeoutMs,
33
+ ]);
34
+
35
+ const isValueChanged = lastValueRef.current !== value;
36
+
37
+ if (isValueChanged) {
38
+ lastValueRef.current = value;
39
+ }
40
+
41
+ return isRecentlyChanged || isValueChanged;
42
+ }
@@ -0,0 +1,17 @@
1
+ import { useState } from 'react';
2
+
3
+ import { UnsignedInteger } from '../types/Number/UnsignedInteger';
4
+
5
+ export function useReload(): [VoidFunction, UnsignedInteger] {
6
+ const [
7
+ reloadIndex,
8
+ setReloadIndex,
9
+ ] = useState(0);
10
+
11
+ return [
12
+ () => {
13
+ setReloadIndex((prevIndex) => prevIndex + 1);
14
+ },
15
+ reloadIndex,
16
+ ];
17
+ }
@@ -0,0 +1,202 @@
1
+ import { useRef, useState } from 'react';
2
+
3
+ import {
4
+ VisualizedProcessesTree,
5
+ VisualizedProcessesTreeChange,
6
+ VisualizedProcessesTreeChangeAdd, VisualizedProcessesTreeChangeDelete, VisualizedProcessesTreeChangeStatus,
7
+ } from '../utils/AbortableProcess';
8
+ import { prettyStringify } from '../utils/prettyStringify';
9
+
10
+ type Node = {
11
+ parentId: VisualizedProcessesTree['id'] | null;
12
+ node: VisualizedProcessesTree;
13
+ };
14
+
15
+ type AllNodesMap = Map<
16
+ VisualizedProcessesTree['id'],
17
+ Node
18
+ >;
19
+
20
+ function reconstructAllNodesMap(allNodesMap: AllNodesMap, tree: VisualizedProcessesTree): void {
21
+ allNodesMap.clear();
22
+
23
+ const stack: {
24
+ node: VisualizedProcessesTree;
25
+ parent: VisualizedProcessesTree['id'] | null;
26
+ }[] = [
27
+ {
28
+ node: tree,
29
+ parent: null,
30
+ },
31
+ ];
32
+
33
+ while (stack.length) {
34
+ const { node, parent } = stack.shift()!;
35
+
36
+ allNodesMap.set(node.id, {
37
+ parentId: parent,
38
+ node,
39
+ });
40
+
41
+ for (let i = node.children.length - 1; i >= 0; i--) {
42
+ stack.push({
43
+ node: node.children[i],
44
+ parent: node.id,
45
+ });
46
+ }
47
+ }
48
+ }
49
+
50
+ function addNodeToAllNodesMap(allNodesMap: AllNodesMap, change: VisualizedProcessesTreeChangeAdd): void {
51
+ const {
52
+ id,
53
+ name,
54
+ parentId,
55
+ startTimestamp,
56
+ } = change;
57
+
58
+ const parentNode = allNodesMap.get(parentId);
59
+
60
+ if (!parentNode) {
61
+ throw new Error(`Parent node is not found in tree for change=[${prettyStringify(change)}]`);
62
+ }
63
+
64
+ const newNode = {
65
+ id,
66
+ name,
67
+ status: undefined,
68
+ startTimestamp,
69
+ children: [],
70
+ };
71
+
72
+ parentNode.node.children.push(newNode);
73
+
74
+ allNodesMap.set(id, {
75
+ parentId,
76
+ node: newNode,
77
+ });
78
+ }
79
+
80
+ function deleteNodeFromAllNodesMap(allNodesMap: AllNodesMap, change: VisualizedProcessesTreeChangeDelete): void {
81
+ const {
82
+ id,
83
+ } = change;
84
+
85
+ const node = allNodesMap.get(id);
86
+
87
+ if (!node) {
88
+ throw new Error(`Node is not found in tree for change=[${prettyStringify(change)}]`);
89
+ }
90
+
91
+ const { parentId } = node;
92
+
93
+ if (!parentId) {
94
+ throw new Error(`Cannot remove node without parent for change=[${prettyStringify(change)}]`);
95
+ }
96
+
97
+ const parentNode = allNodesMap.get(parentId);
98
+
99
+ if (!parentNode) {
100
+ throw new Error(`Parent node is not found in tree for change=[${prettyStringify(change)}]`);
101
+ }
102
+
103
+ const nodeIndex = parentNode.node.children.indexOf(node.node);
104
+
105
+ if (nodeIndex === -1) {
106
+ throw new Error(`Child node is not found the parent node "children" array for change=[${prettyStringify(change)}]`);
107
+ }
108
+
109
+ parentNode.node.children.splice(nodeIndex, 1);
110
+ }
111
+
112
+ function changeNodeStatusInAllNodesMap(allNodesMap: AllNodesMap, change: VisualizedProcessesTreeChangeStatus): void {
113
+ const {
114
+ id,
115
+ status,
116
+ } = change;
117
+
118
+ const node = allNodesMap.get(id);
119
+
120
+ if (!node) {
121
+ throw new Error(`Node is not found in tree for change=[${prettyStringify(change)}]`);
122
+ }
123
+
124
+ node.node.status = status;
125
+ }
126
+
127
+ function getTreeForParentNodeWithId(allNodesMap: AllNodesMap, parentId: VisualizedProcessesTree['id']): VisualizedProcessesTree {
128
+ const parentNode = allNodesMap.get(parentId);
129
+
130
+ if (!parentNode) {
131
+ throw new Error(`Parent node is not found in tree for parentId=[${parentId}]`);
132
+ }
133
+
134
+ return parentNode.node;
135
+ }
136
+
137
+ export function useVisualizedProcessesTree(): {
138
+ tree: VisualizedProcessesTree | null;
139
+ setTree: (tree: VisualizedProcessesTree) => void;
140
+ handleChanges: (changes: VisualizedProcessesTreeChange[]) => void;
141
+ } {
142
+ const [
143
+ tree,
144
+ setTree,
145
+ ] = useState<VisualizedProcessesTree | null>(null);
146
+
147
+ const allNodesMapRef = useRef<AllNodesMap>(new Map());
148
+
149
+ return {
150
+ tree,
151
+ setTree: (nextTree: VisualizedProcessesTree) => {
152
+ const allNodesMap = allNodesMapRef.current;
153
+
154
+ reconstructAllNodesMap(allNodesMap, nextTree);
155
+
156
+ const parentId = nextTree.id;
157
+ const nextTreeToDraw = getTreeForParentNodeWithId(allNodesMap, parentId);
158
+
159
+ setTree(nextTreeToDraw);
160
+ },
161
+ handleChanges: (changes) => {
162
+ const allNodesMap = allNodesMapRef.current;
163
+
164
+ changes.forEach((change) => {
165
+ const { type } = change;
166
+
167
+ if (type === 'add') {
168
+ addNodeToAllNodesMap(allNodesMap, change);
169
+
170
+ return;
171
+ }
172
+
173
+ if (type === 'delete') {
174
+ deleteNodeFromAllNodesMap(allNodesMap, change);
175
+
176
+ return;
177
+ }
178
+
179
+ if (type === 'status') {
180
+ changeNodeStatusInAllNodesMap(allNodesMap, change);
181
+
182
+ return;
183
+ }
184
+
185
+ throw new Error(`Unknown event type=[${type}] for change=[${prettyStringify(change)}]`);
186
+ });
187
+
188
+ setTree((previousTree) => {
189
+ if (!previousTree) {
190
+ throw new Error('Initial tree has NOT been set');
191
+ }
192
+
193
+ const parentId = previousTree.id;
194
+ const nextTreeToDraw = getTreeForParentNodeWithId(allNodesMap, parentId);
195
+
196
+ return {
197
+ ...nextTreeToDraw,
198
+ };
199
+ });
200
+ },
201
+ };
202
+ }
@@ -0,0 +1,9 @@
1
+ export function isBoolean(value: unknown): value is boolean {
2
+ return typeof value === 'boolean';
3
+ }
4
+
5
+ export function assertBoolean(value: unknown, errorMessage?: string): asserts value is boolean {
6
+ if (!isBoolean(value)) {
7
+ throw new Error(`assert boolean error${errorMessage ? ` errorMessage=[${errorMessage}]` : ''}`);
8
+ }
9
+ }
@@ -0,0 +1 @@
1
+ export type DeepPartial<T> = T extends string ? T : { [key in keyof T]?: DeepPartial<T[key]> };
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Глобальный объект.
3
+ * Соответствует `Window & typeof globalThis` начиная с ts3.6,
4
+ * и `Window` в более ранних версия ts
5
+ * @see https://github.com/microsoft/TypeScript/wiki/Breaking-Changes#dom-updates
6
+ */
7
+
8
+ export type GlobalContext = typeof window;
@@ -0,0 +1,9 @@
1
+ /**
2
+ * string like:
3
+ * `#AABBCC`
4
+ * `#9900aa`
5
+ * `#123456`
6
+ *
7
+ * (6 hexadecimal symbols)
8
+ */
9
+ export type HexColorString = `#${string}`;
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Язык формате ISO 639-1
3
+ * Строка вида `"ru"` или `"en"`
4
+ */
5
+ export type ISOLang = string;
@@ -0,0 +1 @@
1
+ export type IsEqualUnion<T, U> = [T] extends [U] ? [U] extends [T] ? true : false : false;
@@ -0,0 +1,4 @@
1
+ export type IterableWithIndexAccess<T> = Iterable<T> & {
2
+ length: number;
3
+ at(index: number): T | undefined;
4
+ };
@@ -0,0 +1,4 @@
1
+ /**
2
+ * Объем информации в килобитах
3
+ */
4
+ export type Kilobits = number;
@@ -0,0 +1,4 @@
1
+ /**
2
+ * Объем информации в битах
3
+ */
4
+ export type Bits = number;
@@ -0,0 +1,4 @@
1
+ /**
2
+ * Скорость в битах в секунду
3
+ */
4
+ export type Bps = number;
@@ -0,0 +1,4 @@
1
+ /**
2
+ * Скорость в килобитах в секунду
3
+ */
4
+ export type Kbps = number;
@@ -0,0 +1,4 @@
1
+ /**
2
+ * Скорость в мегабитах в секунду
3
+ */
4
+ export type Mbps = number;
@@ -0,0 +1,3 @@
1
+ import { Integer } from './Integer';
2
+
3
+ export type AngleInDegrees = Integer;
@@ -0,0 +1,11 @@
1
+ export type FiniteNumber = number;
2
+
3
+ export function isFiniteNumber(val: unknown): val is FiniteNumber {
4
+ return Number.isFinite(val);
5
+ }
6
+
7
+ export function assertFiniteNumber(val: unknown, message?: string): asserts val is FiniteNumber {
8
+ if (!isFiniteNumber(val)) {
9
+ throw new Error(`assertFiniteNumber error${message ? ` message=[${message}]` : ''}`);
10
+ }
11
+ }
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Целое число
3
+ */
4
+ export type Integer = number;
5
+
6
+ export function isInteger(value: unknown): value is Integer {
7
+ return Number.isInteger(value);
8
+ }
9
+
10
+ export function assertInteger(value: unknown): asserts value is Integer {
11
+ if (!isInteger(value)) {
12
+ throw new Error(`value=[${value}] is not an integer`);
13
+ }
14
+ }
@@ -0,0 +1,13 @@
1
+ import { isString } from '../../utils/String/isString';
2
+
3
+ /**
4
+ * Целое цисло (положительное или отрицательное), представленное строкой
5
+ * 💁‍♂️ '0'
6
+ * 💁‍♂️ '-100500'
7
+ * 💁‍♂️ '3456'
8
+ */
9
+ export type IntegerInString = string;
10
+
11
+ export function isIntegerInString(str: unknown): boolean {
12
+ return isString(str) && str === parseInt(str, 10).toString(10);
13
+ }
@@ -0,0 +1,15 @@
1
+ export function isNumber(value: unknown): value is number {
2
+ return typeof value === 'number';
3
+ }
4
+
5
+ export function assertNumber(value: unknown, message?: string): asserts value is number {
6
+ if (!isNumber(value)) {
7
+ let errorMessage = `value=[${value}] is not a number`;
8
+
9
+ if (message !== undefined) {
10
+ errorMessage += ` message=[${message}]`;
11
+ }
12
+
13
+ throw new Error(errorMessage);
14
+ }
15
+ }
@@ -0,0 +1,4 @@
1
+ /**
2
+ * Доля от 0 до 1
3
+ */
4
+ export type Portion = number;
@@ -0,0 +1,30 @@
1
+ import { PositiveNumber } from '../../types/Number/PositiveNumber';
2
+
3
+ import { Integer } from './Integer';
4
+
5
+ /**
6
+ * Целое положительное число
7
+ */
8
+ export type PositiveInteger = PositiveNumber & Integer;
9
+
10
+ export function isPositiveInteger(value: unknown): value is PositiveInteger {
11
+ if (!Number.isInteger(value)) {
12
+ return false;
13
+ }
14
+
15
+ return (value as Integer) > 0;
16
+ }
17
+
18
+ export function assertPositiveInteger(value: unknown, errorMessage?: string): asserts value is PositiveInteger {
19
+ if (!isPositiveInteger(value)) {
20
+ throw new Error(`value=[${value}] is not a positive integer${errorMessage ? ` errorMessage=[${errorMessage}]` : ''}`);
21
+ }
22
+ }
23
+
24
+ export function convertAndAssertPositiveInteger(value: unknown): PositiveInteger {
25
+ const result = Number(value);
26
+
27
+ assertPositiveInteger(result);
28
+
29
+ return result;
30
+ }
@@ -0,0 +1,4 @@
1
+ /**
2
+ * Положительное число
3
+ */
4
+ export type PositiveNumber = number;
@@ -0,0 +1,4 @@
1
+ /**
2
+ * Unsigned int in string
3
+ */
4
+ export type StringUnsignedInteger = string;
@@ -0,0 +1,13 @@
1
+ import { FiniteNumber, isFiniteNumber } from './FiniteNumber';
2
+
3
+ export type UnsignedFiniteNumber = FiniteNumber;
4
+
5
+ export function isUnsignedFiniteNumber(val: unknown): val is UnsignedFiniteNumber {
6
+ return isFiniteNumber(val) && val >= 0;
7
+ }
8
+
9
+ export function assertUnsignedFiniteNumber(val: unknown, message?: string): asserts val is UnsignedFiniteNumber {
10
+ if (!isUnsignedFiniteNumber(val)) {
11
+ throw new Error(`assertUnsignedFiniteNumber error${message ? ` message=[${message}]` : ''}`);
12
+ }
13
+ }