refactor: migrate infinite scroll feature to a new architecture

This CL introduces a new architecture for the features source code.

Bug: twpowertools:176
Change-Id: I9abc4df2fb67f9bb0c9114aaffc6916d34f1b7ff
diff --git a/src/common/nodeWatcher/NodeWatcherHandler.ts b/src/common/nodeWatcher/NodeWatcherHandler.ts
new file mode 100644
index 0000000..b6c3a25
--- /dev/null
+++ b/src/common/nodeWatcher/NodeWatcherHandler.ts
@@ -0,0 +1,52 @@
+export enum NodeMutationType {
+  /**
+   * The node was found during initial discovery.
+   */
+  InitialDiscovery,
+  /**
+   * The node has been added.
+   */
+  NewNode,
+  /**
+   * The node was removed
+   */
+  RemovedNode,
+}
+
+export interface NodeMutation {
+  /**
+   * Node being mutated.
+   */
+  node: Node;
+  /**
+   * Which mutation has occurred to the node.
+   */
+  type: NodeMutationType;
+  /**
+   * MutationRecord from which this node mutation has been extracted. It is null
+   * if the type is {@link NodeMutationType.InitialDiscovery}.
+   */
+  mutationRecord: MutationRecord | null;
+}
+
+export interface NodeWatcherHandler {
+  /**
+   * Only node mutations which pass this filter (it returns true) will be passed
+   * to {@link onMutatedNode}.
+   */
+  nodeFilter: (nodeMutation: NodeMutation) => boolean;
+
+  /**
+   * Optional CSS selector used to discover nodes existing prior to the handler
+   * being established. These matching nodes will be evaluated by
+   * {@link onMutatedNode} if they pass {@link nodeFilter}.
+   *
+   * This is useful when watching an node but it has already been created.
+   */
+  initialDiscoverySelector?: string;
+
+  /**
+   * Function which will be called with each of the filtered node mutations.
+   */
+  onMutatedNode: (nodeMutation: NodeMutation) => void;
+}