import type * as logtape from "@logtape/logtape"; type AnyFunction = (...args: any[]) => any; type WithPromise = T extends Promise ? T : Promise; type ProtocolLike = { readonly [K in keyof S]: AnyFunction; }; type NoReplyProtocolLike = { readonly [K in keyof S]: (...args: any[]) => void; }; /** The variant of a protocol that you implement: the type of `Messenger.handlers`. * A protocol based on S except that all return types may also be Promises. */ export type ProtocolImpl> = { readonly [K in keyof S]: (...args: Parameters) => ReturnType | WithPromise>; }; /** The variant of a protocol that you call; the type of `Messenger.call`. * A protocol based on S except that all return types are Promises. */ export type AsyncProtocol> = { readonly [K in keyof S]: (...args: Parameters) => WithPromise>; }; /** The variant of a protocol for "noreply" calls; the type of `Messenger.noreply`. * A protocol based on S except that all return types are `void`. */ export type NoReplyProtocol> = { readonly [K in keyof S]: (...args: Parameters) => void; }; /** Error returned from an async request when the Messenger on either side has been closed. */ export declare class PortClosedError extends Error { constructor(message: string); } /** Error returned from an async request when the other side has no handler for it. */ export declare class UnhandledRequestError extends Error { constructor(message: string); } export type Refable = object; type Owner = number & { __brand: 'Owner'; }; type RefID = number & { __brand: 'RefID'; }; /** A reference to an object owned by the implementor of a Protocol. */ export interface Ref<_T extends Refable> { readonly $refOwner: Owner; readonly $refID: RefID; } /** A simple RPC mechanism for use with Workers, Shared Workers, MessagePorts, BroadcastChannels. * It allows remote APIs to be declared as regular TypeScript interfaces. Handlers can be * implemented as a regular object, and calls can be made via that interface, with async * semantics (return types are converted to Promises.) * * The type parameter `S` is an interface declaring the RPC methods that can be _sent_. * The `call` and `noreply` properties expose objects on which you can call these methods. * * The type parameter `R` declares the RPC methods that can be _received_. * You must set the `handlers` property to an object matching this interface, which * will be called when a message arrives. * * Obviously a Messenger communicates with a Messenger. * * If no methods will be sent in one direction, use `object` to represent the empty interface type. * * Don't use `Promise` in the return types; that will be added for you. * * Function parameters and return types can be any types supported by JavaScript's * {@ref structuredClone} function. You should probably not pass custom class instances * or objects with prototypes, since they will be stripped into POJOs. * * You can also use the special type Ref in the API, as an opaque reference to an object * of type T that's owned by the API's implementor (a handler method.) * - A handler method can return a reference by calling {@ref createRef}, passing in the real `T` * object and returning the `Ref`. * - A subsequent handler call can take a `Ref` as a parameter, and resolve it back to the * original object by calling {@ref resolveRef}. * - When a reference is no longer needed, call {@ref forgetRef}. This makes that `Ref` invalid * and allows the real object to be garbage-collected. (Any further call to {@link resolveRef} * will throw an exception.) */ export declare class Messenger, R extends ProtocolLike> { #private; private readonly port; readonly name?: string | undefined; private readonly isBroadcast; /** Creates a Web Worker and returns a Messenger that sends to it. */ static withNewWorker, R extends ProtocolLike = object>(script: URL, name?: string): Messenger; /** From a Worker script, connects to its creator. */ static inWorker, R extends ProtocolLike>(name?: string): Messenger; /** Creates a Messenger for communicating over an existing MessagePort. */ static withPort, R extends ProtocolLike>(port: MessagePort, name?: string): Messenger; /** Creates a MessageChannel and returns a pair of Messengers that communicate over * its MessagePorts. */ static withChannel, R extends ProtocolLike>(name1?: string, name2?: string): [Messenger, Messenger]; /** Connects to a SharedWorker that runs the script at the given URL, with the given name. * If a SharedWorker with the same URL and name is running, creates a new connection to it. * Otherwise it creates a new SharedWorker and connects. * * Within the SharedWorker script, use {@link SharedWorkerManager} to handle connections. * @parameter script The URL of the worker's script. It's best to create this relative to * `import.meta.url`, e.g.: `new URL("worker.js", import.meta.url)` * @parameter name A string identifying a particular SharedWorker instance. Can be used to * create multiple independent workers running the same code.*/ static withSharedWorker, R extends ProtocolLike = object>(script: URL, options?: WorkerOptions): Messenger; /** Creates a BroadcastChannel with the given name. When a message is sent by an instance, * it will be received by all other instances with the same name, in this context and any * other contexts with the same origin. * The same protocol is used for sending and receiving. * Replies are not possible; all return types must be `void`, and you must send messages * `noreply`. */ static withBroadcastChannel>(channelName: string): Messenger; private constructor(); private setSharedWorker; /** Set a LogTape logger here to enable logging. */ logger?: logtape.Logger; /** An object defining the implementations of the received functions (`R`) */ handlers: ProtocolImpl; toString(): string; /** Opens the connection to the other side and begins receiving requests. */ open(): void; /** True if the Messenger has been opened but not yet closed. */ get isOpen(): boolean; /** An object that implements the sending schema, with all return types wrapped in Promises. * Calling methods of this object sends an async RPC call to the other side. */ readonly call: AsyncProtocol; /** An object that implements the sending schema, except all return types are `void`. * Requests sent this way are tagged `noreply`, and the other side won't send a reply. * (Any reply returned by the other side's handler is discarded without being sent.) */ readonly noreply: NoReplyProtocol; /** Wraps an arbitrary object in a `Ref` that can be returned from a call. * (Multiple calls to this with the same object are fine; they return an equivalent Ref.) */ createRef(value: T): Ref; /** Given a Ref created earlier by `createRef`, returns the object it wraps. */ resolveRef(ref: Ref): T; /** Returns true if a Ref created by me is still valid (not forgotten.) */ isValidLocalRef(ref: Ref): boolean; /** Destroys a Ref, making the source object garbage-collectable. You can pass: * - A local object that you're previously called createRef on * - A Ref referring to a local object * - A Ref received from the peer referring to a remote object (will be forwarded to the peer.) */ forgetRef(valueOrRef: Ref | T): void; private assertMyRef; /** Implementation of sending a request. */ private call_; /** Implementation of sending a noreply request. */ private callNoReply_; /** Sends a message through the port. */ private postMessage; /** Handles a message from the port. */ private handleMessage; /** Handles an incoming Request. */ private handleRequest; /** Delivers a response to the peer. */ private sendResponse; /** Handles an incoming Response. */ private handleResponse; /** Adds a handler that will be called when this Messenger initiates closing (from either side.) */ onClose(handler: (messenger: Messenger) => void): void; /** Adds a handler that will be called when this Messenger's port disconnects. */ onDisconnect(handler: (messenger: Messenger) => void): void; /** Initiates an orderly close of both sides. After this you may not send any new requests, * but you can still receive responses to earlier requests, and your async request handlers * may finish sending responses. After all that, the Messengers will both disconnect. */ close(): void; /** Changes state to Closing and calls onClose functions. */ private closing; /** Handles a 'closing' meta-message from the peer. */ private handlePeerClosing; /** Returns true if it's finally OK to close the port. */ private okToDisconnect; private maybeDisconnect; /** Abruptly disconnects the port. Use {@ref close} instead for an orderly shutdown. */ disconnect(): void; /** Called when the port sends a 'close' event. */ private handlePortClosed; } export interface SharedWorkerManagerDelegate, R extends ProtocolLike> { /** A client has connected to this shared worker. * You must initialize the new Messenger's handlers. */ onConnect(messenger: Messenger): void; /** A client is closing a connection. */ onClose?(messenger: Messenger): void; /** A connection has disconnected. */ onDisconnect?(messenger: Messenger): void; } /** Within a SharedWorker, waits for connections and handles requests from them. */ export declare class SharedWorkerManager, S extends ProtocolLike = object> { #private; readonly delegate: SharedWorkerManagerDelegate; readonly name?: string | undefined; constructor(delegate: SharedWorkerManagerDelegate, name?: string | undefined); /** Begins listening for connections. */ open(): void; /** Stops listening for connections, and closes all open connections. */ close(): void; /** An array of the Messengers of all open connections. */ get connections(): Messenger[]; private handleConnection; /** Explicitly connects a Messenger. * Can be called directly in "fake" mode, i.e. when not running in a real SharedWorker. */ connectMessenger(messenger: Messenger): void; } export {};