refactor(options): add Option class

This Option class will be one of the pillars of the new architecturally
refactored options classes.

Change-Id: Ic0100717f06b837276c7f5de79ff0539ab114aca
diff --git a/src/common/Option.ts b/src/common/Option.ts
new file mode 100644
index 0000000..2391f45
--- /dev/null
+++ b/src/common/Option.ts
@@ -0,0 +1,38 @@
+export enum KillSwitchType {
+  Option = 'option',
+  Experiment = 'experiment',
+  Ignore = 'ignore',
+  Deprecated = 'deprecated',
+  InternalKillSwitch = 'internalKillSwitch',
+}
+
+export enum OptionContext {
+  Options = 'options',
+  Experiments = 'experiments',
+  Internal = 'internal',
+  Deprecated = 'deprecated',
+}
+
+export interface OptionConfig<T> {
+  codename: string;
+  context: OptionContext;
+  defaultValue: T;
+  killSwitchType: KillSwitchType;
+  optionalPermissions?: string[];
+}
+
+export class Option<T> implements OptionConfig<T> {
+  public codename: string;
+  public context: OptionContext;
+  public defaultValue: T;
+  public killSwitchType: KillSwitchType;
+  public optionalPermissions: string[];
+
+  constructor(config: OptionConfig<T>) {
+    this.codename = config.codename;
+    this.context = config.context;
+    this.defaultValue = config.defaultValue;
+    this.killSwitchType = config.killSwitchType;
+    this.optionalPermissions = config.optionalPermissions ?? [];
+  }
+}
diff --git a/src/common/optionsPrototype.ts b/src/common/optionsPrototype.ts
index 17e9056..277ae4c 100644
--- a/src/common/optionsPrototype.ts
+++ b/src/common/optionsPrototype.ts
@@ -1,238 +1,253 @@
-interface BaseOptionConfig {
-  context: "options" | "experiments" | "internal" | "deprecated";
-}
-export type OptionConfig = BaseOptionConfig &
-  (
-    | {
-        defaultValue: any;
-        killSwitchType:
-          | "option"
-          | "experiment"
-          | "ignore"
-          | "deprecated";
-      }
-    | {
-        killSwitchType: "internalKillSwitch";
-      }
-  );
-export type OptionsPrototype = Record<string, OptionConfig>;
+import {
+  Option,
+  OptionContext,
+  KillSwitchType,
+  OptionConfig,
+} from './Option';
 
-export const optionsPrototype: OptionsPrototype = {
-  // Available options:
+const rawOptionConfigs = {
+  // Available options
   list: {
     defaultValue: true,
-    context: "options",
-    killSwitchType: "option",
+    context: OptionContext.Options,
+    killSwitchType: KillSwitchType.Option,
   },
   thread: {
     defaultValue: false,
-    context: "options",
-    killSwitchType: "option",
+    context: OptionContext.Options,
+    killSwitchType: KillSwitchType.Option,
   },
   threadall: {
     defaultValue: true,
-    context: "options",
-    killSwitchType: "option",
+    context: OptionContext.Options,
+    killSwitchType: KillSwitchType.Option,
   },
   fixedtoolbar: {
     defaultValue: false,
-    context: "options",
-    killSwitchType: "option",
+    context: OptionContext.Options,
+    killSwitchType: KillSwitchType.Option,
   },
   redirect: {
     defaultValue: false,
-    context: "options",
-    killSwitchType: "option",
+    context: OptionContext.Options,
+    killSwitchType: KillSwitchType.Option,
   },
   history: {
     defaultValue: false,
-    context: "options",
-    killSwitchType: "option",
+    context: OptionContext.Options,
+    killSwitchType: KillSwitchType.Option,
   },
   increasecontrast: {
     defaultValue: false,
-    context: "options",
-    killSwitchType: "option",
+    context: OptionContext.Options,
+    killSwitchType: KillSwitchType.Option,
   },
   stickysidebarheaders: {
     defaultValue: false,
-    context: "options",
-    killSwitchType: "option",
+    context: OptionContext.Options,
+    killSwitchType: KillSwitchType.Option,
   },
   profileindicator: {
     defaultValue: false,
-    context: "options",
-    killSwitchType: "option",
+    context: OptionContext.Options,
+    killSwitchType: KillSwitchType.Option,
   },
   profileindicatoralt: {
     defaultValue: false,
-    context: "options",
-    killSwitchType: "option",
+    context: OptionContext.Options,
+    killSwitchType: KillSwitchType.Option,
   },
   profileindicatoralt_months: {
     defaultValue: 12,
-    context: "options",
-    killSwitchType: "ignore",
+    context: OptionContext.Options,
+    killSwitchType: KillSwitchType.Ignore,
   },
   ccdarktheme: {
     defaultValue: false,
-    context: "options",
-    killSwitchType: "option",
+    context: OptionContext.Options,
+    killSwitchType: KillSwitchType.Option,
   },
   ccdarktheme_mode: {
-    defaultValue: "switch",
-    context: "options",
-    killSwitchType: "ignore",
+    defaultValue: 'switch',
+    context: OptionContext.Options,
+    killSwitchType: KillSwitchType.Ignore,
   },
   ccforcehidedrawer: {
     defaultValue: false,
-    context: "options",
-    killSwitchType: "option",
+    context: OptionContext.Options,
+    killSwitchType: KillSwitchType.Option,
   },
   // #!if ['chromium', 'chromium_mv3'].includes(browser_target)
   ccdragndropfix: {
     defaultValue: false,
-    context: "options",
-    killSwitchType: "option",
+    context: OptionContext.Options,
+    killSwitchType: KillSwitchType.Option,
   },
   // #!endif
   batchlock: {
     defaultValue: false,
-    context: "options",
-    killSwitchType: "option",
+    context: OptionContext.Options,
+    killSwitchType: KillSwitchType.Option,
   },
   enhancedannouncementsdot: {
     defaultValue: false,
-    context: "options",
-    killSwitchType: "option",
+    context: OptionContext.Options,
+    killSwitchType: KillSwitchType.Option,
   },
   repositionexpandthread: {
     defaultValue: false,
-    context: "options",
-    killSwitchType: "option",
+    context: OptionContext.Options,
+    killSwitchType: KillSwitchType.Option,
   },
   threadlistavatars: {
     defaultValue: false,
-    context: "options",
-    killSwitchType: "option",
+    context: OptionContext.Options,
+    killSwitchType: KillSwitchType.Option,
   },
   autorefreshlist: {
     defaultValue: false,
-    context: "options",
-    killSwitchType: "option",
+    context: OptionContext.Options,
+    killSwitchType: KillSwitchType.Option,
   },
   imagemaxheight: {
     defaultValue: false,
-    context: "options",
-    killSwitchType: "option",
+    context: OptionContext.Options,
+    killSwitchType: KillSwitchType.Option,
   },
   perforumstats: {
     defaultValue: false,
-    context: "options",
-    killSwitchType: "option",
+    context: OptionContext.Options,
+    killSwitchType: KillSwitchType.Option,
   },
   uispacing: {
     defaultValue: false,
-    context: "options",
-    killSwitchType: "option",
+    context: OptionContext.Options,
+    killSwitchType: KillSwitchType.Option,
   },
   flattenthreads: {
     defaultValue: false,
-    context: "options",
-    killSwitchType: "option",
+    context: OptionContext.Options,
+    killSwitchType: KillSwitchType.Option,
   },
   fixpekb269560789: {
     defaultValue: true,
-    context: "options",
-    killSwitchType: "option",
+    context: OptionContext.Options,
+    killSwitchType: KillSwitchType.Option,
   },
 
-  // Experiments:
+  // Experiments
   workflows: {
     defaultValue: false,
-    context: "experiments",
-    killSwitchType: "experiment",
+    context: OptionContext.Experiments,
+    killSwitchType: KillSwitchType.Experiment,
   },
   extrainfo: {
     defaultValue: false,
-    context: "experiments",
-    killSwitchType: "experiment",
+    context: OptionContext.Experiments,
+    killSwitchType: KillSwitchType.Experiment,
   },
 
-  // Internal options:
+  // Internal options
   ccdarktheme_switch_enabled: {
     defaultValue: true,
-    context: "internal",
-    killSwitchType: "ignore",
+    context: OptionContext.Internal,
+    killSwitchType: KillSwitchType.Ignore,
   },
   flattenthreads_switch_enabled: {
     defaultValue: true,
-    context: "internal",
-    killSwitchType: "ignore",
+    context: OptionContext.Internal,
+    killSwitchType: KillSwitchType.Ignore,
   },
 
-  // Internal kill switches:
+  // Internal kill switches
   killswitch_xhrproxy: {
-    context: "internal",
-    killSwitchType: "internalKillSwitch",
+    defaultValue: undefined as any,
+    context: OptionContext.Internal,
+    killSwitchType: KillSwitchType.InternalKillSwitch,
   },
 
-  // Deprecated options:
+  // Deprecated options
   escalatethreads: {
     defaultValue: false,
-    context: "deprecated",
-    killSwitchType: "deprecated",
+    context: OptionContext.Deprecated,
+    killSwitchType: KillSwitchType.Deprecated,
   },
   movethreads: {
     defaultValue: false,
-    context: "deprecated",
-    killSwitchType: "deprecated",
+    context: OptionContext.Deprecated,
+    killSwitchType: KillSwitchType.Deprecated,
   },
   batchduplicate: {
     defaultValue: false,
-    context: "deprecated",
-    killSwitchType: "deprecated",
+    context: OptionContext.Deprecated,
+    killSwitchType: KillSwitchType.Deprecated,
   },
   smei_sortdirection: {
     defaultValue: false,
-    context: "deprecated",
-    killSwitchType: "deprecated",
+    context: OptionContext.Deprecated,
+    killSwitchType: KillSwitchType.Deprecated,
   },
   forcemarkasread: {
     defaultValue: false,
-    context: "deprecated",
-    killSwitchType: "deprecated",
+    context: OptionContext.Deprecated,
+    killSwitchType: KillSwitchType.Deprecated,
   },
   disableunifiedprofiles: {
     defaultValue: false,
-    context: "deprecated",
-    killSwitchType: "deprecated",
+    context: OptionContext.Deprecated,
+    killSwitchType: KillSwitchType.Deprecated,
   },
   nestedreplies: {
     defaultValue: false,
-    context: "deprecated",
-    killSwitchType: "deprecated",
+    context: OptionContext.Deprecated,
+    killSwitchType: KillSwitchType.Deprecated,
   },
   loaddrafts: {
     defaultValue: false,
-    context: "deprecated",
-    killSwitchType: "deprecated",
+    context: OptionContext.Deprecated,
+    killSwitchType: KillSwitchType.Deprecated,
   },
   // #!if ['chromium', 'chromium_mv3'].includes(browser_target)
   blockdrafts: {
     defaultValue: false,
-    context: "deprecated",
-    killSwitchType: "deprecated",
+    context: OptionContext.Deprecated,
+    killSwitchType: KillSwitchType.Deprecated,
   },
   // #!endif
   interopthreadpage: {
     defaultValue: false,
-    context: "deprecated",
-    killSwitchType: "deprecated",
+    context: OptionContext.Deprecated,
+    killSwitchType: KillSwitchType.Deprecated,
   },
   interopthreadpage_mode: {
-    defaultValue: "previous",
-    context: "deprecated",
-    killSwitchType: "ignore",
+    defaultValue: 'previous',
+    context: OptionContext.Deprecated,
+    killSwitchType: KillSwitchType.Ignore,
   },
 };
+
+export type OptionPrototype<T> = Omit<
+  OptionConfig<T>,
+  'codename' | 'optionalPermissions'
+>;
+export const optionsPrototype: Record<
+  string,
+  OptionPrototype<any>
+> = rawOptionConfigs;
+
+export const options = Object.entries(optionsPrototype).map(
+  ([codename, rawOption]) =>
+    new Option({
+      codename,
+      ...rawOption,
+    }),
+);
+
+export const optionsMap = new Map(
+  options.map((option) => [option.codename, option]),
+);
+
+export type OptionCodename = keyof typeof rawOptionConfigs;
+export type OptionValues = {
+  [K in OptionCodename]: (typeof rawOptionConfigs)[K]['defaultValue'];
+};