aboutsummaryrefslogtreecommitdiff
path: root/src/shared/settings
diff options
context:
space:
mode:
Diffstat (limited to 'src/shared/settings')
-rw-r--r--src/shared/settings/Blacklist.ts66
-rw-r--r--src/shared/settings/Keymaps.ts29
-rw-r--r--src/shared/settings/Properties.ts37
-rw-r--r--src/shared/settings/Search.ts68
-rw-r--r--src/shared/settings/Validator.ts20
5 files changed, 112 insertions, 108 deletions
diff --git a/src/shared/settings/Blacklist.ts b/src/shared/settings/Blacklist.ts
index 0cfbd71..201e7fc 100644
--- a/src/shared/settings/Blacklist.ts
+++ b/src/shared/settings/Blacklist.ts
@@ -1,4 +1,23 @@
import Key from './Key';
+import Validator from './Validator';
+
+const ItemSchema = {
+ anyOf: [
+ { type: 'string' },
+ {
+ type: 'object',
+ properties: {
+ url: { type: 'string' },
+ keys: {
+ type: 'array',
+ items: { type: 'string', minLength: 1 },
+ minItems: 1,
+ }
+ },
+ required: ['url', 'keys'],
+ }
+ ],
+};
export type BlacklistItemJSON = string | {
url: string,
@@ -12,18 +31,6 @@ const regexFromWildcard = (pattern: string): RegExp => {
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;
@@ -47,30 +54,11 @@ export class BlacklistItem {
this.keyEntities = this.keys.map(Key.fromMapKey);
}
- 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)}`);
+ static fromJSON(json: unknown): BlacklistItem {
+ let obj = new Validator<BlacklistItemJSON>(ItemSchema).validate(json);
+ return typeof obj === 'string'
+ ? new BlacklistItem(obj, false, [])
+ : new BlacklistItem(obj.url, true, obj.keys);
}
toJSON(): BlacklistItemJSON {
@@ -103,11 +91,11 @@ export default class Blacklist {
) {
}
- static fromJSON(json: any): Blacklist {
+ static fromJSON(json: unknown): Blacklist {
if (!Array.isArray(json)) {
- throw new TypeError('blacklist is not an array: ' + JSON.stringify(json));
+ throw new TypeError('blacklist is not an array');
}
- let items = Array.from(json).map(item => BlacklistItem.fromJSON(item));
+ let items = json.map(o => BlacklistItem.fromJSON(o));
return new Blacklist(items);
}
diff --git a/src/shared/settings/Keymaps.ts b/src/shared/settings/Keymaps.ts
index a5558b0..7e510d1 100644
--- a/src/shared/settings/Keymaps.ts
+++ b/src/shared/settings/Keymaps.ts
@@ -1,4 +1,18 @@
import * as operations from '../operations';
+import Validator from './Validator';
+
+const Schema = {
+ type: 'object',
+ patternProperties: {
+ '.*': {
+ type: 'object',
+ properties: {
+ type: { type: 'string' },
+ },
+ required: ['type'],
+ },
+ }
+};
export type KeymapsJSON = { [key: string]: operations.Operation };
@@ -8,16 +22,13 @@ export default class Keymaps {
) {
}
- static fromJSON(json: any): Keymaps {
- if (typeof json !== 'object' || json === null) {
- throw new TypeError('invalid keymaps type: ' + JSON.stringify(json));
- }
-
- let data: KeymapsJSON = {};
- for (let key of Object.keys(json)) {
- data[key] = operations.valueOf(json[key]);
+ static fromJSON(json: unknown): Keymaps {
+ let obj = new Validator<KeymapsJSON>(Schema).validate(json);
+ let entries: KeymapsJSON = {};
+ for (let key of Object.keys(obj)) {
+ entries[key] = operations.valueOf(obj[key]);
}
- return new Keymaps(data);
+ return new Keymaps(entries);
}
combine(other: Keymaps): Keymaps {
diff --git a/src/shared/settings/Properties.ts b/src/shared/settings/Properties.ts
index 63ff991..9cdaffe 100644
--- a/src/shared/settings/Properties.ts
+++ b/src/shared/settings/Properties.ts
@@ -1,3 +1,20 @@
+import Validator from './Validator';
+
+const Schema = {
+ type: 'object',
+ properties: {
+ hintchars: {
+ type: 'string',
+ },
+ smoothscroll: {
+ type: 'boolean',
+ },
+ complete: {
+ type: 'string',
+ },
+ },
+};
+
export type PropertiesJSON = {
hintchars?: string;
smoothscroll?: boolean;
@@ -65,23 +82,9 @@ export default class Properties {
this.complete = complete || defaultValues.complete;
}
- static fromJSON(json: any): Properties {
- let defNames: Set<string> = new Set(defs.map(def => def.name));
- let unknownName = Object.keys(json).find(name => !defNames.has(name));
- if (unknownName) {
- throw new TypeError(`Unknown property name: "${unknownName}"`);
- }
-
- for (let def of defs) {
- if (!Object.prototype.hasOwnProperty.call(json, def.name)) {
- continue;
- }
- if (typeof json[def.name] !== def.type) {
- throw new TypeError(
- `property "${def.name}" is not ${def.type}`);
- }
- }
- return new Properties(json);
+ static fromJSON(json: unknown): Properties {
+ let obj = new Validator<PropertiesJSON>(Schema).validate(json);
+ return new Properties(obj);
}
static types(): PropertyTypes {
diff --git a/src/shared/settings/Search.ts b/src/shared/settings/Search.ts
index 4580236..bdbe4a8 100644
--- a/src/shared/settings/Search.ts
+++ b/src/shared/settings/Search.ts
@@ -1,3 +1,22 @@
+import Validator from './Validator';
+
+const Schema = {
+ type: 'object',
+ properties: {
+ default: { type: 'string' },
+ engines: {
+ type: 'object',
+ propertyNames: {
+ pattern: '^[A-Za-z_][A-Za-z0-9_]+$',
+ },
+ patternProperties: {
+ '.*': { type: 'string' },
+ },
+ },
+ },
+ required: ['default'],
+};
+
type Entries = { [name: string]: string };
export type SearchJSON = {
@@ -12,19 +31,10 @@ export default class Search {
) {
}
- static fromJSON(json: any): Search {
- let defaultEngine = Search.getStringField(json, 'default');
- let engines = Search.getObjectField(json, 'engines');
+ static fromJSON(json: unknown): Search {
+ let obj = new Validator<SearchJSON>(Schema).validate(json);
- for (let [name, url] of Object.entries(engines)) {
- if ((/\s/).test(name)) {
- throw new TypeError(
- `While space in the search engine not allowed: "${name}"`);
- }
- if (typeof url !== 'string') {
- throw new TypeError(
- `Invalid type of value in filed "engines": ${JSON.stringify(json)}`);
- }
+ for (let [name, url] of Object.entries(obj.engines)) {
let matches = url.match(/{}/g);
if (matches === null) {
throw new TypeError(`No {}-placeholders in URL of "${name}"`);
@@ -32,15 +42,11 @@ export default class Search {
throw new TypeError(`Multiple {}-placeholders in URL of "${name}"`);
}
}
-
- if (!Object.keys(engines).includes(defaultEngine)) {
- throw new TypeError(`Default engine "${defaultEngine}" not found`);
+ if (!Object.keys(obj.engines).includes(obj.default)) {
+ throw new TypeError(`Default engine "${obj.default}" not found`);
}
- return new Search(
- json.default as string,
- json.engines,
- );
+ return new Search(obj.default, obj.engines);
}
toJSON(): SearchJSON {
@@ -49,28 +55,4 @@ export default class Search {
engines: this.engines,
};
}
-
- private static getStringField(json: any, name: string): string {
- if (!Object.prototype.hasOwnProperty.call(json, name)) {
- throw new TypeError(
- `missing field "${name}" on search: ${JSON.stringify(json)}`);
- }
- if (typeof json[name] !== 'string') {
- throw new TypeError(
- `invalid type of filed "${name}" on search: ${JSON.stringify(json)}`);
- }
- return json[name];
- }
-
- private static getObjectField(json: any, name: string): Object {
- if (!Object.prototype.hasOwnProperty.call(json, name)) {
- throw new TypeError(
- `missing field "${name}" on search: ${JSON.stringify(json)}`);
- }
- if (typeof json[name] !== 'object' || json[name] === null) {
- throw new TypeError(
- `invalid type of filed "${name}" on search: ${JSON.stringify(json)}`);
- }
- return json[name];
- }
}
diff --git a/src/shared/settings/Validator.ts b/src/shared/settings/Validator.ts
new file mode 100644
index 0000000..6aac07f
--- /dev/null
+++ b/src/shared/settings/Validator.ts
@@ -0,0 +1,20 @@
+import Ajv from 'ajv';
+
+export default class Validator<T> {
+ constructor(
+ private schema: object | boolean,
+ ) {
+ }
+
+ validate(data: any): T {
+ let ajv = new Ajv();
+ let valid = ajv.validate(this.schema, data);
+ if (!valid) {
+ let message = ajv.errors!!
+ .map(err => `'${err.dataPath}' of ${err.keyword} ${err.message}`)
+ .join('; ');
+ throw new TypeError(message);
+ }
+ return data as T;
+ }
+}