swarpc
Version:
Full type-safe RPC library for service worker -- move things off of the UI thread with ease!
384 lines • 14.9 kB
TypeScript
/**
* @module
* @mergeModuleWith <project>
*/
import { type Type } from "arktype";
import { RequestBoundLogger } from "./log.js";
/**
* A procedure declaration
*/
export type Procedure<I extends Type, P extends Type, S extends Type> = {
/**
* ArkType type for the input (first argument) of the procedure, when calling it from the client.
*/
input: I;
/**
* ArkType type for the data as the first argument given to the `onProgress` callback
* when calling the procedure from the client.
*/
progress: P;
/**
* ArkType type for the output (return value) of the procedure, when calling it from the client.
*/
success: S;
/**
* When should the procedure automatically add ArrayBuffers and other transferable objects
* to the [transfer list](https://developer.mozilla.org/en-US/docs/Web/API/DedicatedWorkerGlobalScope/postMessage#transfer)
* when sending messages, both from the client to the server and vice versa.
*
* Transferring objects can improve performance by avoiding copies of large objects,
* but _moves_ them to the other context, meaning that they cannot be used in the original context after being sent.
*
* 'output-only' by default: only transferables sent from the server to the client will be transferred.
*/
autotransfer?: "always" | "never" | "output-only";
};
/**
* A promise that you can cancel by calling `.cancel(reason)` on it:
*
* ```js
* const { request, cancel } = client.runProcedure.cancelable(input, onProgress)
* setTimeout(() => cancel("Cancelled by user"), 1000)
* const result = await request
* ```
*/
export type CancelablePromise<T = unknown> = {
request: Promise<T>;
/**
* Abort the request.
* @param reason The reason for cancelling the request.
*/
cancel: (reason: string) => void;
};
/**
* An implementation of a procedure
*/
export type ProcedureImplementation<I extends Type, P extends Type, S extends Type> = (
/**
* Input data for the procedure
*/
input: I["inferOut"],
/**
* Callback to call with progress updates.
*/
onProgress: (progress: P["inferIn"]) => void,
/**
* Additional tools useful when implementing the procedure.
*/
tools: {
/**
* AbortSignal that can be used to handle request cancellation -- see [Make cancellable requests](https://gwennlbh.github.io/swarpc/docs/#make-cancelable-requests)
*/
abortSignal?: AbortSignal;
/**
* Logger instance to use for logging messages related to this procedure call, using the same format as SWARPC's built-in logging.
*/
logger: RequestBoundLogger;
/**
* ID of the Node the request is being processed on.
*/
nodeId: string;
}) => Promise<S["inferIn"]>;
/**
* Declarations of procedures by name.
*
* An example of declaring procedures:
* {@includeCode ../example/src/lib/procedures.ts}
*/
export type ProceduresMap = Record<string, Procedure<Type, Type, Type>>;
/**
* Implementations of procedures by name
*/
export type ImplementationsMap<Procedures extends ProceduresMap> = {
[F in keyof Procedures]: ProcedureImplementation<Procedures[F]["input"], Procedures[F]["progress"], Procedures[F]["success"]>;
};
/**
* Declaration of hooks to run on messages received from the server
*/
export type Hooks<Procedures extends ProceduresMap> = {
/**
* Called when a procedure call has been successful.
*/
success?: <Procedure extends keyof ProceduresMap>(procedure: Procedure, data: Procedures[Procedure]["success"]["inferOut"]) => void;
/**
* Called when a procedure call has failed.
*/
error?: <Procedure extends keyof ProceduresMap>(procedure: Procedure, error: Error) => void;
/**
* Called when a procedure call sends progress updates.
*/
progress?: <Procedure extends keyof ProceduresMap>(procedure: Procedure, data: Procedures[Procedure]["progress"]["inferOut"]) => void;
};
export declare const PayloadInitializeSchema: import("arktype/internal/methods/object.ts").ObjectType<{
by: "sw&rpc";
functionName: "#initialize";
isInitializeRequest: true;
localStorageData: Record<string, unknown>;
nodeId: string;
}, {}>;
export type PayloadInitialize = typeof PayloadInitializeSchema.infer;
/**
* @source
*/
export declare const PayloadHeaderSchema: import("arktype").Generic<[["Name", string]], {
readonly by: "\"sw&rpc\"";
readonly functionName: "Name";
readonly requestId: "string >= 1";
}, {}, {}>;
export type PayloadHeader<PM extends ProceduresMap, Name extends keyof PM = keyof PM> = {
by: "sw&rpc";
functionName: Name & string;
requestId: string;
};
/**
* @source
*/
export declare const PayloadCoreSchema: import("arktype").Generic<[["I", unknown], ["P", unknown], ["S", unknown]], {
readonly "input?": "I";
readonly "progress?": "P";
readonly "result?": "S";
readonly "abort?": {
readonly reason: "string";
};
readonly "error?": {
readonly message: "string";
};
}, {}, {}>;
export type PayloadCore<PM extends ProceduresMap, Name extends keyof PM = keyof PM> = {
input: PM[Name]["input"]["inferOut"];
} | {
progress: PM[Name]["progress"]["inferOut"];
} | {
result: PM[Name]["success"]["inferOut"];
} | {
abort: {
reason: string;
};
} | {
error: {
message: string;
};
};
/**
* @source
*/
export declare const PayloadSchema: import("arktype").Generic<[["Name", string], ["I", unknown], ["P", unknown], ["S", unknown]], readonly [readonly ["PayloadHeaderSchema<Name>", "&", "PayloadCoreSchema<I, P, S>"], "|", "PayloadInitializeSchema"], {
PayloadCoreSchema: import("arktype/internal/scope.ts").bindGenericToScope<import("@ark/schema").GenericAst<[["I", unknown], ["P", unknown], ["S", unknown]], {
readonly "input?": "I";
readonly "progress?": "P";
readonly "result?": "S";
readonly "abort?": {
readonly reason: "string";
};
readonly "error?": {
readonly message: "string";
};
}, {}, {}>, {
PayloadCoreSchema: import("@ark/schema").GenericAst<[["I", unknown], ["P", unknown], ["S", unknown]], {
readonly "input?": "I";
readonly "progress?": "P";
readonly "result?": "S";
readonly "abort?": {
readonly reason: "string";
};
readonly "error?": {
readonly message: "string";
};
}, {}, {}>;
PayloadHeaderSchema: import("@ark/schema").GenericAst<[["Name", string]], {
readonly by: "\"sw&rpc\"";
readonly functionName: "Name";
readonly requestId: "string >= 1";
}, {}, {}>;
PayloadInitializeSchema: import("arktype/internal/methods/object.ts").ObjectType<{
by: "sw&rpc";
functionName: "#initialize";
isInitializeRequest: true;
localStorageData: Record<string, unknown>;
nodeId: string;
}, {}> & {
readonly " brand": [import("arktype/internal/methods/object.ts").ObjectType<{
by: "sw&rpc";
functionName: "#initialize";
isInitializeRequest: true;
localStorageData: Record<string, unknown>;
nodeId: string;
}, {}>, "unparsed"];
};
} & {}>;
PayloadHeaderSchema: import("arktype/internal/scope.ts").bindGenericToScope<import("@ark/schema").GenericAst<[["Name", string]], {
readonly by: "\"sw&rpc\"";
readonly functionName: "Name";
readonly requestId: "string >= 1";
}, {}, {}>, {
PayloadCoreSchema: import("@ark/schema").GenericAst<[["I", unknown], ["P", unknown], ["S", unknown]], {
readonly "input?": "I";
readonly "progress?": "P";
readonly "result?": "S";
readonly "abort?": {
readonly reason: "string";
};
readonly "error?": {
readonly message: "string";
};
}, {}, {}>;
PayloadHeaderSchema: import("@ark/schema").GenericAst<[["Name", string]], {
readonly by: "\"sw&rpc\"";
readonly functionName: "Name";
readonly requestId: "string >= 1";
}, {}, {}>;
PayloadInitializeSchema: import("arktype/internal/methods/object.ts").ObjectType<{
by: "sw&rpc";
functionName: "#initialize";
isInitializeRequest: true;
localStorageData: Record<string, unknown>;
nodeId: string;
}, {}> & {
readonly " brand": [import("arktype/internal/methods/object.ts").ObjectType<{
by: "sw&rpc";
functionName: "#initialize";
isInitializeRequest: true;
localStorageData: Record<string, unknown>;
nodeId: string;
}, {}>, "unparsed"];
};
} & {}>;
PayloadInitializeSchema: {
by: "sw&rpc";
functionName: "#initialize";
isInitializeRequest: true;
localStorageData: Record<string, unknown>;
nodeId: string;
};
}, {
PayloadCoreSchema: import("arktype/internal/scope.ts").bindGenericToScope<import("@ark/schema").GenericAst<[["I", unknown], ["P", unknown], ["S", unknown]], {
readonly "input?": "I";
readonly "progress?": "P";
readonly "result?": "S";
readonly "abort?": {
readonly reason: "string";
};
readonly "error?": {
readonly message: "string";
};
}, {}, {}>, {
PayloadCoreSchema: import("@ark/schema").GenericAst<[["I", unknown], ["P", unknown], ["S", unknown]], {
readonly "input?": "I";
readonly "progress?": "P";
readonly "result?": "S";
readonly "abort?": {
readonly reason: "string";
};
readonly "error?": {
readonly message: "string";
};
}, {}, {}>;
PayloadHeaderSchema: import("@ark/schema").GenericAst<[["Name", string]], {
readonly by: "\"sw&rpc\"";
readonly functionName: "Name";
readonly requestId: "string >= 1";
}, {}, {}>;
PayloadInitializeSchema: import("arktype/internal/methods/object.ts").ObjectType<{
by: "sw&rpc";
functionName: "#initialize";
isInitializeRequest: true;
localStorageData: Record<string, unknown>;
nodeId: string;
}, {}> & {
readonly " brand": [import("arktype/internal/methods/object.ts").ObjectType<{
by: "sw&rpc";
functionName: "#initialize";
isInitializeRequest: true;
localStorageData: Record<string, unknown>;
nodeId: string;
}, {}>, "unparsed"];
};
} & {}>;
PayloadHeaderSchema: import("arktype/internal/scope.ts").bindGenericToScope<import("@ark/schema").GenericAst<[["Name", string]], {
readonly by: "\"sw&rpc\"";
readonly functionName: "Name";
readonly requestId: "string >= 1";
}, {}, {}>, {
PayloadCoreSchema: import("@ark/schema").GenericAst<[["I", unknown], ["P", unknown], ["S", unknown]], {
readonly "input?": "I";
readonly "progress?": "P";
readonly "result?": "S";
readonly "abort?": {
readonly reason: "string";
};
readonly "error?": {
readonly message: "string";
};
}, {}, {}>;
PayloadHeaderSchema: import("@ark/schema").GenericAst<[["Name", string]], {
readonly by: "\"sw&rpc\"";
readonly functionName: "Name";
readonly requestId: "string >= 1";
}, {}, {}>;
PayloadInitializeSchema: import("arktype/internal/methods/object.ts").ObjectType<{
by: "sw&rpc";
functionName: "#initialize";
isInitializeRequest: true;
localStorageData: Record<string, unknown>;
nodeId: string;
}, {}> & {
readonly " brand": [import("arktype/internal/methods/object.ts").ObjectType<{
by: "sw&rpc";
functionName: "#initialize";
isInitializeRequest: true;
localStorageData: Record<string, unknown>;
nodeId: string;
}, {}>, "unparsed"];
};
} & {}>;
PayloadInitializeSchema: {
by: "sw&rpc";
functionName: "#initialize";
isInitializeRequest: true;
localStorageData: Record<string, unknown>;
nodeId: string;
};
}>;
/**
* The effective payload as sent by the server to the client
*/
export type Payload<PM extends ProceduresMap, Name extends keyof PM = keyof PM> = (PayloadHeader<PM, Name> & PayloadCore<PM, Name>) | PayloadInitialize;
/**
* A procedure's corresponding method on the client instance -- used to call the procedure. If you want to be able to cancel the request, you can use the `cancelable` method instead of running the procedure directly.
*/
export type ClientMethod<P extends Procedure<Type, Type, Type>> = ((input: P["input"]["inferIn"], onProgress?: (progress: P["progress"]["inferOut"]) => void) => Promise<P["success"]["inferOut"]>) & {
/**
* A method that returns a `CancelablePromise`. Cancel it by calling `.cancel(reason)` on it, and wait for the request to resolve by awaiting the `request` property on the returned object.
*/
cancelable: (input: P["input"]["inferIn"], onProgress?: (progress: P["progress"]["inferOut"]) => void, requestId?: string) => CancelablePromise<P["success"]["inferOut"]>;
/**
* Send the request to specific nodes, or all nodes.
* Returns an array of results, one for each node the request was sent to.
* Each result is a {@link PromiseSettledResult}, with also an additional property, the node ID of the request
*/
broadcast: (input: P["input"]["inferIn"], onProgress?: (
/** Map of node IDs to their progress updates */
progresses: Map<string, P["progress"]["inferOut"]>) => void,
/** Number of nodes to send the request to. Leave undefined to send to all nodes */
nodes?: number) => Promise<Array<PromiseSettledResult<P["success"]["inferOut"]> & {
node: string;
}>>;
};
/**
* Symbol used as the key for the procedures map on the server instance
* @internal
* @source
*/
export declare const zImplementations: unique symbol;
/**
* Symbol used as the key for the procedures map on instances
* @internal
* @source
*/
export declare const zProcedures: unique symbol;
export type WorkerConstructor<T extends Worker | SharedWorker = Worker | SharedWorker> = {
new (opts?: {
name?: string;
}): T;
};
//# sourceMappingURL=types.d.ts.map