API
defineProxyService
// Type
export function defineProxyService<TService extends Service, TArgs extends any[]>(
name: string,
init: (...args: TArgs) => TService,
config?: ProxyServiceConfig,
): [registerService: (...args: TArgs) => TService, getService: () => ProxyService<TService>];
See Variants for examples on how to define the real implemenation using classes, objects, etc.
Parameters
name: string
: A unique name for the service. Used to identify which service is being executed.init: (...args: TArgs) => TService
: A function that returns your real service implementation. If args are listed,registerService
will require the same set of argumentsconfig?:
ProxyServiceConfig
: Optional configuration options.
Returns
Returns a tuple of two functions, registerService
and getService
:
registerService
: Registers the real implmenetation of the service in the background scriptgetService
: Returns the registered service when called from the background, or aProxyService
when called from anywhere else.
The ProxyService
uses @webext-core/messenger
to forward messages to the background where the registered, real implementation executes the correct function.
Example
import { defineProxyService } from '@webext-core/storage';
export const [registerService, getService] = defineProxyService(
'TodosRepo',
(idb: Promise<IDBPDatabase>) => ({
async getTodo(id: string) {
return (await idb).get('todos', id);
},
}),
);
ProxyService
// Type
type ProxyService<T> = DeepAsync<T> extends T ? T : DeepAsync<T>;
Because of the async nature of messaging, all functions on ProxyService
's are async, but your real implemenatation does not have to be async.
DeepAsync
// Type
type DeepAsync<TService> = TService extends (...args: any) => any
? ToAsyncFunction<TService>
: TService extends { [key: string]: any }
? {
[fn in keyof TService]: DeepAsync<TService[fn]>;
}
: never;
Converts function to async functions and object's with functions to objects with async functions.
All other types of values are converted to never
Example
interface SomeService {
name: string;
syncFn(): number;
asyncFn(): Promise<number>;
nested: {
name: string;
syncFn(): number;
asyncFn(): Promise<number>;
};
}
type AsyncSomeService = DeepAsync<SomeService>;
// type AsyncSomeService = {
// name: never;
// syncFn(): Promise<number>;
// asyncFn(): Promise<number>;
// nested: {
// name: never;
// syncFn(): Promise<number>;
// asyncFn(): Promise<number>;
// };
// }
flattenPromise
// Type
function flattenPromise<T>(promise: Promise<T>): DeepAsync<T>;
flattenPromise
makes it easier to work with Promise<Dependency>
passed into your services.
It works by using a Proxy
to await the promise internally before calling any methods.
Example
function createTodosRepo(idbPromise: Promise<IDBPDatabase>) {
const idb = flattenPromise(idbPromise);
return {
async create(todo: Todo): Promise<void> {
await (await idbPromise).add('todos', todo);
await idb.add('todos', todo);
},
// ...
};
}