@bgord/tools 1.2.19 → 1.2.21
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/dist/duration.service.d.ts +3 -0
- package/dist/duration.service.js +7 -0
- package/package.json +2 -3
- package/src/age-years.vo.ts +0 -16
- package/src/age.vo.ts +0 -60
- package/src/api-key.vo.ts +0 -16
- package/src/basename.vo.ts +0 -33
- package/src/clock-format.service.ts +0 -15
- package/src/clock.vo.ts +0 -55
- package/src/date-calculator.service.ts +0 -18
- package/src/date-formatter.service.ts +0 -21
- package/src/date-range.vo.ts +0 -36
- package/src/day-iso-id.vo.ts +0 -21
- package/src/day.vo.ts +0 -59
- package/src/directory-path-absolute.vo.ts +0 -36
- package/src/directory-path-relative.vo.ts +0 -34
- package/src/distance-value.vo.ts +0 -13
- package/src/distance.vo.ts +0 -76
- package/src/division-factor.vo.ts +0 -12
- package/src/dll.service.ts +0 -177
- package/src/duration-ms.vo.ts +0 -9
- package/src/duration.service.ts +0 -89
- package/src/email-mask.service.ts +0 -17
- package/src/email.vo.ts +0 -9
- package/src/etags.vo.ts +0 -47
- package/src/extension.vo.ts +0 -27
- package/src/feature-flag-value.vo.ts +0 -12
- package/src/feature-flag.vo.ts +0 -11
- package/src/file-path-absolute-schema.vo.ts +0 -34
- package/src/file-path-relative-schema.vo.ts +0 -34
- package/src/file-path.vo.ts +0 -100
- package/src/filename-affix.vo.ts +0 -27
- package/src/filename-from-string.vo.ts +0 -27
- package/src/filename.vo.ts +0 -92
- package/src/height-milimiters.vo.ts +0 -16
- package/src/height.vo.ts +0 -79
- package/src/hour-format.service.ts +0 -22
- package/src/hour-schema.vo.ts +0 -14
- package/src/hour.vo.ts +0 -64
- package/src/iban-mask.service.ts +0 -18
- package/src/iban-schema.vo.ts +0 -17
- package/src/iban.vo.ts +0 -28
- package/src/image.vo.ts +0 -28
- package/src/index.ts +0 -105
- package/src/integer-non-negative.vo.ts +0 -16
- package/src/integer-positive.vo.ts +0 -13
- package/src/integer.vo.ts +0 -9
- package/src/language.vo.ts +0 -13
- package/src/linear-regression.service.ts +0 -73
- package/src/mean.service.ts +0 -18
- package/src/mime-types.vo.ts +0 -7
- package/src/mime-value.vo.ts +0 -14
- package/src/mime.vo.ts +0 -50
- package/src/min-max-scaler.service.ts +0 -96
- package/src/minute-schema.vo.ts +0 -14
- package/src/minute.vo.ts +0 -59
- package/src/money-amount.vo.ts +0 -13
- package/src/money.vo.ts +0 -83
- package/src/month-iso-id.vo.ts +0 -24
- package/src/month.vo.ts +0 -53
- package/src/multiplication-factor.vo.ts +0 -15
- package/src/noop.service.ts +0 -3
- package/src/notification-template.vo.ts +0 -10
- package/src/object-key.vo.ts +0 -36
- package/src/outlier-detector.service.ts +0 -21
- package/src/package-version-schema.vo.ts +0 -23
- package/src/package-version.vo.ts +0 -66
- package/src/pagination-page.vo.ts +0 -11
- package/src/pagination-skip.vo.ts +0 -10
- package/src/pagination-take.vo.ts +0 -10
- package/src/pagination.service.ts +0 -73
- package/src/percentage.service.ts +0 -15
- package/src/population-standard-deviation.service.ts +0 -23
- package/src/quarter-iso-id.vo.ts +0 -15
- package/src/quarter.vo.ts +0 -41
- package/src/random.service.ts +0 -19
- package/src/rate-limiter.service.ts +0 -30
- package/src/relative-date.vo.ts +0 -21
- package/src/reordering-item-position-value.vo.ts +0 -10
- package/src/reordering.service.ts +0 -163
- package/src/revision-value.vo.ts +0 -10
- package/src/revision.vo.ts +0 -44
- package/src/rounding-decimal.strategy.ts +0 -25
- package/src/rounding-down.strategy.ts +0 -7
- package/src/rounding-to-nearest.strategy.ts +0 -7
- package/src/rounding-up.strategy.ts +0 -7
- package/src/rounding.strategy.ts +0 -3
- package/src/size-bytes.vo.ts +0 -13
- package/src/size.vo.ts +0 -113
- package/src/slug.service.ts +0 -18
- package/src/sum.service.ts +0 -19
- package/src/thousands-separator.service.ts +0 -10
- package/src/time-zone-offset-value.vo.ts +0 -21
- package/src/timestamp-value.vo.ts +0 -13
- package/src/timestamp.vo.ts +0 -66
- package/src/timezone.vo.ts +0 -26
- package/src/ts-utils.ts +0 -3
- package/src/url-with-slash.vo.ts +0 -12
- package/src/url-without-slash.vo.ts +0 -12
- package/src/visually-unambiguous-characters-generator.service.ts +0 -39
- package/src/week-iso-id.vo.ts +0 -28
- package/src/week.vo.ts +0 -57
- package/src/weekday.vo.ts +0 -119
- package/src/weight-grams.vo.ts +0 -13
- package/src/weight.vo.ts +0 -91
- package/src/year-iso-id.vo.ts +0 -15
- package/src/year.vo.ts +0 -63
- package/src/z-score.service.ts +0 -25
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
import { Page, type PageType } from "./pagination-page.vo";
|
|
2
|
-
import { Skip, type SkipType } from "./pagination-skip.vo";
|
|
3
|
-
import { Take, type TakeType } from "./pagination-take.vo";
|
|
4
|
-
import { RoundingUpStrategy } from "./rounding-up.strategy";
|
|
5
|
-
|
|
6
|
-
export type PaginationType = { values: { take: TakeType; skip: SkipType }; page: PageType };
|
|
7
|
-
export type PaginationValuesType = Record<string, unknown>;
|
|
8
|
-
export type TotalType = number;
|
|
9
|
-
export type ExhaustedType = boolean;
|
|
10
|
-
export type PaginationExhaustedConfig = { total: TotalType; pagination: PaginationType };
|
|
11
|
-
export type PaginationPrepareConfigType<T> = { total: TotalType; pagination: PaginationType; result: T[] };
|
|
12
|
-
|
|
13
|
-
export class Pagination {
|
|
14
|
-
static parse(values: PaginationValuesType, _take: TakeType): PaginationType {
|
|
15
|
-
const page = Page.parse(values.page);
|
|
16
|
-
const take = Take.parse(_take);
|
|
17
|
-
|
|
18
|
-
const skip = Skip.parse((page - 1) * take);
|
|
19
|
-
|
|
20
|
-
return { values: { take, skip }, page };
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
static prepare<T>(config: PaginationPrepareConfigType<T>): Paged<T> {
|
|
24
|
-
const exhausted = Pagination.isExhausted(config);
|
|
25
|
-
|
|
26
|
-
const currentPage = config.pagination.page;
|
|
27
|
-
const lastPage = Pagination.getLastPage(config);
|
|
28
|
-
|
|
29
|
-
const previousPage = currentPage > 1 ? Page.parse(currentPage - 1) : undefined;
|
|
30
|
-
const nextPage = currentPage < lastPage ? Page.parse(currentPage + 1) : undefined;
|
|
31
|
-
|
|
32
|
-
return {
|
|
33
|
-
result: config.result,
|
|
34
|
-
meta: { exhausted, currentPage, previousPage, nextPage, lastPage, total: config.total },
|
|
35
|
-
};
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
static isExhausted(config: PaginationExhaustedConfig): ExhaustedType {
|
|
39
|
-
return Pagination.getLastPage(config) <= config.pagination.page;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
private static getLastPage(config: PaginationExhaustedConfig): PageType {
|
|
43
|
-
return Page.parse(new RoundingUpStrategy().round(config.total / config.pagination.values.take));
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
static empty = {
|
|
47
|
-
result: [],
|
|
48
|
-
meta: {
|
|
49
|
-
exhausted: true,
|
|
50
|
-
currentPage: 1,
|
|
51
|
-
previousPage: undefined,
|
|
52
|
-
nextPage: undefined,
|
|
53
|
-
lastPage: 1,
|
|
54
|
-
total: 0,
|
|
55
|
-
},
|
|
56
|
-
};
|
|
57
|
-
|
|
58
|
-
static getFirstPage(input: { take: TakeType }): PaginationType {
|
|
59
|
-
return { values: { take: Take.parse(input.take), skip: Skip.parse(0) }, page: Page.parse(1) };
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
export type Paged<T> = {
|
|
64
|
-
result: T[];
|
|
65
|
-
meta: {
|
|
66
|
-
exhausted: ExhaustedType;
|
|
67
|
-
currentPage: PageType;
|
|
68
|
-
previousPage: PageType | undefined;
|
|
69
|
-
nextPage: PageType | undefined;
|
|
70
|
-
lastPage: PageType;
|
|
71
|
-
total: TotalType;
|
|
72
|
-
};
|
|
73
|
-
};
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import type { RoundingStrategy } from "./rounding.strategy";
|
|
2
|
-
import { RoundingToNearestStrategy } from "./rounding-to-nearest.strategy";
|
|
3
|
-
|
|
4
|
-
export const PercentageError = { InvalidDenominator: "percentage.invalid.denominator" };
|
|
5
|
-
|
|
6
|
-
export class Percentage {
|
|
7
|
-
static of(
|
|
8
|
-
numerator: number,
|
|
9
|
-
denominator: number,
|
|
10
|
-
rounding: RoundingStrategy = new RoundingToNearestStrategy(),
|
|
11
|
-
): number {
|
|
12
|
-
if (denominator === 0) throw new Error(PercentageError.InvalidDenominator);
|
|
13
|
-
return rounding.round((numerator / denominator) * 100);
|
|
14
|
-
}
|
|
15
|
-
}
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import { Mean } from "./mean.service";
|
|
2
|
-
import type { RoundingStrategy } from "./rounding.strategy";
|
|
3
|
-
import { RoundingDecimalStrategy } from "./rounding-decimal.strategy";
|
|
4
|
-
import { Sum } from "./sum.service";
|
|
5
|
-
|
|
6
|
-
export const PopulationStandardDeviationError = {
|
|
7
|
-
NotEnoughValues: "population.standard.deviation.not.enough.values",
|
|
8
|
-
};
|
|
9
|
-
|
|
10
|
-
export class PopulationStandardDeviation {
|
|
11
|
-
static calculate(values: number[], rounding: RoundingStrategy = new RoundingDecimalStrategy(2)): number {
|
|
12
|
-
if (values.length < 2) throw new Error(PopulationStandardDeviationError.NotEnoughValues);
|
|
13
|
-
|
|
14
|
-
const mean = Mean.calculate(values);
|
|
15
|
-
const count = values.length;
|
|
16
|
-
|
|
17
|
-
const squaredDifferences = values.map((value) => (value - mean) ** 2);
|
|
18
|
-
|
|
19
|
-
const variance = Sum.of(squaredDifferences) / count;
|
|
20
|
-
|
|
21
|
-
return rounding.round(Math.sqrt(variance));
|
|
22
|
-
}
|
|
23
|
-
}
|
package/src/quarter-iso-id.vo.ts
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import { z } from "zod/v4";
|
|
2
|
-
|
|
3
|
-
export const QuarterIsoIdError = {
|
|
4
|
-
Type: "quarter.iso.id.type",
|
|
5
|
-
BadChars: "quarter.iso.id.bad.chars",
|
|
6
|
-
};
|
|
7
|
-
|
|
8
|
-
// Stryker disable all
|
|
9
|
-
export const QuarterIsoId = z
|
|
10
|
-
// Stryker restore all
|
|
11
|
-
.string(QuarterIsoIdError.Type)
|
|
12
|
-
.regex(/^\d{4}-Q[1-4]$/, QuarterIsoIdError.BadChars)
|
|
13
|
-
.brand("QuarterIsoId");
|
|
14
|
-
|
|
15
|
-
export type QuarterIsoIdType = z.infer<typeof QuarterIsoId>;
|
package/src/quarter.vo.ts
DELETED
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
import { endOfQuarter, getQuarter, getYear, setQuarter, startOfQuarter } from "date-fns";
|
|
2
|
-
import { DateRange } from "./date-range.vo";
|
|
3
|
-
import { QuarterIsoId, type QuarterIsoIdType } from "./quarter-iso-id.vo";
|
|
4
|
-
import { Timestamp } from "./timestamp.vo";
|
|
5
|
-
import type { TimestampValueType } from "./timestamp-value.vo";
|
|
6
|
-
|
|
7
|
-
export class Quarter extends DateRange {
|
|
8
|
-
static fromTimestamp(timestamp: Timestamp): Quarter {
|
|
9
|
-
const start = Timestamp.fromNumber(startOfQuarter(timestamp.ms).getTime());
|
|
10
|
-
const end = Timestamp.fromNumber(endOfQuarter(timestamp.ms).getTime());
|
|
11
|
-
|
|
12
|
-
return new Quarter(start, end);
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
static fromTimestampValue(timestamp: TimestampValueType): Quarter {
|
|
16
|
-
return Quarter.fromTimestamp(Timestamp.fromValue(timestamp));
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
static fromNow(now: Timestamp): Quarter {
|
|
20
|
-
return Quarter.fromTimestamp(now);
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
static fromIsoId(isoId: QuarterIsoIdType): Quarter {
|
|
24
|
-
const [year, quarter] = QuarterIsoId.parse(isoId).split("-Q").map(Number);
|
|
25
|
-
|
|
26
|
-
const reference = setQuarter(Date.UTC(year), quarter).getTime();
|
|
27
|
-
|
|
28
|
-
return Quarter.fromTimestamp(Timestamp.fromNumber(reference));
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
toIsoId(): QuarterIsoIdType {
|
|
32
|
-
const year = getYear(this.getStart().ms);
|
|
33
|
-
const quarter = getQuarter(this.getStart().ms);
|
|
34
|
-
|
|
35
|
-
return QuarterIsoId.parse(`${year}-Q${quarter}`);
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
toString(): string {
|
|
39
|
-
return this.toIsoId();
|
|
40
|
-
}
|
|
41
|
-
}
|
package/src/random.service.ts
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import { RoundingDownStrategy } from "./rounding-down.strategy";
|
|
2
|
-
|
|
3
|
-
type RandomGenerateConfigType = { min: number; max: number };
|
|
4
|
-
|
|
5
|
-
export const RandomError = { MinMax: "random.min.max" };
|
|
6
|
-
|
|
7
|
-
export class Random {
|
|
8
|
-
private static readonly DEFAULT_MIN = 0;
|
|
9
|
-
private static readonly DEFAULT_MAX = 1;
|
|
10
|
-
|
|
11
|
-
static generate(config?: RandomGenerateConfigType): number {
|
|
12
|
-
const min = config ? config.min : Random.DEFAULT_MIN;
|
|
13
|
-
const max = config ? config.max : Random.DEFAULT_MAX;
|
|
14
|
-
|
|
15
|
-
if (min >= max) throw new Error(RandomError.MinMax);
|
|
16
|
-
|
|
17
|
-
return new RoundingDownStrategy().round(Math.random() * (max - min + 1)) + min;
|
|
18
|
-
}
|
|
19
|
-
}
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
import type { Duration } from "./duration.service";
|
|
2
|
-
import type { Timestamp } from "./timestamp.vo";
|
|
3
|
-
|
|
4
|
-
type RateLimiterResultSuccessType = { allowed: true };
|
|
5
|
-
type RateLimiterResultErrorType = { allowed: false; remaining: Duration };
|
|
6
|
-
type RateLimiterResultType = RateLimiterResultSuccessType | RateLimiterResultErrorType;
|
|
7
|
-
|
|
8
|
-
export class RateLimiter {
|
|
9
|
-
private lastInvocation: Timestamp | null = null;
|
|
10
|
-
|
|
11
|
-
constructor(private readonly duration: Duration) {}
|
|
12
|
-
|
|
13
|
-
verify(now: Timestamp): RateLimiterResultType {
|
|
14
|
-
if (this.lastInvocation == null) {
|
|
15
|
-
this.lastInvocation = now;
|
|
16
|
-
|
|
17
|
-
return { allowed: true };
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
const nextAllowedTimestamp = this.lastInvocation.add(this.duration);
|
|
21
|
-
|
|
22
|
-
if (nextAllowedTimestamp.isBeforeOrEqual(now)) {
|
|
23
|
-
this.lastInvocation = now;
|
|
24
|
-
|
|
25
|
-
return { allowed: true };
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
return { allowed: false, remaining: nextAllowedTimestamp.difference(now) };
|
|
29
|
-
}
|
|
30
|
-
}
|
package/src/relative-date.vo.ts
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import { DateFormatters } from "./date-formatter.service";
|
|
2
|
-
import type { Timestamp } from "./timestamp.vo";
|
|
3
|
-
import type { TimestampValueType } from "./timestamp-value.vo";
|
|
4
|
-
import type { Falsy } from "./ts-utils";
|
|
5
|
-
|
|
6
|
-
type RelativeDateType = { raw: TimestampValueType; relative: string };
|
|
7
|
-
|
|
8
|
-
export class RelativeDate {
|
|
9
|
-
static truthy(timestamp: Timestamp): RelativeDateType {
|
|
10
|
-
return RelativeDate._format(timestamp);
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
static falsy(timestamp: Falsy<Timestamp>): RelativeDateType | null {
|
|
14
|
-
if (!timestamp) return null;
|
|
15
|
-
return RelativeDate._format(timestamp);
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
private static _format(timestamp: Timestamp): RelativeDateType {
|
|
19
|
-
return { raw: timestamp.ms, relative: DateFormatters.relative(timestamp.ms) };
|
|
20
|
-
}
|
|
21
|
-
}
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import { z } from "zod/v4";
|
|
2
|
-
|
|
3
|
-
export const ReorderingItemPositionValueError = { Invalid: "reordering.position.type" };
|
|
4
|
-
|
|
5
|
-
export const ReorderingItemPositionValue = z
|
|
6
|
-
.number(ReorderingItemPositionValueError.Invalid)
|
|
7
|
-
.int(ReorderingItemPositionValueError.Invalid)
|
|
8
|
-
.min(0, ReorderingItemPositionValueError.Invalid);
|
|
9
|
-
|
|
10
|
-
export type ReorderingItemPositionValueType = z.infer<typeof ReorderingItemPositionValue>;
|
|
@@ -1,163 +0,0 @@
|
|
|
1
|
-
// Stryker disable all
|
|
2
|
-
import { z } from "zod/v4";
|
|
3
|
-
import { DoublyLinkedList, Node } from "./dll.service";
|
|
4
|
-
import {
|
|
5
|
-
ReorderingItemPositionValue,
|
|
6
|
-
type ReorderingItemPositionValueType,
|
|
7
|
-
} from "./reordering-item-position-value.vo";
|
|
8
|
-
|
|
9
|
-
export const ReorderingError = {
|
|
10
|
-
CannotFindItem: "reordering.cannot.find.item",
|
|
11
|
-
CannotFindCurrent: "reordering.cannot.find.current",
|
|
12
|
-
CannotFindTarget: "reordering.cannot.find.target",
|
|
13
|
-
};
|
|
14
|
-
|
|
15
|
-
const ReorderingIdError = { Type: "reordering.id.type" };
|
|
16
|
-
|
|
17
|
-
export const ReorderingId = z.string(ReorderingIdError.Type).min(1, ReorderingIdError.Type);
|
|
18
|
-
export type ReorderingItemIdType = z.infer<typeof ReorderingId>;
|
|
19
|
-
|
|
20
|
-
export const Reordering = z.object({
|
|
21
|
-
correlationId: ReorderingId,
|
|
22
|
-
id: ReorderingId,
|
|
23
|
-
position: ReorderingItemPositionValue,
|
|
24
|
-
});
|
|
25
|
-
export type ReorderingType = z.infer<typeof Reordering>;
|
|
26
|
-
|
|
27
|
-
export type WithReorderingPositionValue<T> = T & { position: ReorderingItemPositionValueType };
|
|
28
|
-
|
|
29
|
-
export class ReorderingPosition {
|
|
30
|
-
readonly value: ReorderingItemPositionValueType;
|
|
31
|
-
|
|
32
|
-
constructor(value: ReorderingItemPositionValueType) {
|
|
33
|
-
this.value = ReorderingItemPositionValue.parse(value);
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
eq(another: ReorderingPosition): boolean {
|
|
37
|
-
return this.value === another.value;
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
class ReorderingItem {
|
|
42
|
-
constructor(
|
|
43
|
-
readonly id: ReorderingItemIdType,
|
|
44
|
-
readonly position: ReorderingPosition,
|
|
45
|
-
) {}
|
|
46
|
-
|
|
47
|
-
eq(anotherItemId: ReorderingItem["id"]): boolean {
|
|
48
|
-
return this.id === anotherItemId;
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
enum ReorderingTransferDirection {
|
|
53
|
-
upwards = "upwards",
|
|
54
|
-
downwards = "downwards",
|
|
55
|
-
noop = "noop",
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
export class ReorderingTransfer {
|
|
59
|
-
readonly id: ReorderingItem["id"];
|
|
60
|
-
readonly to: ReorderingPosition;
|
|
61
|
-
|
|
62
|
-
constructor(config: { id: ReorderingItem["id"]; to: ReorderingItemPositionValueType }) {
|
|
63
|
-
const id = config.id;
|
|
64
|
-
const to = new ReorderingPosition(config.to);
|
|
65
|
-
|
|
66
|
-
this.id = id;
|
|
67
|
-
this.to = to;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
getDirection(currentPosition: ReorderingPosition): ReorderingTransferDirection {
|
|
71
|
-
if (this.to.value === currentPosition.value) return ReorderingTransferDirection.noop;
|
|
72
|
-
if (this.to.value > currentPosition.value) return ReorderingTransferDirection.downwards;
|
|
73
|
-
return ReorderingTransferDirection.upwards;
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
export class ReorderingCalculator {
|
|
78
|
-
private dll: DoublyLinkedList<ReorderingItem>;
|
|
79
|
-
|
|
80
|
-
constructor() {
|
|
81
|
-
this.dll = DoublyLinkedList.fromArray<ReorderingItem>([]);
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
static fromArray(ids: ReorderingItem["id"][]) {
|
|
85
|
-
const reordering = new ReorderingCalculator();
|
|
86
|
-
for (const id of ids) {
|
|
87
|
-
reordering.add(id);
|
|
88
|
-
}
|
|
89
|
-
return reordering;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
add(id: ReorderingItem["id"]): ReorderingItem {
|
|
93
|
-
const size = this.dll.getSize();
|
|
94
|
-
const position = new ReorderingPosition(ReorderingItemPositionValue.parse(size));
|
|
95
|
-
const item = new ReorderingItem(id, position);
|
|
96
|
-
const node = new Node(item);
|
|
97
|
-
this.dll.append(node);
|
|
98
|
-
return item;
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
delete(id: ReorderingItem["id"]) {
|
|
102
|
-
const node = this.dll.find((x) => x.data.eq(id));
|
|
103
|
-
if (!node) throw new Error(ReorderingError.CannotFindItem);
|
|
104
|
-
|
|
105
|
-
this.dll.remove(node);
|
|
106
|
-
this.recalculate();
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
transfer(transfer: ReorderingTransfer): ReturnType<ReorderingCalculator["read"]> {
|
|
110
|
-
const current = this.dll.find((node) => node.data.eq(transfer.id));
|
|
111
|
-
if (!current) throw new Error(ReorderingError.CannotFindCurrent);
|
|
112
|
-
|
|
113
|
-
const target = this.dll.find((node) => node.data.position.eq(transfer.to));
|
|
114
|
-
if (!target) throw new Error(ReorderingError.CannotFindTarget);
|
|
115
|
-
|
|
116
|
-
const direction = transfer.getDirection(current.data.position);
|
|
117
|
-
if (direction === ReorderingTransferDirection.noop) return this.read();
|
|
118
|
-
|
|
119
|
-
// remove first to avoid temporary invalid duplicates of positions
|
|
120
|
-
this.dll.remove(current);
|
|
121
|
-
|
|
122
|
-
if (direction === ReorderingTransferDirection.upwards) {
|
|
123
|
-
this.dll.insertBefore(current, target);
|
|
124
|
-
} else {
|
|
125
|
-
this.dll.insertAfter(current, target);
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
this.recalculate();
|
|
129
|
-
return this.read();
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
read() {
|
|
133
|
-
const ids = Array.from(this.dll).map((node) => node.data.id);
|
|
134
|
-
const items = Array.from(this.dll).map((node) => node.data);
|
|
135
|
-
return { ids, items };
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
private recalculate() {
|
|
139
|
-
let index = 0;
|
|
140
|
-
for (const node of this.dll) {
|
|
141
|
-
const id = node.data.id;
|
|
142
|
-
const position = new ReorderingPosition(ReorderingItemPositionValue.parse(index));
|
|
143
|
-
node.data = new ReorderingItem(id, position);
|
|
144
|
-
index += 1;
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
export class ReorderingIntegrator {
|
|
150
|
-
static appendPosition(reordering: ReorderingType[]) {
|
|
151
|
-
return function <T extends { id: ReorderingItemIdType }>(item: T): WithReorderingPositionValue<T> {
|
|
152
|
-
const found = reordering.find((x) => x.id === item.id);
|
|
153
|
-
const positionValue = ReorderingItemPositionValue.parse(found?.position ?? 0);
|
|
154
|
-
return { ...item, position: positionValue };
|
|
155
|
-
};
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
static sortByPosition() {
|
|
159
|
-
return (a: WithReorderingPositionValue<unknown>, b: WithReorderingPositionValue<unknown>) =>
|
|
160
|
-
a.position - b.position;
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
// Stryker restore all
|
package/src/revision-value.vo.ts
DELETED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import { z } from "zod/v4";
|
|
2
|
-
|
|
3
|
-
export const RevisionValueError = { Type: "revision.value.type", Invalid: "revision.value.invalid" };
|
|
4
|
-
|
|
5
|
-
export const RevisionValue = z
|
|
6
|
-
.number(RevisionValueError.Type)
|
|
7
|
-
.int(RevisionValueError.Type)
|
|
8
|
-
.min(0, RevisionValueError.Invalid);
|
|
9
|
-
|
|
10
|
-
export type RevisionValueType = z.infer<typeof RevisionValue>;
|
package/src/revision.vo.ts
DELETED
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
import type { ETag, WeakETag } from "./etags.vo";
|
|
2
|
-
import { RevisionValue, type RevisionValueType } from "./revision-value.vo";
|
|
3
|
-
|
|
4
|
-
export const RevisionError = { Missing: "revision.missing", Mismatch: "revision.mismatch" };
|
|
5
|
-
|
|
6
|
-
export class Revision {
|
|
7
|
-
static readonly INITIAL: RevisionValueType = RevisionValue.parse(0);
|
|
8
|
-
|
|
9
|
-
readonly value: RevisionValueType;
|
|
10
|
-
|
|
11
|
-
constructor(value: unknown) {
|
|
12
|
-
this.value = RevisionValue.parse(value);
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
equals(another: RevisionValueType): boolean {
|
|
16
|
-
return this.value === another;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
validate(another: RevisionValueType): void {
|
|
20
|
-
if (!this.equals(another)) throw new Error(RevisionError.Mismatch);
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
next(): Revision {
|
|
24
|
-
return new Revision(this.value + 1);
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
static fromETag(etag: ETag | null): Revision {
|
|
28
|
-
if (!etag) throw new Error(RevisionError.Missing);
|
|
29
|
-
return new Revision(etag.revision);
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
static fromWeakETag(weakEtag: WeakETag | null): Revision {
|
|
33
|
-
if (!weakEtag) throw new Error(RevisionError.Missing);
|
|
34
|
-
return new Revision(weakEtag.revision);
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
toString(): string {
|
|
38
|
-
return this.value.toString();
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
toJSON(): number {
|
|
42
|
-
return this.value;
|
|
43
|
-
}
|
|
44
|
-
}
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import { z } from "zod/v4";
|
|
2
|
-
import type { RoundingStrategy } from "./rounding.strategy";
|
|
3
|
-
|
|
4
|
-
export const RoundingDecimalError = { Type: "rounding.decimal.type", Invalid: "rounding.decimal.invalid" };
|
|
5
|
-
|
|
6
|
-
// Stryker disable all
|
|
7
|
-
export const RoundingDecimal = z
|
|
8
|
-
// Stryker restore all
|
|
9
|
-
.number(RoundingDecimalError.Type)
|
|
10
|
-
.int(RoundingDecimalError.Invalid)
|
|
11
|
-
.min(1, RoundingDecimalError.Invalid)
|
|
12
|
-
.max(100, RoundingDecimalError.Invalid)
|
|
13
|
-
.brand("RoundingDecimal");
|
|
14
|
-
|
|
15
|
-
export class RoundingDecimalStrategy implements RoundingStrategy {
|
|
16
|
-
private readonly decimals: number;
|
|
17
|
-
|
|
18
|
-
constructor(candidate: number) {
|
|
19
|
-
this.decimals = RoundingDecimal.parse(candidate);
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
round(value: number): number {
|
|
23
|
-
return Number.parseFloat(value.toFixed(this.decimals));
|
|
24
|
-
}
|
|
25
|
-
}
|
package/src/rounding.strategy.ts
DELETED
package/src/size-bytes.vo.ts
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import { z } from "zod/v4";
|
|
2
|
-
|
|
3
|
-
export const SizeBytesError = { Invalid: "size.bytes.invalid" };
|
|
4
|
-
|
|
5
|
-
// Stryker disable all
|
|
6
|
-
export const SizeBytes = z
|
|
7
|
-
// Stryker restore all
|
|
8
|
-
.number(SizeBytesError.Invalid)
|
|
9
|
-
.int(SizeBytesError.Invalid)
|
|
10
|
-
.gte(0, SizeBytesError.Invalid)
|
|
11
|
-
.brand("SizeBytes");
|
|
12
|
-
|
|
13
|
-
export type SizeBytesType = z.infer<typeof SizeBytes>;
|
package/src/size.vo.ts
DELETED
|
@@ -1,113 +0,0 @@
|
|
|
1
|
-
import { RoundingDecimalStrategy } from "./rounding-decimal.strategy";
|
|
2
|
-
import { RoundingUpStrategy } from "./rounding-up.strategy";
|
|
3
|
-
import { SizeBytes, type SizeBytesType } from "./size-bytes.vo";
|
|
4
|
-
|
|
5
|
-
enum SizeUnitEnum {
|
|
6
|
-
b = "b",
|
|
7
|
-
kB = "kB",
|
|
8
|
-
MB = "MB",
|
|
9
|
-
GB = "GB",
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
type SizeConfigType = { unit: SizeUnitEnum; value: number };
|
|
13
|
-
|
|
14
|
-
export class Size {
|
|
15
|
-
private readonly unit: SizeUnitEnum;
|
|
16
|
-
private readonly bytes: SizeBytesType;
|
|
17
|
-
|
|
18
|
-
private static readonly KB_MULTIPLIER = 1024;
|
|
19
|
-
private static readonly MB_MULTIPLIER = 1024 * Size.KB_MULTIPLIER;
|
|
20
|
-
private static readonly GB_MULTIPLIER = 1024 * Size.MB_MULTIPLIER;
|
|
21
|
-
|
|
22
|
-
private static readonly CONVERT_ROUND = new RoundingUpStrategy();
|
|
23
|
-
private static readonly FORMAT_ROUND = new RoundingDecimalStrategy(2);
|
|
24
|
-
|
|
25
|
-
private constructor(config: SizeConfigType) {
|
|
26
|
-
this.unit = config.unit;
|
|
27
|
-
this.bytes = this.calculateBytes(config.value, config.unit);
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
static fromBytes(value: SizeConfigType["value"]): Size {
|
|
31
|
-
return new Size({ value, unit: SizeUnitEnum.b });
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
static fromKb(value: SizeConfigType["value"]): Size {
|
|
35
|
-
return new Size({ value, unit: SizeUnitEnum.kB });
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
static fromMB(value: SizeConfigType["value"]): Size {
|
|
39
|
-
return new Size({ value, unit: SizeUnitEnum.MB });
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
static fromGB(value: SizeConfigType["value"]): Size {
|
|
43
|
-
return new Size({ value, unit: SizeUnitEnum.GB });
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
toBytes(): SizeBytesType {
|
|
47
|
-
return this.bytes;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
tokB(): number {
|
|
51
|
-
return Size.CONVERT_ROUND.round(this.bytes / Size.KB_MULTIPLIER);
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
toMB(): number {
|
|
55
|
-
return Size.CONVERT_ROUND.round(this.bytes / Size.MB_MULTIPLIER);
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
toGB(): number {
|
|
59
|
-
return Size.CONVERT_ROUND.round(this.bytes / Size.GB_MULTIPLIER);
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
equals(another: Size): boolean {
|
|
63
|
-
return this.bytes === another.toBytes();
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
isSmallerThan(another: Size): boolean {
|
|
67
|
-
return this.bytes < another.toBytes();
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
isGreaterThan(another: Size): boolean {
|
|
71
|
-
return this.bytes > another.toBytes();
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
format(unit: SizeUnitEnum): string {
|
|
75
|
-
switch (unit) {
|
|
76
|
-
case SizeUnitEnum.kB:
|
|
77
|
-
return `${Size.FORMAT_ROUND.round(this.bytes / Size.KB_MULTIPLIER)} ${SizeUnitEnum.kB}`;
|
|
78
|
-
case SizeUnitEnum.MB:
|
|
79
|
-
return `${Size.FORMAT_ROUND.round(this.bytes / Size.MB_MULTIPLIER)} ${SizeUnitEnum.MB}`;
|
|
80
|
-
case SizeUnitEnum.GB:
|
|
81
|
-
return `${Size.FORMAT_ROUND.round(this.bytes / Size.GB_MULTIPLIER)} ${SizeUnitEnum.GB}`;
|
|
82
|
-
default:
|
|
83
|
-
return `${this.bytes} ${SizeUnitEnum.b}`;
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
static toBytes(config: SizeConfigType): SizeBytesType {
|
|
88
|
-
return new Size(config).toBytes();
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
static unit = SizeUnitEnum;
|
|
92
|
-
|
|
93
|
-
private calculateBytes(value: SizeConfigType["value"], unit: SizeUnitEnum): SizeBytesType {
|
|
94
|
-
switch (unit) {
|
|
95
|
-
case SizeUnitEnum.kB:
|
|
96
|
-
return SizeBytes.parse(value * Size.KB_MULTIPLIER);
|
|
97
|
-
case SizeUnitEnum.MB:
|
|
98
|
-
return SizeBytes.parse(value * Size.MB_MULTIPLIER);
|
|
99
|
-
case SizeUnitEnum.GB:
|
|
100
|
-
return SizeBytes.parse(value * Size.GB_MULTIPLIER);
|
|
101
|
-
default:
|
|
102
|
-
return SizeBytes.parse(value);
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
toString(): string {
|
|
107
|
-
return this.format(this.unit);
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
toJSON(): { bytes: number } {
|
|
111
|
-
return { bytes: this.bytes };
|
|
112
|
-
}
|
|
113
|
-
}
|
package/src/slug.service.ts
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
export class Slug {
|
|
2
|
-
// Any letter or any number
|
|
3
|
-
private static WHITELIST = /[\p{L}\p{N}]/u;
|
|
4
|
-
|
|
5
|
-
static generate(input: string, separator = "-"): string {
|
|
6
|
-
return (
|
|
7
|
-
input
|
|
8
|
-
.split("")
|
|
9
|
-
.map((character) => (Slug.WHITELIST.test(character) ? character : separator))
|
|
10
|
-
.join("")
|
|
11
|
-
// Collapse multiple consecutive separators
|
|
12
|
-
.replace(new RegExp(`\\${separator}+`, "g"), separator)
|
|
13
|
-
// Trim leading and trailing separators
|
|
14
|
-
.replace(new RegExp(`^\\${separator}|\\${separator}$`, "g"), "")
|
|
15
|
-
.toLocaleLowerCase()
|
|
16
|
-
);
|
|
17
|
-
}
|
|
18
|
-
}
|