@zelgadis87/utils-core 4.4.4 → 4.6.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/esbuild/index.cjs +207 -270
- package/esbuild/{index.mjs → index.js} +197 -282
- package/esbuild/package.json +39 -0
- package/package.json +14 -102
- package/src/async/Semaphore.ts +8 -6
- package/src/time/TimeDuration.ts +22 -11
- package/src/time/TimeInstant.ts +38 -21
- package/src/utils/arrays/groupBy.ts +11 -11
- package/src/utils/arrays.ts +9 -3
- package/src/utils/functions/constant.ts +2 -0
- package/src/utils/functions.ts +23 -0
- package/src/utils/random.ts +9 -3
- package/src/utils/records.ts +12 -0
- package/dist/Logger.d.ts +0 -21
- package/dist/Optional.d.ts +0 -70
- package/dist/async/CancelableDeferred.d.ts +0 -17
- package/dist/async/Deferred.d.ts +0 -34
- package/dist/async/RateThrottler.d.ts +0 -13
- package/dist/async/Semaphore.d.ts +0 -14
- package/dist/async/index.d.ts +0 -4
- package/dist/index.d.ts +0 -8
- package/dist/lazy/Lazy.d.ts +0 -22
- package/dist/lazy/LazyAsync.d.ts +0 -24
- package/dist/lazy/index.d.ts +0 -2
- package/dist/sorting/ComparisonChain.d.ts +0 -57
- package/dist/sorting/Sorter.d.ts +0 -100
- package/dist/sorting/index.d.ts +0 -4
- package/dist/sorting/types.d.ts +0 -5
- package/dist/time/RandomTimeDuration.d.ts +0 -9
- package/dist/time/TimeBase.d.ts +0 -38
- package/dist/time/TimeDuration.d.ts +0 -79
- package/dist/time/TimeFrequency.d.ts +0 -10
- package/dist/time/TimeInstant.d.ts +0 -171
- package/dist/time/TimeInstantBuilder.d.ts +0 -77
- package/dist/time/TimeRange.d.ts +0 -11
- package/dist/time/TimeUnit.d.ts +0 -17
- package/dist/time/index.d.ts +0 -7
- package/dist/time/types.d.ts +0 -26
- package/dist/tsconfig.tsbuildinfo +0 -1
- package/dist/upgrade/DataUpgrader.d.ts +0 -22
- package/dist/upgrade/errors.d.ts +0 -12
- package/dist/upgrade/getTransitionsPath.d.ts +0 -3
- package/dist/upgrade/index.d.ts +0 -2
- package/dist/upgrade/types.d.ts +0 -31
- package/dist/utils/arrays/groupBy.d.ts +0 -9
- package/dist/utils/arrays/indexBy.d.ts +0 -7
- package/dist/utils/arrays/statistics.d.ts +0 -8
- package/dist/utils/arrays/uniqBy.d.ts +0 -4
- package/dist/utils/arrays.d.ts +0 -56
- package/dist/utils/booleans.d.ts +0 -2
- package/dist/utils/empties.d.ts +0 -17
- package/dist/utils/errors/withTryCatch.d.ts +0 -3
- package/dist/utils/errors.d.ts +0 -3
- package/dist/utils/functions/constant.d.ts +0 -8
- package/dist/utils/functions/iff.d.ts +0 -8
- package/dist/utils/functions.d.ts +0 -27
- package/dist/utils/index.d.ts +0 -13
- package/dist/utils/json.d.ts +0 -11
- package/dist/utils/nulls.d.ts +0 -8
- package/dist/utils/numbers/round.d.ts +0 -8
- package/dist/utils/numbers.d.ts +0 -34
- package/dist/utils/primitives.d.ts +0 -3
- package/dist/utils/promises.d.ts +0 -6
- package/dist/utils/random.d.ts +0 -2
- package/dist/utils/records/entries.d.ts +0 -4
- package/dist/utils/records.d.ts +0 -21
- package/dist/utils/strings/StringParts.d.ts +0 -32
- package/dist/utils/strings.d.ts +0 -17
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json.schemastore.org/package",
|
|
3
|
+
"name": "@zelgadis87/utils-core",
|
|
4
|
+
"version": "4.5.0",
|
|
5
|
+
"author": "Zelgadis87",
|
|
6
|
+
"license": "ISC",
|
|
7
|
+
"private": false,
|
|
8
|
+
"files": [
|
|
9
|
+
"/src",
|
|
10
|
+
"/dist",
|
|
11
|
+
"/esbuild"
|
|
12
|
+
],
|
|
13
|
+
"types": "./dist/index.d.ts",
|
|
14
|
+
"exports": {
|
|
15
|
+
".": {
|
|
16
|
+
"types": "./dist/index.d.ts",
|
|
17
|
+
"import": "./esbuild/index.js",
|
|
18
|
+
"require": "./esbuild/index.cjs"
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
"packageManager": "pnpm@10.0.0+sha512.b8fef5494bd3fe4cbd4edabd0745df2ee5be3e4b0b8b08fa643aa3e4c6702ccc0f00d68fa8a8c9858a735a0032485a44990ed2810526c875e416f001b17df12b",
|
|
22
|
+
"devDependencies": {
|
|
23
|
+
"@stylistic/eslint-plugin-ts": "catalog:",
|
|
24
|
+
"@typescript-eslint/eslint-plugin": "catalog:",
|
|
25
|
+
"@typescript-eslint/parser": "catalog:",
|
|
26
|
+
"@vitest/coverage-v8": "catalog:",
|
|
27
|
+
"@vitest/ui": "catalog:",
|
|
28
|
+
"esbuild": "catalog:",
|
|
29
|
+
"eslint": "catalog:",
|
|
30
|
+
"nx": "catalog:",
|
|
31
|
+
"rimraf": "catalog:",
|
|
32
|
+
"typescript": "catalog:",
|
|
33
|
+
"typescript-eslint": "^8.20.0",
|
|
34
|
+
"vitest": "catalog:"
|
|
35
|
+
},
|
|
36
|
+
"dependencies": {
|
|
37
|
+
"small-date": "^2.0.1"
|
|
38
|
+
}
|
|
39
|
+
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json.schemastore.org/package",
|
|
3
3
|
"name": "@zelgadis87/utils-core",
|
|
4
|
-
"version": "4.
|
|
4
|
+
"version": "4.6.0",
|
|
5
5
|
"author": "Zelgadis87",
|
|
6
6
|
"license": "ISC",
|
|
7
7
|
"private": false,
|
|
@@ -10,117 +10,29 @@
|
|
|
10
10
|
"/dist",
|
|
11
11
|
"/esbuild"
|
|
12
12
|
],
|
|
13
|
-
"wireit": {
|
|
14
|
-
"build:ts": {
|
|
15
|
-
"command": "tsc --emitDeclarationOnly",
|
|
16
|
-
"clean": "if-file-deleted",
|
|
17
|
-
"files": [
|
|
18
|
-
"./src/**"
|
|
19
|
-
],
|
|
20
|
-
"output": [
|
|
21
|
-
"./dist/**"
|
|
22
|
-
]
|
|
23
|
-
},
|
|
24
|
-
"build:cjs": {
|
|
25
|
-
"command": "esbuild src/index.ts --bundle --platform=node --target=node18 --format=cjs --outfile=esbuild/index.cjs",
|
|
26
|
-
"clean": "if-file-deleted",
|
|
27
|
-
"files": [
|
|
28
|
-
"./src/**"
|
|
29
|
-
],
|
|
30
|
-
"output": [
|
|
31
|
-
"./esbuild/index.cjs"
|
|
32
|
-
]
|
|
33
|
-
},
|
|
34
|
-
"build:esm": {
|
|
35
|
-
"command": "esbuild src/index.ts --bundle --platform=node --target=node18 --format=esm --outfile=esbuild/index.mjs",
|
|
36
|
-
"clean": "if-file-deleted",
|
|
37
|
-
"files": [
|
|
38
|
-
"./src/**"
|
|
39
|
-
],
|
|
40
|
-
"output": [
|
|
41
|
-
"./esbuild/index.mjs"
|
|
42
|
-
]
|
|
43
|
-
},
|
|
44
|
-
"build": {
|
|
45
|
-
"dependencies": [
|
|
46
|
-
"build:ts",
|
|
47
|
-
"build:cjs",
|
|
48
|
-
"build:esm"
|
|
49
|
-
]
|
|
50
|
-
},
|
|
51
|
-
"test": {
|
|
52
|
-
"command": "vitest run --passWithNoTests",
|
|
53
|
-
"clean": true,
|
|
54
|
-
"files": [
|
|
55
|
-
"./src/**/*.ts",
|
|
56
|
-
"./test/**/*.spec.ts"
|
|
57
|
-
],
|
|
58
|
-
"output": []
|
|
59
|
-
},
|
|
60
|
-
"test:coverage": {
|
|
61
|
-
"command": "vitest run --coverage --passWithNoTests",
|
|
62
|
-
"clean": true,
|
|
63
|
-
"files": [
|
|
64
|
-
"./src/**/*.ts",
|
|
65
|
-
"./test/**/*.spec.ts"
|
|
66
|
-
],
|
|
67
|
-
"output": [
|
|
68
|
-
"./coverage/*.*"
|
|
69
|
-
]
|
|
70
|
-
},
|
|
71
|
-
"lint:strict": {
|
|
72
|
-
"command": "eslint .",
|
|
73
|
-
"dependencies": [],
|
|
74
|
-
"clean": false,
|
|
75
|
-
"files": [
|
|
76
|
-
"./*.ts"
|
|
77
|
-
],
|
|
78
|
-
"output": []
|
|
79
|
-
},
|
|
80
|
-
"check": {
|
|
81
|
-
"dependencies": [
|
|
82
|
-
"lint:strict",
|
|
83
|
-
"test"
|
|
84
|
-
]
|
|
85
|
-
}
|
|
86
|
-
},
|
|
87
13
|
"types": "./dist/index.d.ts",
|
|
88
14
|
"exports": {
|
|
89
15
|
".": {
|
|
90
16
|
"types": "./dist/index.d.ts",
|
|
91
|
-
"import": "./esbuild/index.
|
|
17
|
+
"import": "./esbuild/index.js",
|
|
92
18
|
"require": "./esbuild/index.cjs"
|
|
93
19
|
}
|
|
94
20
|
},
|
|
95
21
|
"devDependencies": {
|
|
96
|
-
"@stylistic/eslint-plugin-ts": "2.
|
|
97
|
-
"@typescript-eslint/eslint-plugin": "8.
|
|
98
|
-
"@typescript-eslint/parser": "8.
|
|
99
|
-
"@vitest/coverage-v8": "
|
|
100
|
-
"@vitest/ui": "
|
|
101
|
-
"esbuild": "
|
|
102
|
-
"eslint": "
|
|
103
|
-
"
|
|
104
|
-
"
|
|
105
|
-
"
|
|
106
|
-
"
|
|
22
|
+
"@stylistic/eslint-plugin-ts": "2.13.0",
|
|
23
|
+
"@typescript-eslint/eslint-plugin": "8.20.0",
|
|
24
|
+
"@typescript-eslint/parser": "8.20.0",
|
|
25
|
+
"@vitest/coverage-v8": "2.1.8",
|
|
26
|
+
"@vitest/ui": "2.1.8",
|
|
27
|
+
"esbuild": "0.24.2",
|
|
28
|
+
"eslint": "9.18.0",
|
|
29
|
+
"nx": "20.3.2",
|
|
30
|
+
"rimraf": "6.0.1",
|
|
31
|
+
"typescript": "5.7.3",
|
|
32
|
+
"typescript-eslint": "^8.20.0",
|
|
33
|
+
"vitest": "2.1.8"
|
|
107
34
|
},
|
|
108
35
|
"dependencies": {
|
|
109
36
|
"small-date": "^2.0.1"
|
|
110
|
-
},
|
|
111
|
-
"scripts": {
|
|
112
|
-
"clean": "rimraf .wireit dist esbuild coverage",
|
|
113
|
-
"build": "wireit",
|
|
114
|
-
"lint": "pnpm eslint .",
|
|
115
|
-
"lint:errors": "pnpm lint --quiet",
|
|
116
|
-
"lint:strict": "wireit",
|
|
117
|
-
"test": "wireit",
|
|
118
|
-
"test:watch": "vitest dev --passWithNoTests",
|
|
119
|
-
"test:ui": "vitest dev --ui --coverage --passWithNoTests",
|
|
120
|
-
"test:coverage": "wireit",
|
|
121
|
-
"test:production": "pnpm test -- -- --allowOnly=false",
|
|
122
|
-
"check": "wireit",
|
|
123
|
-
"preversion": "pnpm build && pnpm test:production",
|
|
124
|
-
"prepublish": "pnpm build && pnpm test:production"
|
|
125
37
|
}
|
|
126
38
|
}
|
package/src/async/Semaphore.ts
CHANGED
|
@@ -6,7 +6,11 @@ export class Semaphore {
|
|
|
6
6
|
private readonly _queuedRequests: Deferred<void>[];
|
|
7
7
|
private _inProgress = 0;
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
/** Creates a new Semaphore with a single slot. */
|
|
10
|
+
public constructor();
|
|
11
|
+
/** Creates a new Semaphore with the specified amounts of slots. */
|
|
12
|
+
public constructor( availableSlots: number );
|
|
13
|
+
public constructor(
|
|
10
14
|
private _availableSlots: number = 1
|
|
11
15
|
) {
|
|
12
16
|
this._queuedRequests = [];
|
|
@@ -28,19 +32,17 @@ export class Semaphore {
|
|
|
28
32
|
private _releaseSlot() {
|
|
29
33
|
const waitingSlotToResolve = this._queuedRequests.shift();
|
|
30
34
|
if ( waitingSlotToResolve ) {
|
|
35
|
+
// Let the next request start.
|
|
31
36
|
waitingSlotToResolve.resolve();
|
|
32
37
|
} else {
|
|
38
|
+
// Free a slot.
|
|
33
39
|
this._availableSlots += 1;
|
|
34
40
|
this._inProgress -= 1;
|
|
35
41
|
}
|
|
36
42
|
}
|
|
37
43
|
|
|
38
44
|
public async execute<T>( fn: TAsyncProducer<T> | TProducer<T>, cooldown: TimeDuration = TimeDuration.ZERO ) {
|
|
39
|
-
return this._awaitSlot()
|
|
40
|
-
.then( () => fn() )
|
|
41
|
-
.finally( () => {
|
|
42
|
-
void cooldown.promise().then( () => this._releaseSlot() );
|
|
43
|
-
} );
|
|
45
|
+
return this._awaitSlot().then( fn ).finally( () => { void cooldown.delay( () => this._releaseSlot() ); } );
|
|
44
46
|
}
|
|
45
47
|
|
|
46
48
|
public get availableSlots() {
|
package/src/time/TimeDuration.ts
CHANGED
|
@@ -46,18 +46,17 @@ export class TimeDuration extends TimeBase<TimeDuration> {
|
|
|
46
46
|
}
|
|
47
47
|
|
|
48
48
|
/**
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
49
|
+
* Returns the current duration in a human readable format, prioritizing the most significant unit of time and excluding the rest.
|
|
50
|
+
* @todo[2023-06] Should allow more customization options
|
|
51
|
+
* @todo[2023-06] By default should show the secondary unit only when actually needed (eg, 1h 20m & 3h, instead of 1h 20m & 3h 28m)
|
|
52
|
+
* @todo[2023-06] Should not allow negative durations, as this is a duration and not an instant.
|
|
53
|
+
* @returns the duration, with only the most significant units displayed as a human readable string, eg: 1d 20h, 10m 30s.
|
|
54
54
|
*/
|
|
55
55
|
public get formatted(): string {
|
|
56
56
|
const format = ( x: number, u1: string, y: number, u2: string ): string => `${x}${u1} ${pad( y.toString(), 2, '0' )}${u2}`;
|
|
57
57
|
const units = this.toUnits();
|
|
58
58
|
|
|
59
|
-
if ( units.days >= 1 )
|
|
60
|
-
return format( units.days, 'd', units.hours, 'h' );
|
|
59
|
+
if ( units.days >= 1 ) return format( units.days, 'd', units.hours, 'h' );
|
|
61
60
|
else if ( units.hours >= 1 )
|
|
62
61
|
return format( units.hours, 'h', units.minutes, 'm' );
|
|
63
62
|
else if ( units.minutes >= 1 )
|
|
@@ -85,8 +84,7 @@ export class TimeDuration extends TimeBase<TimeDuration> {
|
|
|
85
84
|
return setTimeout( cb, this.ms );
|
|
86
85
|
}
|
|
87
86
|
|
|
88
|
-
|
|
89
|
-
public promise(): ICancelablePromise<void> {
|
|
87
|
+
public cancelablePromise(): ICancelablePromise<void> {
|
|
90
88
|
const deferred = new Deferred<void>;
|
|
91
89
|
if ( this.ms > 0 ) {
|
|
92
90
|
this.timeout( () => deferred.resolve() );
|
|
@@ -96,8 +94,21 @@ export class TimeDuration extends TimeBase<TimeDuration> {
|
|
|
96
94
|
return deferred.asCancelablePromise();
|
|
97
95
|
}
|
|
98
96
|
|
|
99
|
-
public
|
|
100
|
-
|
|
97
|
+
public promise(): Promise<void> {
|
|
98
|
+
if ( this.isEmpty() )
|
|
99
|
+
return Promise.resolve();
|
|
100
|
+
|
|
101
|
+
const deferred = new Deferred<void>;
|
|
102
|
+
this.timeout( () => deferred.resolve() );
|
|
103
|
+
return deferred.asPromise();
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
public delay( cb: () => unknown ): void {
|
|
107
|
+
void this.promise().then( () => cb() );
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
public cancelableDelay( cb: () => unknown ): ICancelable {
|
|
111
|
+
const deferred = this.cancelablePromise();
|
|
101
112
|
void deferred.then( () => {
|
|
102
113
|
cb();
|
|
103
114
|
}, err => {
|
package/src/time/TimeInstant.ts
CHANGED
|
@@ -33,6 +33,16 @@ export class TimeInstant extends TimeBase<TimeInstant> {
|
|
|
33
33
|
return TimeDuration.fromMs( Math.abs( this.ms - Date.now() ) );
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
+
public timeLeftFrom( instant: TimeInstant ): TimeDuration {
|
|
37
|
+
const distance = this.ms - instant.ms;
|
|
38
|
+
return TimeDuration.fromMs( Math.max( distance, 0 ) );
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
public timeLeftFromNow(): TimeDuration {
|
|
42
|
+
const distance = this.ms - Date.now();
|
|
43
|
+
return TimeDuration.fromMs( Math.max( distance, 0 ) );
|
|
44
|
+
}
|
|
45
|
+
|
|
36
46
|
public distanceFromStartOfDay(): TimeDuration {
|
|
37
47
|
return this.distanceFrom( this.atStartOfDay() );
|
|
38
48
|
}
|
|
@@ -49,16 +59,20 @@ export class TimeInstant extends TimeBase<TimeInstant> {
|
|
|
49
59
|
return this.atTime( { hours: 23, minutes: 59, seconds: 59, milliseconds: 999 } );
|
|
50
60
|
}
|
|
51
61
|
|
|
52
|
-
public promise():
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
62
|
+
public promise(): Promise<void> {
|
|
63
|
+
return this.timeLeftFromNow().promise();
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
public cancelablePromise(): ICancelablePromise<void> {
|
|
67
|
+
return this.timeLeftFromNow().cancelablePromise();
|
|
56
68
|
}
|
|
57
69
|
|
|
58
|
-
public delay( cb: () => unknown ):
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
70
|
+
public delay( cb: () => unknown ): void {
|
|
71
|
+
return this.timeLeftFromNow().delay( cb );
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
public cancelableDelay( cb: () => unknown ): ICancelable {
|
|
75
|
+
return this.timeLeftFromNow().cancelableDelay( cb );
|
|
62
76
|
}
|
|
63
77
|
|
|
64
78
|
public ensureInTheFuture() {
|
|
@@ -191,22 +205,25 @@ export class TimeInstant extends TimeBase<TimeInstant> {
|
|
|
191
205
|
}
|
|
192
206
|
|
|
193
207
|
/**
|
|
194
|
-
|
|
195
|
-
|
|
208
|
+
* @deprecated: Use {@link distanceFromNow} instead
|
|
209
|
+
*/
|
|
210
|
+
// TODO[2023-07-04, Deprecated]: Remove this method in a future version
|
|
196
211
|
public fromNow(): TimeDuration {
|
|
197
212
|
return TimeDuration.ms( this.ms - Date.now() );
|
|
198
213
|
}
|
|
199
214
|
|
|
200
215
|
/**
|
|
201
|
-
|
|
202
|
-
|
|
216
|
+
* @deprecated: Use {@link distanceFrom} instead
|
|
217
|
+
*/
|
|
218
|
+
// TODO[2023-07-04, Deprecated]: Remove this method in a future version
|
|
203
219
|
public from( instant: TimeInstant ): TimeDuration {
|
|
204
220
|
return TimeDuration.ms( this.ms - instant.ms );
|
|
205
221
|
}
|
|
206
222
|
|
|
207
223
|
/**
|
|
208
|
-
|
|
209
|
-
|
|
224
|
+
* @deprecated: Use {@link distanceFromUnixTimestamp} instead
|
|
225
|
+
*/
|
|
226
|
+
// TODO[2023-07-04, Deprecated]: Remove this method in a future version
|
|
210
227
|
public fromTimestamp( timestamp: number ): TimeDuration {
|
|
211
228
|
return TimeDuration.ms( this.ms - timestamp );
|
|
212
229
|
}
|
|
@@ -260,8 +277,8 @@ export class TimeInstant extends TimeBase<TimeInstant> {
|
|
|
260
277
|
}
|
|
261
278
|
|
|
262
279
|
/**
|
|
263
|
-
|
|
264
|
-
|
|
280
|
+
* @deprecated[19/07/2023] Use {@link fromParameters} instead.
|
|
281
|
+
*/
|
|
265
282
|
public static fromUnits( { date, month, year, hours, minutes, seconds }: { date: number, month: number, year: number, hours?: number, minutes?: number, seconds?: number } ): TimeInstant {
|
|
266
283
|
const dt = Date.UTC( year, month - 1, date, hours ?? 0, minutes ?? 0, seconds ?? 0 );
|
|
267
284
|
if ( isNaN( dt ) )
|
|
@@ -334,15 +351,15 @@ export class TimeInstant extends TimeBase<TimeInstant> {
|
|
|
334
351
|
}
|
|
335
352
|
|
|
336
353
|
/**
|
|
337
|
-
|
|
354
|
+
* Returns the week number represented by this instant, according to the ISO 8601 standard.
|
|
338
355
|
* Please note that the instant and the week number could be of two different years, eg the friday 1st january is actually part of week 52 of the previous year.
|
|
339
|
-
|
|
356
|
+
*/
|
|
340
357
|
public get weekNumber(): { weekNumber: TWeekNumber, year: number } {
|
|
341
358
|
/**
|
|
342
359
|
* According to the ISO 8601 Standard, week number 1 is defined as the week containing the 4th of january (or, equivalently, the week that contains the first Thursday of the year).
|
|
343
360
|
* As such, we basically have to count how many Thursdays there has been in this year.
|
|
344
361
|
* Please note that the thursdayOfThisWeek could be in the previous year.
|
|
345
|
-
|
|
362
|
+
*/
|
|
346
363
|
const date = this.toDate();
|
|
347
364
|
const oneDay = 1000 * 60 * 60 * 24;
|
|
348
365
|
const thursdayOfThisWeek = new Date( date.getFullYear(), date.getMonth(), date.getDate() + 4 - ( date.getDay() || 7 ), 14, 0, 0 );
|
|
@@ -353,8 +370,8 @@ export class TimeInstant extends TimeBase<TimeInstant> {
|
|
|
353
370
|
}
|
|
354
371
|
|
|
355
372
|
/**
|
|
356
|
-
|
|
357
|
-
|
|
373
|
+
* This method is used to provide a better DX when inspecting a TimeInstant object in DevTools.
|
|
374
|
+
*/
|
|
358
375
|
protected get _time() {
|
|
359
376
|
return this.asHumanTimestamp();
|
|
360
377
|
}
|
|
@@ -1,40 +1,40 @@
|
|
|
1
1
|
|
|
2
|
-
import { TFunction, TKeysOfType } from "..";
|
|
2
|
+
import { TFunction, TKeysOfType, TReadableArray } from "..";
|
|
3
3
|
|
|
4
|
-
export function groupByString<V, K extends TKeysOfType<V, string>>( arr: V
|
|
4
|
+
export function groupByString<V, K extends TKeysOfType<V, string>>( arr: TReadableArray<V>, field: K ): Record<V[ K ] & string, V[]> {
|
|
5
5
|
return groupByStringWith( arr, t => t[ field ] as string );
|
|
6
6
|
}
|
|
7
7
|
|
|
8
|
-
export function groupByNumber<V, K extends TKeysOfType<V, number>>( arr: V
|
|
8
|
+
export function groupByNumber<V, K extends TKeysOfType<V, number>>( arr: TReadableArray<V>, field: K ): Record<V[ K ] & number, V[]> {
|
|
9
9
|
return groupByNumberWith( arr, t => t[ field ] as number );
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
-
export function groupByBoolean<V, K extends TKeysOfType<V, boolean>>( arr: V
|
|
12
|
+
export function groupByBoolean<V, K extends TKeysOfType<V, boolean>>( arr: TReadableArray<V>, field: K ): Record<"true" | "false", V[]> {
|
|
13
13
|
return groupByBooleanWith( arr, t => t[ field ] as boolean );
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
-
export function groupBySymbol<V, K extends TKeysOfType<V, symbol>>( arr: V
|
|
16
|
+
export function groupBySymbol<V, K extends TKeysOfType<V, symbol>>( arr: TReadableArray<V>, field: K ): Record<V[ K ] & symbol, V[]> {
|
|
17
17
|
return groupBySymbolWith( arr, t => t[ field ] as symbol );
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
-
export function groupByStringWith<V, K extends string>( arr: V
|
|
20
|
+
export function groupByStringWith<V, K extends string>( arr: TReadableArray<V>, getter: TFunction<V, K> ): Record<K, V[]> {
|
|
21
21
|
return doGroupByWith<K, V>( arr, getter );
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
export function groupByNumberWith<V, K extends number>( arr: V
|
|
24
|
+
export function groupByNumberWith<V, K extends number>( arr: TReadableArray<V>, getter: TFunction<V, K> ): Record<K, V[]> {
|
|
25
25
|
return doGroupByWith<K, V>( arr, getter );
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
-
export function groupByBooleanWith<V, K extends boolean>( arr: V
|
|
28
|
+
export function groupByBooleanWith<V, K extends boolean>( arr: TReadableArray<V>, getter: TFunction<V, K> ): Record<"true" | "false", V[]> {
|
|
29
29
|
return doGroupByWith<"true" | "false", V>( arr, item => getter( item ) ? "true" : "false" );
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
-
export function groupBySymbolWith<V, K extends symbol>( arr: V
|
|
32
|
+
export function groupBySymbolWith<V, K extends symbol>( arr: TReadableArray<V>, getter: TFunction<V, K> ): Record<K, V[]> {
|
|
33
33
|
return doGroupByWith<K, V>( arr, getter );
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
-
function doGroupByWith<K extends string | number | symbol, V>( arr: V
|
|
37
|
-
return arr.reduce( ( dict, cur ) => {
|
|
36
|
+
function doGroupByWith<K extends string | number | symbol, V>( arr: TReadableArray<V>, getter: TFunction<V, K> ): Record<K, V[]> { // TODO[2025-01-03, Native]: Migrate to native @ https://devblogs.microsoft.com/typescript/announcing-typescript-5-4/#object.groupby-and-map.groupby
|
|
37
|
+
return [ ...arr ].reduce( ( dict, cur ) => {
|
|
38
38
|
const key = getter( cur );
|
|
39
39
|
if ( key !== null && key !== undefined ) {
|
|
40
40
|
const arr = dict[ key ] ?? [];
|
package/src/utils/arrays.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { TComparisonFunction } from "../sorting/index.js";
|
|
2
2
|
import type { TAccumulator, TBiPredicate, TPredicate, TTransformer } from "./functions.js";
|
|
3
|
-
import type
|
|
3
|
+
import { isNullOrUndefined, type TMaybe } from "./nulls.js";
|
|
4
4
|
|
|
5
5
|
export * from './arrays/groupBy.js';
|
|
6
6
|
export * from './arrays/indexBy.js';
|
|
@@ -11,10 +11,12 @@ export type TReadableArray<T> = Array<T> | ReadonlyArray<T>;
|
|
|
11
11
|
export type TArrayable<T> = T | T[];
|
|
12
12
|
export type TEmptyArray = [];
|
|
13
13
|
|
|
14
|
-
export function ensureArray<const T>( t: T | Array<T> ): Array<T> {
|
|
14
|
+
export function ensureArray<const T>( t: T | Array<T> | null | undefined ): Array<T> {
|
|
15
|
+
if ( isNullOrUndefined( t ) ) return [];
|
|
15
16
|
return t instanceof Array ? t : [ t ];
|
|
16
17
|
}
|
|
17
|
-
export function ensureReadableArray<const T>( t: T | Array<T> | ReadonlyArray<T> ): TReadableArray<T> {
|
|
18
|
+
export function ensureReadableArray<const T>( t: T | Array<T> | ReadonlyArray<T> | null | undefined ): TReadableArray<T> {
|
|
19
|
+
if ( isNullOrUndefined( t ) ) return [];
|
|
18
20
|
return t instanceof Array ? t : [ t ];
|
|
19
21
|
}
|
|
20
22
|
|
|
@@ -138,3 +140,7 @@ export function mapFirstTruthy<T, R>(arr: T[], mapFn: TTransformer<T, R>): R | n
|
|
|
138
140
|
}
|
|
139
141
|
return null;
|
|
140
142
|
}
|
|
143
|
+
|
|
144
|
+
export function makeArrayEmpty( arr: unknown[] ): void {
|
|
145
|
+
arr.splice( 0, arr.length );
|
|
146
|
+
}
|
|
@@ -5,5 +5,7 @@ export default constant;
|
|
|
5
5
|
export const constantNull = constant( null );
|
|
6
6
|
export const constantTrue = constant( true );
|
|
7
7
|
export const constantFalse = constant( false );
|
|
8
|
+
export const alwaysTrue = constantTrue;
|
|
9
|
+
export const alwaysFalse = constantFalse;
|
|
8
10
|
export const constantZero = constant( 0 );
|
|
9
11
|
export const constantOne = constant( 1 );
|
package/src/utils/functions.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { constantTrue } from './functions.js';
|
|
1
2
|
|
|
2
3
|
export * from './functions/constant.js';
|
|
3
4
|
export * from './functions/iff.js';
|
|
@@ -38,4 +39,26 @@ export function filterWithTypePredicate<T, R extends T>( filter: TPredicate<T> )
|
|
|
38
39
|
return ( t: T ): t is R => filter( t );
|
|
39
40
|
}
|
|
40
41
|
|
|
42
|
+
export function not<T>( predicate: TPredicate<T> ): TPredicate<T> {
|
|
43
|
+
return ( t: T ) => !predicate( t );
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function and<T>( a: TPredicate<T>, b: TPredicate<T> ): TPredicate<T>;
|
|
47
|
+
export function and<T>( ...predicates: Array<TPredicate<T>> ): TPredicate<T> {
|
|
48
|
+
if ( predicates.length === 0 ) return constantTrue;
|
|
49
|
+
else if ( predicates.length === 1 ) return predicates[ 0 ];
|
|
50
|
+
return ( t: T ) => predicates.reduce( ( prev, cur ) => prev ? cur( t ) : false, true );
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export function or<T>( a: TPredicate<T>, b: TPredicate<T> ): TPredicate<T>;
|
|
54
|
+
export function or<T>( ...predicates: Array<TPredicate<T>> ): TPredicate<T> {
|
|
55
|
+
if ( predicates.length === 0 ) return constantTrue;
|
|
56
|
+
else if ( predicates.length === 1 ) return predicates[ 0 ];
|
|
57
|
+
return ( t: T ) => predicates.reduce( ( prev, cur ) => prev ? true : cur( t ), false );
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export function xor<T>( a: TPredicate<T>, b: TPredicate<T> ): TPredicate<T> {
|
|
61
|
+
return ( t: T ) => a( t ) !== b( t );
|
|
62
|
+
}
|
|
63
|
+
|
|
41
64
|
|
package/src/utils/random.ts
CHANGED
|
@@ -1,9 +1,15 @@
|
|
|
1
|
+
import { TPositiveNumber } from "./numbers.ts";
|
|
1
2
|
|
|
3
|
+
/** @deprecated[2025-01-14]: Use {@link randomNumberInInterval} instead. */
|
|
4
|
+
export const randomInterval = ( min: number, max: number ): number => randomNumberInInterval( min, max );
|
|
2
5
|
|
|
3
|
-
|
|
6
|
+
/** @deprecated[2025-01-14]: Use {@link randomNumberInInterval} / 100 instead. */
|
|
7
|
+
export const randomPercentage = ( min: number, max: number ): number => randomNumberInInterval( min, max ) / 100;
|
|
8
|
+
|
|
9
|
+
export function randomNumberInInterval( min: number, max: number ): number {
|
|
4
10
|
return Math.floor( Math.random() * ( max - min + 1 ) + min );
|
|
5
11
|
}
|
|
6
12
|
|
|
7
|
-
export
|
|
8
|
-
return
|
|
13
|
+
export const randomId = ( length: TPositiveNumber ): string => {
|
|
14
|
+
return Math.random().toString(36).substring(2, length + 2);
|
|
9
15
|
}
|
package/src/utils/records.ts
CHANGED
|
@@ -26,3 +26,15 @@ export type TConditionalParameterOptions<T, R> = TConditionalParameter<TOptionsW
|
|
|
26
26
|
* Given a type T, replaces all fields of type From to fields of type To.
|
|
27
27
|
*/
|
|
28
28
|
export type TReplaceType<T, From, To> = { [ K in keyof T ]: T[ K ] extends From ? To : T[ K ]; };
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Given a type T, make field K required instead of optional.
|
|
32
|
+
*/
|
|
33
|
+
export type TWithRequiredProperty<T, K extends keyof T> = T & {
|
|
34
|
+
[ Property in K ]-?: T[ Property ];
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Given a type T, make all fields required instead of optional.
|
|
39
|
+
*/
|
|
40
|
+
export type TWithRequiredProperties<T> = Required<T>;
|
package/dist/Logger.d.ts
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
declare const LEVELS: readonly ["log", "debug", "info", "warn", "error"];
|
|
2
|
-
type TLogLevel = keyof Console & typeof LEVELS[number];
|
|
3
|
-
export type TLoggerOpts = {
|
|
4
|
-
console: Console;
|
|
5
|
-
enabled: boolean;
|
|
6
|
-
minLevel: Uppercase<TLogLevel>;
|
|
7
|
-
};
|
|
8
|
-
export declare class Logger {
|
|
9
|
-
readonly name: string;
|
|
10
|
-
private enabled;
|
|
11
|
-
private console;
|
|
12
|
-
private minLevel;
|
|
13
|
-
constructor(name: string, args?: Partial<TLoggerOpts>);
|
|
14
|
-
get log(): (...args: any[]) => void;
|
|
15
|
-
get debug(): (...args: any[]) => void;
|
|
16
|
-
get info(): (...args: any[]) => void;
|
|
17
|
-
get warn(): (...args: any[]) => void;
|
|
18
|
-
get error(): (...args: any[]) => void;
|
|
19
|
-
private logWrap;
|
|
20
|
-
}
|
|
21
|
-
export default Logger;
|
package/dist/Optional.d.ts
DELETED
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
import { TConsumer, TFunction, TPredicate, TProducer, TVoidFunction } from "./utils/functions.js";
|
|
2
|
-
export declare class Optional<T> implements TOptional<T> {
|
|
3
|
-
private _present;
|
|
4
|
-
private _value;
|
|
5
|
-
private constructor();
|
|
6
|
-
get rawValue(): T | undefined;
|
|
7
|
-
get(): T | never;
|
|
8
|
-
set(t: T): void;
|
|
9
|
-
clear(): void;
|
|
10
|
-
isEmpty(): this is TEmptyOptional<T>;
|
|
11
|
-
isPresent(): this is TPresentOptional<T>;
|
|
12
|
-
ifEmpty(callback: TVoidFunction): void;
|
|
13
|
-
ifPresent(callback: TConsumer<T>): void;
|
|
14
|
-
apply(callbackIfPresent: TConsumer<T>, callbackIfEmpty: TVoidFunction): void;
|
|
15
|
-
orElse(newValue: T | null | undefined): any;
|
|
16
|
-
orElseGet(produceValue: TProducer<T | null | undefined>): TOptional<T>;
|
|
17
|
-
orElseThrow(produceError: TProducer<Error>): TPresentOptional<T>;
|
|
18
|
-
mapTo<R>(mapper: TFunction<T, R | null | undefined>): any;
|
|
19
|
-
flatMapTo<R>(mapper: TFunction<T, TOptional<R>>): any;
|
|
20
|
-
filter(predicate: TPredicate<T>): this | TEmptyOptional<T>;
|
|
21
|
-
static empty<T>(): TEmptyOptional<T>;
|
|
22
|
-
static of<T>(t: T): TPresentOptional<T>;
|
|
23
|
-
static ofNullable<T>(t: T | null | undefined): TOptional<T>;
|
|
24
|
-
}
|
|
25
|
-
export default Optional;
|
|
26
|
-
export type TOptional<T> = {
|
|
27
|
-
readonly rawValue: T | undefined;
|
|
28
|
-
/**
|
|
29
|
-
* @returns the currently stored value, if any; throws {@link ErrorGetEmptyOptional} otherwise;
|
|
30
|
-
*/
|
|
31
|
-
get(): T | never;
|
|
32
|
-
/**
|
|
33
|
-
* Places a new value inside this optional. Throws {@link ErrorSetEmptyOptional} if t is null or undefined.
|
|
34
|
-
*/
|
|
35
|
-
set(t: T): void;
|
|
36
|
-
/**
|
|
37
|
-
* Clears the current value from this optional, if any.
|
|
38
|
-
*/
|
|
39
|
-
clear(): void;
|
|
40
|
-
isEmpty(): boolean;
|
|
41
|
-
isPresent(): boolean;
|
|
42
|
-
ifEmpty: (callback: TVoidFunction) => void;
|
|
43
|
-
ifPresent: (callback: TConsumer<T>) => void;
|
|
44
|
-
apply(callbackIfPresent: TConsumer<T>, callbackIfEmpty: TVoidFunction): void;
|
|
45
|
-
orElse: ((newValue: T | null | undefined) => TOptional<T>);
|
|
46
|
-
orElseGet: (produceValue: TProducer<T | null | undefined>) => TOptional<T>;
|
|
47
|
-
orElseThrow: (produceError: TProducer<Error>) => TPresentOptional<T>;
|
|
48
|
-
mapTo<R>(mapper: TFunction<T, R>): TOptional<R>;
|
|
49
|
-
flatMapTo<R>(mapper: TFunction<T, TOptional<R>>): TOptional<R>;
|
|
50
|
-
filter(predicate: TPredicate<T>): TOptional<T>;
|
|
51
|
-
};
|
|
52
|
-
export type TEmptyOptional<T> = TOptional<T> & {
|
|
53
|
-
readonly rawValue: undefined;
|
|
54
|
-
isEmpty(): true;
|
|
55
|
-
isPresent(): false;
|
|
56
|
-
};
|
|
57
|
-
export type TPresentOptional<T> = TOptional<T> & {
|
|
58
|
-
readonly rawValue: T;
|
|
59
|
-
isEmpty(): false;
|
|
60
|
-
isPresent(): true;
|
|
61
|
-
};
|
|
62
|
-
export declare class ErrorGetEmptyOptional extends Error {
|
|
63
|
-
constructor();
|
|
64
|
-
}
|
|
65
|
-
export declare class ErrorSetEmptyOptional extends Error {
|
|
66
|
-
constructor();
|
|
67
|
-
}
|
|
68
|
-
export declare class ErrorCannotInstantiatePresentOptionalWithEmptyValue extends Error {
|
|
69
|
-
constructor();
|
|
70
|
-
}
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import Deferred from "./Deferred.js";
|
|
2
|
-
interface ICancelablePromise<T> extends Promise<T> {
|
|
3
|
-
cancel(): void;
|
|
4
|
-
}
|
|
5
|
-
/** @deprecated use Deferred instead. */
|
|
6
|
-
export default class CancelableDeferred<T = void> extends Deferred<T> implements ICancelablePromise<T> {
|
|
7
|
-
private _canceled;
|
|
8
|
-
get canceled(): boolean;
|
|
9
|
-
constructor();
|
|
10
|
-
cancel(): void;
|
|
11
|
-
protected createInternals(): {
|
|
12
|
-
promise: Promise<T>;
|
|
13
|
-
resolve: (value: T) => void;
|
|
14
|
-
reject: (reason: Error) => void;
|
|
15
|
-
};
|
|
16
|
-
}
|
|
17
|
-
export {};
|