ava 3.12.1 → 3.13.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.
@@ -195,6 +195,7 @@ class Reporter {
195
195
  this.internalErrors = [];
196
196
  this.knownFailures = [];
197
197
  this.lineNumberErrors = [];
198
+ this.sharedWorkerErrors = [];
198
199
  this.uncaughtExceptions = [];
199
200
  this.unhandledRejections = [];
200
201
  this.unsavedSnapshots = [];
@@ -340,6 +341,19 @@ class Reporter {
340
341
  break;
341
342
  }
342
343
 
344
+ case 'shared-worker-error': {
345
+ this.sharedWorkerErrors.push(event);
346
+
347
+ if (this.verbose) {
348
+ this.lineWriter.ensureEmptyLine();
349
+ this.lineWriter.writeLine(colors.error(`${figures.cross} Error in shared worker`));
350
+ this.lineWriter.writeLine();
351
+ this.writeErr(event);
352
+ }
353
+
354
+ break;
355
+ }
356
+
343
357
  case 'snapshot-error':
344
358
  this.unsavedSnapshots.push(event);
345
359
  break;
@@ -710,7 +724,7 @@ class Reporter {
710
724
  }
711
725
 
712
726
  if (this.failures.length > 0) {
713
- const writeTrailingLines = this.internalErrors.length > 0 || this.uncaughtExceptions.length > 0 || this.unhandledRejections.length > 0;
727
+ const writeTrailingLines = this.internalErrors.length > 0 || this.sharedWorkerErrors.length > 0 || this.uncaughtExceptions.length > 0 || this.unhandledRejections.length > 0;
714
728
 
715
729
  const lastFailure = this.failures[this.failures.length - 1];
716
730
  for (const event of this.failures) {
@@ -734,7 +748,7 @@ class Reporter {
734
748
 
735
749
  if (!this.verbose) {
736
750
  if (this.internalErrors.length > 0) {
737
- const writeTrailingLines = this.uncaughtExceptions.length > 0 || this.unhandledRejections.length > 0;
751
+ const writeTrailingLines = this.sharedWorkerErrors.length > 0 || this.uncaughtExceptions.length > 0 || this.unhandledRejections.length > 0;
738
752
 
739
753
  const last = this.internalErrors[this.internalErrors.length - 1];
740
754
  for (const event of this.internalErrors) {
@@ -756,6 +770,23 @@ class Reporter {
756
770
  }
757
771
  }
758
772
 
773
+ if (this.sharedWorkerErrors.length > 0) {
774
+ const writeTrailingLines = this.uncaughtExceptions.length > 0 || this.unhandledRejections.length > 0;
775
+
776
+ const last = this.sharedWorkerErrors[this.sharedWorkerErrors.length - 1];
777
+ for (const evt of this.sharedWorkerErrors) {
778
+ this.lineWriter.writeLine(colors.error(`${figures.cross} Error in shared worker`));
779
+ this.lineWriter.writeLine();
780
+ this.writeErr(evt.err);
781
+ if (evt !== last || writeTrailingLines) {
782
+ this.lineWriter.writeLine();
783
+ this.lineWriter.writeLine();
784
+ }
785
+
786
+ wroteSomething = true;
787
+ }
788
+ }
789
+
759
790
  if (this.uncaughtExceptions.length > 0) {
760
791
  const writeTrailingLines = this.unhandledRejections.length > 0;
761
792
 
package/lib/run-status.js CHANGED
@@ -27,6 +27,7 @@ class RunStatus extends Emittery {
27
27
  passedKnownFailingTests: 0,
28
28
  passedTests: 0,
29
29
  selectedTests: 0,
30
+ sharedWorkerErrors: 0,
30
31
  skippedTests: 0,
31
32
  timeouts: 0,
32
33
  todoTests: 0,
@@ -93,6 +94,9 @@ class RunStatus extends Emittery {
93
94
  this.addPendingTest(event);
94
95
  }
95
96
 
97
+ break;
98
+ case 'shared-worker-error':
99
+ stats.sharedWorkerErrors++;
96
100
  break;
97
101
  case 'test-failed':
98
102
  stats.failedTests++;
@@ -164,6 +168,7 @@ class RunStatus extends Emittery {
164
168
  this.stats.failedHooks > 0 ||
165
169
  this.stats.failedTests > 0 ||
166
170
  this.stats.failedWorkers > 0 ||
171
+ this.stats.sharedWorkerErrors > 0 ||
167
172
  this.stats.timeouts > 0 ||
168
173
  this.stats.uncaughtExceptions > 0 ||
169
174
  this.stats.unhandledRejections > 0
package/lib/runner.js CHANGED
@@ -42,6 +42,7 @@ class Runner extends Emittery {
42
42
  serial: [],
43
43
  todo: []
44
44
  };
45
+ this.waitForReady = [];
45
46
 
46
47
  const uniqueTestTitles = new Set();
47
48
  this.registerUniqueTitle = title => {
@@ -444,6 +445,8 @@ class Runner extends Emittery {
444
445
  });
445
446
  }
446
447
 
448
+ await Promise.all(this.waitForReady);
449
+
447
450
  if (concurrentTests.length === 0 && serialTests.length === 0) {
448
451
  this.emit('finish');
449
452
  // Don't run any hooks if there are no tests to run.
package/lib/test.js CHANGED
@@ -187,7 +187,8 @@ class ExecutionContext extends assert.Assertions {
187
187
  }
188
188
 
189
189
  get passed() {
190
- return testMap.get(this).testPassed;
190
+ const test = testMap.get(this);
191
+ return test.isHook ? test.testPassed : !test.assertError;
191
192
  }
192
193
 
193
194
  _throwsArgStart(assertion, file, line) {
package/lib/worker/ipc.js CHANGED
@@ -1,50 +1,42 @@
1
1
  'use strict';
2
- const Emittery = require('emittery');
2
+ const events = require('events');
3
+ const pEvent = require('p-event');
4
+ const {controlFlow} = require('../ipc-flow-control');
5
+ const {get: getOptions} = require('./options');
3
6
 
4
- const emitter = new Emittery();
5
- process.on('message', message => {
6
- if (!message.ava) {
7
- return;
8
- }
9
-
10
- switch (message.ava.type) {
11
- case 'options':
12
- emitter.emit('options', message.ava.options);
13
- break;
14
- case 'peer-failed':
15
- emitter.emit('peerFailed');
16
- break;
17
- case 'pong':
18
- emitter.emit('pong');
19
- break;
20
- default:
21
- break;
22
- }
23
- });
7
+ const selectAvaMessage = type => message => message.ava && message.ava.type === type;
24
8
 
25
- exports.options = emitter.once('options');
26
- exports.peerFailed = emitter.once('peerFailed');
9
+ exports.options = pEvent(process, 'message', selectAvaMessage('options')).then(message => message.ava.options);
10
+ exports.peerFailed = pEvent(process, 'message', selectAvaMessage('peer-failed'));
27
11
 
12
+ const bufferedSend = controlFlow(process);
28
13
  function send(evt) {
29
- if (process.connected) {
30
- process.send({ava: evt});
31
- }
14
+ bufferedSend({ava: evt});
32
15
  }
33
16
 
34
17
  exports.send = send;
35
18
 
19
+ let refs = 1;
20
+ function ref() {
21
+ if (++refs === 1) {
22
+ process.channel.ref();
23
+ }
24
+ }
25
+
36
26
  function unref() {
37
- process.channel.unref();
27
+ if (refs > 0 && --refs === 0) {
28
+ process.channel.unref();
29
+ }
38
30
  }
39
31
 
40
32
  exports.unref = unref;
41
33
 
42
34
  let pendingPings = Promise.resolve();
43
35
  async function flush() {
44
- process.channel.ref();
36
+ ref();
45
37
  const promise = pendingPings.then(async () => { // eslint-disable-line promise/prefer-await-to-then
46
38
  send({type: 'ping'});
47
- await emitter.once('pong');
39
+ await pEvent(process, 'message', selectAvaMessage('pong'));
48
40
  if (promise === pendingPings) {
49
41
  unref();
50
42
  }
@@ -54,3 +46,156 @@ async function flush() {
54
46
  }
55
47
 
56
48
  exports.flush = flush;
49
+
50
+ let channelCounter = 0;
51
+ let messageCounter = 0;
52
+
53
+ const channelEmitters = new Map();
54
+ function createChannelEmitter(channelId) {
55
+ if (channelEmitters.size === 0) {
56
+ process.on('message', message => {
57
+ if (!message.ava) {
58
+ return;
59
+ }
60
+
61
+ const {channelId, type, ...payload} = message.ava;
62
+ if (
63
+ type === 'shared-worker-error' ||
64
+ type === 'shared-worker-message' ||
65
+ type === 'shared-worker-ready'
66
+ ) {
67
+ const emitter = channelEmitters.get(channelId);
68
+ if (emitter !== undefined) {
69
+ emitter.emit(type, payload);
70
+ }
71
+ }
72
+ });
73
+ }
74
+
75
+ const emitter = new events.EventEmitter();
76
+ channelEmitters.set(channelId, emitter);
77
+ return [emitter, () => channelEmitters.delete(channelId)];
78
+ }
79
+
80
+ function registerSharedWorker(filename, initialData) {
81
+ const channelId = `${getOptions().forkId}/channel/${++channelCounter}`;
82
+ const [channelEmitter, unsubscribe] = createChannelEmitter(channelId);
83
+
84
+ let forcedUnref = false;
85
+ let refs = 0;
86
+ const forceUnref = () => {
87
+ if (forcedUnref) {
88
+ return;
89
+ }
90
+
91
+ forcedUnref = true;
92
+ if (refs > 0) {
93
+ unref();
94
+ }
95
+ };
96
+
97
+ const refChannel = () => {
98
+ if (!forcedUnref && ++refs === 1) {
99
+ ref();
100
+ }
101
+ };
102
+
103
+ const unrefChannel = () => {
104
+ if (!forcedUnref && refs > 0 && --refs === 0) {
105
+ unref();
106
+ }
107
+ };
108
+
109
+ send({
110
+ type: 'shared-worker-connect',
111
+ channelId,
112
+ filename,
113
+ initialData
114
+ });
115
+
116
+ let currentlyAvailable = false;
117
+ let error = null;
118
+
119
+ refChannel();
120
+ const ready = pEvent(channelEmitter, 'shared-worker-ready').then(() => { // eslint-disable-line promise/prefer-await-to-then
121
+ currentlyAvailable = error === null;
122
+ }).finally(unrefChannel);
123
+
124
+ const messageEmitters = new Set();
125
+ const handleMessage = message => {
126
+ // Wait for a turn of the event loop, to allow new subscriptions to be set
127
+ // up in response to the previous message.
128
+ setImmediate(() => {
129
+ for (const emitter of messageEmitters) {
130
+ emitter.emit('message', message);
131
+ }
132
+ });
133
+ };
134
+
135
+ channelEmitter.on('shared-worker-message', handleMessage);
136
+
137
+ pEvent(channelEmitter, 'shared-worker-error').then(() => { // eslint-disable-line promise/prefer-await-to-then
138
+ unsubscribe();
139
+ forceUnref();
140
+
141
+ error = new Error('The shared worker is no longer available');
142
+ currentlyAvailable = false;
143
+ for (const emitter of messageEmitters) {
144
+ emitter.emit('error', error);
145
+ }
146
+ });
147
+
148
+ return {
149
+ forceUnref,
150
+ ready,
151
+ channel: {
152
+ available: ready,
153
+
154
+ get currentlyAvailable() {
155
+ return currentlyAvailable;
156
+ },
157
+
158
+ async * receive() {
159
+ if (error !== null) {
160
+ throw error;
161
+ }
162
+
163
+ const emitter = new events.EventEmitter();
164
+ messageEmitters.add(emitter);
165
+ try {
166
+ refChannel();
167
+ for await (const [message] of events.on(emitter, 'message')) {
168
+ yield message;
169
+ }
170
+ } finally {
171
+ unrefChannel();
172
+ messageEmitters.delete(emitter);
173
+ }
174
+ },
175
+
176
+ post(serializedData, replyTo) {
177
+ if (error !== null) {
178
+ throw error;
179
+ }
180
+
181
+ if (!currentlyAvailable) {
182
+ throw new Error('Shared worker is not yet available');
183
+ }
184
+
185
+ const messageId = `${channelId}/message/${++messageCounter}`;
186
+ send({
187
+ type: 'shared-worker-message',
188
+ channelId,
189
+ messageId,
190
+ replyTo,
191
+ serializedData
192
+ });
193
+
194
+ return messageId;
195
+ }
196
+ }
197
+ };
198
+ }
199
+
200
+ exports.registerSharedWorker = registerSharedWorker;
201
+
@@ -0,0 +1,121 @@
1
+ const v8 = require('v8');
2
+ const pkg = require('../../package.json');
3
+ const subprocess = require('./subprocess');
4
+ const options = require('./options');
5
+
6
+ const workers = new Map();
7
+ const workerTeardownFns = new WeakMap();
8
+
9
+ function createSharedWorker(filename, initialData, teardown) {
10
+ const channel = subprocess.registerSharedWorker(filename, initialData, teardown);
11
+
12
+ class ReceivedMessage {
13
+ constructor(id, serializedData) {
14
+ this.id = id;
15
+ this.data = v8.deserialize(new Uint8Array(serializedData));
16
+ }
17
+
18
+ reply(data) {
19
+ return publishMessage(data, this.id);
20
+ }
21
+ }
22
+
23
+ // Ensure that, no matter how often it's received, we have a stable message
24
+ // object.
25
+ const messageCache = new WeakMap();
26
+ async function * receiveMessages(replyTo) {
27
+ for await (const evt of channel.receive()) {
28
+ if (replyTo === undefined && evt.replyTo !== undefined) {
29
+ continue;
30
+ }
31
+
32
+ if (replyTo !== undefined && evt.replyTo !== replyTo) {
33
+ continue;
34
+ }
35
+
36
+ let message = messageCache.get(evt);
37
+ if (message === undefined) {
38
+ message = new ReceivedMessage(evt.messageId, evt.serializedData);
39
+ messageCache.set(evt, message);
40
+ }
41
+
42
+ yield message;
43
+ }
44
+ }
45
+
46
+ function publishMessage(data, replyTo) {
47
+ const id = channel.post([...v8.serialize(data)], replyTo);
48
+
49
+ return {
50
+ id,
51
+ async * replies() {
52
+ yield * receiveMessages(id);
53
+ }
54
+ };
55
+ }
56
+
57
+ return {
58
+ available: channel.available,
59
+ protocol: 'experimental',
60
+
61
+ get currentlyAvailable() {
62
+ return channel.currentlyAvailable;
63
+ },
64
+
65
+ publish(data) {
66
+ return publishMessage(data);
67
+ },
68
+
69
+ async * subscribe() {
70
+ yield * receiveMessages();
71
+ }
72
+ };
73
+ }
74
+
75
+ const supportsSharedWorkers = process.versions.node >= '12.17.0';
76
+
77
+ function registerSharedWorker({
78
+ filename,
79
+ initialData,
80
+ supportedProtocols,
81
+ teardown
82
+ }) {
83
+ if (!options.get().experiments.sharedWorkers) {
84
+ throw new Error('Shared workers are experimental. Opt in to them in your AVA configuration');
85
+ }
86
+
87
+ if (!supportsSharedWorkers) {
88
+ throw new Error('Shared workers require Node.js 12.17 or newer');
89
+ }
90
+
91
+ if (!supportedProtocols.includes('experimental')) {
92
+ throw new Error(`This version of AVA (${pkg.version}) does not support any of the desired shared worker protocols: ${supportedProtocols.join()}`);
93
+ }
94
+
95
+ let worker = workers.get(filename);
96
+ if (worker === undefined) {
97
+ worker = createSharedWorker(filename, initialData, async () => {
98
+ // Run possibly asynchronous teardown functions serially, in reverse
99
+ // order. Any error will crash the worker.
100
+ const teardownFns = workerTeardownFns.get(worker);
101
+ if (teardownFns !== undefined) {
102
+ for await (const fn of [...teardownFns].reverse()) {
103
+ await fn();
104
+ }
105
+ }
106
+ });
107
+ workers.set(filename, worker);
108
+ }
109
+
110
+ if (teardown !== undefined) {
111
+ if (workerTeardownFns.has(worker)) {
112
+ workerTeardownFns.get(worker).push(teardown);
113
+ } else {
114
+ workerTeardownFns.set(worker, [teardown]);
115
+ }
116
+ }
117
+
118
+ return worker;
119
+ }
120
+
121
+ exports.registerSharedWorker = registerSharedWorker;
@@ -32,6 +32,8 @@ ipc.options.then(async options => {
32
32
  const dependencyTracking = require('./dependency-tracker');
33
33
  const lineNumberSelection = require('./line-numbers');
34
34
 
35
+ const sharedWorkerTeardowns = [];
36
+
35
37
  async function exit(code) {
36
38
  if (!process.exitCode) {
37
39
  process.exitCode = code;
@@ -89,7 +91,7 @@ ipc.options.then(async options => {
89
91
  exit(1);
90
92
  });
91
93
 
92
- runner.on('finish', () => {
94
+ runner.on('finish', async () => {
93
95
  try {
94
96
  const {cannotSave, touchedFiles} = runner.saveSnapshotState();
95
97
  if (cannotSave) {
@@ -103,6 +105,14 @@ ipc.options.then(async options => {
103
105
  return;
104
106
  }
105
107
 
108
+ try {
109
+ await Promise.all(sharedWorkerTeardowns.map(fn => fn()));
110
+ } catch (error) {
111
+ ipc.send({type: 'uncaught-exception', err: serializeError('Shared worker teardown error', false, error, runner.file)});
112
+ exit(1);
113
+ return;
114
+ }
115
+
106
116
  nowAndTimers.setImmediate(() => {
107
117
  currentlyUnhandled()
108
118
  .filter(rejection => !attributedRejections.has(rejection.promise))
@@ -129,6 +139,19 @@ ipc.options.then(async options => {
129
139
  return runner;
130
140
  };
131
141
 
142
+ exports.registerSharedWorker = (filename, initialData, teardown) => {
143
+ const {channel, forceUnref, ready} = ipc.registerSharedWorker(filename, initialData);
144
+ runner.waitForReady.push(ready);
145
+ sharedWorkerTeardowns.push(async () => {
146
+ try {
147
+ await teardown();
148
+ } finally {
149
+ forceUnref();
150
+ }
151
+ });
152
+ return channel;
153
+ };
154
+
132
155
  // Store value to prevent required modules from modifying it.
133
156
  const testPath = options.file;
134
157
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ava",
3
- "version": "3.12.1",
3
+ "version": "3.13.0",
4
4
  "description": "Node.js test runner that lets you develop with confidence.",
5
5
  "license": "MIT",
6
6
  "repository": "avajs/ava",
@@ -16,7 +16,8 @@
16
16
  "lib",
17
17
  "*.js",
18
18
  "!*.config.js",
19
- "index.d.ts"
19
+ "index.d.ts",
20
+ "*.d.ts"
20
21
  ],
21
22
  "keywords": [
22
23
  "🦄",
@@ -75,8 +76,8 @@
75
76
  "concordance": "^5.0.1",
76
77
  "convert-source-map": "^1.7.0",
77
78
  "currently-unhandled": "^0.4.1",
78
- "debug": "^4.1.1",
79
- "del": "^5.1.0",
79
+ "debug": "^4.2.0",
80
+ "del": "^6.0.0",
80
81
  "emittery": "^0.7.1",
81
82
  "equal-length": "^1.0.0",
82
83
  "figures": "^3.2.0",
@@ -85,19 +86,20 @@
85
86
  "import-local": "^3.0.2",
86
87
  "indent-string": "^4.0.0",
87
88
  "is-error": "^2.2.2",
88
- "is-plain-object": "^4.1.1",
89
+ "is-plain-object": "^5.0.0",
89
90
  "is-promise": "^4.0.0",
90
91
  "lodash": "^4.17.20",
91
92
  "matcher": "^3.0.0",
92
93
  "md5-hex": "^3.0.1",
93
- "mem": "^6.1.0",
94
+ "mem": "^6.1.1",
94
95
  "ms": "^2.1.2",
95
- "ora": "^5.0.0",
96
+ "ora": "^5.1.0",
97
+ "p-event": "^4.2.0",
96
98
  "p-map": "^4.0.0",
97
99
  "picomatch": "^2.2.2",
98
100
  "pkg-conf": "^3.1.0",
99
101
  "plur": "^4.0.0",
100
- "pretty-ms": "^7.0.0",
102
+ "pretty-ms": "^7.0.1",
101
103
  "read-pkg": "^5.2.0",
102
104
  "resolve-cwd": "^3.0.0",
103
105
  "slash": "^3.0.0",
@@ -109,7 +111,7 @@
109
111
  "trim-off-newlines": "^1.0.1",
110
112
  "update-notifier": "^4.1.1",
111
113
  "write-file-atomic": "^3.0.3",
112
- "yargs": "^15.4.1"
114
+ "yargs": "^16.0.3"
113
115
  },
114
116
  "devDependencies": {
115
117
  "@ava/babel": "^1.0.1",
@@ -117,12 +119,12 @@
117
119
  "@babel/plugin-proposal-do-expressions": "^7.10.4",
118
120
  "@sinonjs/fake-timers": "^6.0.1",
119
121
  "ansi-escapes": "^4.3.1",
120
- "c8": "^7.3.0",
122
+ "c8": "^7.3.1",
121
123
  "delay": "^4.4.0",
122
124
  "esm": "^3.2.25",
123
125
  "execa": "^4.0.3",
124
126
  "get-stream": "^6.0.0",
125
- "p-event": "^4.2.0",
127
+ "it-first": "^1.0.4",
126
128
  "proxyquire": "^2.1.3",
127
129
  "react": "^16.13.1",
128
130
  "react-test-renderer": "^16.13.1",
@@ -131,11 +133,11 @@
131
133
  "source-map-fixtures": "^2.1.0",
132
134
  "tap": "^14.10.8",
133
135
  "temp-write": "^4.0.0",
134
- "tempy": "^0.6.0",
136
+ "tempy": "^0.7.1",
135
137
  "touch": "^3.1.0",
136
138
  "tsd": "^0.13.1",
137
- "typescript": "^3.9.7",
138
- "xo": "^0.33.0",
139
+ "typescript": "^4.0.3",
140
+ "xo": "^0.33.1",
139
141
  "zen-observable": "^0.8.15"
140
142
  }
141
143
  }
package/plugin.d.ts ADDED
@@ -0,0 +1,81 @@
1
+ export namespace SharedWorker {
2
+ export type ProtocolIdentifier = 'experimental';
3
+
4
+ /* eslint-disable @typescript-eslint/method-signature-style */
5
+ export type FactoryOptions = {
6
+ negotiateProtocol <Data = unknown>(supported: readonly ['experimental']): Experimental.Protocol<Data>;
7
+ // Add overloads for additional protocols.
8
+ };
9
+ /* eslint-enable @typescript-eslint/method-signature-style */
10
+
11
+ export type Factory = (options: FactoryOptions) => void;
12
+
13
+ export namespace Experimental {
14
+ export type Protocol<Data = unknown> = {
15
+ readonly initialData: Data;
16
+ readonly protocol: 'experimental';
17
+ broadcast: (data: Data) => BroadcastMessage<Data>;
18
+ ready: () => Protocol<Data>;
19
+ subscribe: () => AsyncIterableIterator<ReceivedMessage<Data>>;
20
+ testWorkers: () => AsyncIterableIterator<TestWorker<Data>>;
21
+ };
22
+
23
+ export type BroadcastMessage<Data = unknown> = {
24
+ readonly id: string;
25
+ replies: () => AsyncIterableIterator<ReceivedMessage<Data>>;
26
+ };
27
+
28
+ export type PublishedMessage<Data = unknown> = {
29
+ readonly id: string;
30
+ replies: () => AsyncIterableIterator<ReceivedMessage<Data>>;
31
+ };
32
+
33
+ export type ReceivedMessage<Data = unknown> = {
34
+ readonly data: Data;
35
+ readonly id: string;
36
+ readonly testWorker: TestWorker;
37
+ reply: (data: Data) => PublishedMessage<Data>;
38
+ };
39
+
40
+ export type TestWorker<Data = unknown> = {
41
+ readonly id: string;
42
+ readonly file: string;
43
+ publish: (data: Data) => PublishedMessage<Data>;
44
+ subscribe: () => AsyncIterableIterator<ReceivedMessage<Data>>;
45
+ teardown: <TeardownFn extends () => void> (fn: TeardownFn) => TeardownFn;
46
+ };
47
+ }
48
+
49
+ export namespace Plugin {
50
+ export type RegistrationOptions<Identifier extends ProtocolIdentifier, Data = unknown> = {
51
+ readonly filename: string;
52
+ readonly initialData?: Data;
53
+ readonly supportedProtocols: readonly Identifier[];
54
+ readonly teardown?: () => void;
55
+ };
56
+
57
+ export namespace Experimental {
58
+ export type Protocol<Data = unknown> = {
59
+ readonly available: Promise<void>;
60
+ readonly currentlyAvailable: boolean;
61
+ readonly protocol: 'experimental';
62
+ publish: (data: Data) => PublishedMessage<Data>;
63
+ subscribe: () => AsyncIterableIterator<ReceivedMessage<Data>>;
64
+ };
65
+
66
+ export type PublishedMessage<Data = unknown> = {
67
+ readonly id: string;
68
+ replies: () => AsyncIterableIterator<ReceivedMessage<Data>>;
69
+ };
70
+
71
+ export type ReceivedMessage<Data = unknown> = {
72
+ readonly data: Data;
73
+ readonly id: string;
74
+ reply: (data: Data) => PublishedMessage<Data>;
75
+ };
76
+ }
77
+ }
78
+ }
79
+
80
+ export function registerSharedWorker<Data = unknown>(options: SharedWorker.Plugin.RegistrationOptions<'experimental', Data>): SharedWorker.Plugin.Experimental.Protocol<Data>;
81
+ // Add overloads for additional protocols.