@design.estate/dees-comms 1.1.0 → 1.1.2
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.
- package/.smartconfig.json +1 -6
- package/dist_bundle/bundle.js +7 -7
- package/dist_bundle/bundle.js.map +1 -1
- package/dist_bundle/bundle.js.third-party-notices.json +408 -0
- package/dist_bundle/bundle.js.third-party-notices.txt +968 -0
- package/dist_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/dees-comms.classes.deescomms.d.ts +7 -0
- package/dist_ts/dees-comms.classes.deescomms.js +35 -1
- package/dist_ts/dees-comms.plugins.d.ts +1 -2
- package/dist_ts/dees-comms.plugins.js +3 -4
- package/package.json +6 -7
- package/readme.md +3 -1
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/dees-comms.classes.deescomms.ts +50 -0
- package/ts/dees-comms.plugins.ts +2 -3
- /package/{license → license.md} +0 -0
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
export const commitinfo = {
|
|
5
5
|
name: '@design.estate/dees-comms',
|
|
6
|
-
version: '1.1.
|
|
6
|
+
version: '1.1.2',
|
|
7
7
|
description: 'A communications module for enabling DOM-based messaging and synchronization across browser tabs and workers.'
|
|
8
8
|
};
|
|
9
9
|
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiMDBfY29tbWl0aW5mb19kYXRhLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vdHMvMDBfY29tbWl0aW5mb19kYXRhLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOztHQUVHO0FBQ0gsTUFBTSxDQUFDLE1BQU0sVUFBVSxHQUFHO0lBQ3hCLElBQUksRUFBRSwyQkFBMkI7SUFDakMsT0FBTyxFQUFFLE9BQU87SUFDaEIsV0FBVyxFQUFFLCtHQUErRztDQUM3SCxDQUFBIn0=
|
|
@@ -32,6 +32,7 @@ export declare class DeesComms {
|
|
|
32
32
|
readonly channelName: string;
|
|
33
33
|
private readonly channel;
|
|
34
34
|
private readonly eventHandlers;
|
|
35
|
+
private hasTypedHandlers;
|
|
35
36
|
private closed;
|
|
36
37
|
private closing;
|
|
37
38
|
typedrouter: plugins.typedrequest.TypedRouter<plugins.typedrequestInterfaces.ITypedRequest>;
|
|
@@ -69,6 +70,12 @@ export declare class DeesComms {
|
|
|
69
70
|
* and ends with its own timeout. Closing again does nothing.
|
|
70
71
|
*/
|
|
71
72
|
close(): Promise<void>;
|
|
73
|
+
/**
|
|
74
|
+
* In Node.js an instance keeps its process alive while something listens on
|
|
75
|
+
* it, as a MessagePort with a message listener does: a subscription, or a
|
|
76
|
+
* typed handler it created. One nobody listens on lets the process end.
|
|
77
|
+
*/
|
|
78
|
+
private holdProcessWhileListened;
|
|
72
79
|
private assertOpen;
|
|
73
80
|
private receive;
|
|
74
81
|
private dispatch;
|
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
import * as plugins from './dees-comms.plugins.js';
|
|
2
|
+
const isProcessHolding = (channelArg) => 'ref' in channelArg &&
|
|
3
|
+
typeof channelArg.ref === 'function' &&
|
|
4
|
+
'unref' in channelArg &&
|
|
5
|
+
typeof channelArg.unref === 'function';
|
|
2
6
|
/**
|
|
3
7
|
* Opens a channel that hands every message it receives to `receiveArg` as the
|
|
4
8
|
* message itself: the platform's BroadcastChannel delivers a MessageEvent, the
|
|
@@ -10,6 +14,9 @@ const openChannel = (nameArg, receiveArg) => {
|
|
|
10
14
|
channel.onmessage = (eventArg) => {
|
|
11
15
|
receiveArg(eventArg.data);
|
|
12
16
|
};
|
|
17
|
+
const processHolding = isProcessHolding(channel) ? channel : null;
|
|
18
|
+
// an open channel alone does not keep a Node.js process running
|
|
19
|
+
processHolding?.unref();
|
|
13
20
|
return {
|
|
14
21
|
post: async (messageArg) => {
|
|
15
22
|
channel.postMessage(messageArg);
|
|
@@ -17,6 +24,14 @@ const openChannel = (nameArg, receiveArg) => {
|
|
|
17
24
|
close: async () => {
|
|
18
25
|
channel.close();
|
|
19
26
|
},
|
|
27
|
+
holdProcess: (holdArg) => {
|
|
28
|
+
if (holdArg) {
|
|
29
|
+
processHolding?.ref();
|
|
30
|
+
}
|
|
31
|
+
else {
|
|
32
|
+
processHolding?.unref();
|
|
33
|
+
}
|
|
34
|
+
},
|
|
20
35
|
};
|
|
21
36
|
}
|
|
22
37
|
const channel = new plugins.BroadCastChannelPolyfill(nameArg);
|
|
@@ -26,6 +41,9 @@ const openChannel = (nameArg, receiveArg) => {
|
|
|
26
41
|
return {
|
|
27
42
|
post: (messageArg) => channel.postMessage(messageArg),
|
|
28
43
|
close: () => channel.close(),
|
|
44
|
+
// the polyfill stands in only where the platform has no channel, which
|
|
45
|
+
// Node.js always has
|
|
46
|
+
holdProcess: () => undefined,
|
|
29
47
|
};
|
|
30
48
|
};
|
|
31
49
|
const isObject = (valueArg) => typeof valueArg === 'object' && valueArg !== null;
|
|
@@ -65,6 +83,7 @@ export class DeesComms {
|
|
|
65
83
|
// receiving messages
|
|
66
84
|
constructor(optionsArg = {}) {
|
|
67
85
|
this.eventHandlers = new Map();
|
|
86
|
+
this.hasTypedHandlers = false;
|
|
68
87
|
this.closed = false;
|
|
69
88
|
this.closing = null;
|
|
70
89
|
// sending messages
|
|
@@ -99,6 +118,8 @@ export class DeesComms {
|
|
|
99
118
|
*/
|
|
100
119
|
async createTypedHandler(methodArg, handlerFunction) {
|
|
101
120
|
this.typedrouter.addTypedHandler(new plugins.typedrequest.TypedHandler(methodArg, handlerFunction));
|
|
121
|
+
this.hasTypedHandlers = true;
|
|
122
|
+
this.holdProcessWhileListened();
|
|
102
123
|
}
|
|
103
124
|
/**
|
|
104
125
|
* Sends a typed event to every other instance on this channel and expects
|
|
@@ -132,6 +153,7 @@ export class DeesComms {
|
|
|
132
153
|
this.eventHandlers.set(nameArg, handlers);
|
|
133
154
|
}
|
|
134
155
|
handlers.add(handler);
|
|
156
|
+
this.holdProcessWhileListened();
|
|
135
157
|
return {
|
|
136
158
|
unsubscribe: () => {
|
|
137
159
|
const current = this.eventHandlers.get(nameArg);
|
|
@@ -141,6 +163,7 @@ export class DeesComms {
|
|
|
141
163
|
current.delete(handler);
|
|
142
164
|
if (current.size === 0) {
|
|
143
165
|
this.eventHandlers.delete(nameArg);
|
|
166
|
+
this.holdProcessWhileListened();
|
|
144
167
|
}
|
|
145
168
|
},
|
|
146
169
|
};
|
|
@@ -159,6 +182,17 @@ export class DeesComms {
|
|
|
159
182
|
}
|
|
160
183
|
await this.closing;
|
|
161
184
|
}
|
|
185
|
+
/**
|
|
186
|
+
* In Node.js an instance keeps its process alive while something listens on
|
|
187
|
+
* it, as a MessagePort with a message listener does: a subscription, or a
|
|
188
|
+
* typed handler it created. One nobody listens on lets the process end.
|
|
189
|
+
*/
|
|
190
|
+
holdProcessWhileListened() {
|
|
191
|
+
if (this.closed) {
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
this.channel.holdProcess(this.hasTypedHandlers || this.eventHandlers.size > 0);
|
|
195
|
+
}
|
|
162
196
|
assertOpen() {
|
|
163
197
|
if (this.closed) {
|
|
164
198
|
throw new Error(`DeesComms channel "${this.channelName}" is closed`);
|
|
@@ -206,4 +240,4 @@ export class DeesComms {
|
|
|
206
240
|
}
|
|
207
241
|
}
|
|
208
242
|
}
|
|
209
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
243
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZGVlcy1jb21tcy5jbGFzc2VzLmRlZXNjb21tcy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3RzL2RlZXMtY29tbXMuY2xhc3Nlcy5kZWVzY29tbXMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxLQUFLLE9BQU8sTUFBTSx5QkFBeUIsQ0FBQztBQWdFbkQsTUFBTSxnQkFBZ0IsR0FBRyxDQUFDLFVBQWtCLEVBQXdDLEVBQUUsQ0FDcEYsS0FBSyxJQUFJLFVBQVU7SUFDbkIsT0FBTyxVQUFVLENBQUMsR0FBRyxLQUFLLFVBQVU7SUFDcEMsT0FBTyxJQUFJLFVBQVU7SUFDckIsT0FBTyxVQUFVLENBQUMsS0FBSyxLQUFLLFVBQVUsQ0FBQztBQUV6Qzs7OztHQUlHO0FBQ0gsTUFBTSxXQUFXLEdBQUcsQ0FDbEIsT0FBZSxFQUNmLFVBQXlDLEVBQ3RCLEVBQUU7SUFDckIsSUFBSSxPQUFPLFVBQVUsQ0FBQyxnQkFBZ0IsS0FBSyxVQUFVLEVBQUUsQ0FBQztRQUN0RCxNQUFNLE9BQU8sR0FBRyxJQUFJLFVBQVUsQ0FBQyxnQkFBZ0IsQ0FBQyxPQUFPLENBQUMsQ0FBQztRQUN6RCxPQUFPLENBQUMsU0FBUyxHQUFHLENBQUMsUUFBK0IsRUFBRSxFQUFFO1lBQ3RELFVBQVUsQ0FBQyxRQUFRLENBQUMsSUFBSSxDQUFDLENBQUM7UUFDNUIsQ0FBQyxDQUFDO1FBQ0YsTUFBTSxjQUFjLEdBQUcsZ0JBQWdCLENBQUMsT0FBTyxDQUFDLENBQUMsQ0FBQyxDQUFDLE9BQU8sQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFDO1FBQ2xFLGdFQUFnRTtRQUNoRSxjQUFjLEVBQUUsS0FBSyxFQUFFLENBQUM7UUFDeEIsT0FBTztZQUNMLElBQUksRUFBRSxLQUFLLEVBQUUsVUFBVSxFQUFFLEVBQUU7Z0JBQ3pCLE9BQU8sQ0FBQyxXQUFXLENBQUMsVUFBVSxDQUFDLENBQUM7WUFDbEMsQ0FBQztZQUNELEtBQUssRUFBRSxLQUFLLElBQUksRUFBRTtn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
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import * as smartdelay from '@push.rocks/smartdelay';
|
|
2
1
|
import * as typedrequestInterfaces from '@api.global/typedrequest-interfaces';
|
|
3
2
|
import * as typedrequest from '@api.global/typedrequest';
|
|
4
|
-
export {
|
|
3
|
+
export { typedrequestInterfaces, typedrequest };
|
|
5
4
|
import { BroadcastChannel as BroadCastChannelPolyfill } from 'broadcast-channel';
|
|
6
5
|
export { BroadCastChannelPolyfill };
|
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
//
|
|
2
|
-
import * as smartdelay from '@push.rocks/smartdelay';
|
|
1
|
+
// apiglobal scope
|
|
3
2
|
import * as typedrequestInterfaces from '@api.global/typedrequest-interfaces';
|
|
4
3
|
import * as typedrequest from '@api.global/typedrequest';
|
|
5
|
-
export {
|
|
4
|
+
export { typedrequestInterfaces, typedrequest };
|
|
6
5
|
// third party scope
|
|
7
6
|
import { BroadcastChannel as BroadCastChannelPolyfill } from 'broadcast-channel';
|
|
8
7
|
export { BroadCastChannelPolyfill };
|
|
9
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
8
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZGVlcy1jb21tcy5wbHVnaW5zLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vdHMvZGVlcy1jb21tcy5wbHVnaW5zLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLGtCQUFrQjtBQUNsQixPQUFPLEtBQUssc0JBQXNCLE1BQU0scUNBQXFDLENBQUM7QUFDOUUsT0FBTyxLQUFLLFlBQVksTUFBTSwwQkFBMEIsQ0FBQztBQUV6RCxPQUFPLEVBQUUsc0JBQXNCLEVBQUUsWUFBWSxFQUFFLENBQUM7QUFFaEQsb0JBQW9CO0FBQ3BCLE9BQU8sRUFBRSxnQkFBZ0IsSUFBSSx3QkFBd0IsRUFBRSxNQUFNLG1CQUFtQixDQUFDO0FBRWpGLE9BQU8sRUFDTCx3QkFBd0IsRUFDekIsQ0FBQSJ9
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@design.estate/dees-comms",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.2",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "A communications module for enabling DOM-based messaging and synchronization across browser tabs and workers.",
|
|
6
6
|
"main": "dist_ts/index.js",
|
|
@@ -9,16 +9,15 @@
|
|
|
9
9
|
"author": "Lossless GmbH",
|
|
10
10
|
"license": "MIT",
|
|
11
11
|
"devDependencies": {
|
|
12
|
-
"@git.zone/tsbuild": "^
|
|
13
|
-
"@git.zone/tsbundle": "^
|
|
14
|
-
"@git.zone/tsrun": "^3.0.
|
|
15
|
-
"@git.zone/tstest": "^6.2
|
|
16
|
-
"@types/node": "^26.6.
|
|
12
|
+
"@git.zone/tsbuild": "^5.0.0",
|
|
13
|
+
"@git.zone/tsbundle": "^3.0.0",
|
|
14
|
+
"@git.zone/tsrun": "^3.0.2",
|
|
15
|
+
"@git.zone/tstest": "^6.3.2",
|
|
16
|
+
"@types/node": "^26.6.2"
|
|
17
17
|
},
|
|
18
18
|
"dependencies": {
|
|
19
19
|
"@api.global/typedrequest": "^8.0.3",
|
|
20
20
|
"@api.global/typedrequest-interfaces": "^7.1.0",
|
|
21
|
-
"@push.rocks/smartdelay": "^3.0.5",
|
|
22
21
|
"broadcast-channel": "^7.2.0"
|
|
23
22
|
},
|
|
24
23
|
"files": [
|
package/readme.md
CHANGED
|
@@ -109,7 +109,9 @@ subscription.unsubscribe();
|
|
|
109
109
|
await comms.close();
|
|
110
110
|
```
|
|
111
111
|
|
|
112
|
-
A closed instance receives nothing: its subscriptions end and its typed handlers are no longer asked. `postMessage()`, `broadcast()` and the typed requests it fires reject, and `subscribe()` throws. A typed request that is still waiting for its response when the channel closes cannot receive it and ends with its own timeout. Closing again does nothing.
|
|
112
|
+
A closed instance receives nothing: its subscriptions end and its typed handlers are no longer asked. `postMessage()`, `broadcast()` and the typed requests it fires reject, and `subscribe()` throws. A typed request that is still waiting for its response when the channel closes cannot receive it and ends with its own timeout. Closing again does nothing.
|
|
113
|
+
|
|
114
|
+
In Node.js an instance keeps the process alive only while something listens on it, as a `MessagePort` with a message listener does: while it has a subscription, or once it has created a typed handler. An instance nobody listens on, such as the one `@design.estate/dees-domtools` opens on import, lets the process end. An unsubscribed or closed instance lets it end too, so close every instance with a typed handler that a test or a script opens. In browsers and workers nothing changes.
|
|
113
115
|
|
|
114
116
|
### What travels
|
|
115
117
|
|
package/ts/00_commitinfo_data.ts
CHANGED
|
@@ -46,8 +46,28 @@ type TStoredEventHandler = (
|
|
|
46
46
|
interface IDeesCommsChannel {
|
|
47
47
|
post(messageArg: unknown): Promise<void>;
|
|
48
48
|
close(): Promise<void>;
|
|
49
|
+
/**
|
|
50
|
+
* Whether the open channel keeps its process alive. Only Node.js's channel
|
|
51
|
+
* can hold a process; elsewhere this does nothing.
|
|
52
|
+
*/
|
|
53
|
+
holdProcess(holdArg: boolean): void;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* A channel that decides whether it keeps its process alive, as Node.js's
|
|
58
|
+
* BroadcastChannel does. A browser's channel has neither method.
|
|
59
|
+
*/
|
|
60
|
+
interface IProcessHoldingChannel {
|
|
61
|
+
ref(): void;
|
|
62
|
+
unref(): void;
|
|
49
63
|
}
|
|
50
64
|
|
|
65
|
+
const isProcessHolding = (channelArg: object): channelArg is IProcessHoldingChannel =>
|
|
66
|
+
'ref' in channelArg &&
|
|
67
|
+
typeof channelArg.ref === 'function' &&
|
|
68
|
+
'unref' in channelArg &&
|
|
69
|
+
typeof channelArg.unref === 'function';
|
|
70
|
+
|
|
51
71
|
/**
|
|
52
72
|
* Opens a channel that hands every message it receives to `receiveArg` as the
|
|
53
73
|
* message itself: the platform's BroadcastChannel delivers a MessageEvent, the
|
|
@@ -62,6 +82,9 @@ const openChannel = (
|
|
|
62
82
|
channel.onmessage = (eventArg: MessageEvent<unknown>) => {
|
|
63
83
|
receiveArg(eventArg.data);
|
|
64
84
|
};
|
|
85
|
+
const processHolding = isProcessHolding(channel) ? channel : null;
|
|
86
|
+
// an open channel alone does not keep a Node.js process running
|
|
87
|
+
processHolding?.unref();
|
|
65
88
|
return {
|
|
66
89
|
post: async (messageArg) => {
|
|
67
90
|
channel.postMessage(messageArg);
|
|
@@ -69,6 +92,13 @@ const openChannel = (
|
|
|
69
92
|
close: async () => {
|
|
70
93
|
channel.close();
|
|
71
94
|
},
|
|
95
|
+
holdProcess: (holdArg) => {
|
|
96
|
+
if (holdArg) {
|
|
97
|
+
processHolding?.ref();
|
|
98
|
+
} else {
|
|
99
|
+
processHolding?.unref();
|
|
100
|
+
}
|
|
101
|
+
},
|
|
72
102
|
};
|
|
73
103
|
}
|
|
74
104
|
const channel = new plugins.BroadCastChannelPolyfill<unknown>(nameArg);
|
|
@@ -78,6 +108,9 @@ const openChannel = (
|
|
|
78
108
|
return {
|
|
79
109
|
post: (messageArg) => channel.postMessage(messageArg),
|
|
80
110
|
close: () => channel.close(),
|
|
111
|
+
// the polyfill stands in only where the platform has no channel, which
|
|
112
|
+
// Node.js always has
|
|
113
|
+
holdProcess: () => undefined,
|
|
81
114
|
};
|
|
82
115
|
};
|
|
83
116
|
|
|
@@ -129,6 +162,7 @@ export class DeesComms {
|
|
|
129
162
|
public readonly channelName: string;
|
|
130
163
|
private readonly channel: IDeesCommsChannel;
|
|
131
164
|
private readonly eventHandlers = new Map<string, Set<TStoredEventHandler>>();
|
|
165
|
+
private hasTypedHandlers = false;
|
|
132
166
|
private closed = false;
|
|
133
167
|
private closing: Promise<void> | null = null;
|
|
134
168
|
|
|
@@ -179,6 +213,8 @@ export class DeesComms {
|
|
|
179
213
|
this.typedrouter.addTypedHandler(
|
|
180
214
|
new plugins.typedrequest.TypedHandler<T>(methodArg, handlerFunction)
|
|
181
215
|
);
|
|
216
|
+
this.hasTypedHandlers = true;
|
|
217
|
+
this.holdProcessWhileListened();
|
|
182
218
|
}
|
|
183
219
|
|
|
184
220
|
/**
|
|
@@ -221,6 +257,7 @@ export class DeesComms {
|
|
|
221
257
|
this.eventHandlers.set(nameArg, handlers);
|
|
222
258
|
}
|
|
223
259
|
handlers.add(handler);
|
|
260
|
+
this.holdProcessWhileListened();
|
|
224
261
|
return {
|
|
225
262
|
unsubscribe: () => {
|
|
226
263
|
const current = this.eventHandlers.get(nameArg);
|
|
@@ -230,6 +267,7 @@ export class DeesComms {
|
|
|
230
267
|
current.delete(handler);
|
|
231
268
|
if (current.size === 0) {
|
|
232
269
|
this.eventHandlers.delete(nameArg);
|
|
270
|
+
this.holdProcessWhileListened();
|
|
233
271
|
}
|
|
234
272
|
},
|
|
235
273
|
};
|
|
@@ -250,6 +288,18 @@ export class DeesComms {
|
|
|
250
288
|
await this.closing;
|
|
251
289
|
}
|
|
252
290
|
|
|
291
|
+
/**
|
|
292
|
+
* In Node.js an instance keeps its process alive while something listens on
|
|
293
|
+
* it, as a MessagePort with a message listener does: a subscription, or a
|
|
294
|
+
* typed handler it created. One nobody listens on lets the process end.
|
|
295
|
+
*/
|
|
296
|
+
private holdProcessWhileListened(): void {
|
|
297
|
+
if (this.closed) {
|
|
298
|
+
return;
|
|
299
|
+
}
|
|
300
|
+
this.channel.holdProcess(this.hasTypedHandlers || this.eventHandlers.size > 0);
|
|
301
|
+
}
|
|
302
|
+
|
|
253
303
|
private assertOpen(): void {
|
|
254
304
|
if (this.closed) {
|
|
255
305
|
throw new Error(`DeesComms channel "${this.channelName}" is closed`);
|
package/ts/dees-comms.plugins.ts
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
//
|
|
2
|
-
import * as smartdelay from '@push.rocks/smartdelay';
|
|
1
|
+
// apiglobal scope
|
|
3
2
|
import * as typedrequestInterfaces from '@api.global/typedrequest-interfaces';
|
|
4
3
|
import * as typedrequest from '@api.global/typedrequest';
|
|
5
4
|
|
|
6
|
-
export {
|
|
5
|
+
export { typedrequestInterfaces, typedrequest };
|
|
7
6
|
|
|
8
7
|
// third party scope
|
|
9
8
|
import { BroadcastChannel as BroadCastChannelPolyfill } from 'broadcast-channel';
|
/package/{license → license.md}
RENAMED
|
File without changes
|