Compare commits
	
		
			3 Commits
		
	
	
		
			89751faad2
			...
			d25a844fac
		
	
	| Author | SHA1 | Date | 
|---|---|---|
| 
							
							
								
									
								
								 | 
						d25a844fac | |
| 
							
							
								
									
								
								 | 
						e55c791386 | |
| 
							
							
								
									
								
								 | 
						3fe297b8f1 | 
| 
						 | 
				
			
			@ -20,6 +20,7 @@
 | 
			
		|||
    "@holllo/preact-components": "^0.2.3",
 | 
			
		||||
    "htm": "^3.1.1",
 | 
			
		||||
    "modern-normalize": "^1.1.0",
 | 
			
		||||
    "nanoid": "^4.0.0",
 | 
			
		||||
    "preact": "^10.11.0",
 | 
			
		||||
    "webextension-polyfill": "^0.10.0"
 | 
			
		||||
  },
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -10,6 +10,7 @@ specifiers:
 | 
			
		|||
  c8: ^7.12.0
 | 
			
		||||
  htm: ^3.1.1
 | 
			
		||||
  modern-normalize: ^1.1.0
 | 
			
		||||
  nanoid: ^4.0.0
 | 
			
		||||
  postcss: ^8.4.16
 | 
			
		||||
  preact: ^10.11.0
 | 
			
		||||
  sass: ^1.55.0
 | 
			
		||||
| 
						 | 
				
			
			@ -29,6 +30,7 @@ dependencies:
 | 
			
		|||
  '@holllo/preact-components': 0.2.3_htm@3.1.1+preact@10.11.0
 | 
			
		||||
  htm: 3.1.1
 | 
			
		||||
  modern-normalize: 1.1.0
 | 
			
		||||
  nanoid: 4.0.0
 | 
			
		||||
  preact: 10.11.0
 | 
			
		||||
  webextension-polyfill: 0.10.0
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -5197,6 +5199,12 @@ packages:
 | 
			
		|||
    hasBin: true
 | 
			
		||||
    dev: true
 | 
			
		||||
 | 
			
		||||
  /nanoid/4.0.0:
 | 
			
		||||
    resolution: {integrity: sha512-IgBP8piMxe/gf73RTQx7hmnhwz0aaEXYakvqZyE302IXW3HyVNhdNGC+O2MwMAVhLEnvXlvKtGbtJf6wvHihCg==}
 | 
			
		||||
    engines: {node: ^14 || ^16 || >=18}
 | 
			
		||||
    hasBin: true
 | 
			
		||||
    dev: false
 | 
			
		||||
 | 
			
		||||
  /natural-compare/1.4.0:
 | 
			
		||||
    resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==}
 | 
			
		||||
    dev: true
 | 
			
		||||
| 
						 | 
				
			
			@ -7637,6 +7645,11 @@ packages:
 | 
			
		|||
    resolution: {integrity: sha512-liCEteZ5z+QRyh3XzsYWQyxedBHBvx8CDlNvvi+BJz74L0E5/ID2v7JtoX3bD541AlMuOy4e/iWif6hhNGBFNw==}
 | 
			
		||||
    engines: {node: '>=12.20'}
 | 
			
		||||
    hasBin: true
 | 
			
		||||
    peerDependencies:
 | 
			
		||||
      webpack: '>=1.11.0'
 | 
			
		||||
    peerDependenciesMeta:
 | 
			
		||||
      webpack:
 | 
			
		||||
        optional: true
 | 
			
		||||
    dependencies:
 | 
			
		||||
      '@eslint/eslintrc': 1.3.2
 | 
			
		||||
      '@typescript-eslint/eslint-plugin': 5.36.2_q7mrctxvxoqtj4jpjqmbsyg2qy
 | 
			
		||||
| 
						 | 
				
			
			@ -7677,7 +7690,6 @@ packages:
 | 
			
		|||
    transitivePeerDependencies:
 | 
			
		||||
      - eslint-import-resolver-typescript
 | 
			
		||||
      - supports-color
 | 
			
		||||
      - webpack
 | 
			
		||||
    dev: true
 | 
			
		||||
    bundledDependencies:
 | 
			
		||||
      - '@typescript-eslint/eslint-plugin'
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -21,8 +21,10 @@ browser.runtime.onInstalled.addListener(async () => {
 | 
			
		|||
browser.webNavigation.onBeforeNavigate.addListener(async (details) => {
 | 
			
		||||
  const url = new URL(details.url);
 | 
			
		||||
 | 
			
		||||
  for (const parameters of Object.values(await browser.storage.local.get())) {
 | 
			
		||||
    const redirect = parseRedirect(parameters);
 | 
			
		||||
  for (const [id, parameters] of Object.entries(
 | 
			
		||||
    await browser.storage.local.get(),
 | 
			
		||||
  )) {
 | 
			
		||||
    const redirect = parseRedirect(parameters, id);
 | 
			
		||||
    if (redirect === undefined) {
 | 
			
		||||
      continue;
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,10 +1,12 @@
 | 
			
		|||
import {customAlphabet} from 'nanoid';
 | 
			
		||||
 | 
			
		||||
export const matcherTypes = ['hostname'] as const;
 | 
			
		||||
export const redirectTypes = ['hostname', 'simple'] as const;
 | 
			
		||||
 | 
			
		||||
export type MatcherType = typeof matcherTypes[number];
 | 
			
		||||
export type RedirectType = typeof redirectTypes[number];
 | 
			
		||||
 | 
			
		||||
export function narrowMatchType(value: string): value is MatcherType {
 | 
			
		||||
export function narrowMatcherType(value: string): value is MatcherType {
 | 
			
		||||
  return matcherTypes.includes(value as MatcherType);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -22,7 +24,17 @@ export type RedirectParameters = {
 | 
			
		|||
};
 | 
			
		||||
 | 
			
		||||
export abstract class Redirect<P extends RedirectParameters> {
 | 
			
		||||
  constructor(public parameters: P & Matcher) {}
 | 
			
		||||
  public static generateId(): string {
 | 
			
		||||
    const alphabet = 'abcdefghijklmnopqrstuvwxyz';
 | 
			
		||||
    const nanoid = customAlphabet(`${alphabet}${alphabet.toUpperCase()}`, 20);
 | 
			
		||||
    return nanoid();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public id: string;
 | 
			
		||||
 | 
			
		||||
  constructor(public parameters: P & Matcher, id?: string) {
 | 
			
		||||
    this.id = id ?? Redirect.generateId();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public isMatch(url: URL): boolean {
 | 
			
		||||
    if (this.parameters.matcherType === 'hostname') {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -9,14 +9,15 @@ export type Redirects = HostnameRedirect | SimpleRedirect;
 | 
			
		|||
 | 
			
		||||
export function parseRedirect<P extends Redirects['parameters']>(
 | 
			
		||||
  parameters: P,
 | 
			
		||||
  id: string,
 | 
			
		||||
): Redirects | undefined {
 | 
			
		||||
  const redirectType = parameters?.redirectType;
 | 
			
		||||
 | 
			
		||||
  if (redirectType === 'hostname') {
 | 
			
		||||
    return new HostnameRedirect(parameters);
 | 
			
		||||
    return new HostnameRedirect(parameters, id);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (redirectType === 'simple') {
 | 
			
		||||
    return new SimpleRedirect(parameters);
 | 
			
		||||
    return new SimpleRedirect(parameters, id);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -4,7 +4,7 @@ import test from 'ava';
 | 
			
		|||
 | 
			
		||||
import {
 | 
			
		||||
  matcherTypes,
 | 
			
		||||
  narrowMatchType,
 | 
			
		||||
  narrowMatcherType,
 | 
			
		||||
  narrowRedirectType,
 | 
			
		||||
  parseRedirect,
 | 
			
		||||
  redirectTypes,
 | 
			
		||||
| 
						 | 
				
			
			@ -40,11 +40,13 @@ test('parseRedirect', (t) => {
 | 
			
		|||
  ];
 | 
			
		||||
 | 
			
		||||
  for (const sample of samples) {
 | 
			
		||||
    const redirect = parseRedirect(sample);
 | 
			
		||||
    const redirect = parseRedirect(sample, Redirect.generateId());
 | 
			
		||||
 | 
			
		||||
    if (redirect === undefined) {
 | 
			
		||||
      t.pass('parseRedirect returned undefined');
 | 
			
		||||
    } else {
 | 
			
		||||
      t.regex(redirect.id, /^[a-z]{20}$/i);
 | 
			
		||||
      redirect.id = 'id';
 | 
			
		||||
      t.snapshot(redirect, `Class ${redirect.constructor.name}`);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
| 
						 | 
				
			
			@ -99,8 +101,22 @@ test('Redirect.isMatch', (t) => {
 | 
			
		|||
});
 | 
			
		||||
 | 
			
		||||
test('Narrow match & redirect types', (t) => {
 | 
			
		||||
  t.false(narrowMatchType('invalid'));
 | 
			
		||||
  t.false(narrowMatcherType('invalid'));
 | 
			
		||||
  t.false(narrowRedirectType('invalid'));
 | 
			
		||||
  t.true(matcherTypes.every((value) => narrowMatchType(value)));
 | 
			
		||||
  t.true(matcherTypes.every((value) => narrowMatcherType(value)));
 | 
			
		||||
  t.true(redirectTypes.every((value) => narrowRedirectType(value)));
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
test('Redirect getters & setters', (t) => {
 | 
			
		||||
  const samples: Array<[Redirects, string]> = [
 | 
			
		||||
    [new HostnameRedirect(hostnameParameters), hostnameParameters.hostname],
 | 
			
		||||
    [new SimpleRedirect(simpleParameters), simpleParameters.target],
 | 
			
		||||
  ];
 | 
			
		||||
 | 
			
		||||
  for (const [redirect, value] of samples) {
 | 
			
		||||
    t.is(redirect.redirectValue, value);
 | 
			
		||||
    const newValue = `${value} test`;
 | 
			
		||||
    redirect.redirectValue = newValue;
 | 
			
		||||
    t.is(redirect.redirectValue, newValue);
 | 
			
		||||
  }
 | 
			
		||||
});
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -9,6 +9,7 @@ Generated by [AVA](https://avajs.dev).
 | 
			
		|||
> Class HostnameRedirect
 | 
			
		||||
 | 
			
		||||
    HostnameRedirect {
 | 
			
		||||
      id: 'id',
 | 
			
		||||
      parameters: {
 | 
			
		||||
        hostname: 'example.org',
 | 
			
		||||
        matcherType: 'hostname',
 | 
			
		||||
| 
						 | 
				
			
			@ -20,6 +21,7 @@ Generated by [AVA](https://avajs.dev).
 | 
			
		|||
> Class SimpleRedirect
 | 
			
		||||
 | 
			
		||||
    SimpleRedirect {
 | 
			
		||||
      id: 'id',
 | 
			
		||||
      parameters: {
 | 
			
		||||
        matcherType: 'hostname',
 | 
			
		||||
        redirectType: 'simple',
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
										
											Binary file not shown.
										
									
								
							
		Reference in New Issue