Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: ISSN validation #324

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions library/src/validations/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export * from './isoTime/index.ts';
export * from './isoTimeSecond/index.ts';
export * from './isoTimestamp/index.ts';
export * from './isoWeek/index.ts';
export * from './issn/index.ts';
export * from './length/index.ts';
export * from './mac/index.ts';
export * from './mac48/index.ts';
Expand Down
1 change: 1 addition & 0 deletions library/src/validations/issn/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './issn.ts';
47 changes: 47 additions & 0 deletions library/src/validations/issn/issn.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { describe, expect, test } from 'vitest';
import { issn } from './issn.ts';

describe('issn', () => {
test('should pass only valid ISSN', () => {
const validate = issn();

const value1 = '2049-3630';
expect(validate._parse(value1).output).toBe(value1);
const value2 = '0317-8471';
expect(validate._parse(value2).output).toBe(value2);
const value3 = '0378-5955';
expect(validate._parse(value3).output).toBe(value3);
const value4 = '2434-561X';
expect(validate._parse(value4).output).toBe(value4);
const value5 = '2434-561x';
expect(validate._parse(value5).output).toBe(value5);
const value6 = '1037-6178';
expect(validate._parse(value6).output).toBe(value6);
const value7 = '01896016';
expect(validate._parse(value7).output).toBe(value7);
const value8 = '20905076';
expect(validate._parse(value8).output).toBe(value8);
});

test('should reject invalid ISSN', () => {
const validate = issn();

expect(validate._parse('').issues).toBeTruthy();
expect(validate._parse('0000-0000').issues).toBeTruthy();
expect(validate._parse('1234-5678').issues).toBeTruthy();
expect(validate._parse('abcd-efgh').issues).toBeTruthy();
expect(validate._parse('1234-567A').issues).toBeTruthy();
expect(validate._parse('1234-567X').issues).toBeTruthy();
expect(validate._parse('0').issues).toBeTruthy();
expect(validate._parse('2434-561c').issues).toBeTruthy();
expect(validate._parse('1684-5370').issues).toBeTruthy();
expect(validate._parse('19960791').issues).toBeTruthy();
expect(validate._parse('2090507&').issues).toBeTruthy();
});

test('should return custom error message', () => {
const error = 'Value is not an ISSN!';
const validate = issn(error);
expect(validate._parse('test').issues?.[0].message).toBe(error);
});
});
58 changes: 58 additions & 0 deletions library/src/validations/issn/issn.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import type { BaseValidation, ErrorMessage } from '../../types/index.ts';
import { actionIssue, actionOutput } from '../../utils/index.ts';

/**
* ISSN validation type.
*/
export type IssnValidation<TInput extends string> = BaseValidation<TInput> & {
/**
* The validation type.
*/
type: 'issn';
/**
* The ISSN validation function.
*/
requirement: (input: TInput) => boolean;
};

/**
* Creates a pipeline validation action that validates an [ISSN](https://en.wikipedia.org/wiki/ISSN).
*
* @param message The error message.
*
* @returns A validation action.
*/
export function issn<TInput extends string>(
message: ErrorMessage = 'Invalid ISSN'
): IssnValidation<TInput> {
return {
type: 'issn',
async: false,
message,
requirement: isISSN,
_parse(input) {
return !this.requirement(input)
? actionIssue(this.type, this.message, input, this.requirement)
: actionOutput(input);
},
};
}

const ISSN_REGEX = /^(?!0{4}-?0{3}[0X])\d{4}-?\d{3}[\dX]$/iu;

function isISSN(issn: string): boolean {
issn = issn.toUpperCase();
if (!ISSN_REGEX.test(issn)) {
return false;
}

const numbers = issn
.replace('-', '')
.split('')
.map((digit, index) => {
return digit === 'X' ? 10 : parseInt(digit, 10) * (8 - index);
});

const sum = numbers.reduce((acc, value) => acc + value, 0);
return sum % 11 === 0;
}
9 changes: 9 additions & 0 deletions website/src/routes/api/(validations)/issn/index.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
title: issn
contributors:
- ariskemper
---

# issn

> The content of this page is not yet ready. Until then just use the [source code](https://github.com/fabian-hiller/valibot/blob/main/library/src/validations/issn/issn.ts).
1 change: 1 addition & 0 deletions website/src/routes/api/menu.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@
- [isoTimeSecond](/api/isoTimeSecond/)
- [isoTimestamp](/api/isoTimestamp/)
- [isoWeek](/api/isoWeek/)
- [issn](/api/issn/)
- [length](/api/length/)
- [mac](/api/mac/)
- [mac48](/api/mac48/)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ Pipeline validation actions examine the input and, if the input does not meet a
'isoTimeSecond',
'isoTimestamp',
'isoWeek',
'issn',
'length',
'mac',
'mac48',
Expand Down