File size: 2,669 Bytes
43a06dc
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
import type { Optional } from '$lib/types/generic';
import defaultSettings from './defaults'
import {
    downloadModeOptions,
    filenameStyleOptions,
    savingMethodOptions,
    themeOptions,
    videoQualityOptions,
    youtubeVideoCodecOptions,
    type PartialSettings,
} from '$lib/types/settings';
import { youtubeLanguages } from './youtube-lang';

function validateTypes(input: unknown, reference = defaultSettings as unknown) {
    if (typeof input === 'undefined')
        return true;

    if (typeof input !== typeof reference)
        return false;

    if (typeof reference !== 'object')
        return true;

    if (reference === null || input === null)
        return input === reference;

    if (Array.isArray(reference)) {
        // TODO: we dont expect the reference array to hold any
        //       elements, but we should at maybe check whether
        //       the input array types are all matching.
        return true;
    }

    // we know that `input` is an `object` based on the first
    // two `if`s, but for some reason typescript doesn't.  :)
    if (typeof input !== 'object')
        return false;

    const keys = new Set([
        ...Object.keys(input),
        ...Object.keys(reference)
    ]);

    for (const key of keys) {
        const _input = input as Record<string, unknown>;
        const _reference = reference as Record<string, unknown>;

        if (!validateTypes(_input[key], _reference[key])) {
            return false;
        }
    }

    return true;
}

function validateLiteral(value: Optional<string>, allowed: readonly string[]) {
    return value === undefined || allowed.includes(value);
}

function validateLiterals(literals: [Optional<string>, readonly string[]][]) {
    for (const [ value, allowed ] of literals) {
        if (!validateLiteral(value, allowed))
            return false;
    }

    return true;
}

// performs a basic check on an "untrusted" settings object.
export function validateSettings(settings: PartialSettings) {
    if (!settings?.schemaVersion) {
        return false;
    }

    return (
        validateTypes(settings)
        && validateLiterals([
            [ settings?.appearance?.theme      , themeOptions ],
            [ settings?.save?.downloadMode     , downloadModeOptions ],
            [ settings?.save?.filenameStyle    , filenameStyleOptions ],
            [ settings?.save?.videoQuality     , videoQualityOptions ],
            [ settings?.save?.youtubeVideoCodec, youtubeVideoCodecOptions ],
            [ settings?.save?.savingMethod     , savingMethodOptions ],
            [ settings?.save?.youtubeDubLang   , youtubeLanguages ]
        ])
    );
}