import { assert, Has, NotHas, IsAny, IsExact } from "conditional-type-checks"; import * as Comlink from "../src/comlink.js"; async function closureSoICanUseAwait() { { function simpleNumberFunction() { return 4; } const proxy = Comlink.wrap(0 as any); assert>(false); const v = proxy(); assert>>(true); } { function simpleObjectFunction() { return { a: 3 }; } const proxy = Comlink.wrap(0 as any); const v = await proxy(); assert>(true); } { async function simpleAsyncFunction() { return { a: 3 }; } const proxy = Comlink.wrap(0 as any); const v = await proxy(); assert>(true); } { function functionWithProxy() { return Comlink.proxy({ a: 3 }); } const proxy = Comlink.wrap(0 as any); const subproxy = await proxy(); const prop = subproxy.a; assert>>(true); } { class X { static staticFunc() { return 4; } private f = 4; public g = 9; sayHi() { return "hi"; } } const proxy = Comlink.wrap(0 as any); assert Promise }>>(true); const instance = await new proxy(); assert Promise }>>(true); assert }>>(true); assert }>>(true); assert>(false); } { const x = { a: 4, b() { return 9; }, c: { d: 3, }, }; const proxy = Comlink.wrap(0 as any); assert>(false); const a = proxy.a; assert>>(true); assert>(false); const b = proxy.b; assert Promise>>(true); assert>(false); const subproxy = proxy.c; assert>>(true); assert>(false); const copy = await proxy.c; assert>(true); } { Comlink.wrap(new MessageChannel().port1); Comlink.expose({}, new MessageChannel().port2); interface Baz { baz: number; method(): number; } class Foo { constructor(cParam: string) { const self = this; assert>(true); } prop1: string = "abc"; proxyProp = Comlink.proxy(new Bar()); methodWithTupleParams(...args: [string] | [number, string]): number { return 123; } methodWithProxiedReturnValue(): Baz & Comlink.ProxyMarked { return Comlink.proxy({ baz: 123, method: () => 123 }); } methodWithProxyParameter(param: Baz & Comlink.ProxyMarked): void {} } class Bar { prop2: string | number = "abc"; method(param: string): number { return 123; } methodWithProxiedReturnValue(): Baz & Comlink.ProxyMarked { return Comlink.proxy({ baz: 123, method: () => 123 }); } } const proxy = Comlink.wrap(Comlink.windowEndpoint(self)); assert>>(true); proxy[Comlink.releaseProxy](); const endp = proxy[Comlink.createEndpoint](); assert>>(true); assert>(false); assert>>(true); const r1 = proxy.methodWithTupleParams(123, "abc"); assert>>(true); const r2 = proxy.methodWithTupleParams("abc"); assert>>(true); assert< IsExact> >(true); assert>(false); assert>>(true); assert>>(true); const r3 = proxy.proxyProp.method("param"); assert>(false); assert>>(true); // @ts-expect-error proxy.proxyProp.method(123); // @ts-expect-error proxy.proxyProp.method(); const r4 = proxy.methodWithProxiedReturnValue(); assert>(false); assert< IsExact>> >(true); const r5 = proxy.proxyProp.methodWithProxiedReturnValue(); assert< IsExact>> >(true); const r6 = (await proxy.methodWithProxiedReturnValue()).baz; assert>(false); assert>>(true); const r7 = (await proxy.methodWithProxiedReturnValue()).method(); assert>(false); assert>>(true); const ProxiedFooClass = Comlink.wrap( Comlink.windowEndpoint(self) ); const inst1 = await new ProxiedFooClass("test"); assert>>(true); inst1[Comlink.releaseProxy](); inst1[Comlink.createEndpoint](); // @ts-expect-error await new ProxiedFooClass(123); // @ts-expect-error await new ProxiedFooClass(); // // Tests for advanced proxy use cases // // Type round trips // This tests that Local is the exact inverse of Remote for objects: assert< IsExact< Comlink.Local>, Comlink.ProxyMarked > >(true); // This tests that Local is the exact inverse of Remote for functions, with one difference: // The local version of a remote function can be either implemented as a sync or async function, // because Remote always makes the function async. assert< IsExact< Comlink.Local string>>, (a: number) => string | Promise > >(true); interface Subscriber { closed?: boolean; next?: (value: T) => void; } interface Unsubscribable { unsubscribe(): void; } /** A Subscribable that can get proxied by Comlink */ interface ProxyableSubscribable extends Comlink.ProxyMarked { subscribe( subscriber: Comlink.Remote & Comlink.ProxyMarked> ): Unsubscribable & Comlink.ProxyMarked; } /** Simple parameter object that gets cloned (not proxied) */ interface Params { textDocument: string; } class Registry { async registerProvider( provider: Comlink.Remote< ((params: Params) => ProxyableSubscribable) & Comlink.ProxyMarked > ) { const resultPromise = provider({ textDocument: "foo" }); assert< IsExact< typeof resultPromise, Promise>> > >(true); const result = await resultPromise; const subscriptionPromise = result.subscribe({ [Comlink.proxyMarker]: true, next: (value) => { assert>(true); }, }); assert< IsExact< typeof subscriptionPromise, Promise> > >(true); const subscriber = Comlink.proxy({ next: (value: string) => console.log(value), }); result.subscribe(subscriber); const r1 = (await subscriptionPromise).unsubscribe(); assert>>(true); } } const proxy2 = Comlink.wrap(Comlink.windowEndpoint(self)); proxy2.registerProvider( // Synchronous callback Comlink.proxy(({ textDocument }: Params) => { const subscribable = Comlink.proxy({ subscribe( subscriber: Comlink.Remote & Comlink.ProxyMarked> ): Unsubscribable & Comlink.ProxyMarked { // Important to test here is that union types (such as Function | undefined) distribute properly // when wrapped in Promises/proxied assert>(false); assert< IsExact< typeof subscriber.closed, Promise | Promise | Promise | undefined > >(true); assert>(false); assert< IsExact< typeof subscriber.next, | Comlink.Remote<(value: string) => void> | Promise | undefined > >(true); // @ts-expect-error subscriber.next(); if (subscriber.next) { // Only checking for presence is not enough, since it could be a Promise // @ts-expect-error subscriber.next(); } if (typeof subscriber.next === "function") { subscriber.next("abc"); } return Comlink.proxy({ unsubscribe() {} }); }, }); assert>(true); return subscribable; }) ); proxy2.registerProvider( // Async callback Comlink.proxy(async ({ textDocument }: Params) => { const subscribable = Comlink.proxy({ subscribe( subscriber: Comlink.Remote & Comlink.ProxyMarked> ): Unsubscribable & Comlink.ProxyMarked { assert>(false); assert< IsExact< typeof subscriber.next, | Comlink.Remote<(value: string) => void> | Promise | undefined > >(true); // Only checking for presence is not enough, since it could be a Promise if (typeof subscriber.next === "function") { subscriber.next("abc"); } return Comlink.proxy({ unsubscribe() {} }); }, }); return subscribable; }) ); } // Transfer handlers { const urlTransferHandler: Comlink.TransferHandler = { canHandle: (val): val is URL => { assert>(true); return val instanceof URL; }, serialize: (url) => { assert>(true); return [url.href, []]; }, deserialize: (str) => { assert>(true); return new URL(str); }, }; Comlink.transferHandlers.set("URL", urlTransferHandler); } }