Skip to content

Commit

Permalink
No extra APIs
Browse files Browse the repository at this point in the history
  • Loading branch information
igorkamyshev committed Jul 30, 2024
1 parent b3b86ae commit f51bf0c
Show file tree
Hide file tree
Showing 5 changed files with 25 additions and 87 deletions.
15 changes: 9 additions & 6 deletions apps/website/docs/contracts/cookbook/custom_matchers.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,16 @@ Since `@withease/contracts` is built on top of [_Contract_](/protocols/contract)
Let us write a custom matcher that checks if an age of a user is within a certain range:

```ts
import { type Contract, contract, nul } from '@withease/contracts';
import { type Contract, and, num } from '@withease/contracts';

const age = contract(num, ({ min, max }: { min: number; max: number }) => ({
isData: (data) => data >= min && data <= max,
getErrorMessages: (data) =>
`Expected a number between ${min} and ${max}, but got ${data}`,
}));
function age({ min, max }: { min: number; max: number }) {
return and(num, {
isData: (data) => data >= min && data <= max,
getErrorMessages: (data) => [
`Expected a number between ${min} and ${max}, but got ${data}`,
],
});
}
```

Now you can use this matcher in your schema:
Expand Down
2 changes: 1 addition & 1 deletion packages/contracts/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
"size-limit": [
{
"path": "./dist/contracts.js",
"limit": "863 B"
"limit": "797 B"
}
]
}
14 changes: 14 additions & 0 deletions packages/contracts/src/contract.test-d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { describe, test, expectTypeOf } from 'vitest';

import { and, Contract, num } from './index';

describe('and', () => {
test('inline contract', () => {
const contract = and(num, {
isData: (data): data is number => data > 0,
getErrorMessages: () => ['data must be greater than 0'],
});

expectTypeOf(contract).toEqualTypeOf<Contract<unknown, number>>();
});
});
47 changes: 1 addition & 46 deletions packages/contracts/src/contracts.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,7 @@ import {
arr,
and,
tuple,
contract,
Contract,
type Contract,
} from './index';

describe('bool', () => {
Expand Down Expand Up @@ -548,47 +547,3 @@ describe('tuple', () => {
`);
});
});

describe('contract', () => {
const age = contract(num, ({ min, max }: { min: number; max: number }) => ({
isData: (t): t is number => t >= min && t <= max,
getErrorMessages: (t) => [
'expected ' + min + ' <= x <= ' + max + ', got ' + t,
],
}));

it('valid', () => {
const cntrct = age({ min: 18, max: 100 });

expect(cntrct.isData(18)).toBeTruthy();
expect(cntrct.getErrorMessages(18)).toEqual([]);

expect(cntrct.isData(100)).toBeTruthy();
expect(cntrct.getErrorMessages(100)).toEqual([]);
});

it('invalid', () => {
const cntrct = age({ min: 18, max: 100 });

expect(cntrct.isData('KEK')).toBeFalsy();
expect(cntrct.getErrorMessages('KEK')).toMatchInlineSnapshot(`
[
"expected number, got string",
]
`);

expect(cntrct.isData(17)).toBeFalsy();
expect(cntrct.getErrorMessages(17)).toMatchInlineSnapshot(`
[
"expected 18 <= x <= 100, got 17",
]
`);

expect(cntrct.isData(101)).toBeFalsy();
expect(cntrct.getErrorMessages(101)).toMatchInlineSnapshot(`
[
"expected 18 <= x <= 100, got 101",
]
`);
});
});
34 changes: 0 additions & 34 deletions packages/contracts/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -352,40 +352,6 @@ export function tuple(...contracts: Array<Contract<unknown, any>>): any {
};
}

/**
* Creates a _Contract_ based on a passed function and a base _Contract_.
* Base _Contract_ is checked first, then the _Contract_ created by the function.
*
* @example
*
* const age = contract(num, ({ min, max }) => ({
* isData: (data) => data >= min && data <= max,
* getErrorMessages: (data) =>
* `Expected a number between ${min} and ${max}, but got ${data}`,
* }));
*/
export function contract<P, I, O extends I>(
base: Contract<unknown, I>,
fn: (config: P) => Contract<I, O>
): (config: P) => Contract<unknown, O> {
return (params: P) => {
const next = fn(params);
const check = (data: unknown): data is O =>
base.isData(data) && next.isData(data);

return {
isData: check,
getErrorMessages: createGetErrorMessages(check, (data) => {
if (!base.isData(data)) {
return base.getErrorMessages(data);
}

return next.getErrorMessages(data);
}),
};
};
}

// -- utils

function createSimpleContract<T>(
Expand Down

0 comments on commit f51bf0c

Please sign in to comment.