@jsfsi-core/ts-crossplatform 1.0.5
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/package.json +38 -0
- package/src/configuration/configuration.test.ts +41 -0
- package/src/configuration/configuration.ts +14 -0
- package/src/datetime/datetime.test.ts +42 -0
- package/src/datetime/datetime.ts +24 -0
- package/src/failures/failure.ts +7 -0
- package/src/failures/index.ts +2 -0
- package/src/failures/matchers.ts +13 -0
- package/src/failures/matchers.unit.test.ts +61 -0
- package/src/guid/Guid.test.ts +44 -0
- package/src/guid/Guid.ts +34 -0
- package/src/index.ts +8 -0
- package/src/mocks/mock.ts +3 -0
- package/src/mocks/mock.unit.test.ts +41 -0
- package/src/paging/Page.ts +20 -0
- package/src/paging/PageQuery.ts +8 -0
- package/src/paging/index.ts +2 -0
- package/src/partials/RecursivePartial.ts +3 -0
- package/src/result/result.ts +10 -0
- package/src/result/result.unit.test.ts +100 -0
- package/tsconfig.json +8 -0
- package/vite.config.ts +24 -0
- package/vitest.config.ts +28 -0
package/package.json
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@jsfsi-core/ts-crossplatform",
|
|
3
|
+
"version": "1.0.5",
|
|
4
|
+
"description": "",
|
|
5
|
+
"license": "ISC",
|
|
6
|
+
"author": "",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/src/index.d.ts",
|
|
11
|
+
"import": "./dist/index.mjs",
|
|
12
|
+
"require": "./dist/index.cjs"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"main": "./dist/index.cjs",
|
|
16
|
+
"module": "./dist/index.mjs",
|
|
17
|
+
"types": "./dist/src/index.d.ts",
|
|
18
|
+
"scripts": {
|
|
19
|
+
"build": "npm run lint && tsc && vite build",
|
|
20
|
+
"clean": "rm -rf dist && rm -rf node_modules && rm -rf coverage",
|
|
21
|
+
"format": "prettier --write \"src/**/*.{ts,tsx,js,jsx,json,css,md}\"",
|
|
22
|
+
"lint": "npm run lint:fix && npm run format",
|
|
23
|
+
"lint:fix": "eslint src --ext ts,tsx --report-unused-disable-directives --max-warnings 0 --fix",
|
|
24
|
+
"test": "vitest run",
|
|
25
|
+
"test:coverage": "vitest run --coverage",
|
|
26
|
+
"test:watch": "vitest"
|
|
27
|
+
},
|
|
28
|
+
"dependencies": {
|
|
29
|
+
"uuid": "11.1.0",
|
|
30
|
+
"zod": "3.25.67"
|
|
31
|
+
},
|
|
32
|
+
"devDependencies": {
|
|
33
|
+
"@vitest/coverage-v8": "3.2.4",
|
|
34
|
+
"vite": "7.0.0",
|
|
35
|
+
"vite-plugin-dts": "4.5.4",
|
|
36
|
+
"vitest": "3.2.4"
|
|
37
|
+
}
|
|
38
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
|
|
4
|
+
import { parseConfig } from './configuration';
|
|
5
|
+
|
|
6
|
+
describe('configuration', () => {
|
|
7
|
+
describe('parseConfig', () => {
|
|
8
|
+
it('parses config with valid schema', () => {
|
|
9
|
+
const schema = z.object({
|
|
10
|
+
foo: z.number(),
|
|
11
|
+
bar: z.string(),
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
const config = parseConfig(schema, {
|
|
15
|
+
foo: 3000,
|
|
16
|
+
bar: 'test',
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
expect(config).toEqual({
|
|
20
|
+
foo: 3000,
|
|
21
|
+
bar: 'test',
|
|
22
|
+
});
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it('throws an error with invalid schema', () => {
|
|
26
|
+
const schema = z.object({
|
|
27
|
+
foo: z.number(),
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
expect(() =>
|
|
31
|
+
parseConfig(schema, {
|
|
32
|
+
foo: 'test',
|
|
33
|
+
}),
|
|
34
|
+
).toThrow(
|
|
35
|
+
new Error(
|
|
36
|
+
'Invalid environment variables: [{"code":"invalid_type","expected":"number","received":"string","path":["foo"],"message":"Expected number, received string"}]',
|
|
37
|
+
),
|
|
38
|
+
);
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
});
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { z, ZodSchema } from 'zod';
|
|
2
|
+
|
|
3
|
+
export const parseConfig = <T extends ZodSchema>(
|
|
4
|
+
configSchema: T,
|
|
5
|
+
env: unknown = process.env,
|
|
6
|
+
): z.infer<T> => {
|
|
7
|
+
const result = configSchema.safeParse(env);
|
|
8
|
+
|
|
9
|
+
if (!result.success) {
|
|
10
|
+
throw new Error(`Invalid environment variables: ${JSON.stringify(result.error.issues)}`);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
return result.data;
|
|
14
|
+
};
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
|
|
3
|
+
import { formatDate, formatDateTime, formatTime, sleep } from './datetime';
|
|
4
|
+
|
|
5
|
+
describe('datetime', () => {
|
|
6
|
+
describe('#sleep', () => {
|
|
7
|
+
it('sleeps for a 15ms', async () => {
|
|
8
|
+
const start = Date.now();
|
|
9
|
+
await sleep(15);
|
|
10
|
+
const end = Date.now();
|
|
11
|
+
|
|
12
|
+
expect(end - start).toBeGreaterThanOrEqual(15);
|
|
13
|
+
});
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
describe('#formatDate', () => {
|
|
17
|
+
it('formats a date', () => {
|
|
18
|
+
expect(formatDate(new Date('2025-01-01').getTime())).toEqual('01/01/2025');
|
|
19
|
+
});
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
describe('#formatTime', () => {
|
|
23
|
+
it.each`
|
|
24
|
+
date | expected
|
|
25
|
+
${new Date('2025-01-01T00:00:00').getTime()} | ${'00:00:00'}
|
|
26
|
+
${new Date('2025-01-01T00:00:02').getTime()} | ${'00:00:02'}
|
|
27
|
+
${new Date('2025-01-01T00:01:00').getTime()} | ${'00:01:00'}
|
|
28
|
+
${new Date('2025-01-01T10:16:00').getTime()} | ${'10:16:00'}
|
|
29
|
+
${new Date('2025-01-01T16:00:00').getTime()} | ${'16:00:00'}
|
|
30
|
+
`('formats a time', ({ date, expected }) => {
|
|
31
|
+
expect(formatTime(date)).toEqual(expected);
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
describe('#formatDateTime', () => {
|
|
36
|
+
it('formats a date and time', () => {
|
|
37
|
+
expect(formatDateTime(new Date('2025-01-01T00:00:00').getTime())).toEqual(
|
|
38
|
+
'01/01/2025 00:00:00',
|
|
39
|
+
);
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
});
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export function sleep(ms: number): Promise<void> {
|
|
2
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
export function formatDate(date: number, locales?: Intl.LocalesArgument) {
|
|
6
|
+
return new Date(date).toLocaleDateString(locales, {
|
|
7
|
+
year: 'numeric',
|
|
8
|
+
month: '2-digit',
|
|
9
|
+
day: '2-digit',
|
|
10
|
+
});
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function formatTime(date: number, locales?: Intl.LocalesArgument) {
|
|
14
|
+
return new Date(date).toLocaleTimeString(locales, {
|
|
15
|
+
hour12: false,
|
|
16
|
+
hour: '2-digit',
|
|
17
|
+
minute: '2-digit',
|
|
18
|
+
second: '2-digit',
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function formatDateTime(date: number, locales?: Intl.LocalesArgument) {
|
|
23
|
+
return `${formatDate(date, locales)} ${formatTime(date, locales)}`;
|
|
24
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { Failure } from './failure';
|
|
2
|
+
|
|
3
|
+
export const notFailure =
|
|
4
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
5
|
+
<F extends Failure>(f: new (...args: any[]) => F) =>
|
|
6
|
+
<T>(m: T | F): m is Exclude<T, F> =>
|
|
7
|
+
!(m instanceof f);
|
|
8
|
+
|
|
9
|
+
export const isFailure =
|
|
10
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
11
|
+
<F extends Failure>(f: new (...args: any[]) => F) =>
|
|
12
|
+
<T>(m: T | F): m is F =>
|
|
13
|
+
m instanceof f;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
|
|
3
|
+
import { Failure } from './failure';
|
|
4
|
+
import { isFailure, notFailure } from './matchers';
|
|
5
|
+
|
|
6
|
+
class FailureWithSingleProperty extends Failure {
|
|
7
|
+
constructor(public readonly property: string) {
|
|
8
|
+
super();
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
class FailureWithMultipleProperties extends Failure {
|
|
13
|
+
constructor(
|
|
14
|
+
public readonly property1: string,
|
|
15
|
+
public readonly property2: string,
|
|
16
|
+
) {
|
|
17
|
+
super();
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
describe('matchers', () => {
|
|
22
|
+
it('matches when is not a failure', () => {
|
|
23
|
+
class NotAFailure {}
|
|
24
|
+
|
|
25
|
+
const result = notFailure(Failure)(NotAFailure);
|
|
26
|
+
|
|
27
|
+
expect(result).toBe(true);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it('matches when is not a failure with single property', () => {
|
|
31
|
+
const result = notFailure(FailureWithSingleProperty)(new Failure());
|
|
32
|
+
|
|
33
|
+
expect(result).toBe(true);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it('matches when is not a failure with multiple properties', () => {
|
|
37
|
+
const result = notFailure(FailureWithMultipleProperties)(new Failure());
|
|
38
|
+
|
|
39
|
+
expect(result).toBe(true);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it('matches when is a failure', () => {
|
|
43
|
+
const result = isFailure(Failure)(new Failure());
|
|
44
|
+
|
|
45
|
+
expect(result).toBe(true);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it('matches when is a failure with single property', () => {
|
|
49
|
+
const result = isFailure(FailureWithSingleProperty)(new FailureWithSingleProperty('test'));
|
|
50
|
+
|
|
51
|
+
expect(result).toBe(true);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it('matches when is a failure with multiple properties', () => {
|
|
55
|
+
const result = isFailure(FailureWithMultipleProperties)(
|
|
56
|
+
new FailureWithMultipleProperties('test1', 'test2'),
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
expect(result).toBe(true);
|
|
60
|
+
});
|
|
61
|
+
});
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
|
|
3
|
+
import { Guid } from './Guid';
|
|
4
|
+
|
|
5
|
+
describe('#Guid', () => {
|
|
6
|
+
it('created a valid guid', () => {
|
|
7
|
+
const guid = Guid.new();
|
|
8
|
+
|
|
9
|
+
expect(Guid.isValid(guid.toString())).toBe(true);
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
it.each([undefined, null])('null or empty guid is not valid', (guid?: string | null) => {
|
|
13
|
+
expect(Guid.isValid(guid)).toBe(false);
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
it('creates an empty string', () => {
|
|
17
|
+
const emptyGuid = Guid.empty();
|
|
18
|
+
|
|
19
|
+
expect(emptyGuid.toString()).toEqual('00000000-0000-0000-0000-000000000000');
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it('validates different guids are equal', () => {
|
|
23
|
+
const firstGuid = Guid.new();
|
|
24
|
+
const secondGuid = new Guid(firstGuid.toString());
|
|
25
|
+
|
|
26
|
+
expect(firstGuid.equals(secondGuid)).toBe(true);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it.each`
|
|
30
|
+
value | result
|
|
31
|
+
${undefined} | ${undefined}
|
|
32
|
+
${null} | ${undefined}
|
|
33
|
+
${''} | ${undefined}
|
|
34
|
+
${'invalid guid'} | ${undefined}
|
|
35
|
+
${'9474c66b-4516-48d9-915b-be006d86fc4d'} | ${new Guid('9474c66b-4516-48d9-915b-be006d86fc4d')}
|
|
36
|
+
`(
|
|
37
|
+
'parses string `$value` to guid `$result`',
|
|
38
|
+
({ value, result }: { value: string; result: Guid }) => {
|
|
39
|
+
const parsedGuid = Guid.parse(value);
|
|
40
|
+
|
|
41
|
+
expect(parsedGuid).toEqual(result);
|
|
42
|
+
},
|
|
43
|
+
);
|
|
44
|
+
});
|
package/src/guid/Guid.ts
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import * as uuid from 'uuid';
|
|
2
|
+
|
|
3
|
+
export class Guid {
|
|
4
|
+
private readonly uuid: string;
|
|
5
|
+
|
|
6
|
+
constructor(value: string) {
|
|
7
|
+
uuid.parse(value);
|
|
8
|
+
this.uuid = value;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
public toString(): string {
|
|
12
|
+
return this.uuid;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
public equals(other: Guid): boolean {
|
|
16
|
+
return this.uuid === other.toString();
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
public static new(): Guid {
|
|
20
|
+
return new Guid(uuid.v4());
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
public static empty(): Guid {
|
|
24
|
+
return new Guid(uuid.NIL);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
public static isValid(value?: string | null): boolean {
|
|
28
|
+
return Boolean(value && uuid.validate(value));
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
public static parse(value: string): Guid | undefined {
|
|
32
|
+
return value && Guid.isValid(value) ? new Guid(value) : undefined;
|
|
33
|
+
}
|
|
34
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export * from './mocks/mock';
|
|
2
|
+
export * from './partials/RecursivePartial';
|
|
3
|
+
export * from './result/result';
|
|
4
|
+
export * from './failures';
|
|
5
|
+
export * from './configuration/configuration';
|
|
6
|
+
export * from './datetime/datetime';
|
|
7
|
+
export * from './guid/Guid';
|
|
8
|
+
export * from './paging';
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
|
|
3
|
+
import { mock } from './mock';
|
|
4
|
+
|
|
5
|
+
class SomeEntity {
|
|
6
|
+
a: string;
|
|
7
|
+
b: number;
|
|
8
|
+
c: boolean;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
describe('mock', () => {
|
|
12
|
+
it('mocks an entity without properties', () => {
|
|
13
|
+
const mockedEntity = mock<SomeEntity>();
|
|
14
|
+
|
|
15
|
+
expect(mockedEntity).toEqual({});
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it('mocks an entity with partial properties', () => {
|
|
19
|
+
const mockedEntity = mock<SomeEntity>({
|
|
20
|
+
b: 1,
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
expect(mockedEntity).toEqual({
|
|
24
|
+
b: 1,
|
|
25
|
+
});
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it('mocks an entity with all properties', () => {
|
|
29
|
+
const mockedEntity = mock<SomeEntity>({
|
|
30
|
+
a: 'a',
|
|
31
|
+
b: 1,
|
|
32
|
+
c: true,
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
expect(mockedEntity).toEqual({
|
|
36
|
+
a: 'a',
|
|
37
|
+
b: 1,
|
|
38
|
+
c: true,
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
});
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { z, ZodTypeAny } from 'zod';
|
|
2
|
+
|
|
3
|
+
export const createPageSchema = <T extends ZodTypeAny>(elementSchema: T) =>
|
|
4
|
+
z.object({
|
|
5
|
+
pages: z.number(),
|
|
6
|
+
nextPage: z.number().optional(),
|
|
7
|
+
totalElements: z.number(),
|
|
8
|
+
currentPage: z.number(),
|
|
9
|
+
pageSize: z.number(),
|
|
10
|
+
elements: z.array(elementSchema),
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
export type Page<T> = {
|
|
14
|
+
pages: number;
|
|
15
|
+
nextPage: number | undefined;
|
|
16
|
+
totalElements: number;
|
|
17
|
+
currentPage: number;
|
|
18
|
+
pageSize: number;
|
|
19
|
+
elements: T[];
|
|
20
|
+
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { Failure } from '../failures/failure';
|
|
2
|
+
|
|
3
|
+
export type Result<T = undefined, E extends Failure | undefined = Failure> = [T, E | undefined];
|
|
4
|
+
|
|
5
|
+
export const Ok = <T>(value: T): Result<T, undefined> => [value, undefined];
|
|
6
|
+
|
|
7
|
+
export const Fail = <T = undefined, E extends Failure = Failure>(failure: E): Result<T, E> => [
|
|
8
|
+
undefined as T,
|
|
9
|
+
failure,
|
|
10
|
+
];
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
|
|
3
|
+
import { Failure, isFailure } from '../failures';
|
|
4
|
+
|
|
5
|
+
import { Fail, Ok, Result } from './result';
|
|
6
|
+
|
|
7
|
+
describe('ResultTuple', () => {
|
|
8
|
+
describe('result with value', () => {
|
|
9
|
+
it('returns value', () => {
|
|
10
|
+
const doSomething = (): Result<number, Failure> => Ok(1);
|
|
11
|
+
|
|
12
|
+
const [value, error] = doSomething();
|
|
13
|
+
|
|
14
|
+
expect(value).toEqual(1);
|
|
15
|
+
expect(error).toBeUndefined();
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it('returns value object with properties', () => {
|
|
19
|
+
const doSomething = (): Result<{ foo: string }, Failure> => Ok({ foo: 'bar' });
|
|
20
|
+
|
|
21
|
+
const [value] = doSomething();
|
|
22
|
+
|
|
23
|
+
expect(value.foo).toEqual('bar');
|
|
24
|
+
});
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
describe('result with failure', () => {
|
|
28
|
+
it('returns failure', () => {
|
|
29
|
+
const doSomething = () => Fail(new Failure());
|
|
30
|
+
|
|
31
|
+
const [value, failure] = doSomething();
|
|
32
|
+
|
|
33
|
+
expect(value).toBeUndefined();
|
|
34
|
+
expect(failure).toEqual(new Failure());
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it('returns custom failure', () => {
|
|
38
|
+
class CustomFailure extends Failure {
|
|
39
|
+
constructor(public readonly message: string) {
|
|
40
|
+
super();
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const doSomething = () => Fail(new CustomFailure('error'));
|
|
45
|
+
|
|
46
|
+
const [value, failure] = doSomething();
|
|
47
|
+
|
|
48
|
+
expect(value).toBeUndefined();
|
|
49
|
+
expect(failure).toEqual(new CustomFailure('error'));
|
|
50
|
+
expect(isFailure(CustomFailure)(failure)).toBe(true);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it('returns multiple custom failure types', () => {
|
|
54
|
+
class CustomFailure extends Failure {}
|
|
55
|
+
class AnotherCustomFailure extends Failure {}
|
|
56
|
+
|
|
57
|
+
const doSomething = (
|
|
58
|
+
failureType: 'custom' | 'another',
|
|
59
|
+
): Result<number, CustomFailure | AnotherCustomFailure> =>
|
|
60
|
+
failureType === 'custom' ? Fail(new CustomFailure()) : Fail(new AnotherCustomFailure());
|
|
61
|
+
|
|
62
|
+
const [customValue, customFailure] = doSomething('custom');
|
|
63
|
+
const [anotherValue, anotherCustomFailure] = doSomething('another');
|
|
64
|
+
|
|
65
|
+
expect(customValue).toBeUndefined();
|
|
66
|
+
expect(anotherValue).toBeUndefined();
|
|
67
|
+
expect(customFailure).toEqual(new CustomFailure());
|
|
68
|
+
expect(anotherCustomFailure).toEqual(new AnotherCustomFailure());
|
|
69
|
+
expect(isFailure(CustomFailure)(customFailure)).toBe(true);
|
|
70
|
+
expect(isFailure(AnotherCustomFailure)(anotherCustomFailure)).toBe(true);
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it('returns value without Ok function', () => {
|
|
74
|
+
const doSomething = (): Result<number, Failure> => [1, undefined];
|
|
75
|
+
|
|
76
|
+
const [value, failure] = doSomething();
|
|
77
|
+
|
|
78
|
+
expect(value).toEqual(1);
|
|
79
|
+
expect(failure).toBeUndefined();
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
it('returns failure without Fail function', () => {
|
|
83
|
+
const doSomething = (): Result<undefined, Failure> => [undefined, new Failure()];
|
|
84
|
+
|
|
85
|
+
const [value, failure] = doSomething();
|
|
86
|
+
|
|
87
|
+
expect(value).toBeUndefined();
|
|
88
|
+
expect(isFailure(Failure)(failure)).toEqual(true);
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
it('returns undefined value for void', () => {
|
|
92
|
+
const doSomething = () => Ok(undefined);
|
|
93
|
+
|
|
94
|
+
const [value, failure] = doSomething();
|
|
95
|
+
|
|
96
|
+
expect(value).toBeUndefined();
|
|
97
|
+
expect(failure).toBeUndefined();
|
|
98
|
+
});
|
|
99
|
+
});
|
|
100
|
+
});
|
package/tsconfig.json
ADDED
package/vite.config.ts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { defineConfig } from 'vite';
|
|
2
|
+
import dts from 'vite-plugin-dts';
|
|
3
|
+
|
|
4
|
+
export default defineConfig({
|
|
5
|
+
plugins: [
|
|
6
|
+
dts({
|
|
7
|
+
insertTypesEntry: true,
|
|
8
|
+
}),
|
|
9
|
+
],
|
|
10
|
+
build: {
|
|
11
|
+
emptyOutDir: true,
|
|
12
|
+
lib: {
|
|
13
|
+
entry: 'src/index.ts',
|
|
14
|
+
formats: ['es', 'cjs'],
|
|
15
|
+
fileName: (format) => `index.${format === 'es' ? 'mjs' : 'cjs'}`,
|
|
16
|
+
},
|
|
17
|
+
rollupOptions: {
|
|
18
|
+
external: [],
|
|
19
|
+
output: {
|
|
20
|
+
globals: {},
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
},
|
|
24
|
+
});
|
package/vitest.config.ts
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { defineConfig } from 'vitest/config';
|
|
2
|
+
|
|
3
|
+
export default defineConfig({
|
|
4
|
+
plugins: [],
|
|
5
|
+
test: {
|
|
6
|
+
globals: true,
|
|
7
|
+
environment: 'node',
|
|
8
|
+
setupFiles: [],
|
|
9
|
+
css: true,
|
|
10
|
+
coverage: {
|
|
11
|
+
provider: 'v8',
|
|
12
|
+
reporter: ['text', 'json', 'html'],
|
|
13
|
+
include: ['src/**/*.ts'],
|
|
14
|
+
exclude: ['src/index.ts', 'src/paging/**'],
|
|
15
|
+
thresholds: {
|
|
16
|
+
statements: 100,
|
|
17
|
+
branches: 100,
|
|
18
|
+
functions: 100,
|
|
19
|
+
lines: 100,
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
resolve: {
|
|
24
|
+
alias: {
|
|
25
|
+
'@': '/src',
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
});
|