vigor-bridge 1.0.0

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.
@@ -0,0 +1,25 @@
1
+ import resolve from '@rollup/plugin-node-resolve';
2
+ import commonjs from '@rollup/plugin-commonjs';
3
+ import typescript from '@rollup/plugin-typescript';
4
+
5
+ export default {
6
+ input: 'src/index.ts',
7
+ output: [
8
+ {
9
+ file: 'dist/index.js',
10
+ format: 'cjs',
11
+ exports: 'named',
12
+ sourcemap: true,
13
+ },
14
+ {
15
+ file: 'dist/index.mjs',
16
+ format: 'esm',
17
+ sourcemap: true,
18
+ }
19
+ ],
20
+ plugins: [
21
+ resolve(),
22
+ commonjs(),
23
+ typescript({ tsconfig: './tsconfig.json' })
24
+ ]
25
+ };
package/src/index.ts ADDED
@@ -0,0 +1,672 @@
1
+ const Bridge_Error_Messages = {
2
+ INVALID_TYPE: ({expected, received}: {expected: Array<unknown>, received: unknown}) => `Invalid Type: ${typeof received} (expected: ${expected.map(e => JSON.stringify(e)).join(', ')})`,
3
+ INVALID_VALIDATION: ({expected, received}: {expected: Array<string>, received: unknown}) => `Invalid Validation: ${typeof received} (expected: ${expected.join(', ')})`,
4
+ INVALID_PORT: ({expected, received}: {expected: Array<unknown>, received: unknown}) => `Invalid Type: ${typeof received} (expected: ${expected.map(e => JSON.stringify(e)).join(', ')})`,
5
+ MALICIOUS_DATA: ({position, step}: {position: string, step: number}) => `Malicious Data Detected: ${position}`
6
+ }
7
+
8
+ type BridgeErrorCode = keyof typeof Bridge_Error_Messages
9
+ type BridgeErrorData<C extends BridgeErrorCode> =
10
+ Parameters<typeof Bridge_Error_Messages[C]> extends [infer A]
11
+ ? A
12
+ : undefined;
13
+
14
+ type BridgeErrorOptions<C extends BridgeErrorCode> = {
15
+ data: BridgeErrorData<C>
16
+ }
17
+
18
+ class BridgeError<C extends BridgeErrorCode> extends Error {
19
+ public readonly timestamp: Date = new Date()
20
+
21
+ constructor(
22
+ public readonly code: C,
23
+ options: BridgeErrorOptions<C>
24
+ ) {
25
+ const messageFn = Bridge_Error_Messages[code] as (args: BridgeErrorData<C>) => string
26
+ const message = `[${code}] ${messageFn(options.data)}`
27
+ super(message)
28
+ this.name = new.target.name
29
+ Object.assign(this, options)
30
+
31
+ Object.setPrototypeOf(this, new.target.prototype)
32
+ (Error as any).captureStackTrace?.(this, new.target)
33
+ }
34
+ }
35
+
36
+ class BridgeConnectError<C extends "INVALID_PORT"|"INVALID_VALIDATION"> extends BridgeError<C> {
37
+ constructor(code: C, options: BridgeErrorOptions<C>) {
38
+ super(code, options)
39
+ }
40
+ }
41
+
42
+ class BridgeDirectError<C extends "MALICIOUS_DATA"> extends BridgeError<C> {
43
+ constructor(code: C, options: BridgeErrorOptions<C>) {
44
+ super(code, options)
45
+ }
46
+ }
47
+
48
+ class BridgeChannelError<C extends "MALICIOUS_DATA"> extends BridgeError<C> {
49
+ constructor(code: C, options: BridgeErrorOptions<C>) {
50
+ super(code, options)
51
+ }
52
+ }
53
+
54
+ type BridgeUtilCryptoResult = {
55
+ iv: Array<number>,
56
+ encrypted: Uint8Array<ArrayBuffer>
57
+ }
58
+
59
+ class BridgeUtilCrypto {
60
+ public static generateRandomHex(length: number = 100) {
61
+ const byteLength = Math.ceil(length / 2);
62
+ const array = new Uint8Array(byteLength);
63
+
64
+ self.crypto.getRandomValues(array);
65
+
66
+ const hex = Array.from(array)
67
+ .map(byte => byte.toString(16).padStart(2, '0'))
68
+ .join('');
69
+
70
+ return hex.substring(0, length);
71
+ }
72
+ public static aes = {
73
+ convertPassword: async(passwordRaw: string) => {
74
+ const encoder = new TextEncoder()
75
+ const password = encoder.encode(passwordRaw.padEnd(32, '0').substring(0, 32))
76
+
77
+ return await window.crypto.subtle.importKey(
78
+ "raw",
79
+ password,
80
+ { name: "AES-GCM" },
81
+ false,
82
+ ["encrypt", "decrypt"]
83
+ );
84
+ },
85
+ encrypt: async(text: string, passwordRaw: string): Promise<BridgeUtilCryptoResult> => {
86
+ const encoder = new TextEncoder()
87
+ const password = await BridgeUtilCrypto.aes.convertPassword(passwordRaw)
88
+ const data = encoder.encode(text)
89
+ const iv = window.crypto.getRandomValues(new Uint8Array(12))
90
+ const encryptedBuffer = await window.crypto.subtle.encrypt(
91
+ { name: "AES-GCM", iv: iv },
92
+ password,
93
+ data
94
+ )
95
+ return {
96
+ iv: Array.from(iv),
97
+ encrypted: new Uint8Array(encryptedBuffer)
98
+ }
99
+ },
100
+ decrypt: async({iv: ivArray, encrypted}: BridgeUtilCryptoResult, passwordRaw: string) => {
101
+ const decoder = new TextDecoder();
102
+
103
+ const password = await BridgeUtilCrypto.aes.convertPassword(passwordRaw);
104
+ const iv = new Uint8Array(ivArray);
105
+ const decryptedBuffer = await window.crypto.subtle.decrypt(
106
+ {
107
+ name: "AES-GCM",
108
+ iv
109
+ },
110
+ password,
111
+ encrypted
112
+ )
113
+
114
+ return decoder.decode(decryptedBuffer);
115
+ }
116
+ }
117
+ public static hmac = {
118
+ convertPassword: async (passwordRaw: string): Promise<CryptoKey> => {
119
+ const encoder = new TextEncoder();
120
+ const passwordBytes = encoder.encode(passwordRaw.padEnd(32, '0').substring(0, 32));
121
+
122
+ return await window.crypto.subtle.importKey(
123
+ "raw",
124
+ passwordBytes,
125
+ {
126
+ name: "HMAC",
127
+ hash: { name: "SHA-256" }
128
+ },
129
+ false,
130
+ ["sign", "verify"]
131
+ );
132
+ },
133
+ sign: async(text: string, passwordRaw: string) => {
134
+ const encoder = new TextEncoder()
135
+ const password = await BridgeUtilCrypto.hmac.convertPassword(passwordRaw)
136
+ const data = encoder.encode(text)
137
+
138
+ const encryptedBuffer = await window.crypto.subtle.sign(
139
+ "HMAC",
140
+ password,
141
+ data
142
+ )
143
+
144
+ return new Uint8Array(encryptedBuffer) as BufferSource
145
+ },
146
+ verify: async(text: string, passwordRaw: string, signature: BufferSource) => {
147
+ const encoder = new TextEncoder();
148
+ const password = await BridgeUtilCrypto.hmac.convertPassword(passwordRaw)
149
+ const data = encoder.encode(text)
150
+
151
+ return await window.crypto.subtle.verify(
152
+ "HMAC",
153
+ password,
154
+ signature,
155
+ data
156
+ )
157
+ }
158
+ }
159
+ }
160
+
161
+ type BridgeUtilListenerId = symbol & {
162
+ __brand__: "ListenerId"
163
+ }
164
+
165
+ class BridgeUtilListener {
166
+ private static listenerMap = new Map<symbol, (event: MessageEvent) => void>()
167
+ public static addEventListener(callback: (event: MessageEvent) => void) {
168
+ const listenerId = Symbol("Listener") as unknown as BridgeUtilListenerId
169
+ window.addEventListener("message", callback)
170
+ this.listenerMap.set(listenerId, callback)
171
+ return {
172
+ id: listenerId,
173
+ removeEventListener: () => this.removeEventListener(listenerId)
174
+ }
175
+ }
176
+ public static removeEventListener(listenerId: BridgeUtilListenerId) {
177
+ const callback = this.listenerMap.get(listenerId)
178
+ if(!callback) return false
179
+ window.removeEventListener("message", callback)
180
+ this.listenerMap.delete(listenerId)
181
+ return true
182
+ }
183
+ }
184
+
185
+ type BridgeTokensType = {
186
+ validation: {
187
+ request: string
188
+ receive: string
189
+ }
190
+ }
191
+
192
+ type BridgeTokens = {
193
+ validation: string,
194
+ bridge: string,
195
+ testFn: string,
196
+ testResult: string,
197
+ portValidation: {
198
+ host: string,
199
+ remote: string
200
+ },
201
+ directValidation: string
202
+ }
203
+
204
+ type BridgeChannelListenerId = symbol & {
205
+ __brand__: 'Bridge Listener Id'
206
+ }
207
+
208
+ type BridgeChannelContent = any
209
+
210
+ type BridgeValidationWrapper = (
211
+ getEncrypted: (random: string) => BridgeUtilCryptoResult | Promise<BridgeUtilCryptoResult>
212
+ ) => Promise<(<T>(content: BridgeChannelContent) => any)>;
213
+
214
+ class BridgeDirect {
215
+ private caches: Array<{action: string, content: BridgeChannelContent, type: string, host: boolean}> = []
216
+ constructor(
217
+ private listeners: Map<
218
+ BridgeChannelListenerId,
219
+ { action: string, callback: BridgeValidationWrapper, type: string, host: boolean }
220
+ >,
221
+ private host: boolean,
222
+ private readonly validation: string
223
+ ) {
224
+
225
+ }
226
+ public addEventListener(action: string, callback: (content: BridgeChannelContent) => void) {
227
+ const type = "normal"
228
+ for(const cache of this.caches) {
229
+ if(cache.action !== action || cache.type !== type) continue
230
+ callback(cache.content)
231
+ }
232
+ const listenerId = Symbol("LISTENER_NORMAL") as unknown as BridgeChannelListenerId
233
+ this.listeners.set(listenerId, {
234
+ action,
235
+ callback: async(getEncrypted) => {
236
+ const random = BridgeUtilCrypto.generateRandomHex()
237
+ const encryptedRandom = await getEncrypted(random)
238
+ const decryptedRandom = await BridgeUtilCrypto.aes.decrypt(encryptedRandom, this.validation)
239
+ if (decryptedRandom !== random) throw new BridgeDirectError("MALICIOUS_DATA", {
240
+ data: {position: this.host ? "host" : "remote", step: 2}
241
+ })
242
+ return callback
243
+ },
244
+ type,
245
+ host: this.host
246
+ })
247
+ return {
248
+ id: listenerId,
249
+ removeEventListener: () => this.removeEventListener(listenerId)
250
+ }
251
+ }
252
+ public removeEventListener(listenerId: BridgeChannelListenerId) {
253
+ return this.listeners.delete(listenerId)
254
+ }
255
+ public postMessage(action: string, content: BridgeChannelContent) {
256
+ const type = "normal"
257
+ const targets = Array.from(this.listeners.values()).filter(
258
+ listener => listener.action === action && listener.type === type && listener.host !== this.host
259
+ )
260
+ const promises = targets.map(async (listener) => {
261
+ const callback = await listener.callback(async (random: string) => {
262
+ return await BridgeUtilCrypto.aes.encrypt(random, this.validation)
263
+ })
264
+ if (callback) callback(content)
265
+ });
266
+ Promise.all(promises)
267
+ }
268
+ public handle(action: string, callback: <T>(content: BridgeChannelContent) => T|Promise<T>) {
269
+ const type = "request"
270
+ for(const [listenerId, listener] of this.listeners) {
271
+ if(listener.action !== action || listener.type !== type) continue
272
+ this.listeners.delete(listenerId)
273
+ }
274
+ for(const cache of this.caches) {
275
+ if(cache.action !== action || cache.type !== type) continue
276
+ callback(cache.content)
277
+ }
278
+ const listenerId = Symbol("LISTENER_HANDLE") as unknown as BridgeChannelListenerId
279
+ this.listeners.set(listenerId, {
280
+ action,
281
+ callback: async (getEncrypted) => {
282
+ const random = BridgeUtilCrypto.generateRandomHex()
283
+ const encryptedRandom = await getEncrypted(random)
284
+ const decryptedRandom = await BridgeUtilCrypto.aes.decrypt(encryptedRandom, this.validation)
285
+ if (decryptedRandom !== random) throw new BridgeDirectError("MALICIOUS_DATA", {
286
+ data: {position: this.host ? "host" : "remote", step: 2}
287
+ })
288
+ return async(content) => {
289
+ const {requestId, data, host} = content as {requestId: number, data: BridgeChannelContent, host: boolean}
290
+ try {
291
+ const result = await callback(data)
292
+ return {
293
+ action,
294
+ content: {
295
+ success: true,
296
+ data: result,
297
+ error: null,
298
+ responseId: requestId
299
+ },
300
+ type,
301
+ host
302
+ }
303
+ }
304
+ catch(error) {
305
+ return {
306
+ action,
307
+ content: {
308
+ success: false,
309
+ data: null,
310
+ error: (error instanceof Error) ? error.message : error,
311
+ responseId: requestId
312
+ },
313
+ type,
314
+ host
315
+ }
316
+ }
317
+ }
318
+ },
319
+ type,
320
+ host: this.host
321
+ })
322
+ }
323
+ public request(action: string, content: BridgeChannelContent) {
324
+ return new Promise((resolve, reject) => {
325
+ const requestId = BridgeUtilCrypto.generateRandomHex()
326
+
327
+ const type = "request"
328
+ const listener = Array.from(this.listeners.values()).find(
329
+ listener => listener.action === action && listener.type === type && listener.host !== this.host
330
+ )
331
+ listener?.callback(async (random: string) => {
332
+ const encryptedRandom = await BridgeUtilCrypto.aes.encrypt(random, this.validation)
333
+ return encryptedRandom
334
+ }).then(callback => callback({
335
+ action,
336
+ content: {
337
+ requestId,
338
+ data: content
339
+ },
340
+ type
341
+ }).then(({content}: {content: {success: boolean, data: unknown, error: unknown, responseId: string}}) => {
342
+ const {success, data, error, responseId} = content
343
+ if(responseId !== requestId) return
344
+
345
+ if(success) resolve(data)
346
+ else reject(error)
347
+ }))
348
+ })
349
+ }
350
+ }
351
+
352
+ class BridgeChannel {
353
+ private listeners = new Map<BridgeChannelListenerId, {action: string, callback: (content: BridgeChannelContent) => void, type: string}>()
354
+ private caches: Array<{action: string, content: BridgeChannelContent, type: string}> = []
355
+ constructor(
356
+ private readonly port: MessagePort,
357
+ ) {
358
+ port.onmessage = ({data}) => {
359
+ this.caches.push({action: data.action, content: data.content, type: data.type})
360
+ for(const listener of this.listeners.values()) {
361
+ if(listener.action !== data.action || listener.type !== data.type) continue
362
+ listener.callback(data.content)
363
+ }
364
+ }
365
+ }
366
+ public addEventListener(action: string, callback: (content: BridgeChannelContent) => void) {
367
+ const type = "normal"
368
+ for(const cache of this.caches) {
369
+ if(cache.action !== action || cache.type !== type) continue
370
+ callback(cache.content)
371
+ }
372
+ const listenerId = Symbol("LISTENER_NORMAL") as unknown as BridgeChannelListenerId
373
+ this.listeners.set(listenerId, {action, callback, type})
374
+ return {
375
+ id: listenerId,
376
+ removeEventListener: () => this.removeEventListener(listenerId)
377
+ }
378
+ }
379
+ public removeEventListener(listenerId: BridgeChannelListenerId) {
380
+ return this.listeners.delete(listenerId)
381
+ }
382
+ public postMessage(action: string, content: BridgeChannelContent) {
383
+ const type = "normal"
384
+ this.port.postMessage({
385
+ action,
386
+ content,
387
+ type
388
+ })
389
+ }
390
+ public handle(action: string, callback: <T>(content: BridgeChannelContent) => T|Promise<T>) {
391
+ const type = "request"
392
+ for(const [listenerId, listener] of this.listeners) {
393
+ if(listener.action !== action || listener.type !== type) continue
394
+ this.listeners.delete(listenerId)
395
+ }
396
+ for(const cache of this.caches) {
397
+ if(cache.action !== action || cache.type !== type) continue
398
+ callback(cache.content)
399
+ }
400
+ const listenerId = Symbol("LISTENER_HANDLE") as unknown as BridgeChannelListenerId
401
+ this.listeners.set(listenerId, {action, callback: async(content) => {
402
+ const type = "response"
403
+ const {requestId, data} = content as {requestId: number, data: BridgeChannelContent}
404
+ try {
405
+ const result = await callback(data)
406
+ this.port.postMessage({
407
+ action,
408
+ content: {
409
+ success: true,
410
+ data: result,
411
+ error: null,
412
+ responseId: requestId
413
+ },
414
+ type
415
+ })
416
+ }
417
+ catch(error) {
418
+ this.port.postMessage({
419
+ action,
420
+ content: {
421
+ success: false,
422
+ data: null,
423
+ error: (error instanceof Error) ? error.message : error,
424
+ responseId: requestId
425
+ },
426
+ type
427
+ })
428
+ }
429
+ }, type})
430
+ return {
431
+ id: listenerId,
432
+ removeEventListener: () => this.removeEventListener(listenerId)
433
+ }
434
+ }
435
+ public request(action: string, content: BridgeChannelContent) {
436
+ return new Promise((resolve, reject) => {
437
+ const listenerId = Symbol("LISTENER_RESPONSE") as unknown as BridgeChannelListenerId
438
+ const requestId = BridgeUtilCrypto.generateRandomHex()
439
+ this.listeners.set(listenerId, {action, callback: (content) => {
440
+ const {success, data, error, responseId} = content
441
+ if(responseId !== requestId) return
442
+ this.listeners.delete(listenerId)
443
+
444
+ if(success) resolve(data)
445
+ else reject(error)
446
+ }, type: "response"})
447
+
448
+ const type = "request"
449
+ this.port.postMessage({
450
+ action,
451
+ content: {
452
+ requestId,
453
+ data: content
454
+ },
455
+ type
456
+ })
457
+ })
458
+ }
459
+ public static async create(
460
+ port: MessagePort,
461
+ host: boolean,
462
+ validation: {
463
+ host: string,
464
+ remote: string
465
+ }
466
+ ) {
467
+ await new Promise((resolve, reject) => {
468
+ const random = BridgeUtilCrypto.generateRandomHex()
469
+ if(host) {
470
+ port.onmessage = async({data}) => {
471
+ if(data.type === 'port-validation-1') {
472
+ const encryptedRandom = await BridgeUtilCrypto.aes.encrypt(data.content.random, validation.remote)
473
+ port.postMessage({
474
+ type: 'port-validation-2',
475
+ content: {
476
+ random,
477
+ encrypted: encryptedRandom
478
+ }
479
+ })
480
+ }
481
+ else if(data.type === 'port-validation-3') {
482
+ const decryptedRandom = await BridgeUtilCrypto.aes.decrypt(data.content.encrypted, validation.host)
483
+ if(decryptedRandom !== random) {
484
+ port.close()
485
+ return reject(new BridgeChannelError("MALICIOUS_DATA", {
486
+ data: {
487
+ position: "host",
488
+ step: 3
489
+ }
490
+ }))
491
+ }
492
+ port.postMessage({
493
+ type: 'port-validation-4',
494
+ content: {
495
+ }
496
+ })
497
+ resolve({})
498
+ }
499
+ else {
500
+ port.close()
501
+ return reject(new BridgeChannelError("MALICIOUS_DATA", {
502
+ data: {
503
+ position: "host",
504
+ step: 0
505
+ }
506
+ }))
507
+ }
508
+ }
509
+ }
510
+ else {
511
+ const portValidationRequest = setInterval(() => {
512
+ port.postMessage({
513
+ type: 'port-validation-1',
514
+ content: {
515
+ random
516
+ }
517
+ })
518
+ }, 100)
519
+ port.onmessage = async({data}) => {
520
+ clearInterval(portValidationRequest)
521
+ if(data.type === 'port-validation-2') {
522
+ const decryptedRandom = await BridgeUtilCrypto.aes.decrypt(data.content.encrypted, validation.remote)
523
+ if(decryptedRandom !== random) {
524
+ port.close()
525
+ return reject(new BridgeChannelError("MALICIOUS_DATA", {
526
+ data: {
527
+ position: "remote",
528
+ step: 2
529
+ }
530
+ }))
531
+ }
532
+ const encryptedRandom = await BridgeUtilCrypto.aes.encrypt(data.content.random, validation.host)
533
+ port.postMessage({
534
+ type: 'port-validation-3',
535
+ content: {
536
+ encrypted: encryptedRandom
537
+ }
538
+ })
539
+ }
540
+ else if(data.type === 'port-validation-4') {
541
+ resolve({})
542
+ }
543
+ else {
544
+ port.close()
545
+ return reject(new BridgeChannelError("MALICIOUS_DATA", {
546
+ data: {
547
+ position: "host",
548
+ step: 0
549
+ }
550
+ }))
551
+ }
552
+ }
553
+ }
554
+ })
555
+ return new BridgeChannel(
556
+ port
557
+ )
558
+ }
559
+ }
560
+
561
+ const BridgeEntry = {
562
+ create: () => {
563
+ const type: BridgeTokensType = {
564
+ validation: {
565
+ request: BridgeUtilCrypto.generateRandomHex(),
566
+ receive: BridgeUtilCrypto.generateRandomHex()
567
+ }
568
+ }
569
+ const tokens: BridgeTokens = {
570
+ validation: BridgeUtilCrypto.generateRandomHex(),
571
+ bridge: BridgeUtilCrypto.generateRandomHex(),
572
+ testFn: BridgeUtilCrypto.generateRandomHex(),
573
+ testResult: BridgeUtilCrypto.generateRandomHex(),
574
+ portValidation: {
575
+ host: BridgeUtilCrypto.generateRandomHex(),
576
+ remote: BridgeUtilCrypto.generateRandomHex()
577
+ },
578
+ directValidation: BridgeUtilCrypto.generateRandomHex()
579
+ }
580
+
581
+ const bridge = new Promise<BridgeChannel|BridgeDirect>(async(resolve, reject) => {
582
+ const connectReceive = BridgeUtilListener.addEventListener(async(event) => {
583
+ const {data} = event
584
+ if(data.type !== type.validation.request) return
585
+ connectReceive.removeEventListener()
586
+ const main = (window as any)[tokens.bridge]
587
+ delete (window as any)[tokens.bridge]
588
+ const shareContext = ((main) => {
589
+ if(main instanceof Map) {
590
+ const testFn = main.get(tokens.testFn)
591
+ if(typeof testFn === 'function' && testFn() === tokens.testResult) return true
592
+ }
593
+ return false
594
+ })(main)
595
+
596
+
597
+ const encryptedRandom = await BridgeUtilCrypto.aes.encrypt(data.content.random, tokens.validation)
598
+
599
+ if(shareContext) {
600
+ window.postMessage({
601
+ type: type.validation.receive,
602
+ content: {
603
+ encrypted: encryptedRandom,
604
+ shareContext
605
+ }
606
+ }, event.origin)
607
+ resolve(new BridgeDirect(main, true, tokens.directValidation))
608
+ }
609
+ else {
610
+ const channel = new MessageChannel()
611
+ const port1 = channel.port1
612
+ window.postMessage({
613
+ type: type.validation.receive,
614
+ content: {
615
+ encrypted: encryptedRandom,
616
+ shareContext
617
+ }
618
+ }, event.origin, [channel.port2])
619
+
620
+ resolve(await BridgeChannel.create(port1, true, tokens.portValidation))
621
+ }
622
+ })
623
+ })
624
+ return {certification: {tokens, type}, bridge}
625
+ },
626
+ connect: ({tokens, type}: {tokens: BridgeTokens, type: BridgeTokensType}) => {
627
+ return new Promise<BridgeChannel|BridgeDirect>(async(resolve, reject) => {
628
+ const random = BridgeUtilCrypto.generateRandomHex()
629
+ const main = (window as any)[tokens.bridge] = new Map([[tokens.testFn, () => tokens.testResult]]) as Map<any,any>
630
+ const connectValidationRequest = setInterval(() => {
631
+ window.postMessage({
632
+ type: type.validation.request,
633
+ content: {
634
+ random
635
+ }
636
+ })
637
+ }, 100)
638
+ const connectValidationReceive = BridgeUtilListener.addEventListener(async(event) => {
639
+ const {data} = event
640
+ if(data.type !== type.validation.receive) return
641
+ connectValidationReceive.removeEventListener()
642
+ clearInterval(connectValidationRequest)
643
+
644
+ const decryptedRandom = await BridgeUtilCrypto.aes.decrypt(data.content.encrypted, tokens.validation)
645
+ if(random !== decryptedRandom) return reject(new BridgeConnectError("INVALID_VALIDATION", {
646
+ data: {
647
+ expected: [random],
648
+ received: decryptedRandom
649
+ }
650
+ }))
651
+ const shareContext = data.content.shareContext
652
+
653
+ if(shareContext) {
654
+ resolve(new BridgeDirect(main, false, tokens.directValidation))
655
+ }
656
+ else {
657
+ const [port2] = event.ports
658
+ if(!(port2 instanceof MessagePort)) return reject(new BridgeConnectError("INVALID_PORT", {
659
+ data: {
660
+ expected: ["MessagePort"],
661
+ received: port2
662
+ }
663
+ }))
664
+
665
+ resolve(await BridgeChannel.create(port2, false, tokens.portValidation))
666
+ }
667
+ })
668
+ })
669
+ }
670
+ }
671
+
672
+ export default BridgeEntry