1 | import { assert, Has, NotHas, IsAny, IsExact } from "conditional-type-checks";
|
2 |
|
3 | import * as Comlink from "../src/comlink.js";
|
4 |
|
5 | async function closureSoICanUseAwait() {
|
6 | {
|
7 | function simpleNumberFunction() {
|
8 | return 4;
|
9 | }
|
10 |
|
11 | const proxy = Comlink.wrap<typeof simpleNumberFunction>(0 as any);
|
12 | assert<IsAny<typeof proxy>>(false);
|
13 | const v = proxy();
|
14 | assert<Has<typeof v, Promise<number>>>(true);
|
15 | }
|
16 |
|
17 | {
|
18 | function simpleObjectFunction() {
|
19 | return { a: 3 };
|
20 | }
|
21 |
|
22 | const proxy = Comlink.wrap<typeof simpleObjectFunction>(0 as any);
|
23 | const v = await proxy();
|
24 | assert<Has<typeof v, { a: number }>>(true);
|
25 | }
|
26 |
|
27 | {
|
28 | async function simpleAsyncFunction() {
|
29 | return { a: 3 };
|
30 | }
|
31 |
|
32 | const proxy = Comlink.wrap<typeof simpleAsyncFunction>(0 as any);
|
33 | const v = await proxy();
|
34 | assert<Has<typeof v, { a: number }>>(true);
|
35 | }
|
36 |
|
37 | {
|
38 | function functionWithProxy() {
|
39 | return Comlink.proxy({ a: 3 });
|
40 | }
|
41 |
|
42 | const proxy = Comlink.wrap<typeof functionWithProxy>(0 as any);
|
43 | const subproxy = await proxy();
|
44 | const prop = subproxy.a;
|
45 | assert<Has<typeof prop, Promise<number>>>(true);
|
46 | }
|
47 |
|
48 | {
|
49 | class X {
|
50 | static staticFunc() {
|
51 | return 4;
|
52 | }
|
53 | private f = 4;
|
54 | public g = 9;
|
55 | sayHi() {
|
56 | return "hi";
|
57 | }
|
58 | }
|
59 |
|
60 | const proxy = Comlink.wrap<typeof X>(0 as any);
|
61 | assert<Has<typeof proxy, { staticFunc: () => Promise<number> }>>(true);
|
62 | const instance = await new proxy();
|
63 | assert<Has<typeof instance, { sayHi: () => Promise<string> }>>(true);
|
64 | assert<Has<typeof instance, { g: Promise<number> }>>(true);
|
65 | assert<NotHas<typeof instance, { f: Promise<number> }>>(true);
|
66 | assert<IsAny<typeof instance>>(false);
|
67 | }
|
68 |
|
69 | {
|
70 | const x = {
|
71 | a: 4,
|
72 | b() {
|
73 | return 9;
|
74 | },
|
75 | c: {
|
76 | d: 3,
|
77 | },
|
78 | };
|
79 |
|
80 | const proxy = Comlink.wrap<typeof x>(0 as any);
|
81 | assert<IsAny<typeof proxy>>(false);
|
82 | const a = proxy.a;
|
83 | assert<Has<typeof a, Promise<number>>>(true);
|
84 | assert<IsAny<typeof a>>(false);
|
85 | const b = proxy.b;
|
86 | assert<Has<typeof b, () => Promise<number>>>(true);
|
87 | assert<IsAny<typeof b>>(false);
|
88 | const subproxy = proxy.c;
|
89 | assert<Has<typeof subproxy, Promise<{ d: number }>>>(true);
|
90 | assert<IsAny<typeof subproxy>>(false);
|
91 | const copy = await proxy.c;
|
92 | assert<Has<typeof copy, { d: number }>>(true);
|
93 | }
|
94 |
|
95 | {
|
96 | Comlink.wrap(new MessageChannel().port1);
|
97 | Comlink.expose({}, new MessageChannel().port2);
|
98 |
|
99 | interface Baz {
|
100 | baz: number;
|
101 | method(): number;
|
102 | }
|
103 |
|
104 | class Foo {
|
105 | constructor(cParam: string) {
|
106 | const self = this;
|
107 | assert<IsExact<typeof self.proxyProp, Bar & Comlink.ProxyMarked>>(true);
|
108 | }
|
109 | prop1: string = "abc";
|
110 | proxyProp = Comlink.proxy(new Bar());
|
111 | methodWithTupleParams(...args: [string] | [number, string]): number {
|
112 | return 123;
|
113 | }
|
114 | methodWithProxiedReturnValue(): Baz & Comlink.ProxyMarked {
|
115 | return Comlink.proxy({ baz: 123, method: () => 123 });
|
116 | }
|
117 | methodWithProxyParameter(param: Baz & Comlink.ProxyMarked): void {}
|
118 | }
|
119 |
|
120 | class Bar {
|
121 | prop2: string | number = "abc";
|
122 | method(param: string): number {
|
123 | return 123;
|
124 | }
|
125 | methodWithProxiedReturnValue(): Baz & Comlink.ProxyMarked {
|
126 | return Comlink.proxy({ baz: 123, method: () => 123 });
|
127 | }
|
128 | }
|
129 | const proxy = Comlink.wrap<Foo>(Comlink.windowEndpoint(self));
|
130 | assert<IsExact<typeof proxy, Comlink.Remote<Foo>>>(true);
|
131 |
|
132 | proxy[Comlink.releaseProxy]();
|
133 | const endp = proxy[Comlink.createEndpoint]();
|
134 | assert<IsExact<typeof endp, Promise<MessagePort>>>(true);
|
135 |
|
136 | assert<IsAny<typeof proxy.prop1>>(false);
|
137 | assert<Has<typeof proxy.prop1, Promise<string>>>(true);
|
138 |
|
139 | const r1 = proxy.methodWithTupleParams(123, "abc");
|
140 | assert<IsExact<typeof r1, Promise<number>>>(true);
|
141 |
|
142 | const r2 = proxy.methodWithTupleParams("abc");
|
143 | assert<IsExact<typeof r2, Promise<number>>>(true);
|
144 |
|
145 | assert<
|
146 | IsExact<typeof proxy.proxyProp, Comlink.Remote<Bar & Comlink.ProxyMarked>>
|
147 | >(true);
|
148 |
|
149 | assert<IsAny<typeof proxy.proxyProp.prop2>>(false);
|
150 | assert<Has<typeof proxy.proxyProp.prop2, Promise<string>>>(true);
|
151 | assert<Has<typeof proxy.proxyProp.prop2, Promise<number>>>(true);
|
152 |
|
153 | const r3 = proxy.proxyProp.method("param");
|
154 | assert<IsAny<typeof r3>>(false);
|
155 | assert<Has<typeof r3, Promise<number>>>(true);
|
156 |
|
157 |
|
158 | proxy.proxyProp.method(123);
|
159 |
|
160 |
|
161 | proxy.proxyProp.method();
|
162 |
|
163 | const r4 = proxy.methodWithProxiedReturnValue();
|
164 | assert<IsAny<typeof r4>>(false);
|
165 | assert<
|
166 | IsExact<typeof r4, Promise<Comlink.Remote<Baz & Comlink.ProxyMarked>>>
|
167 | >(true);
|
168 |
|
169 | const r5 = proxy.proxyProp.methodWithProxiedReturnValue();
|
170 | assert<
|
171 | IsExact<typeof r5, Promise<Comlink.Remote<Baz & Comlink.ProxyMarked>>>
|
172 | >(true);
|
173 |
|
174 | const r6 = (await proxy.methodWithProxiedReturnValue()).baz;
|
175 | assert<IsAny<typeof r6>>(false);
|
176 | assert<Has<typeof r6, Promise<number>>>(true);
|
177 |
|
178 | const r7 = (await proxy.methodWithProxiedReturnValue()).method();
|
179 | assert<IsAny<typeof r7>>(false);
|
180 | assert<Has<typeof r7, Promise<number>>>(true);
|
181 |
|
182 | const ProxiedFooClass = Comlink.wrap<typeof Foo>(
|
183 | Comlink.windowEndpoint(self)
|
184 | );
|
185 | const inst1 = await new ProxiedFooClass("test");
|
186 | assert<IsExact<typeof inst1, Comlink.Remote<Foo>>>(true);
|
187 | inst1[Comlink.releaseProxy]();
|
188 | inst1[Comlink.createEndpoint]();
|
189 |
|
190 |
|
191 | await new ProxiedFooClass(123);
|
192 |
|
193 |
|
194 | await new ProxiedFooClass();
|
195 |
|
196 |
|
197 |
|
198 |
|
199 |
|
200 |
|
201 |
|
202 | assert<
|
203 | IsExact<
|
204 | Comlink.Local<Comlink.Remote<Comlink.ProxyMarked>>,
|
205 | Comlink.ProxyMarked
|
206 | >
|
207 | >(true);
|
208 |
|
209 |
|
210 |
|
211 | assert<
|
212 | IsExact<
|
213 | Comlink.Local<Comlink.Remote<(a: number) => string>>,
|
214 | (a: number) => string | Promise<string>
|
215 | >
|
216 | >(true);
|
217 |
|
218 | interface Subscriber<T> {
|
219 | closed?: boolean;
|
220 | next?: (value: T) => void;
|
221 | }
|
222 | interface Unsubscribable {
|
223 | unsubscribe(): void;
|
224 | }
|
225 |
|
226 | interface ProxyableSubscribable<T> extends Comlink.ProxyMarked {
|
227 | subscribe(
|
228 | subscriber: Comlink.Remote<Subscriber<T> & Comlink.ProxyMarked>
|
229 | ): Unsubscribable & Comlink.ProxyMarked;
|
230 | }
|
231 |
|
232 |
|
233 | interface Params {
|
234 | textDocument: string;
|
235 | }
|
236 |
|
237 | class Registry {
|
238 | async registerProvider(
|
239 | provider: Comlink.Remote<
|
240 | ((params: Params) => ProxyableSubscribable<string>) &
|
241 | Comlink.ProxyMarked
|
242 | >
|
243 | ) {
|
244 | const resultPromise = provider({ textDocument: "foo" });
|
245 | assert<
|
246 | IsExact<
|
247 | typeof resultPromise,
|
248 | Promise<Comlink.Remote<ProxyableSubscribable<string>>>
|
249 | >
|
250 | >(true);
|
251 | const result = await resultPromise;
|
252 |
|
253 | const subscriptionPromise = result.subscribe({
|
254 | [Comlink.proxyMarker]: true,
|
255 | next: (value) => {
|
256 | assert<IsExact<typeof value, string>>(true);
|
257 | },
|
258 | });
|
259 | assert<
|
260 | IsExact<
|
261 | typeof subscriptionPromise,
|
262 | Promise<Comlink.Remote<Unsubscribable & Comlink.ProxyMarked>>
|
263 | >
|
264 | >(true);
|
265 | const subscriber = Comlink.proxy({
|
266 | next: (value: string) => console.log(value),
|
267 | });
|
268 | result.subscribe(subscriber);
|
269 |
|
270 | const r1 = (await subscriptionPromise).unsubscribe();
|
271 | assert<IsExact<typeof r1, Promise<void>>>(true);
|
272 | }
|
273 | }
|
274 | const proxy2 = Comlink.wrap<Registry>(Comlink.windowEndpoint(self));
|
275 |
|
276 | proxy2.registerProvider(
|
277 |
|
278 | Comlink.proxy(({ textDocument }: Params) => {
|
279 | const subscribable = Comlink.proxy({
|
280 | subscribe(
|
281 | subscriber: Comlink.Remote<Subscriber<string> & Comlink.ProxyMarked>
|
282 | ): Unsubscribable & Comlink.ProxyMarked {
|
283 |
|
284 |
|
285 |
|
286 | assert<IsAny<typeof subscriber.closed>>(false);
|
287 | assert<
|
288 | IsExact<
|
289 | typeof subscriber.closed,
|
290 | Promise<true> | Promise<false> | Promise<undefined> | undefined
|
291 | >
|
292 | >(true);
|
293 |
|
294 | assert<IsAny<typeof subscriber.next>>(false);
|
295 | assert<
|
296 | IsExact<
|
297 | typeof subscriber.next,
|
298 | | Comlink.Remote<(value: string) => void>
|
299 | | Promise<undefined>
|
300 | | undefined
|
301 | >
|
302 | >(true);
|
303 |
|
304 |
|
305 | subscriber.next();
|
306 |
|
307 | if (subscriber.next) {
|
308 |
|
309 |
|
310 | subscriber.next();
|
311 | }
|
312 |
|
313 | if (typeof subscriber.next === "function") {
|
314 | subscriber.next("abc");
|
315 | }
|
316 |
|
317 | return Comlink.proxy({ unsubscribe() {} });
|
318 | },
|
319 | });
|
320 | assert<Has<typeof subscribable, Comlink.ProxyMarked>>(true);
|
321 | return subscribable;
|
322 | })
|
323 | );
|
324 | proxy2.registerProvider(
|
325 |
|
326 | Comlink.proxy(async ({ textDocument }: Params) => {
|
327 | const subscribable = Comlink.proxy({
|
328 | subscribe(
|
329 | subscriber: Comlink.Remote<Subscriber<string> & Comlink.ProxyMarked>
|
330 | ): Unsubscribable & Comlink.ProxyMarked {
|
331 | assert<IsAny<typeof subscriber.next>>(false);
|
332 | assert<
|
333 | IsExact<
|
334 | typeof subscriber.next,
|
335 | | Comlink.Remote<(value: string) => void>
|
336 | | Promise<undefined>
|
337 | | undefined
|
338 | >
|
339 | >(true);
|
340 |
|
341 |
|
342 | if (typeof subscriber.next === "function") {
|
343 | subscriber.next("abc");
|
344 | }
|
345 | return Comlink.proxy({ unsubscribe() {} });
|
346 | },
|
347 | });
|
348 | return subscribable;
|
349 | })
|
350 | );
|
351 | }
|
352 |
|
353 |
|
354 | {
|
355 | const urlTransferHandler: Comlink.TransferHandler<URL, string> = {
|
356 | canHandle: (val): val is URL => {
|
357 | assert<IsExact<typeof val, unknown>>(true);
|
358 | return val instanceof URL;
|
359 | },
|
360 | serialize: (url) => {
|
361 | assert<IsExact<typeof url, URL>>(true);
|
362 | return [url.href, []];
|
363 | },
|
364 | deserialize: (str) => {
|
365 | assert<IsExact<typeof str, string>>(true);
|
366 | return new URL(str);
|
367 | },
|
368 | };
|
369 | Comlink.transferHandlers.set("URL", urlTransferHandler);
|
370 | }
|
371 | }
|