reflected 0.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.
package/package.json ADDED
@@ -0,0 +1,16 @@
1
+ {
2
+ "name": "reflected",
3
+ "version": "0.0.0",
4
+ "description": "",
5
+ "main": "index.js",
6
+ "scripts": {
7
+ "test": "echo \"Error: no test specified\" && exit 1"
8
+ },
9
+ "keywords": [],
10
+ "author": "",
11
+ "license": "ISC",
12
+ "type": "commonjs",
13
+ "dependencies": {
14
+ "@webreflection/utils": "^0.1.2"
15
+ }
16
+ }
package/src/index.js ADDED
@@ -0,0 +1,34 @@
1
+ import { native } from '/node_modules/@webreflection/utils/src/shared-array-buffer.js';
2
+ import withResolvers from '/node_modules/@webreflection/utils/src/with-resolvers.js';
3
+
4
+ let module;
5
+
6
+ if ('importScripts' in globalThis) {
7
+ let get;
8
+ const { promise, resolve } = withResolvers();
9
+ const reflected = new URL(location).searchParams.get('reflected');
10
+ if (reflected === 'chrome') get = import('./worker/chrome.js');
11
+ else if (reflected === 'firefox') get = import('./worker/firefox.js');
12
+ else if (reflected === 'fallback') get = import('./worker/fallback.js');
13
+ module = async options => {
14
+ const { data, ports } = await promise;
15
+ const { default: reflect } = await get;
16
+ const event = new Event('message');
17
+ event.data = data;
18
+ event.ports = ports;
19
+ dispatchEvent(event);
20
+ return reflect(options);
21
+ };
22
+ addEventListener('message', resolve, { once: true });
23
+ }
24
+ else if (native) {
25
+ if ('InstallTrigger' in globalThis)
26
+ module = (await import('./main/firefox.js')).default;
27
+ else
28
+ module = (await import('./main/chrome.js')).default;
29
+ }
30
+ else {
31
+ module = (await import('./main/fallback.js')).default;
32
+ }
33
+
34
+ export default module;
@@ -0,0 +1,19 @@
1
+ import { SAB, handler, post, url, withResolvers } from './shared.js';
2
+
3
+ class Worker extends globalThis.Worker {
4
+ constructor(scriptURL, options, resolve) {
5
+ const { port1, port2 } = new MessageChannel;
6
+ const sab = SAB(options);
7
+ port1.addEventListener('message', handler(sab, options, true));
8
+ port1.start();
9
+ super(url(scriptURL, 'chrome'), { ...options, type: 'module' });
10
+ super.addEventListener('message', () => resolve(this), { once: true });
11
+ super.postMessage(post(sab, options), [port2]);
12
+ }
13
+ }
14
+
15
+ export default (scriptURL, options) => {
16
+ const { promise, resolve } = withResolvers();
17
+ new Worker(scriptURL, options, resolve);
18
+ return promise;
19
+ };
@@ -0,0 +1,29 @@
1
+ import CHANNEL from './shared-id.js';
2
+ import { handler, post, url, withResolvers } from './shared.js';
3
+ import { SharedArrayBuffer } from '/node_modules/@webreflection/utils/src/shared-array-buffer.js';
4
+
5
+ const SAB = ({ minByteLength = 1032, maxByteLength = 8200 }) =>
6
+ new SharedArrayBuffer(minByteLength, { maxByteLength });
7
+
8
+ const sharedBC = new BroadcastChannel(CHANNEL);
9
+ sharedBC.addEventListener('message', event => {
10
+ console.log(event.data);
11
+ });
12
+
13
+ class Worker extends globalThis.Worker {
14
+ constructor(scriptURL, options) {
15
+ const channel = crypto.randomUUID();
16
+ const bc = new BroadcastChannel(channel);
17
+ const sab = SAB(options);
18
+ bc.addEventListener('message', handler(sab, options, false));
19
+ super(url(scriptURL, 'fallback'), { ...options, type: 'module' });
20
+ super.addEventListener('message', () => resolve(this), { once: true });
21
+ super.postMessage(post(sab, options).concat(channel));
22
+ }
23
+ };
24
+
25
+ export default (scriptURL, options) => {
26
+ const { promise, resolve } = withResolvers();
27
+ new Worker(scriptURL, options, resolve);
28
+ return promise;
29
+ };
@@ -0,0 +1,19 @@
1
+ import { SAB, handler, post, url, withResolvers } from './shared.js';
2
+
3
+ class Worker extends globalThis.Worker {
4
+ constructor(scriptURL, options, resolve) {
5
+ const channel = crypto.randomUUID();
6
+ const bc = new BroadcastChannel(channel);
7
+ const sab = SAB(options);
8
+ bc.addEventListener('message', handler(sab, options, true));
9
+ super(url(scriptURL, 'firefox'), { ...options, type: 'module' });
10
+ super.addEventListener('message', () => resolve(this), { once: true });
11
+ super.postMessage(post(sab, options).concat(channel));
12
+ }
13
+ };
14
+
15
+ export default (scriptURL, options) => {
16
+ const { promise, resolve } = withResolvers();
17
+ new Worker(scriptURL, options, resolve);
18
+ return promise;
19
+ };
@@ -0,0 +1 @@
1
+ export default 'reflected-0123456789';
@@ -0,0 +1,36 @@
1
+ import withResolvers from '/node_modules/@webreflection/utils/src/with-resolvers.js';
2
+
3
+ const { notify, store } = Atomics;
4
+ const { isView } = ArrayBuffer;
5
+
6
+ const minByteLength = Int32Array.BYTES_PER_ELEMENT * 2;
7
+
8
+ export const SAB = ({ minByteLength = 1032, maxByteLength = 8200 }) =>
9
+ new SharedArrayBuffer(minByteLength, { maxByteLength });
10
+
11
+ export const handler = (sab, options, useAtomics) => {
12
+ const i32a = new Int32Array(sab);
13
+ return async ({ data }, ...rest) => {
14
+ let result = await options.ondata(data, ...rest);
15
+ if (!isView(result)) result = new Int32Array(result);
16
+ const { byteLength } = result.buffer;
17
+ const requiredByteLength = byteLength + minByteLength;
18
+ if (sab.byteLength < requiredByteLength) sab.grow(requiredByteLength);
19
+ i32a.set(result, 2);
20
+ if (useAtomics) {
21
+ store(i32a, 1, result.length);
22
+ store(i32a, 0, 1);
23
+ notify(i32a, 0);
24
+ }
25
+ };
26
+ };
27
+
28
+ export const post = (sab, options) => [sab, { ...options, ondata: void 0 }];
29
+
30
+ export const url = (scriptURL, reflected) => {
31
+ const url = new URL(scriptURL, location.href);
32
+ url.searchParams.set('reflected', reflected);
33
+ return url;
34
+ };
35
+
36
+ export { withResolvers };
@@ -0,0 +1,8 @@
1
+ import { handler, withResolvers } from './shared.js';
2
+
3
+ const { promise, resolve } = withResolvers();
4
+
5
+ export default handler(
6
+ promise,
7
+ ({ data: [sab, main], ports: [channel] }) => resolve([sab, main, channel])
8
+ );
@@ -0,0 +1 @@
1
+ console.log('worker', 'epic fail');
@@ -0,0 +1,8 @@
1
+ import { handler, withResolvers } from './shared.js';
2
+
3
+ const { promise, resolve } = withResolvers();
4
+
5
+ export default handler(
6
+ promise,
7
+ ({ data: [sab, main, channel] }) => resolve([sab, main, new BroadcastChannel(channel)]),
8
+ );
@@ -0,0 +1,22 @@
1
+ import withResolvers from '/node_modules/@webreflection/utils/src/with-resolvers.js';
2
+
3
+ const { load, store, wait } = Atomics;
4
+
5
+ const handle = (channel, i32a, options) => (data, ...rest) => {
6
+ channel.postMessage(data, ...rest);
7
+ wait(i32a, 0, 0);
8
+ store(i32a, 0, 0);
9
+ return options.ondata(i32a.subarray(2, 2 + load(i32a, 1)), ...rest);
10
+ };
11
+
12
+ export const handler = (promise, listener) => {
13
+ addEventListener('message', listener, { once: true });
14
+ return options => promise.then(
15
+ ([sab, main, channel]) => {
16
+ postMessage(1);
17
+ return handle(channel, new Int32Array(sab), { ...main, ...options });
18
+ }
19
+ );
20
+ };
21
+
22
+ export { withResolvers };
@@ -0,0 +1,18 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Document</title>
7
+ <script type="module">
8
+ import reflected from '../src/index.js';
9
+ const ref = await reflected('./worker.js', {
10
+ ondata: async (data, ...rest) => {
11
+ console.log('main', data, rest);
12
+ await new Promise(resolve => setTimeout(resolve, 1000));
13
+ return new Int32Array([6, 7, 8, 9, 10]);
14
+ }
15
+ });
16
+ </script>
17
+ </head>
18
+ </html>
package/test/worker.js ADDED
@@ -0,0 +1,10 @@
1
+ import reflected from '../src/index.js';
2
+
3
+ const sync = await reflected({
4
+ ondata: (data, ...rest) => {
5
+ console.log('worker', [...data], rest);
6
+ return data;
7
+ }
8
+ });
9
+
10
+ console.log('result', sync('test'));