Skip to content

Latest commit

 

History

History
219 lines (176 loc) · 8.14 KB

ARCHITECTURE.md

File metadata and controls

219 lines (176 loc) · 8.14 KB

Bexer Architecture

This file may be hard to maintain, you may face some outdated info here.
For more genuine API definition look into the sources.

$ tree ./src/src
./src/src/
├── error-event-listeners.js
├── error-event-to-plain-object.js
├── error-notifier.js
├── error-reporter.js
├── error-types.js
├── global-error-event-handlers.js
├── index.js
├── private
│   └── ...
└── utils.js

Variable Names

errorType

May take values defined in error-types.js (e.g. enums EXT_ERROR and PAC_ERROR).

errorEvent

Object passed to a native error listener.
For window.onerror (EXT_ERROR) it's ErrorEvent.
For chrome.proxy.onProxyError (PAC_ERROR) it's:

{
  details: "line: 7: Uncaught Error: This is error, man.",
  error: "net::ERR_PAC_SCRIPT_FAILED",
  fatal: false,
} 

TODO: Add FireFox's browser.proxy.onError.

Components

Objectives: For a given window object install one handler for all error events: error, unhandledrejection and chrome.proxy.onProxyError (PAC script errors).
Handler has a signature: (errorType, errorEvent) => {...}.

// Definitions:
export const installTypedErrorEventListenersOn = ({
  hostWindow = mandatory(),
  nameForDebug = bgName, // Anything that shortly names hostWindow: 'BG' (background), 'PUP' (popup), 'STNGS' (settings).
  typedErrorEventListener = mandatory(),
} = {}, cb) => {...};

nameForDebug — Anything that shortly names hostWindow: 'BG' (background), 'PUP' (popup), 'SET' (settings), etc.

You have to pass typedErrorEventListener on every call of this function.
Wouldn't it be more convenient to install one global error handler for all windows?
Take a look at global-error-event-handlers.js then.

Objectives: Install error handlers that are triggered on errors of all registered window objects.
Each handler has a signature: (errorType, errorEvent) => {...}.

// Definitions:
export const addGlobalHandler = (handler = mandatory()) => {...};
export const installGlobalHandlersOn = (
  { hostWindow, nameForDebug },
  cb,
) => {...};
export const installGlobalHandlersOnAsync = (opts) => {...}; // Same as above but w/o cb and returns Promise.

// Example

// In the background script.
import {
  addGlobalHandler,
  installGlobalHandlersOn,
  installGlobalHandlersOnAsync,
} from './path/to/global-error-event-handlers';
import { EXT_ERROR } from './path/to/error-types';
import { timeouted } from './path/to/utils';

// Expose for non-bg windows.
window.Bexer = {
  installGlobalHandlersOnAsync,
  Utils: { timeouted }, // Optional.
}

addGlobalHandler((errorType, errorEvent) => {
  if (errorType === EXT_ERROR) {
    Sentry.captureException(errorEvent.error); // TODO: not tested.
  } else { /* TODO: figure out how to use Sentry for custom data objects. */ }
}))
installGlobalHandlersOn(
  { hostWindow: window, nameForDebug: 'BG' },
);

// In the popup script.
chrome.runtime.getBackgroundPage(async (bgWindow) => {
  await bgWindow.Bexer.installGlobalHandlersOnAsync(
    { hostWindow: window, nameForDebug: 'PUP' },
  );
  throw new TypeError('This error triggers all global triggers installed.');
  // Or:  
  chrome.tabs.getCurrent(Bexer.Utils.timeouted(() => {

    throw new Error('Timeouted Chrome API callback (caught by Bexer)');

  }));
}

Objectives:

  1. Help in catching errors with Bexer (timeouted).
  2. Help in handling errors of the WebExtensions API (checkChromeError, chromified, getOrDie).
  3. Help in writing more robust code (assert, mandatory, throwIfError).

For a full API see API.md.

Objectives: Given a serializable object describing an error (variable report), open new tab with an error reporter. Error reporter shows the error report to the user and allows to submit it to develoeprs via email. Error reporter demo.

export const openErrorReporter = ({
  toEmail = mandatory(),
  sendReportsInLanguages = mandatory(),
  errorTitle = mandatory(),
  report = mandatory(),
} = {}) => {...};

toEmail — To which email report must be sent after pressing "Send".
sendReportsInLanguages — E.g. ['ru', 'en']. In what language to show message template to the user (based on navigator.language).
errorTitle — Name of the error showed to the user, e.g. Error "TypeError: FOOBAR" occurred.... Don't confuse with error.name (e.g. 'TypeError').
report — Serializable object with required properties: extName, version and payload (error or errorEvent as plain object).

export const makeReport = ({
  errorType,
  serializablePayload = mandatory(),
}) => ({...});

Objectives: Given errorEvent-like object ({ message: String }, { error: String }) and clickHandler return function notifyAboutError to notify about errors.

TODO: Think about other paths for icons, they are not related to the error reporter directly which may be used without notifications at all.

export const installErrorNotifier = ({
  extErrorIconUrl = 'https://error-reporter.github.io/v0/icons/ext-error-128.png',
  pacErrorIconUrl = 'https://error-reporter.github.io/v0/icons/pac-error-128.png',
  maskIconUrl = false,
} = {}) => {
  ...
  const notifyAboutError = async ({
    clickHandler = mandatory(), // Takes no arguments.
    errorEventLike = mandatory(),
    errorType = ErrorTypes.EXT_ERROR,
    notyTitle = 'Extension error',
    context = `${manifest.name} ${manifest.version}`,
    ifSticky = true,
  }) => {...}
  ...
  return { notifyAboutError, ... };
}

Objectives: Covert Error or ErrorEvent object into a plain object which is serializable so it may be passed over a network.

export const errorToPlainObject = (error = mandatory()) => {...}
export const errorEventToPlainObject = (errorEvent = mandatory()) => {...}

Now you have all the parts to combine addGlobalHandlernotifyAboutErrorerrorEventToPlainObjectopenErrorReporter into an error reporting soluition like Bexer defined in the index.js.

Objectives: Provide minimal high-level API for installing a solution that:

  1. Catches all errors.
  2. Shows notifications.
  3. Opens error reporter when clicked.
export {
  Utils,
  installGlobalHandlersOn,
  installGlobalHandlersOnAsync,
  addGlobalHandler,
};
// ...
export const installErrorReporter = ({
  toEmail = mandatory(),
  sendReportsInLanguages = ['en'],
  ifToNotifyAboutAsync = (/* errorType, errorEvent */) => true,
} = {}) => {