@farcaster/frame-host 0.0.54 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,654 +0,0 @@
1
- import {
2
- type Endpoint,
3
- type EventSource,
4
- type Message,
5
- MessageType,
6
- type PostMessageWithOrigin,
7
- type WireValue,
8
- WireValueType,
9
- } from './protocol'
10
- export type { Endpoint }
11
-
12
- export const proxyMarker = Symbol('Comlink.proxy')
13
- export const createEndpoint = Symbol('Comlink.endpoint')
14
- export const releaseProxy = Symbol('Comlink.releaseProxy')
15
- export const finalizer = Symbol('Comlink.finalizer')
16
-
17
- const throwMarker = Symbol('Comlink.thrown')
18
-
19
- /**
20
- * Interface of values that were marked to be proxied with `comlink.proxy()`.
21
- * Can also be implemented by classes.
22
- */
23
- export interface ProxyMarked {
24
- [proxyMarker]: true
25
- }
26
-
27
- /**
28
- * Takes a type and wraps it in a Promise, if it not already is one.
29
- * This is to avoid `Promise<Promise<T>>`.
30
- *
31
- * This is the inverse of `Unpromisify<T>`.
32
- */
33
- type Promisify<T> = T extends Promise<unknown> ? T : Promise<T>
34
- /**
35
- * Takes a type that may be Promise and unwraps the Promise type.
36
- * If `P` is not a Promise, it returns `P`.
37
- *
38
- * This is the inverse of `Promisify<T>`.
39
- */
40
- type Unpromisify<P> = P extends Promise<infer T> ? T : P
41
-
42
- /**
43
- * Takes the raw type of a remote property and returns the type that is visible to the local thread on the proxy.
44
- *
45
- * Note: This needs to be its own type alias, otherwise it will not distribute over unions.
46
- * See https://www.typescriptlang.org/docs/handbook/advanced-types.html#distributive-conditional-types
47
- */
48
- type RemoteProperty<T> =
49
- // If the value is a method, comlink will proxy it automatically.
50
- // Objects are only proxied if they are marked to be proxied.
51
- // Otherwise, the property is converted to a Promise that resolves the cloned value.
52
- T extends Function | ProxyMarked ? Remote<T> : Promisify<T>
53
-
54
- /**
55
- * Takes the raw type of a property as a remote thread would see it through a proxy (e.g. when passed in as a function
56
- * argument) and returns the type that the local thread has to supply.
57
- *
58
- * This is the inverse of `RemoteProperty<T>`.
59
- *
60
- * Note: This needs to be its own type alias, otherwise it will not distribute over unions. See
61
- * https://www.typescriptlang.org/docs/handbook/advanced-types.html#distributive-conditional-types
62
- */
63
- type LocalProperty<T> = T extends Function | ProxyMarked
64
- ? Local<T>
65
- : Unpromisify<T>
66
-
67
- /**
68
- * Proxies `T` if it is a `ProxyMarked`, clones it otherwise (as handled by structured cloning and transfer handlers).
69
- */
70
- export type ProxyOrClone<T> = T extends ProxyMarked ? Remote<T> : T
71
- /**
72
- * Inverse of `ProxyOrClone<T>`.
73
- */
74
- export type UnproxyOrClone<T> = T extends RemoteObject<ProxyMarked>
75
- ? Local<T>
76
- : T
77
-
78
- /**
79
- * Takes the raw type of a remote object in the other thread and returns the type as it is visible to the local thread
80
- * when proxied with `Comlink.proxy()`.
81
- *
82
- * This does not handle call signatures, which is handled by the more general `Remote<T>` type.
83
- *
84
- * @template T The raw type of a remote object as seen in the other thread.
85
- */
86
- export type RemoteObject<T> = { [P in keyof T]: RemoteProperty<T[P]> }
87
- /**
88
- * Takes the type of an object as a remote thread would see it through a proxy (e.g. when passed in as a function
89
- * argument) and returns the type that the local thread has to supply.
90
- *
91
- * This does not handle call signatures, which is handled by the more general `Local<T>` type.
92
- *
93
- * This is the inverse of `RemoteObject<T>`.
94
- *
95
- * @template T The type of a proxied object.
96
- */
97
- export type LocalObject<T> = { [P in keyof T]: LocalProperty<T[P]> }
98
-
99
- /**
100
- * Additional special comlink methods available on each proxy returned by `Comlink.wrap()`.
101
- */
102
- export interface ProxyMethods {
103
- [createEndpoint]: () => Promise<MessagePort>
104
- [releaseProxy]: () => void
105
- }
106
-
107
- /**
108
- * Takes the raw type of a remote object, function or class in the other thread and returns the type as it is visible to
109
- * the local thread from the proxy return value of `Comlink.wrap()` or `Comlink.proxy()`.
110
- */
111
- export type Remote<T> =
112
- // Handle properties
113
- RemoteObject<T> &
114
- // Handle call signature (if present)
115
- (T extends (...args: infer TArguments) => infer TReturn
116
- ? (
117
- ...args: { [I in keyof TArguments]: UnproxyOrClone<TArguments[I]> }
118
- ) => Promisify<ProxyOrClone<Unpromisify<TReturn>>>
119
- : unknown) &
120
- // Handle construct signature (if present)
121
- // The return of construct signatures is always proxied (whether marked or not)
122
- (T extends { new (...args: infer TArguments): infer TInstance }
123
- ? {
124
- new (
125
- ...args: {
126
- [I in keyof TArguments]: UnproxyOrClone<TArguments[I]>
127
- }
128
- ): Promisify<Remote<TInstance>>
129
- }
130
- : unknown) &
131
- // Include additional special comlink methods available on the proxy.
132
- ProxyMethods
133
-
134
- /**
135
- * Expresses that a type can be either a sync or async.
136
- */
137
- type MaybePromise<T> = Promise<T> | T
138
-
139
- /**
140
- * Takes the raw type of a remote object, function or class as a remote thread would see it through a proxy (e.g. when
141
- * passed in as a function argument) and returns the type the local thread has to supply.
142
- *
143
- * This is the inverse of `Remote<T>`. It takes a `Remote<T>` and returns its original input `T`.
144
- */
145
- export type Local<T> =
146
- // Omit the special proxy methods (they don't need to be supplied, comlink adds them)
147
- Omit<LocalObject<T>, keyof ProxyMethods> &
148
- // Handle call signatures (if present)
149
- (T extends (...args: infer TArguments) => infer TReturn
150
- ? (
151
- ...args: { [I in keyof TArguments]: ProxyOrClone<TArguments[I]> }
152
- ) => MaybePromise<UnproxyOrClone<Unpromisify<TReturn>>> // The raw function could either be sync or async, but is always proxied automatically
153
- : unknown) &
154
- // Handle construct signature (if present)
155
- // The return of construct signatures is always proxied (whether marked or not)
156
- (T extends { new (...args: infer TArguments): infer TInstance }
157
- ? {
158
- new (
159
- ...args: {
160
- [I in keyof TArguments]: ProxyOrClone<TArguments[I]>
161
- }
162
- ): // The raw constructor could either be sync or async, but is always proxied automatically
163
- MaybePromise<Local<Unpromisify<TInstance>>>
164
- }
165
- : unknown)
166
-
167
- const isObject = (val: unknown): val is object =>
168
- (typeof val === 'object' && val !== null) || typeof val === 'function'
169
-
170
- /**
171
- * Customizes the serialization of certain values as determined by `canHandle()`.
172
- *
173
- * @template T The input type being handled by this transfer handler.
174
- * @template S The serialized type sent over the wire.
175
- */
176
- export interface TransferHandler<T, S> {
177
- /**
178
- * Gets called for every value to determine whether this transfer handler
179
- * should serialize the value, which includes checking that it is of the right
180
- * type (but can perform checks beyond that as well).
181
- */
182
- canHandle(value: unknown): value is T
183
-
184
- /**
185
- * Gets called with the value if `canHandle()` returned `true` to produce a
186
- * value that can be sent in a message, consisting of structured-cloneable
187
- * values and/or transferrable objects.
188
- */
189
- serialize(value: T): [S, Transferable[]]
190
-
191
- /**
192
- * Gets called to deserialize an incoming value that was serialized in the
193
- * other thread with this transfer handler (known through the name it was
194
- * registered under).
195
- */
196
- deserialize(value: S): T
197
- }
198
-
199
- /**
200
- * Internal transfer handle to handle objects marked to proxy.
201
- */
202
- const proxyTransferHandler: TransferHandler<object, MessagePort> = {
203
- canHandle: (val): val is ProxyMarked =>
204
- isObject(val) && (val as ProxyMarked)[proxyMarker],
205
- serialize(obj) {
206
- const { port1, port2 } = new MessageChannel()
207
- expose(obj, port1)
208
- return [port2, [port2]]
209
- },
210
- deserialize(port) {
211
- port.start()
212
- return wrap(port)
213
- },
214
- }
215
-
216
- interface ThrownValue {
217
- [throwMarker]: unknown // just needs to be present
218
- value: unknown
219
- }
220
- type SerializedThrownValue =
221
- | { isError: true; value: Error }
222
- | { isError: false; value: unknown }
223
- type PendingListenersMap = Map<
224
- string,
225
- (value: WireValue | PromiseLike<WireValue>) => void
226
- >
227
-
228
- /**
229
- * Internal transfer handler to handle thrown exceptions.
230
- */
231
- const throwTransferHandler: TransferHandler<
232
- ThrownValue,
233
- SerializedThrownValue
234
- > = {
235
- canHandle: (value): value is ThrownValue =>
236
- isObject(value) && throwMarker in value,
237
- serialize({ value }) {
238
- let serialized: SerializedThrownValue
239
- if (value instanceof Error) {
240
- serialized = {
241
- isError: true,
242
- value: {
243
- message: value.message,
244
- name: value.name,
245
- stack: value.stack,
246
- },
247
- }
248
- } else {
249
- serialized = { isError: false, value }
250
- }
251
- return [serialized, []]
252
- },
253
- deserialize(serialized) {
254
- if (serialized.isError) {
255
- throw Object.assign(new Error(serialized.value.message), serialized.value)
256
- }
257
- throw serialized.value
258
- },
259
- }
260
-
261
- /**
262
- * Allows customizing the serialization of certain values.
263
- */
264
- export const transferHandlers = new Map<
265
- string,
266
- TransferHandler<unknown, unknown>
267
- >([
268
- ['proxy', proxyTransferHandler],
269
- ['throw', throwTransferHandler],
270
- ])
271
-
272
- function isAllowedOrigin(
273
- allowedOrigins: (string | RegExp)[],
274
- origin: string,
275
- ): boolean {
276
- for (const allowedOrigin of allowedOrigins) {
277
- if (origin === allowedOrigin || allowedOrigin === '*') {
278
- return true
279
- }
280
- if (allowedOrigin instanceof RegExp && allowedOrigin.test(origin)) {
281
- return true
282
- }
283
- }
284
- return false
285
- }
286
-
287
- export function expose(
288
- obj: any,
289
- ep: Endpoint = globalThis as any,
290
- allowedOrigins: (string | RegExp)[] = ['*'],
291
- ) {
292
- function callback(ev: MessageEvent) {
293
- if (!ev || !ev.data) {
294
- return
295
- }
296
- if (!isAllowedOrigin(allowedOrigins, ev.origin)) {
297
- console.warn(`Invalid origin '${ev.origin}' for comlink proxy`)
298
- return
299
- }
300
- const { id, type, path } = {
301
- path: [] as string[],
302
- ...(ev.data as Message),
303
- }
304
-
305
- const argumentList = (ev.data.argumentList || []).map(fromWireValue)
306
- let returnValue
307
- try {
308
- switch (type) {
309
- case MessageType.GET:
310
- if (path[0] === 'context') {
311
- returnValue = obj.context;
312
- } else {
313
- throw new Error(`Unsupported GET for ${path.join('/')}`)
314
- }
315
- break
316
- case MessageType.APPLY: {
317
- returnValue = (() => {
318
- switch (path[0]) {
319
- case 'close':
320
- return obj.close();
321
- case 'ready':
322
- return obj.ready(...argumentList);
323
- case 'openUrl':
324
- return obj.openUrl(...argumentList);
325
- case 'signIn':
326
- return obj.signIn(...argumentList);
327
- case 'setPrimaryButton':
328
- return obj.setPrimaryButton(...argumentList);
329
- case 'ethProviderRequest':
330
- return obj.ethProviderRequest(...argumentList);
331
- case 'ethProviderRequestV2':
332
- return obj.ethProviderRequestV2(...argumentList);
333
- case 'eip6963RequestProvider':
334
- return obj.eip6963RequestProvider(...argumentList);
335
- case 'addFrame':
336
- return obj.addFrame(...argumentList);
337
- case 'composeCast':
338
- return obj.composeCast(...argumentList);
339
- case 'viewCast':
340
- return obj.viewCast(...argumentList);
341
- case 'viewProfile':
342
- return obj.viewProfile(...argumentList);
343
- case 'viewToken':
344
- return obj.viewToken(...argumentList);
345
- case 'swapToken':
346
- return obj.swapToken(...argumentList);
347
- case 'sendToken':
348
- return obj.sendToken(...argumentList);
349
- case 'solanaProviderRequest':
350
- return obj.solanaProviderRequest(...argumentList);
351
- case 'getCapabilities':
352
- return obj.getCapabilities(...argumentList);
353
- case 'getChains':
354
- return obj.getChains(...argumentList);
355
- case 'impactOccurred':
356
- return obj.impactOccurred(...argumentList);
357
- case 'notificationOccurred':
358
- return obj.notificationOccurred(...argumentList);
359
- case 'selectionChanged':
360
- return obj.selectionChanged(...argumentList);
361
- case 'updateBackState':
362
- return obj.updateBackState(...argumentList);
363
- default:
364
- throw new Error(`Unsupported APPLY for ${path.join('/')}`)
365
- }
366
- })()
367
- break;
368
- }
369
- case MessageType.SET:
370
- throw new Error(`Unsupported SET for ${path.join('/')}`)
371
- case MessageType.CONSTRUCT:
372
- throw new Error(`Unsupported CONSTRUCT for ${path.join('/')}`)
373
- case MessageType.ENDPOINT:
374
- throw new Error(`Unsupported ENDPOINT for ${path.join('/')}`)
375
- default:
376
- return
377
- }
378
- } catch (value) {
379
- returnValue = { value, [throwMarker]: 0 }
380
- }
381
-
382
- Promise.resolve(returnValue)
383
- .catch((value) => {
384
- return { value, [throwMarker]: 0 }
385
- })
386
- .then((returnValue) => {
387
- const [wireValue, transferables] = toWireValue(returnValue)
388
- ep.postMessage({ ...wireValue, id }, transferables)
389
- if (type === MessageType.RELEASE) {
390
- // detach and deactive after sending release response above.
391
- ep.removeEventListener('message', callback as any)
392
- closeEndPoint(ep)
393
- if (finalizer in obj && typeof obj[finalizer] === 'function') {
394
- obj[finalizer]()
395
- }
396
- }
397
- })
398
- .catch(() => {
399
- // Send Serialization Error To Caller
400
- const [wireValue, transferables] = toWireValue({
401
- value: new TypeError('Unserializable return value'),
402
- [throwMarker]: 0,
403
- })
404
- ep.postMessage({ ...wireValue, id }, transferables)
405
- })
406
- }
407
-
408
- ep.addEventListener('message', callback as any)
409
- if (ep.start) {
410
- ep.start()
411
- }
412
-
413
- return () => {
414
- ep.removeEventListener('message', callback as any)
415
- closeEndPoint(ep)
416
- if (finalizer in obj && typeof obj[finalizer] === 'function') {
417
- obj[finalizer]()
418
- }
419
- }
420
- }
421
-
422
- function isMessagePort(endpoint: Endpoint): endpoint is MessagePort {
423
- return endpoint.constructor.name === 'MessagePort'
424
- }
425
-
426
- function closeEndPoint(endpoint: Endpoint) {
427
- if (isMessagePort(endpoint)) endpoint.close()
428
- }
429
-
430
- export function wrap<T>(ep: Endpoint, target?: any): Remote<T> {
431
- const pendingListeners: PendingListenersMap = new Map()
432
-
433
- ep.addEventListener('message', function handleMessage(ev: Event) {
434
- const { data } = ev as MessageEvent
435
- if (!data || !data.id) {
436
- return
437
- }
438
- const resolver = pendingListeners.get(data.id)
439
- if (!resolver) {
440
- return
441
- }
442
-
443
- try {
444
- resolver(data)
445
- } finally {
446
- pendingListeners.delete(data.id)
447
- }
448
- })
449
-
450
- return createProxy<T>(ep, pendingListeners, [], target) as any
451
- }
452
-
453
- function throwIfProxyReleased(isReleased: boolean) {
454
- if (isReleased) {
455
- throw new Error('Proxy has been released and is not useable')
456
- }
457
- }
458
-
459
- function releaseEndpoint(ep: Endpoint) {
460
- return requestResponseMessage(ep, new Map(), {
461
- type: MessageType.RELEASE,
462
- }).then(() => {
463
- closeEndPoint(ep)
464
- })
465
- }
466
-
467
- interface FinalizationRegistry<T> {
468
- new (cb: (heldValue: T) => void): FinalizationRegistry<T>
469
- register(
470
- weakItem: object,
471
- heldValue: T,
472
- unregisterToken?: object | undefined,
473
- ): void
474
- unregister(unregisterToken: object): void
475
- }
476
- declare let FinalizationRegistry: FinalizationRegistry<Endpoint>
477
-
478
- const proxyCounter = new WeakMap<Endpoint, number>()
479
- const proxyFinalizers =
480
- 'FinalizationRegistry' in globalThis &&
481
- new FinalizationRegistry((ep: Endpoint) => {
482
- const newCount = (proxyCounter.get(ep) || 0) - 1
483
- proxyCounter.set(ep, newCount)
484
- if (newCount === 0) {
485
- releaseEndpoint(ep)
486
- }
487
- })
488
-
489
- function registerProxy(proxy: object, ep: Endpoint) {
490
- const newCount = (proxyCounter.get(ep) || 0) + 1
491
- proxyCounter.set(ep, newCount)
492
- if (proxyFinalizers) {
493
- proxyFinalizers.register(proxy, ep, proxy)
494
- }
495
- }
496
-
497
- function unregisterProxy(proxy: object) {
498
- if (proxyFinalizers) {
499
- proxyFinalizers.unregister(proxy)
500
- }
501
- }
502
-
503
- function createProxy<T>(
504
- ep: Endpoint,
505
- pendingListeners: PendingListenersMap,
506
- path: (string | number | symbol)[] = [],
507
- target: object = () => {},
508
- ): Remote<T> {
509
- let isProxyReleased = false
510
- const proxy = new Proxy(target, {
511
- get(_target, prop) {
512
- throwIfProxyReleased(isProxyReleased)
513
- if (prop === releaseProxy) {
514
- return () => {
515
- unregisterProxy(proxy)
516
- releaseEndpoint(ep)
517
- pendingListeners.clear()
518
- isProxyReleased = true
519
- }
520
- }
521
- if (prop === 'then') {
522
- if (path.length === 0) {
523
- return { then: () => proxy }
524
- }
525
- const r = requestResponseMessage(ep, pendingListeners, {
526
- type: MessageType.GET,
527
- path: path.map((p) => p.toString()),
528
- }).then(fromWireValue)
529
- return r.then.bind(r)
530
- }
531
- return createProxy(ep, pendingListeners, [...path, prop])
532
- },
533
- set(_target) {
534
- throwIfProxyReleased(isProxyReleased)
535
- throw new Error("Unsupported call to set");
536
- },
537
- apply(_target, _thisArg, rawArgumentList) {
538
- throwIfProxyReleased(isProxyReleased)
539
- const last = path[path.length - 1]
540
- if ((last as any) === createEndpoint) {
541
- return requestResponseMessage(ep, pendingListeners, {
542
- type: MessageType.ENDPOINT,
543
- }).then(fromWireValue)
544
- }
545
- // We just pretend that `bind()` didn’t happen.
546
- if (last === 'bind') {
547
- return createProxy(ep, pendingListeners, path.slice(0, -1))
548
- }
549
- const [argumentList, transferables] = processArguments(rawArgumentList)
550
- return requestResponseMessage(
551
- ep,
552
- pendingListeners,
553
- {
554
- type: MessageType.APPLY,
555
- path: path.map((p) => p.toString()),
556
- argumentList,
557
- },
558
- transferables,
559
- ).then(fromWireValue)
560
- },
561
- construct(_target) {
562
- throw new Error("Unsupported call to construct");
563
- },
564
- })
565
- registerProxy(proxy, ep)
566
- return proxy as any
567
- }
568
-
569
- function myFlat<T>(arr: (T | T[])[]): T[] {
570
- return Array.prototype.concat.apply([], arr)
571
- }
572
-
573
- function processArguments(argumentList: any[]): [WireValue[], Transferable[]] {
574
- const processed = argumentList.map(toWireValue)
575
- return [processed.map((v) => v[0]), myFlat(processed.map((v) => v[1]))]
576
- }
577
-
578
- const transferCache = new WeakMap<any, Transferable[]>()
579
- export function transfer<T>(obj: T, transfers: Transferable[]): T {
580
- transferCache.set(obj, transfers)
581
- return obj
582
- }
583
-
584
- export function proxy<T extends {}>(obj: T): T & ProxyMarked {
585
- return Object.assign(obj, { [proxyMarker]: true }) as any
586
- }
587
-
588
- export function windowEndpoint(
589
- w: PostMessageWithOrigin,
590
- context: EventSource = globalThis,
591
- targetOrigin = '*',
592
- ): Endpoint {
593
- return {
594
- postMessage: (msg: any, transferables: Transferable[]) =>
595
- w.postMessage(msg, targetOrigin, transferables),
596
- addEventListener: context.addEventListener.bind(context),
597
- removeEventListener: context.removeEventListener.bind(context),
598
- }
599
- }
600
-
601
- function toWireValue(value: any): [WireValue, Transferable[]] {
602
- for (const [name, handler] of transferHandlers) {
603
- if (handler.canHandle(value)) {
604
- const [serializedValue, transferables] = handler.serialize(value)
605
- return [
606
- {
607
- type: WireValueType.HANDLER,
608
- name,
609
- value: serializedValue,
610
- },
611
- transferables,
612
- ]
613
- }
614
- }
615
- return [
616
- {
617
- type: WireValueType.RAW,
618
- value,
619
- },
620
- transferCache.get(value) || [],
621
- ]
622
- }
623
-
624
- function fromWireValue(value: WireValue): any {
625
- switch (value.type) {
626
- case WireValueType.HANDLER:
627
- return transferHandlers.get(value.name)!.deserialize(value.value)
628
- case WireValueType.RAW:
629
- return value.value
630
- }
631
- }
632
-
633
- function requestResponseMessage(
634
- ep: Endpoint,
635
- pendingListeners: PendingListenersMap,
636
- msg: Message,
637
- transfers?: Transferable[],
638
- ): Promise<WireValue> {
639
- return new Promise((resolve) => {
640
- const id = generateUUID()
641
- pendingListeners.set(id, resolve)
642
- if (ep.start) {
643
- ep.start()
644
- }
645
- ep.postMessage({ id, ...msg }, transfers)
646
- })
647
- }
648
-
649
- function generateUUID(): string {
650
- return new Array(4)
651
- .fill(0)
652
- .map(() => Math.floor(Math.random() * Number.MAX_SAFE_INTEGER).toString(16))
653
- .join('-')
654
- }
@@ -1 +0,0 @@
1
- export * from './comlink.ts'
@@ -1,49 +0,0 @@
1
- /**
2
- * @license
3
- * Copyright 2019 Google LLC
4
- * SPDX-License-Identifier: Apache-2.0
5
- */
6
-
7
- import type { Endpoint } from './protocol'
8
-
9
- export interface NodeEndpoint {
10
- postMessage(message: any, transfer?: any[]): void
11
- on(
12
- type: string,
13
- listener: EventListenerOrEventListenerObject,
14
- options?: {},
15
- ): void
16
- off(
17
- type: string,
18
- listener: EventListenerOrEventListenerObject,
19
- options?: {},
20
- ): void
21
- start?: () => void
22
- }
23
-
24
- export default function nodeEndpoint(nep: NodeEndpoint): Endpoint {
25
- const listeners = new WeakMap()
26
- return {
27
- postMessage: nep.postMessage.bind(nep),
28
- addEventListener: (_, eh) => {
29
- const l = (data: any) => {
30
- if ('handleEvent' in eh) {
31
- eh.handleEvent({ data } as MessageEvent)
32
- } else {
33
- eh({ data } as MessageEvent)
34
- }
35
- }
36
- nep.on('message', l)
37
- listeners.set(eh, l)
38
- },
39
- removeEventListener: (_, eh) => {
40
- const l = listeners.get(eh)
41
- if (!l) {
42
- return
43
- }
44
- nep.off('message', l)
45
- listeners.delete(eh)
46
- },
47
- start: nep.start?.bind(nep),
48
- }
49
- }