diff options
author | Shin'ya UEOKA <ueokande@i-beam.org> | 2019-10-05 08:52:49 +0000 |
---|---|---|
committer | Shin'ya UEOKA <ueokande@i-beam.org> | 2019-10-07 13:01:31 +0000 |
commit | 9ff80fcac3600401c9fed053cc8422f89c404940 (patch) | |
tree | 49950c7c2d997438f9e30ca83d1aa604e8b424af /src/shared/settings/Blacklist.ts | |
parent | 8eddcc1785a85bbe74be254d1055ebe5125dad10 (diff) |
Add partial blacklist item
Diffstat (limited to 'src/shared/settings/Blacklist.ts')
-rw-r--r-- | src/shared/settings/Blacklist.ts | 116 |
1 files changed, 97 insertions, 19 deletions
diff --git a/src/shared/settings/Blacklist.ts b/src/shared/settings/Blacklist.ts index a95b606..5648611 100644 --- a/src/shared/settings/Blacklist.ts +++ b/src/shared/settings/Blacklist.ts @@ -1,39 +1,117 @@ -export type BlacklistJSON = string[]; +export type BlacklistItemJSON = string | { + url: string, + keys: string[], +}; + +export type BlacklistJSON = BlacklistItemJSON[]; -const fromWildcard = (pattern: string): RegExp => { +const regexFromWildcard = (pattern: string): RegExp => { let regexStr = '^' + pattern.replace(/\*/g, '.*') + '$'; return new RegExp(regexStr); }; +const isArrayOfString = (raw: any): boolean => { + if (!Array.isArray(raw)) { + return false; + } + for (let x of Array.from(raw)) { + if (typeof x !== 'string') { + return false; + } + } + return true; +}; + +export class BlacklistItem { + public readonly pattern: string; + + private regex: RegExp; + + public readonly partial: boolean; + + public readonly keys: string[]; + + private constructor( + pattern: string, + partial: boolean, + keys: string[] + ) { + this.pattern = pattern; + this.regex = regexFromWildcard(pattern); + this.partial = partial; + this.keys = keys; + } + + static fromJSON(raw: any): BlacklistItem { + if (typeof raw === 'string') { + return new BlacklistItem(raw, false, []); + } else if (typeof raw === 'object' && raw !== null) { + if (!('url' in raw)) { + throw new TypeError( + `missing field "url" of blacklist item: ${JSON.stringify(raw)}`); + } + if (typeof raw.url !== 'string') { + throw new TypeError( + `invalid field "url" of blacklist item: ${JSON.stringify(raw)}`); + } + if (!('keys' in raw)) { + throw new TypeError( + `missing field "keys" of blacklist item: ${JSON.stringify(raw)}`); + } + if (!isArrayOfString(raw.keys)) { + throw new TypeError( + `invalid field "keys" of blacklist item: ${JSON.stringify(raw)}`); + } + return new BlacklistItem(raw.url as string, true, raw.keys as string[]); + } + throw new TypeError( + `invalid format of blacklist item: ${JSON.stringify(raw)}`); + } + + toJSON(): BlacklistItemJSON { + if (!this.partial) { + return this.pattern; + } + return { url: this.pattern, keys: this.keys }; + } + + matches(url: URL): boolean { + return this.pattern.includes('/') + ? this.regex.test(url.host + url.pathname) + : this.regex.test(url.host); + } + + includeKey(url: URL, keys: string): boolean { + if (!this.matches(url)) { + return false; + } + return !this.partial || this.keys.includes(keys); + } +} + export default class Blacklist { constructor( - private blacklist: string[], + private blacklist: BlacklistItem[], ) { } static fromJSON(json: any): Blacklist { if (!Array.isArray(json)) { - throw new TypeError(`"blacklist" is not an array of string`); - } - for (let x of json) { - if (typeof x !== 'string') { - throw new TypeError(`"blacklist" is not an array of string`); - } + throw new TypeError('blacklist is not an array: ' + JSON.stringify(json)); } - return new Blacklist(json); + let items = Array.from(json).map(item => BlacklistItem.fromJSON(item)); + return new Blacklist(items); } toJSON(): BlacklistJSON { - return this.blacklist; + return this.blacklist.map(item => item.toJSON()); } - includes(url: string): boolean { - let u = new URL(url); - return this.blacklist.some((item) => { - if (!item.includes('/')) { - return fromWildcard(item).test(u.host); - } - return fromWildcard(item).test(u.host + u.pathname); - }); + includesEntireBlacklist(url: URL): boolean { + return this.blacklist.some(item => !item.partial && item.matches(url)); + } + + includeKey(url: URL, key: string) { + return this.blacklist.some(item => item.includeKey(url, key)); } } |