diff --git a/source/redirect/base.ts b/source/redirect/base.ts
new file mode 100644
index 0000000..376199b
--- /dev/null
+++ b/source/redirect/base.ts
@@ -0,0 +1,9 @@
+import {HostnameParameters} from './hostname.js';
+
+export type RedirectParameters = HostnameParameters;
+
+export abstract class Redirect
{
+ constructor(public parameters: P) {}
+
+ public abstract redirect(url: URL | string): URL;
+}
diff --git a/source/redirect/exports.ts b/source/redirect/exports.ts
new file mode 100644
index 0000000..0b5e5f2
--- /dev/null
+++ b/source/redirect/exports.ts
@@ -0,0 +1,15 @@
+import {RedirectParameters} from './base.js';
+import {HostnameRedirect} from './hostname.js';
+
+export * from './base.js';
+export * from './hostname.js';
+
+export type Redirects = HostnameRedirect;
+
+export function parseRedirect
(
+ parameters: P,
+): Redirects | undefined {
+ if (parameters.type === 'hostname') {
+ return new HostnameRedirect(parameters);
+ }
+}
diff --git a/source/redirect/hostname.ts b/source/redirect/hostname.ts
new file mode 100644
index 0000000..2c7245a
--- /dev/null
+++ b/source/redirect/hostname.ts
@@ -0,0 +1,14 @@
+import {Redirect} from './base.js';
+
+export type HostnameParameters = {
+ hostname: string;
+ type: 'hostname';
+};
+
+export class HostnameRedirect extends Redirect {
+ public redirect(url: URL | string): URL {
+ const redirected = new URL(url);
+ redirected.hostname = this.parameters.hostname;
+ return redirected;
+ }
+}
diff --git a/tests/redirect.test.ts b/tests/redirect.test.ts
new file mode 100644
index 0000000..ae7b89b
--- /dev/null
+++ b/tests/redirect.test.ts
@@ -0,0 +1,54 @@
+///
+
+import test from 'ava';
+
+import {
+ parseRedirect,
+ HostnameRedirect,
+ Redirect,
+ RedirectParameters,
+} from '../source/redirect/exports.js';
+
+test('parseRedirect', (t) => {
+ const samples: RedirectParameters[] = [
+ {
+ test: 'Invalid parameters',
+ } as unknown as RedirectParameters,
+ {
+ hostname: 'example.org',
+ type: 'hostname',
+ },
+ ];
+
+ for (const sample of samples) {
+ const redirect = parseRedirect(sample);
+
+ if (redirect === undefined) {
+ t.pass('parseRedirect returned undefined');
+ } else {
+ t.snapshot(redirect, `Class ${redirect.constructor.name}`);
+ }
+ }
+});
+
+test('Redirect.redirect', (t) => {
+ const hostnameRedirect = new HostnameRedirect({
+ hostname: 'example.org',
+ type: 'hostname',
+ });
+
+ const samples: Array<[string, Redirect]> = [
+ ['https://example.com', hostnameRedirect],
+ ['https://example.com/path#hash?query=test', hostnameRedirect],
+ ];
+
+ for (const [index, [url, redirect]] of samples.entries()) {
+ t.snapshot(
+ {
+ original: url,
+ redirected: redirect.redirect(url).href,
+ },
+ `${index} ${redirect.constructor.name}`,
+ );
+ }
+});
diff --git a/tests/snapshots/tests/redirect.test.ts.md b/tests/snapshots/tests/redirect.test.ts.md
new file mode 100644
index 0000000..0cb63b1
--- /dev/null
+++ b/tests/snapshots/tests/redirect.test.ts.md
@@ -0,0 +1,32 @@
+# Snapshot report for `tests/redirect.test.ts`
+
+The actual snapshot is saved in `redirect.test.ts.snap`.
+
+Generated by [AVA](https://avajs.dev).
+
+## parseRedirect
+
+> Class HostnameRedirect
+
+ HostnameRedirect {
+ parameters: {
+ hostname: 'example.org',
+ type: 'hostname',
+ },
+ }
+
+## Redirect.redirect
+
+> 0 HostnameRedirect
+
+ {
+ original: 'https://example.com',
+ redirected: 'https://example.org/',
+ }
+
+> 1 HostnameRedirect
+
+ {
+ original: 'https://example.com/path#hash?query=test',
+ redirected: 'https://example.org/path#hash?query=test',
+ }
diff --git a/tests/snapshots/tests/redirect.test.ts.snap b/tests/snapshots/tests/redirect.test.ts.snap
new file mode 100644
index 0000000..57ab73d
Binary files /dev/null and b/tests/snapshots/tests/redirect.test.ts.snap differ