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 +16 -0
- package/src/index.js +34 -0
- package/src/main/chrome.js +19 -0
- package/src/main/fallback.js +29 -0
- package/src/main/firefox.js +19 -0
- package/src/main/shared-id.js +1 -0
- package/src/main/shared.js +36 -0
- package/src/worker/chrome.js +8 -0
- package/src/worker/fallback.js +1 -0
- package/src/worker/firefox.js +8 -0
- package/src/worker/shared.js +22 -0
- package/test/index.html +18 -0
- package/test/worker.js +10 -0
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 @@
|
|
|
1
|
+
console.log('worker', 'epic fail');
|
|
@@ -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 };
|
package/test/index.html
ADDED
|
@@ -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>
|