fix: add FetchProxy

This will let us intercept fetch requests (until now we're only proxying
XMLHttpRequest), in order to fix the issues we're experiencing with some
features.

Bug: twpowertools:229
Change-Id: I277473c05479ca39bb6183a51855382124890bde
diff --git a/src/xhrInterceptor/interceptors/InterceptorHandler.adapter.ts b/src/xhrInterceptor/interceptors/InterceptorHandler.adapter.ts
new file mode 100644
index 0000000..197890f
--- /dev/null
+++ b/src/xhrInterceptor/interceptors/InterceptorHandler.adapter.ts
@@ -0,0 +1,27 @@
+import { ProtobufObject } from '../../common/protojs.types';
+import {
+  Interceptor,
+  InterceptorFilter,
+  InterceptorHandlerPort,
+} from './InterceptorHandler.port';
+
+export default class InterceptorHandlerAdapter
+  implements InterceptorHandlerPort
+{
+  constructor(private interceptors: Interceptor[]) {}
+
+  matchInterceptors(filter: InterceptorFilter, url: string): Interceptor[] {
+    return this.interceptors.filter((interceptor) => {
+      return interceptor.intercepts == filter && interceptor.urlRegex.test(url);
+    });
+  }
+  triggerEvent(eventName: string, body: ProtobufObject, id: number): void {
+    const e = new CustomEvent('TWPT_' + eventName, {
+      detail: {
+        body,
+        id,
+      },
+    });
+    window.dispatchEvent(e);
+  }
+}
diff --git a/src/xhrInterceptor/interceptors/InterceptorHandler.port.ts b/src/xhrInterceptor/interceptors/InterceptorHandler.port.ts
new file mode 100644
index 0000000..2bf920a
--- /dev/null
+++ b/src/xhrInterceptor/interceptors/InterceptorHandler.port.ts
@@ -0,0 +1,14 @@
+import { ProtobufObject } from '../../common/protojs.types';
+
+export interface InterceptorHandlerPort {
+  matchInterceptors(filter: InterceptorFilter, url: string): Interceptor[];
+  triggerEvent(eventName: string, body: ProtobufObject, id: number): void;
+}
+
+export interface Interceptor {
+  eventName: string;
+  urlRegex: RegExp;
+  intercepts: InterceptorFilter;
+}
+
+export type InterceptorFilter = 'request' | 'response';
diff --git a/src/xhrInterceptor/interceptors/__mocks__/InterceptorHandler.mock.ts b/src/xhrInterceptor/interceptors/__mocks__/InterceptorHandler.mock.ts
new file mode 100644
index 0000000..ea3501c
--- /dev/null
+++ b/src/xhrInterceptor/interceptors/__mocks__/InterceptorHandler.mock.ts
@@ -0,0 +1,23 @@
+import { jest } from '@jest/globals';
+import { InterceptorHandlerPort } from '../InterceptorHandler.port';
+
+export const matchInterceptorsMock =
+  jest.fn<InterceptorHandlerPort['matchInterceptors']>();
+export const triggerEventMock =
+  jest.fn<InterceptorHandlerPort['triggerEvent']>();
+
+class InterceptorHandlerMock {
+  matchInterceptors(
+    ...args: Parameters<InterceptorHandlerPort['matchInterceptors']>
+  ): ReturnType<InterceptorHandlerPort['matchInterceptors']> {
+    return matchInterceptorsMock(...args);
+  }
+
+  triggerEvent(
+    ...args: Parameters<InterceptorHandlerPort['triggerEvent']>
+  ): ReturnType<InterceptorHandlerPort['triggerEvent']> {
+    return triggerEventMock(...args);
+  }
+}
+
+export { InterceptorHandlerMock };
diff --git a/src/xhrInterceptor/interceptors/interceptors.ts b/src/xhrInterceptor/interceptors/interceptors.ts
new file mode 100644
index 0000000..1454aad
--- /dev/null
+++ b/src/xhrInterceptor/interceptors/interceptors.ts
@@ -0,0 +1,38 @@
+import { Interceptor } from './InterceptorHandler.port';
+
+const interceptors: { interceptors: Interceptor[] } = {
+  interceptors: [
+    {
+      eventName: 'ViewForumRequest',
+      urlRegex: /api\/ViewForum/,
+      intercepts: 'request',
+    },
+    {
+      eventName: 'ViewForumResponse',
+      urlRegex: /api\/ViewForum/,
+      intercepts: 'response',
+    },
+    {
+      eventName: 'CreateMessageRequest',
+      urlRegex: /api\/CreateMessage/,
+      intercepts: 'request',
+    },
+    {
+      eventName: 'ViewUnifiedUserResponse',
+      urlRegex: /api\/ViewUnifiedUser/,
+      intercepts: 'response',
+    },
+    {
+      eventName: 'ListCannedResponsesResponse',
+      urlRegex: /api\/ListCannedResponses/,
+      intercepts: 'response',
+    },
+    {
+      eventName: 'ViewThreadResponse',
+      urlRegex: /api\/ViewThread/,
+      intercepts: 'response',
+    },
+  ],
+};
+
+export default interceptors;