reflected 0.0.1 → 0.0.3
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/README.md +28 -9
- package/dist/async-DF1WaSCr.js +1 -0
- package/dist/async.js +1 -1
- package/dist/{broadcast-BPNfR9nK.js → broadcast-D0xibjmN.js} +1 -1
- package/dist/{broadcast-CwqVSk2p.js → broadcast-Dc7wAEE6.js} +1 -1
- package/dist/broadcast.js +1 -1
- package/dist/channel-CdS9bLt4.js +1 -0
- package/dist/index.js +1 -1
- package/dist/{message-CgNarJIa.js → message-D7s0I-EX.js} +1 -1
- package/dist/{message-YPvgpfHS.js → message-DLH7cJo6.js} +1 -1
- package/dist/message.js +1 -1
- package/dist/{sender-23_Enxlo.js → sender-BMLGKAss.js} +1 -1
- package/dist/shared-C_kd5Il-.js +1 -0
- package/dist/shared-MkVjuKUg.js +1 -0
- package/dist/sw.js +1 -1
- package/dist/{xhr-CYct95wr.js → xhr-D5y-AocB.js} +1 -1
- package/dist/xhr-DAG_4oRo.js +1 -0
- package/dist/xhr.js +1 -1
- package/package.json +7 -3
- package/rollup.js +2 -2
- package/src/channel.js +1 -1
- package/src/index.js +3 -0
- package/src/main/async.js +2 -2
- package/src/main/shared.js +3 -3
- package/src/main/xhr.js +4 -7
- package/src/service/listeners.js +2 -2
- package/src/worker/async.js +7 -7
- package/src/worker/shared.js +6 -6
- package/src/worker/xhr.js +3 -3
- package/test/README/index.html +11 -0
- package/test/{readme.html → README/index.js} +3 -15
- package/test/README/mini-coi.js +28 -0
- package/test/{readme.js → README/worker.js} +2 -2
- package/test/index.js +1 -1
- package/test/sw.js +1 -1
- package/test/worker.js +1 -1
- package/types/broadcast.d.ts +1 -1
- package/types/channel.d.ts +1 -1
- package/types/index.d.ts +4 -0
- package/types/main/shared.d.ts +2 -2
- package/types/message.d.ts +1 -1
- package/types/reflected/src/main/shared.d.ts +1 -1
- package/types/reflected/src/worker/shared.d.ts +1 -1
- package/types/worker/broadcast.d.ts +1 -1
- package/types/worker/message.d.ts +1 -1
- package/types/worker/shared.d.ts +4 -4
- package/dist/async-Cn7CWifh.js +0 -1
- package/dist/channel-BwDpH9fR.js +0 -1
- package/dist/shared-D9sRqO2H.js +0 -1
- package/dist/shared-DdAq4SGF.js +0 -1
- package/dist/xhr-C97Ssg1V.js +0 -1
package/README.md
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
# reflected
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
<sup>**Social Media Photo by [Marc-Olivier Jodoin](https://unsplash.com/@marcojodoin) on [Unsplash](https://unsplash.com/)**</sup>
|
|
4
|
+
|
|
5
|
+
A primitive to allow workers to call **synchronously** any functionality exposed on the main thread.
|
|
6
|
+
|
|
7
|
+
## Strategies
|
|
4
8
|
|
|
5
9
|
This module uses 3 synchronous strategies + 1 asynchronous fallback:
|
|
6
10
|
|
|
@@ -9,14 +13,23 @@ This module uses 3 synchronous strategies + 1 asynchronous fallback:
|
|
|
9
13
|
* **xhr** based on synchronous *XMLHttpRequest* and a dedicated *ServiceWorker* able to intercept such *POST* requests, broadcast to all listening channels the request and resolve as response for the worker.
|
|
10
14
|
* **async** which will always return a *Promise* and will not need special headers or *ServiceWorker*. This is also a fallback for the *xhr* case if the `serviceWorker` option field has not been provided.
|
|
11
15
|
|
|
12
|
-
All strategies are automatically detected through the default/main `import` but all dedicated strategies can be retrieved directly, example:
|
|
16
|
+
All strategies are automatically detected through the default/main `import` but all dedicated strategies can be retrieved directly, for example:
|
|
13
17
|
|
|
14
18
|
* `import reflect from 'reflected'` will decide automatically which strategy should be used.
|
|
15
19
|
* `import reflect from 'reflected/message'` will return the right *message* based module on the main thread and the worker mode within the worker.
|
|
16
20
|
* `import reflect from 'reflected/main/message'` will return the *message* strategy for the main thread only. This requires the least amount of bandwidth when you are sure that *message* strategy will work on main.
|
|
17
21
|
* `import reflect from 'reflected/worker/message'` will return the *message* strategy for the worker thread only. This requires the least amount of bandwidth when you are sure that *message* strategy will work within the worker.
|
|
18
22
|
|
|
19
|
-
Swap `message` with `broadcast`, `xhr` or `async`, and all exports will work equally well
|
|
23
|
+
Swap `message` with `broadcast`, `xhr` or `async`, and all exports will work equally well according to the chosen "*channel strategy*".
|
|
24
|
+
|
|
25
|
+
| Import | Use case |
|
|
26
|
+
|--------|----------|
|
|
27
|
+
| `reflected` | Auto-pick strategy (main + worker) |
|
|
28
|
+
| `reflected/message` \| `broadcast` \| `xhr` \| `async` | Specific strategy, context-aware |
|
|
29
|
+
| `reflected/main/<strategy>` | Main thread only (smaller bundle) |
|
|
30
|
+
| `reflected/worker/<strategy>` | Worker only (smaller bundle) |
|
|
31
|
+
|
|
32
|
+
**Requirements:** Synchronous strategies except for `xhr` need [Cross-Origin Isolation](https://developer.mozilla.org/en-US/docs/Web/API/Window/crossOriginIsolated) (e.g. `Cross-Origin-Opener-Policy: same-origin` and `Cross-Origin-Embedder-Policy: require-corp`). The `async` strategy works without those headers and always returns a Promise.
|
|
20
33
|
|
|
21
34
|
### Worker Thread API
|
|
22
35
|
|
|
@@ -30,7 +43,7 @@ const reflected = await reflect({
|
|
|
30
43
|
// use this helper to transform such data into something
|
|
31
44
|
// that the worker can use/understand after invoke
|
|
32
45
|
// ⚠️ must be synchronous and it's invoked synchronously
|
|
33
|
-
|
|
46
|
+
onsync(response:Int32Array) {
|
|
34
47
|
return response.length ? response[0] : undefined;
|
|
35
48
|
},
|
|
36
49
|
|
|
@@ -45,7 +58,7 @@ const reflected = await reflect({
|
|
|
45
58
|
},
|
|
46
59
|
});
|
|
47
60
|
|
|
48
|
-
//
|
|
61
|
+
// retrieve the result of `test_sum(1, 2, 3)`
|
|
49
62
|
// directly from the main thread.
|
|
50
63
|
// only the async channel variant would need to await
|
|
51
64
|
const value = reflected({
|
|
@@ -74,16 +87,16 @@ const worker = await reflect(
|
|
|
74
87
|
// Worker scriptURL
|
|
75
88
|
'./worker.js',
|
|
76
89
|
// Worker options + required utilities / helpers
|
|
77
|
-
// ℹ️ type is enforced to be 'module' due top-level await
|
|
90
|
+
// ℹ️ type is enforced to be 'module' due to top-level await
|
|
78
91
|
{
|
|
79
92
|
// invoked when the worker asks to synchronize a call
|
|
80
|
-
// and it
|
|
93
|
+
// and it must return an Int32Array reference to populate
|
|
81
94
|
// the SharedArrayBuffer and notify/unlock the worker
|
|
82
95
|
// ℹ️ works even if synchronous but it's resolved asynchronously
|
|
83
96
|
// ⚠️ the worker is not responsive until this returns so
|
|
84
97
|
// be sure you handle errors gracefully to still provide
|
|
85
98
|
// a result the worker can consume out of the shared buffer!
|
|
86
|
-
async
|
|
99
|
+
async onsync(payload:unknown) {
|
|
87
100
|
const { invoke, args } = payload;
|
|
88
101
|
|
|
89
102
|
if (invoke === 'test_sum') {
|
|
@@ -124,4 +137,10 @@ const worker = await reflect(
|
|
|
124
137
|
const value = await worker.send({ any: 'payload' });
|
|
125
138
|
```
|
|
126
139
|
|
|
127
|
-
|
|
140
|
+
### Extras
|
|
141
|
+
|
|
142
|
+
- **Named export `channel`:** After initialization, `import reflect, { channel } from 'reflected'` gives the active strategy name (`'message'`, `'broadcast'`, `'xhr'`, or `'async'`).
|
|
143
|
+
- **Errors:** From main-thread `onsync`, return `new Int32Array(0)` (or a convention of your choice) so the worker always gets a result; handle that in the worker’s `onsync` to avoid hanging.
|
|
144
|
+
- **Types:** you can import `MainOptions` and `WorkerOptions` from the root of the porject because *main* `reflect(string, MainOptions)` and *worker* `reflect(WorkerOptions)` are different in a subtle way you probably don't want to mess around with (in particular, the `onsync` which must be sync on the *worker* side of affairs or it cannnot work)
|
|
145
|
+
|
|
146
|
+
Test [live](https://webreflection.github.io/reflected/test/README/) or read the [main thread](./test/README/index.js) and [worker thread](./test/README/worker.js) code.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{w as e}from"./with-resolvers-CHEvl4oe.js";import{i as s}from"./i32-C78nBJH2.js";import{s as t}from"./sender-BMLGKAss.js";import"./channel-CdS9bLt4.js";const{promise:r,resolve:a}=e();addEventListener("message",({data:[e,s,t]})=>a([e,s,t]),{once:!0});const n="async";var o=a=>r.then(([r,n,o])=>(postMessage(1),((t,r,a)=>{const n=new BroadcastChannel(t),o=s(),d=new Map;return n.addEventListener("message",({data:[e,s]})=>{r.set(s,0),d.get(e)(a.onsync(r.subarray(2,2+r[1]))),d.delete(e)}),(s,...t)=>{const{promise:r,resolve:a}=e(),i=o();return d.set(i,a),n.postMessage([i,s],...t),r}})(o,new Int32Array(r),t({...n,...a}))));export{n as channel,o as default};
|
package/dist/async.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
const e=Promise.withResolvers||function(){var e,t,s=new this((s,n)=>{e=s,t=n});return{resolve:e,reject:t,promise:s}};var t=e.bind(Promise),s=e=>{const t=new Int32Array(1);return()=>t[0]++},n="
|
|
1
|
+
const e=Promise.withResolvers||function(){var e,t,s=new this((s,n)=>{e=s,t=n});return{resolve:e,reject:t,promise:s}};var t=e.bind(Promise),s=e=>{const t=new Int32Array(1);return()=>t[0]++},n="fc260aad-4404-43b8-ae9d-2c06554bb294";const{isArray:r}=Array,a=e=>e;class o extends Worker{#e;#t;constructor(e,t){super(e,t),this.#e=s(),this.#t=new Map,t.onsend||(t.onsend=a),super.addEventListener("message",async e=>{const{data:s}=e;if(r(s)&&s[0]===n){e.stopImmediatePropagation(),e.preventDefault();const[n,r]=s[1],a=this.#t.get(n);this.#t.delete(n),a(await t.onsend(r))}})}send(e,...s){const r=this.#e(),{promise:a,resolve:o}=t();return this.#t.set(r,o),super.postMessage([n,[r,e]],...s),a}}let{SharedArrayBuffer:c}=globalThis;try{new c(4,{maxByteLength:8})}catch(e){c=class extends ArrayBuffer{get growable(){return super.resizable}grow(e){super.resize(e)}}}const i=2*Int32Array.BYTES_PER_ELEMENT,d=e=>{switch(typeof e){case"symbol":case"function":return!1}return!0};let u=!0;try{crypto.randomUUID()}catch(e){u=!1}const p=u?()=>crypto.randomUUID():()=>(Date.now()+Math.random()).toString(36),y="async";var g=(e=>(s,n)=>{const{promise:r,resolve:a}=t();return new e(s,n,a),r})(class extends o{constructor(e,t,s){const n=p(),r=new BroadcastChannel(n),a=(({initByteLength:e=1024,maxByteLength:t=8192})=>new c(i+e,{maxByteLength:i+t}))(t),o=new Int32Array(a),u=((e,t)=>{const s=new Int32Array(e);return async({data:n})=>{const r=await t.onsync(n),a=r.length,o=i+r.buffer.byteLength;e.byteLength<o&&e.grow(o),s.set(r,2),s[1]=a,s[0]=1}})(a,t);r.addEventListener("message",async({data:[e,t]})=>{await u({data:t}),r.postMessage([e,o.slice(0,2+o[1])])}),super(...((e,t,s)=>{const n=new URL(e,location.href);return n.searchParams.set("reflected",t),[n,{...s,type:"module"}]})(e,y,t)),super.addEventListener("message",()=>s(this),{once:!0}),super.postMessage(((e,t)=>{const s={};for(const e in t){const n=t[e];d(e)&&d(n)&&(s[e]=n)}return[e,s]})(a,t).concat(n))}get channel(){return y}});const{isArray:l}=Array;const{promise:h,resolve:m}=t();addEventListener("message",({data:[e,t,s]})=>m([e,t,s]),{once:!0});const w="async";var f="importScripts"in globalThis?e=>h.then(([r,a,o])=>(postMessage(1),((e,n,r)=>{const a=new BroadcastChannel(e),o=s(),c=new Map;return a.addEventListener("message",({data:[e,t]})=>{n.set(t,0),c.get(e)(r.onsync(n.subarray(2,2+n[1]))),c.delete(e)}),(e,...s)=>{const{promise:n,resolve:r}=t(),i=o();return c.set(i,r),a.postMessage([i,e],...s),n}})(o,new Int32Array(r),(e=>(addEventListener("message",async t=>{const{data:s}=t;if(l(s)&&s[0]===n){t.stopImmediatePropagation(),t.preventDefault();const[r,a]=s[1];postMessage([n,[r,await e.onsend(a)]])}}),e))({...a,...e})))):g;export{w as channel,f as default};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{b as s,S as e,a,h as r,u as t,p as n}from"./shared-
|
|
1
|
+
import{b as s,S as e,a,h as r,u as t,p as n}from"./shared-MkVjuKUg.js";import{r as o}from"./shared-RFmxa5x4.js";import"./with-resolvers-CHEvl4oe.js";import"./i32-C78nBJH2.js";import"./channel-CdS9bLt4.js";const c="broadcast";var d=s(class extends e{constructor(s,e,d){const p=o(),i=new BroadcastChannel(p),m=a(e);i.addEventListener("message",r(m,e,!0)),super(...t(s,c,e)),super.addEventListener("message",()=>d(this),{once:!0}),super.postMessage(n(m,e).concat(p))}get channel(){return c}});export{d as default};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{w as r}from"./with-resolvers-CHEvl4oe.js";import{h as s}from"./shared-
|
|
1
|
+
import{w as r}from"./with-resolvers-CHEvl4oe.js";import{h as s}from"./shared-C_kd5Il-.js";import"./sender-BMLGKAss.js";import"./channel-CdS9bLt4.js";const{promise:a,resolve:e}=r(),o="broadcast";var t=s(a,({data:[r,s,a]})=>e([r,s,new BroadcastChannel(a)]));export{o as channel,t as default};
|
package/dist/broadcast.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
const e=Promise.withResolvers||function(){var e,t,s=new this((s,n)=>{e=s,t=n});return{resolve:e,reject:t,promise:s}};var t=e.bind(Promise),s="
|
|
1
|
+
const e=Promise.withResolvers||function(){var e,t,s=new this((s,n)=>{e=s,t=n});return{resolve:e,reject:t,promise:s}};var t=e.bind(Promise),s="fc260aad-4404-43b8-ae9d-2c06554bb294";const{isArray:n}=Array,r=e=>e;class a extends Worker{#e;#t;constructor(e,t){super(e,t),this.#e=(()=>{const e=new Int32Array(1);return()=>e[0]++})(),this.#t=new Map,t.onsend||(t.onsend=r),super.addEventListener("message",async e=>{const{data:r}=e;if(n(r)&&r[0]===s){e.stopImmediatePropagation(),e.preventDefault();const[s,n]=r[1],a=this.#t.get(s);this.#t.delete(s),a(await t.onsend(n))}})}send(e,...n){const r=this.#e(),{promise:a,resolve:o}=t();return this.#t.set(r,o),super.postMessage([s,[r,e]],...n),a}}const{notify:o,store:c}=Atomics,i=2*Int32Array.BYTES_PER_ELEMENT,d=e=>{switch(typeof e){case"symbol":case"function":return!1}return!0};let u=!0;try{crypto.randomUUID()}catch(e){u=!1}const p=u?()=>crypto.randomUUID():()=>(Date.now()+Math.random()).toString(36),y="broadcast";var h=(e=>(s,n)=>{const{promise:r,resolve:a}=t();return new e(s,n,a),r})(class extends a{constructor(e,t,s){const n=p(),r=new BroadcastChannel(n),a=(({initByteLength:e=1024,maxByteLength:t=8192})=>new SharedArrayBuffer(i+e,{maxByteLength:i+t}))(t);r.addEventListener("message",((e,t)=>{const s=new Int32Array(e);return async({data:n})=>{const r=await t.onsync(n),a=r.length,d=i+r.buffer.byteLength;e.byteLength<d&&e.grow(d),s.set(r,2),c(s,1,a),c(s,0,1),o(s,0)}})(a,t)),super(...((e,t,s)=>{const n=new URL(e,location.href);return n.searchParams.set("reflected",t),[n,{...s,type:"module"}]})(e,y,t)),super.addEventListener("message",()=>s(this),{once:!0}),super.postMessage(((e,t)=>{const s={};for(const e in t){const n=t[e];d(e)&&d(n)&&(s[e]=n)}return[e,s]})(a,t).concat(n))}get channel(){return y}});const{isArray:m}=Array;const{load:g,store:l,wait:w}=Atomics,{promise:f,resolve:v}=t(),b="broadcast";var L=((e,t)=>(addEventListener("message",t,{once:!0}),t=>e.then(([e,n,r])=>(postMessage(1),((e,t,s)=>(n,...r)=>(e.postMessage(n,...r),w(t,0,0),l(t,0,0),s.onsync(t.subarray(2,2+g(t,1)))))(r,new Int32Array(e),(e=>(addEventListener("message",async t=>{const{data:n}=t;if(m(n)&&n[0]===s){t.stopImmediatePropagation(),t.preventDefault();const[r,a]=n[1];postMessage([s,[r,await e.onsend(a)]])}}),e))({...n,...t}))))))(f,({data:[e,t,s]})=>v([e,t,new BroadcastChannel(s)])),A="importScripts"in globalThis?L:h;export{b as channel,A as default};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var a="fc260aad-4404-43b8-ae9d-2c06554bb294";export{a as S};
|
package/dist/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{w as a}from"./with-resolvers-CHEvl4oe.js";import{n as s}from"./shared-array-buffer-cwdMr2mc.js";let e,t;if("importScripts"in globalThis){let s;const{promise:r,resolve:o}=a(),i=new URL(location).searchParams.get("reflected");e=i,s="message"===i?import("./message-
|
|
1
|
+
import{w as a}from"./with-resolvers-CHEvl4oe.js";import{n as s}from"./shared-array-buffer-cwdMr2mc.js";let e,t;if("importScripts"in globalThis){let s;const{promise:r,resolve:o}=a(),i=new URL(location).searchParams.get("reflected");e=i,s="message"===i?import("./message-D7s0I-EX.js"):"broadcast"===i?import("./broadcast-Dc7wAEE6.js"):"xhr"===i?import("./xhr-D5y-AocB.js"):import("./async-DF1WaSCr.js"),t=async a=>{const{data:e,ports:t}=await r,{default:o}=await s,i=new Event("message");return i.data=e,i.ports=t,dispatchEvent(i),o(a)},addEventListener("message",o,{once:!0})}else s?"InstallTrigger"in globalThis?(e="broadcast",t=(await import("./broadcast-D0xibjmN.js")).default):(e="message",t=(await import("./message-DLH7cJo6.js")).default):navigator.serviceWorker?(e="xhr",t=(await import("./xhr-DAG_4oRo.js")).default):(e="fallback",t=()=>{});var r=t;export{e as channel,r as default};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{w as s}from"./with-resolvers-CHEvl4oe.js";import{h as r}from"./shared-
|
|
1
|
+
import{w as s}from"./with-resolvers-CHEvl4oe.js";import{h as r}from"./shared-C_kd5Il-.js";import"./sender-BMLGKAss.js";import"./channel-CdS9bLt4.js";const{promise:e,resolve:o}=s(),a="message";var t=r(e,({data:[s,r],ports:[e]})=>o([s,r,e]));export{a as channel,t as default};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{b as s,S as e,a as t,h as r,u as a,p as n}from"./shared-
|
|
1
|
+
import{b as s,S as e,a as t,h as r,u as a,p as n}from"./shared-MkVjuKUg.js";import"./with-resolvers-CHEvl4oe.js";import"./i32-C78nBJH2.js";import"./channel-CdS9bLt4.js";const o="message";var p=s(class extends e{constructor(s,e,p){const{port1:c,port2:i}=new MessageChannel,d=t(e);c.addEventListener(o,r(d,e,!0)),c.start(),super(...a(s,o,e)),super.addEventListener(o,()=>p(this),{once:!0}),super.postMessage(n(d,e),[i])}get channel(){return o}});export{p as default};
|
package/dist/message.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
const e=Promise.withResolvers||function(){var e,t,s=new this((s,n)=>{e=s,t=n});return{resolve:e,reject:t,promise:s}};var t=e.bind(Promise),s="
|
|
1
|
+
const e=Promise.withResolvers||function(){var e,t,s=new this((s,n)=>{e=s,t=n});return{resolve:e,reject:t,promise:s}};var t=e.bind(Promise),s="fc260aad-4404-43b8-ae9d-2c06554bb294";const{isArray:n}=Array,r=e=>e;class a extends Worker{#e;#t;constructor(e,t){super(e,t),this.#e=(()=>{const e=new Int32Array(1);return()=>e[0]++})(),this.#t=new Map,t.onsend||(t.onsend=r),super.addEventListener("message",async e=>{const{data:r}=e;if(n(r)&&r[0]===s){e.stopImmediatePropagation(),e.preventDefault();const[s,n]=r[1],a=this.#t.get(s);this.#t.delete(s),a(await t.onsend(n))}})}send(e,...n){const r=this.#e(),{promise:a,resolve:o}=t();return this.#t.set(r,o),super.postMessage([s,[r,e]],...n),a}}const{notify:o,store:c}=Atomics,i=2*Int32Array.BYTES_PER_ELEMENT,d=e=>{switch(typeof e){case"symbol":case"function":return!1}return!0},u="message";var p=(e=>(s,n)=>{const{promise:r,resolve:a}=t();return new e(s,n,a),r})(class extends a{constructor(e,t,s){const{port1:n,port2:r}=new MessageChannel,a=(({initByteLength:e=1024,maxByteLength:t=8192})=>new SharedArrayBuffer(i+e,{maxByteLength:i+t}))(t);n.addEventListener(u,((e,t)=>{const s=new Int32Array(e);return async({data:n})=>{const r=await t.onsync(n),a=r.length,d=i+r.buffer.byteLength;e.byteLength<d&&e.grow(d),s.set(r,2),c(s,1,a),c(s,0,1),o(s,0)}})(a,t)),n.start(),super(...((e,t,s)=>{const n=new URL(e,location.href);return n.searchParams.set("reflected",t),[n,{...s,type:"module"}]})(e,u,t)),super.addEventListener(u,()=>s(this),{once:!0}),super.postMessage(((e,t)=>{const s={};for(const e in t){const n=t[e];d(e)&&d(n)&&(s[e]=n)}return[e,s]})(a,t),[r])}get channel(){return u}});const{isArray:y}=Array;const{load:g,store:h,wait:m}=Atomics,{promise:l,resolve:f}=t(),v="message";var w=((e,t)=>(addEventListener("message",t,{once:!0}),t=>e.then(([e,n,r])=>(postMessage(1),((e,t,s)=>(n,...r)=>(e.postMessage(n,...r),m(t,0,0),h(t,0,0),s.onsync(t.subarray(2,2+g(t,1)))))(r,new Int32Array(e),(e=>(addEventListener("message",async t=>{const{data:n}=t;if(y(n)&&n[0]===s){t.stopImmediatePropagation(),t.preventDefault();const[r,a]=n[1];postMessage([s,[r,await e.onsend(a)]])}}),e))({...n,...t}))))))(l,({data:[e,t],ports:[s]})=>f([e,t,s])),L="importScripts"in globalThis?w:p;export{v as channel,L as default};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{S as a}from"./channel-
|
|
1
|
+
import{S as a}from"./channel-CdS9bLt4.js";const{isArray:s}=Array;var e=e=>(addEventListener("message",async t=>{const{data:n}=t;if(s(n)&&n[0]===a){t.stopImmediatePropagation(),t.preventDefault();const[s,o]=n[1];postMessage([a,[s,await e.onsend(o)]])}}),e);export{e as s};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{s}from"./sender-BMLGKAss.js";const{load:e,store:t,wait:a}=Atomics,o=(o,n)=>(addEventListener("message",n,{once:!0}),n=>o.then(([o,r,c])=>(postMessage(1),((s,o,n)=>(r,...c)=>(s.postMessage(r,...c),a(o,0,0),t(o,0,0),n.onsync(o.subarray(2,2+e(o,1)))))(c,new Int32Array(o),s({...r,...n})))));export{o as h};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{w as e}from"./with-resolvers-CHEvl4oe.js";import{i as t}from"./i32-C78nBJH2.js";import{S as s}from"./channel-CdS9bLt4.js";const{isArray:r}=Array,n=e=>e;class o extends Worker{#e;#t;constructor(e,o){super(e,o),this.#e=t(),this.#t=new Map,o.onsend||(o.onsend=n),super.addEventListener("message",async e=>{const{data:t}=e;if(r(t)&&t[0]===s){e.stopImmediatePropagation(),e.preventDefault();const[s,r]=t[1],n=this.#t.get(s);this.#t.delete(s),n(await o.onsend(r))}})}send(t,...r){const n=this.#e(),{promise:o,resolve:a}=e();return this.#t.set(n,a),super.postMessage([s,[n,t]],...r),o}}const{notify:a,store:i}=Atomics,c=2*Int32Array.BYTES_PER_ELEMENT,u=({initByteLength:e=1024,maxByteLength:t=8192})=>new SharedArrayBuffer(c+e,{maxByteLength:c+t}),h=t=>(s,r)=>{const{promise:n,resolve:o}=e();return new t(s,r,o),n},m=(e,t,s)=>{const r=new Int32Array(e);return async({data:n})=>{const o=await t.onsync(n),u=o.length,h=c+o.buffer.byteLength;e.byteLength<h&&e.grow(h),r.set(o,2),s?(i(r,1,u),i(r,0,1),a(r,0)):(r[1]=u,r[0]=1)}},p=e=>{switch(typeof e){case"symbol":case"function":return!1}return!0},y=(e,t)=>{const s={};for(const e in t){const r=t[e];p(e)&&p(r)&&(s[e]=r)}return[e,s]},d=(e,t,s)=>{const r=new URL(e,location.href);return r.searchParams.set("reflected",t),[r,{...s,type:"module"}]};export{o as S,u as a,h as b,m as h,c as m,y as p,d as u};
|
package/dist/sw.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
const e=Promise.withResolvers||function(){var e,t,n=new this((n,s)=>{e=n,t=s});return{resolve:e,reject:t,promise:n}};var t=e.bind(Promise);const[n,s]=((e=e=>e)=>{const n=new Map,s=(()=>{const e=new Int32Array(1);return()=>e[0]++})();return[()=>{let
|
|
1
|
+
const e=Promise.withResolvers||function(){var e,t,n=new this((n,s)=>{e=n,t=s});return{resolve:e,reject:t,promise:n}};var t=e.bind(Promise);const[n,s]=((e=e=>e)=>{const n=new Map,s=(()=>{const e=new Int32Array(1);return()=>e[0]++})();return[()=>{let a;do{a=e(s())}while(n.has(a));const r=t();return n.set(a,r),[a,r.promise]},(e,t,s)=>{const a=n.get(e);n.delete(e),s?a?.reject(s):a?.resolve(t)}]})(),{protocol:a,host:r,pathname:o}=location,i=`${a}//${r}${o}`,c=new BroadcastChannel("fc260aad-4404-43b8-ae9d-2c06554bb294");c.addEventListener("message",({data:[e,t]})=>{if("response"===e){const[e,n]=t;s(e,`[${n.join(",")}]`)}});const d=async e=>{const[t,s]=n();return c.postMessage(["request",[t,e]]),new Response(await s)};addEventListener("activate",e=>e.waitUntil(clients.claim())),addEventListener("fetch",async e=>{const{request:t}=e;"POST"===t.method&&t.url.startsWith(i)&&(e.stopImmediatePropagation(),e.respondWith(t.json().then(d)),e.preventDefault())}),addEventListener("install",()=>skipWaiting());
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{w as e}from"./with-resolvers-CHEvl4oe.js";import{i as s}from"./i32-C78nBJH2.js";import{s as t}from"./sender-
|
|
1
|
+
import{w as e}from"./with-resolvers-CHEvl4oe.js";import{i as s}from"./i32-C78nBJH2.js";import{s as t}from"./sender-BMLGKAss.js";import"./channel-CdS9bLt4.js";const{parse:r,stringify:n}=JSON,{promise:o,resolve:a}=e();addEventListener("message",({data:[e,s,t]})=>a([e,s,t]),{once:!0});const p="xhr";var i=e=>o.then(([o,a,p])=>(postMessage(1),((e,t,o)=>{const a=new BroadcastChannel(e),p=s(),{serviceWorker:i}=o;return(s,...c)=>{const d=p();a.postMessage([d,s],...c);const m=new XMLHttpRequest;return m.open("POST",i,!1),m.setRequestHeader("Content-Type","application/json"),m.send(n([d,e])),t.set(r(m.responseText),0),o.onsync(t.subarray(2,2+t[1]))}})(p,new Int32Array(o),t({...a,...e}))));export{p as channel,i as default};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{w as e}from"./with-resolvers-CHEvl4oe.js";import{m as t,S as s,u as n,p as a,h as r}from"./shared-MkVjuKUg.js";import{S as o}from"./shared-array-buffer-cwdMr2mc.js";import{r as i}from"./shared-RFmxa5x4.js";import{S as c}from"./channel-CdS9bLt4.js";import"./i32-C78nBJH2.js";var l=({initByteLength:e=1024,maxByteLength:s=8192})=>new o(t+e,{maxByteLength:t+s});const d="async";let h=class extends s{constructor(e,t,s){const o=i(),c=new BroadcastChannel(o),h=l(t),p=new Int32Array(h),g=r(h,t,!1);c.addEventListener("message",async({data:[e,t]})=>{await g({data:t}),c.postMessage([e,p.slice(0,2+p[1])])}),super(...n(e,d,t)),super.addEventListener("message",()=>s(this),{once:!0}),super.postMessage(a(h,t).concat(o))}get channel(){return d}};const p=new Map,g=new BroadcastChannel(c);g.addEventListener("message",async({data:[e,t]})=>{if("request"===e){const[e,[s,n]]=t,a=p.get(n);a&&(g.postMessage(["response",[e,await a.get(s)]]),a.delete(s))}});const{promise:u,resolve:m}=e();let f=!0;const v=(e,t)=>{let s,n=!0,{url:a}=t;e.getRegistration(a).then(s=>s??e.register(a,t)).then(function r(o){const{controller:i}=e;if(n=n&&!!i,s=o.installing||o.waiting||o.active,!s)return v(e,t);if("activated"===s.state){if(n){if(i.scriptURL===a)return m();o.unregister()}location.reload()}else s.addEventListener("statechange",()=>r(o),{once:!0})})};class w extends s{#e;constructor(t,s,o){if(f){f=!1;let{serviceWorker:e}=s;if(!e)return new h(t,s,o);"string"==typeof e&&(e={url:e}),e.url=new URL(e.url,location.href).href,v(navigator.serviceWorker,e)}const c=i(),d=new BroadcastChannel(c),g=l(s),u=new Map,m=new Int32Array(g),w=r(g,s,!1);p.set(c,u),d.addEventListener("message",async({data:[t,s]})=>{const{promise:n,resolve:a}=e();u.set(t,n),await w({data:s}),a(m.slice(0,2+m[1]))}),super(...n(t,"xhr",s)),super.addEventListener("message",()=>o(this),{once:!0}),super.postMessage(a(g,s).concat(c)),this.#e=c}terminate(){p.delete(this.#e),super.terminate()}get channel(){return"xhr"}}var y=(t,s)=>{const{promise:n,resolve:a}=e();return new w(t,s,a)instanceof h?n:u.then(()=>n)};export{y as default};
|
package/dist/xhr.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
const e=Promise.withResolvers||function(){var e,t,s=new this((s,n)=>{e=s,t=n});return{resolve:e,reject:t,promise:s}};var t=e.bind(Promise),s=e=>{const t=new Int32Array(1);return()=>t[0]++},n="
|
|
1
|
+
const e=Promise.withResolvers||function(){var e,t,s=new this((s,n)=>{e=s,t=n});return{resolve:e,reject:t,promise:s}};var t=e.bind(Promise),s=e=>{const t=new Int32Array(1);return()=>t[0]++},n="fc260aad-4404-43b8-ae9d-2c06554bb294";const{isArray:r}=Array,a=e=>e;class o extends Worker{#e;#t;constructor(e,t){super(e,t),this.#e=s(),this.#t=new Map,t.onsend||(t.onsend=a),super.addEventListener("message",async e=>{const{data:s}=e;if(r(s)&&s[0]===n){e.stopImmediatePropagation(),e.preventDefault();const[n,r]=s[1],a=this.#t.get(n);this.#t.delete(n),a(await t.onsend(r))}})}send(e,...s){const r=this.#e(),{promise:a,resolve:o}=t();return this.#t.set(r,o),super.postMessage([n,[r,e]],...s),a}}let{SharedArrayBuffer:c}=globalThis;try{new c(4,{maxByteLength:8})}catch(e){c=class extends ArrayBuffer{get growable(){return super.resizable}grow(e){super.resize(e)}}}const i=2*Int32Array.BYTES_PER_ELEMENT,d=(e,t,s)=>{const n=new Int32Array(e);return async({data:s})=>{const r=await t.onsync(s),a=r.length,o=i+r.buffer.byteLength;e.byteLength<o&&e.grow(o),n.set(r,2),n[1]=a,n[0]=1}},l=e=>{switch(typeof e){case"symbol":case"function":return!1}return!0},u=(e,t)=>{const s={};for(const e in t){const n=t[e];l(e)&&l(n)&&(s[e]=n)}return[e,s]},p=(e,t,s)=>{const n=new URL(e,location.href);return n.searchParams.set("reflected",t),[n,{...s,type:"module"}]};var g=({initByteLength:e=1024,maxByteLength:t=8192})=>new c(i+e,{maxByteLength:i+t});let h=!0;try{crypto.randomUUID()}catch(e){h=!1}const y=h?()=>crypto.randomUUID():()=>(Date.now()+Math.random()).toString(36),w="async";let m=class extends o{constructor(e,t,s){const n=y(),r=new BroadcastChannel(n),a=g(t),o=new Int32Array(a),c=d(a,t);r.addEventListener("message",async({data:[e,t]})=>{await c({data:t}),r.postMessage([e,o.slice(0,2+o[1])])}),super(...p(e,w,t)),super.addEventListener("message",()=>s(this),{once:!0}),super.postMessage(u(a,t).concat(n))}get channel(){return w}};const f=new Map,v=new BroadcastChannel(n);v.addEventListener("message",async({data:[e,t]})=>{if("request"===e){const[e,[s,n]]=t,r=f.get(n);r&&(v.postMessage(["response",[e,await r.get(s)]]),r.delete(s))}});const{promise:L,resolve:x}=t();let E=!0;const M=(e,t)=>{let s,n=!0,{url:r}=t;e.getRegistration(r).then(s=>s??e.register(r,t)).then(function a(o){const{controller:c}=e;if(n=n&&!!c,s=o.installing||o.waiting||o.active,!s)return M(e,t);if("activated"===s.state){if(n){if(c.scriptURL===r)return x();o.unregister()}location.reload()}else s.addEventListener("statechange",()=>a(o),{once:!0})})};let b=class extends o{#s;constructor(e,s,n){if(E){E=!1;let{serviceWorker:t}=s;if(!t)return new m(e,s,n);"string"==typeof t&&(t={url:t}),t.url=new URL(t.url,location.href).href,M(navigator.serviceWorker,t)}const r=y(),a=new BroadcastChannel(r),o=g(s),c=new Map,i=new Int32Array(o),l=d(o,s);f.set(r,c),a.addEventListener("message",async({data:[e,s]})=>{const{promise:n,resolve:r}=t();c.set(e,n),await l({data:s}),r(i.slice(0,2+i[1]))}),super(...p(e,"xhr",s)),super.addEventListener("message",()=>n(this),{once:!0}),super.postMessage(u(o,s).concat(r)),this.#s=r}terminate(){f.delete(this.#s),super.terminate()}get channel(){return"xhr"}};const{isArray:A}=Array;const{parse:B,stringify:I}=JSON,{promise:q,resolve:R}=t();addEventListener("message",({data:[e,t,s]})=>R([e,t,s]),{once:!0});const P="xhr";var T="importScripts"in globalThis?e=>q.then(([t,r,a])=>(postMessage(1),((e,t,n)=>{const r=new BroadcastChannel(e),a=s(),{serviceWorker:o}=n;return(s,...c)=>{const i=a();r.postMessage([i,s],...c);const d=new XMLHttpRequest;return d.open("POST",o,!1),d.setRequestHeader("Content-Type","application/json"),d.send(I([i,e])),t.set(B(d.responseText),0),n.onsync(t.subarray(2,2+t[1]))}})(a,new Int32Array(t),(e=>(addEventListener("message",async t=>{const{data:s}=t;if(A(s)&&s[0]===n){t.stopImmediatePropagation(),t.preventDefault();const[r,a]=s[1];postMessage([n,[r,await e.onsend(a)]])}}),e))({...r,...e})))):(e,s)=>{const{promise:n,resolve:r}=t();return new b(e,s,r)instanceof m?n:L.then(()=>n)};export{P as channel,T as default};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "reflected",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.3",
|
|
4
4
|
"description": "A primitive to allow workers to call synchronously any functionality exposed on the main thread.",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": {
|
|
@@ -61,8 +61,12 @@
|
|
|
61
61
|
"build": "rm -rf dist && rollup -c rollup.js && cp dist/sw.js test/sw.js && npm run types",
|
|
62
62
|
"types": "tsc --allowJs --checkJs --lib dom,esnext --module nodeNext --target esnext -d --emitDeclarationOnly --outDir ./types ./src/*.js ./src/*/*.js"
|
|
63
63
|
},
|
|
64
|
-
"keywords": [
|
|
65
|
-
|
|
64
|
+
"keywords": [
|
|
65
|
+
"worker",
|
|
66
|
+
"SharedArrayBuffer",
|
|
67
|
+
"synchronous"
|
|
68
|
+
],
|
|
69
|
+
"author": "Andrea Giammarchi (webreflection)",
|
|
66
70
|
"license": "MIT",
|
|
67
71
|
"type": "module",
|
|
68
72
|
"types": "./types/index.d.ts",
|
package/rollup.js
CHANGED
|
@@ -2,8 +2,8 @@ import { nodeResolve } from '@rollup/plugin-node-resolve';
|
|
|
2
2
|
import terser from '@rollup/plugin-terser';
|
|
3
3
|
|
|
4
4
|
import { writeFileSync } from 'fs';
|
|
5
|
-
|
|
6
|
-
writeFileSync(`./src/channel.js`, `export default '${crypto.randomUUID()}';`);
|
|
5
|
+
if (process.env.NEW_CHANNEL)
|
|
6
|
+
writeFileSync(`./src/channel.js`, `export default '${crypto.randomUUID()}';`);
|
|
7
7
|
|
|
8
8
|
const plugins = [nodeResolve()].concat(process.env.NO_MIN ? [] : [terser()]);
|
|
9
9
|
|
package/src/channel.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export default '
|
|
1
|
+
export default 'fc260aad-4404-43b8-ae9d-2c06554bb294';
|
package/src/index.js
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import withResolvers from '@webreflection/utils/with-resolvers';
|
|
2
2
|
import { native } from '@webreflection/utils/shared-array-buffer';
|
|
3
3
|
|
|
4
|
+
/** @typedef {import('./main/shared.js').Options} MainOptions */
|
|
5
|
+
/** @typedef {import('./worker/shared.js').Options} WorkerOptions */
|
|
6
|
+
|
|
4
7
|
/** @type {string} */
|
|
5
8
|
let channel;
|
|
6
9
|
|
package/src/main/async.js
CHANGED
|
@@ -12,8 +12,8 @@ export class Worker extends Sender {
|
|
|
12
12
|
const sab = SAB(options);
|
|
13
13
|
const i32a = new Int32Array(sab);
|
|
14
14
|
const handle = handler(sab, options, false);
|
|
15
|
-
bc.addEventListener('message', async ({ data: [id,
|
|
16
|
-
await handle({ data });
|
|
15
|
+
bc.addEventListener('message', async ({ data: [id, payload] }) => {
|
|
16
|
+
await handle({ data: payload });
|
|
17
17
|
bc.postMessage([id, i32a.slice(0, 2 + i32a[1])]);
|
|
18
18
|
});
|
|
19
19
|
super(...url(scriptURL, CHANNEL, options));
|
package/src/main/shared.js
CHANGED
|
@@ -22,8 +22,8 @@ export const SAB = ({
|
|
|
22
22
|
|
|
23
23
|
/**
|
|
24
24
|
* @typedef {Object} Options
|
|
25
|
-
* @property {(
|
|
26
|
-
* @property {(
|
|
25
|
+
* @property {(payload: unknown) => Int32Array | Promise<Int32Array>} onsync invoked when the worker expect a response as `Int32Array` to populate the SharedArrayBuffer with.
|
|
26
|
+
* @property {(payload: unknown) => unknown | Promise<unknown>} [onsend] invoked when the worker replies to a `worker.send(data)` call.
|
|
27
27
|
* @property {number} [initByteLength=1024] defines the initial byte length of the SharedArrayBuffer.
|
|
28
28
|
* @property {number} [maxByteLength=8192] defines the maximum byte length (growth) of the SharedArrayBuffer.
|
|
29
29
|
* @property {string | ServiceWorkerOptions} [serviceWorker] defines the service worker to use as fallback if SharedArrayBuffer is not supported. If not defined, the `async` fallback will be used so that no `sync` operations from the worker will be possible.
|
|
@@ -52,7 +52,7 @@ export const bootstrap = Worker => {
|
|
|
52
52
|
export const handler = (sab, options, useAtomics) => {
|
|
53
53
|
const i32a = new Int32Array(sab);
|
|
54
54
|
return async ({ data }) => {
|
|
55
|
-
const result = await options.
|
|
55
|
+
const result = await options.onsync(data);
|
|
56
56
|
const length = result.length;
|
|
57
57
|
const requiredByteLength = minByteLength + result.buffer.byteLength;
|
|
58
58
|
if (sab.byteLength < requiredByteLength) sab.grow(requiredByteLength);
|
package/src/main/xhr.js
CHANGED
|
@@ -18,11 +18,8 @@ sharedBC.addEventListener('message', async ({ data: [op, details] }) => {
|
|
|
18
18
|
const [uid, [id, channel]] = details;
|
|
19
19
|
const responses = channels.get(channel);
|
|
20
20
|
if (responses) {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
responses.delete(id);
|
|
24
|
-
sharedBC.postMessage(['response', [uid, await promise]]);
|
|
25
|
-
}
|
|
21
|
+
sharedBC.postMessage(['response', [uid, await responses.get(id)]]);
|
|
22
|
+
responses.delete(id);
|
|
26
23
|
}
|
|
27
24
|
}
|
|
28
25
|
});
|
|
@@ -75,10 +72,10 @@ class Worker extends Sender {
|
|
|
75
72
|
const i32a = new Int32Array(sab);
|
|
76
73
|
const handle = handler(sab, options, false);
|
|
77
74
|
channels.set(channel, responses);
|
|
78
|
-
bc.addEventListener('message', async ({ data: [id,
|
|
75
|
+
bc.addEventListener('message', async ({ data: [id, payload] }) => {
|
|
79
76
|
const { promise, resolve } = withResolvers();
|
|
80
77
|
responses.set(id, promise);
|
|
81
|
-
await handle({ data });
|
|
78
|
+
await handle({ data: payload });
|
|
82
79
|
resolve(i32a.slice(0, 2 + i32a[1]));
|
|
83
80
|
});
|
|
84
81
|
super(...url(scriptURL, CHANNEL, options));
|
package/src/service/listeners.js
CHANGED
|
@@ -10,8 +10,8 @@ const url = `${protocol}//${host}${pathname}`;
|
|
|
10
10
|
const bc = new BroadcastChannel(CHANNEL);
|
|
11
11
|
bc.addEventListener('message', ({ data: [op, details] }) => {
|
|
12
12
|
if (op === 'response') {
|
|
13
|
-
const [uid,
|
|
14
|
-
resolve(uid, `[${
|
|
13
|
+
const [uid, payload] = details;
|
|
14
|
+
resolve(uid, `[${payload.join(',')}]`);
|
|
15
15
|
}
|
|
16
16
|
});
|
|
17
17
|
|
package/src/worker/async.js
CHANGED
|
@@ -17,17 +17,17 @@ const handle = (channel, i32a, options) => {
|
|
|
17
17
|
const bc = new BroadcastChannel(channel);
|
|
18
18
|
const next = i32();
|
|
19
19
|
const map = new Map;
|
|
20
|
-
bc.addEventListener('message', ({ data: [id,
|
|
21
|
-
|
|
22
|
-
i32a.
|
|
23
|
-
|
|
20
|
+
bc.addEventListener('message', ({ data: [id, payload] }) => {
|
|
21
|
+
i32a.set(payload, 0);
|
|
22
|
+
map.get(id)(options.onsync(i32a.subarray(2, 2 + i32a[1])));
|
|
23
|
+
map.delete(id);
|
|
24
24
|
});
|
|
25
|
-
return (
|
|
25
|
+
return (payload, ...rest) => {
|
|
26
26
|
const { promise, resolve } = withResolvers();
|
|
27
27
|
const id = next();
|
|
28
|
-
map.set(id,
|
|
28
|
+
map.set(id, resolve);
|
|
29
29
|
// @ts-ignore
|
|
30
|
-
bc.postMessage([id,
|
|
30
|
+
bc.postMessage([id, payload], ...rest);
|
|
31
31
|
return promise;
|
|
32
32
|
};
|
|
33
33
|
};
|
package/src/worker/shared.js
CHANGED
|
@@ -4,22 +4,22 @@ const { load, store, wait } = Atomics;
|
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* @typedef {Object} Options
|
|
7
|
-
* @property {(
|
|
8
|
-
* @property {(
|
|
7
|
+
* @property {(payload: Int32Array) => unknown} onsync transforms the resulting `Int32Array` from *main* thread into a value usable within the worker.
|
|
8
|
+
* @property {(payload: unknown) => unknown |Promise<unknown>} onsend invoked to define what to return to the *main* thread when it calls `worker.send(payload)`.
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
11
|
/**
|
|
12
12
|
* @param {MessageChannel | BroadcastChannel} channel
|
|
13
13
|
* @param {Int32Array} i32a
|
|
14
14
|
* @param {Options} options
|
|
15
|
-
* @returns {(
|
|
15
|
+
* @returns {(payload: unknown, ...rest: unknown[]) => unknown}
|
|
16
16
|
*/
|
|
17
|
-
const handle = (channel, i32a, options) => (
|
|
17
|
+
const handle = (channel, i32a, options) => (payload, ...rest) => {
|
|
18
18
|
// @ts-ignore
|
|
19
|
-
channel.postMessage(
|
|
19
|
+
channel.postMessage(payload, ...rest);
|
|
20
20
|
wait(i32a, 0, 0);
|
|
21
21
|
store(i32a, 0, 0);
|
|
22
|
-
return options.
|
|
22
|
+
return options.onsync(i32a.subarray(2, 2 + load(i32a, 1)));
|
|
23
23
|
};
|
|
24
24
|
|
|
25
25
|
/**
|
package/src/worker/xhr.js
CHANGED
|
@@ -19,16 +19,16 @@ const handle = (channel, i32a, options) => {
|
|
|
19
19
|
const bc = new BroadcastChannel(channel);
|
|
20
20
|
const next = i32();
|
|
21
21
|
const { serviceWorker } = options;
|
|
22
|
-
return (
|
|
22
|
+
return (payload, ...rest) => {
|
|
23
23
|
const id = next();
|
|
24
24
|
// @ts-ignore
|
|
25
|
-
bc.postMessage([id,
|
|
25
|
+
bc.postMessage([id, payload], ...rest);
|
|
26
26
|
const xhr = new XMLHttpRequest;
|
|
27
27
|
xhr.open('POST', serviceWorker, false);
|
|
28
28
|
xhr.setRequestHeader('Content-Type', 'application/json');
|
|
29
29
|
xhr.send(stringify([id, channel]));
|
|
30
30
|
i32a.set(parse(xhr.responseText), 0);
|
|
31
|
-
return options.
|
|
31
|
+
return options.onsync(i32a.subarray(2, 2 + i32a[1]));
|
|
32
32
|
};
|
|
33
33
|
};
|
|
34
34
|
|
|
@@ -0,0 +1,11 @@
|
|
|
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
|
+
<style>body{font-family:sans-serif}</style>
|
|
7
|
+
<title>reflected README</title>
|
|
8
|
+
<script src="./mini-coi.js"></script>
|
|
9
|
+
<script type="module" src="./index.js"></script>
|
|
10
|
+
</head>
|
|
11
|
+
</html>
|
|
@@ -1,13 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
<html lang="en">
|
|
3
|
-
<head>
|
|
4
|
-
<meta charset="UTF-8">
|
|
5
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
-
<style>body{font-family:sans-serif}</style>
|
|
7
|
-
<title>reflected README</title>
|
|
8
|
-
<script src="mini-coi.js"></script>
|
|
9
|
-
<script type="module">
|
|
10
|
-
import reflect, { channel } from '../dist/index.js';
|
|
1
|
+
import reflect, { channel } from '../../dist/index.js';
|
|
11
2
|
|
|
12
3
|
function test_sum(...args) {
|
|
13
4
|
let i = 0;
|
|
@@ -19,7 +10,7 @@ function test_sum(...args) {
|
|
|
19
10
|
// ℹ️ must await the initialization
|
|
20
11
|
const worker = await reflect(
|
|
21
12
|
// Worker scriptURL
|
|
22
|
-
'./
|
|
13
|
+
'./worker.js',
|
|
23
14
|
// Worker options + required utilities / helpers
|
|
24
15
|
// ℹ️ type is enforced to be 'module' due top-level await
|
|
25
16
|
{
|
|
@@ -30,7 +21,7 @@ const worker = await reflect(
|
|
|
30
21
|
// ⚠️ the worker is not responsive until this returns so
|
|
31
22
|
// be sure you handle errors gracefully to still provide
|
|
32
23
|
// a result the worker can consume out of the shared buffer!
|
|
33
|
-
async
|
|
24
|
+
async onsync(payload) {
|
|
34
25
|
const { invoke, args } = payload;
|
|
35
26
|
|
|
36
27
|
if (invoke === 'test_sum') {
|
|
@@ -61,6 +52,3 @@ const worker = await reflect(
|
|
|
61
52
|
|
|
62
53
|
document.body.append(channel, ' ');
|
|
63
54
|
document.body.append(JSON.stringify(await worker.send({ any: 'payload' })));
|
|
64
|
-
</script>
|
|
65
|
-
</head>
|
|
66
|
-
</html>
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/*! coi-serviceworker v0.1.7 - Guido Zuidhof and contributors, licensed under MIT */
|
|
2
|
+
/*! mini-coi - Andrea Giammarchi and contributors, licensed under MIT */
|
|
3
|
+
(({ document: d, navigator: { serviceWorker: s } }) => {
|
|
4
|
+
if (d) {
|
|
5
|
+
const { currentScript: c } = d;
|
|
6
|
+
s.register(c.src, { scope: c.getAttribute('scope') || '.' }).then(r => {
|
|
7
|
+
r.addEventListener('updatefound', () => location.reload());
|
|
8
|
+
if (r.active && !s.controller) location.reload();
|
|
9
|
+
});
|
|
10
|
+
}
|
|
11
|
+
else {
|
|
12
|
+
addEventListener('install', () => skipWaiting());
|
|
13
|
+
addEventListener('activate', e => e.waitUntil(clients.claim()));
|
|
14
|
+
addEventListener('fetch', e => {
|
|
15
|
+
const { request: r } = e;
|
|
16
|
+
if (r.cache === 'only-if-cached' && r.mode !== 'same-origin') return;
|
|
17
|
+
e.respondWith(fetch(r).then(r => {
|
|
18
|
+
const { body, status, statusText } = r;
|
|
19
|
+
if (!status || status > 399) return r;
|
|
20
|
+
const h = new Headers(r.headers);
|
|
21
|
+
h.set('Cross-Origin-Opener-Policy', 'same-origin');
|
|
22
|
+
h.set('Cross-Origin-Embedder-Policy', 'require-corp');
|
|
23
|
+
h.set('Cross-Origin-Resource-Policy', 'cross-origin');
|
|
24
|
+
return new Response(status == 204 ? null : body, { status, statusText, headers: h });
|
|
25
|
+
}));
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
})(self);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import reflect, { channel } from '
|
|
1
|
+
import reflect, { channel } from '../../dist/index.js';
|
|
2
2
|
|
|
3
3
|
// ℹ️ must await the initialization
|
|
4
4
|
const reflected = await reflect({
|
|
@@ -6,7 +6,7 @@ const reflected = await reflect({
|
|
|
6
6
|
// use this helper to transform such data into something
|
|
7
7
|
// that the worker can use/understand after invoke
|
|
8
8
|
// ℹ️ must be synchronous and it's invoked synchronously
|
|
9
|
-
|
|
9
|
+
onsync(response) {
|
|
10
10
|
return response.length ? response[0] : undefined;
|
|
11
11
|
},
|
|
12
12
|
|
package/test/index.js
CHANGED
|
@@ -8,7 +8,7 @@ try {
|
|
|
8
8
|
// console.log('onsend', data, rest);
|
|
9
9
|
return data;
|
|
10
10
|
},
|
|
11
|
-
|
|
11
|
+
onsync: async (data, ...rest) => {
|
|
12
12
|
// console.log('main', data, rest);
|
|
13
13
|
// await new Promise(resolve => setTimeout(resolve, 1000));
|
|
14
14
|
return new Int32Array([6, 7, 8, 9, 10]);
|
package/test/sw.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
const e=Promise.withResolvers||function(){var e,t,n=new this((n,s)=>{e=n,t=s});return{resolve:e,reject:t,promise:n}};var t=e.bind(Promise);const[n,s]=((e=e=>e)=>{const n=new Map,s=(()=>{const e=new Int32Array(1);return()=>e[0]++})();return[()=>{let
|
|
1
|
+
const e=Promise.withResolvers||function(){var e,t,n=new this((n,s)=>{e=n,t=s});return{resolve:e,reject:t,promise:n}};var t=e.bind(Promise);const[n,s]=((e=e=>e)=>{const n=new Map,s=(()=>{const e=new Int32Array(1);return()=>e[0]++})();return[()=>{let a;do{a=e(s())}while(n.has(a));const r=t();return n.set(a,r),[a,r.promise]},(e,t,s)=>{const a=n.get(e);n.delete(e),s?a?.reject(s):a?.resolve(t)}]})(),{protocol:a,host:r,pathname:o}=location,i=`${a}//${r}${o}`,c=new BroadcastChannel("fc260aad-4404-43b8-ae9d-2c06554bb294");c.addEventListener("message",({data:[e,t]})=>{if("response"===e){const[e,n]=t;s(e,`[${n.join(",")}]`)}});const d=async e=>{const[t,s]=n();return c.postMessage(["request",[t,e]]),new Response(await s)};addEventListener("activate",e=>e.waitUntil(clients.claim())),addEventListener("fetch",async e=>{const{request:t}=e;"POST"===t.method&&t.url.startsWith(i)&&(e.stopImmediatePropagation(),e.respondWith(t.json().then(d)),e.preventDefault())}),addEventListener("install",()=>skipWaiting());
|
package/test/worker.js
CHANGED
package/types/broadcast.d.ts
CHANGED
|
@@ -17,5 +17,5 @@ declare const _default: ((scriptURL: string, options: import("./main/shared.js")
|
|
|
17
17
|
onmessage: (this: Worker, ev: MessageEvent) => any;
|
|
18
18
|
onmessageerror: (this: Worker, ev: MessageEvent) => any;
|
|
19
19
|
};
|
|
20
|
-
}>) | ((options: import("./worker/shared.js").Options) => Promise<(
|
|
20
|
+
}>) | ((options: import("./worker/shared.js").Options) => Promise<(payload: unknown, ...rest: unknown[]) => unknown>);
|
|
21
21
|
export default _default;
|
package/types/channel.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
declare const _default: "
|
|
1
|
+
declare const _default: "fc260aad-4404-43b8-ae9d-2c06554bb294";
|
|
2
2
|
export default _default;
|
package/types/index.d.ts
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
export default module;
|
|
2
|
+
export type MainOptions = import("./main/shared.js").Options;
|
|
3
|
+
export type WorkerOptions = import("./worker/shared.js").Options;
|
|
4
|
+
/** @typedef {import('./main/shared.js').Options} MainOptions */
|
|
5
|
+
/** @typedef {import('./worker/shared.js').Options} WorkerOptions */
|
|
2
6
|
/** @type {string} */
|
|
3
7
|
export let channel: string;
|
|
4
8
|
/** @type {Function} */
|
package/types/main/shared.d.ts
CHANGED
|
@@ -24,11 +24,11 @@ export type Options = {
|
|
|
24
24
|
/**
|
|
25
25
|
* invoked when the worker expect a response as `Int32Array` to populate the SharedArrayBuffer with.
|
|
26
26
|
*/
|
|
27
|
-
|
|
27
|
+
onsync: (payload: unknown) => Int32Array | Promise<Int32Array>;
|
|
28
28
|
/**
|
|
29
29
|
* invoked when the worker replies to a `worker.send(data)` call.
|
|
30
30
|
*/
|
|
31
|
-
onsend?: (
|
|
31
|
+
onsend?: (payload: unknown) => unknown | Promise<unknown>;
|
|
32
32
|
/**
|
|
33
33
|
* defines the initial byte length of the SharedArrayBuffer.
|
|
34
34
|
*/
|
package/types/message.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { channel } from "./worker/message.js";
|
|
2
|
-
declare const _default: ((options: import("./worker/shared.js").Options) => Promise<(
|
|
2
|
+
declare const _default: ((options: import("./worker/shared.js").Options) => Promise<(payload: unknown, ...rest: unknown[]) => unknown>) | ((scriptURL: string, options: import("./main/shared.js").Options) => Promise<{
|
|
3
3
|
new (scriptURL: any, options: any, resolve: any): {
|
|
4
4
|
get channel(): string;
|
|
5
5
|
"__#private@#next": any;
|
|
@@ -24,7 +24,7 @@ export type Options = {
|
|
|
24
24
|
/**
|
|
25
25
|
* invoked when the worker expect a response as `Int32Array` to populate the SharedArrayBuffer with.
|
|
26
26
|
*/
|
|
27
|
-
|
|
27
|
+
onsync: (data: unknown) => Promise<Int32Array>;
|
|
28
28
|
/**
|
|
29
29
|
* invoked when the worker replies to a `worker.send(data)` call.
|
|
30
30
|
*/
|
|
@@ -3,7 +3,7 @@ export type Options = {
|
|
|
3
3
|
/**
|
|
4
4
|
* transforms the resulting `Int32Array` from *main* thread into a value usable within the worker.
|
|
5
5
|
*/
|
|
6
|
-
|
|
6
|
+
onsync: (data: Int32Array) => unknown;
|
|
7
7
|
/**
|
|
8
8
|
* invoked to define what to return to the *main* thread when it calls `worker.send(data)`.
|
|
9
9
|
*/
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
export const channel: "broadcast";
|
|
2
|
-
declare const _default: (options: import("./shared.js").Options) => Promise<(
|
|
2
|
+
declare const _default: (options: import("./shared.js").Options) => Promise<(payload: unknown, ...rest: unknown[]) => unknown>;
|
|
3
3
|
export default _default;
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
export const channel: "message";
|
|
2
|
-
declare const _default: (options: import("./shared.js").Options) => Promise<(
|
|
2
|
+
declare const _default: (options: import("./shared.js").Options) => Promise<(payload: unknown, ...rest: unknown[]) => unknown>;
|
|
3
3
|
export default _default;
|
package/types/worker/shared.d.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
export function handler(promise: Promise<[SharedArrayBuffer, Options, MessageChannel | BroadcastChannel]>, listener: (event: MessageEvent) => void): (options: Options) => Promise<(
|
|
1
|
+
export function handler(promise: Promise<[SharedArrayBuffer, Options, MessageChannel | BroadcastChannel]>, listener: (event: MessageEvent) => void): (options: Options) => Promise<(payload: unknown, ...rest: unknown[]) => unknown>;
|
|
2
2
|
export type Options = {
|
|
3
3
|
/**
|
|
4
4
|
* transforms the resulting `Int32Array` from *main* thread into a value usable within the worker.
|
|
5
5
|
*/
|
|
6
|
-
|
|
6
|
+
onsync: (payload: Int32Array) => unknown;
|
|
7
7
|
/**
|
|
8
|
-
* invoked to define what to return to the *main* thread when it calls `worker.send(
|
|
8
|
+
* invoked to define what to return to the *main* thread when it calls `worker.send(payload)`.
|
|
9
9
|
*/
|
|
10
|
-
onsend: (
|
|
10
|
+
onsend: (payload: unknown) => unknown | Promise<unknown>;
|
|
11
11
|
};
|
package/dist/async-Cn7CWifh.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{w as e}from"./with-resolvers-CHEvl4oe.js";import{i as s}from"./i32-C78nBJH2.js";import{s as t}from"./sender-23_Enxlo.js";import"./channel-BwDpH9fR.js";const{promise:a,resolve:r}=e();addEventListener("message",({data:[e,s,t]})=>r([e,s,t]),{once:!0});const n="async";var o=r=>a.then(([a,n,o])=>(postMessage(1),((t,a,r)=>{const n=new BroadcastChannel(t),o=s(),d=new Map;return n.addEventListener("message",({data:[e,s]})=>{const[t,n]=d.get(e);a.set(s,0),t(r.ondata(a.subarray(2,2+a[1])))}),(s,...t)=>{const{promise:a,resolve:r}=e(),i=o();return d.set(i,[r,t]),n.postMessage([i,s],...t),a}})(o,new Int32Array(a),t({...n,...r}))));export{n as channel,o as default};
|
package/dist/channel-BwDpH9fR.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
var a="eac8422c-ea74-4190-9b8c-7966827497f0";export{a as S};
|
package/dist/shared-D9sRqO2H.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{w as e}from"./with-resolvers-CHEvl4oe.js";import{i as t}from"./i32-C78nBJH2.js";import{S as s}from"./channel-BwDpH9fR.js";const{isArray:r}=Array,n=e=>e;class a extends Worker{#e;#t;constructor(e,a){super(e,a),this.#e=t(),this.#t=new Map,a.onsend||(a.onsend=n),super.addEventListener("message",async e=>{const{data:t}=e;if(r(t)&&t[0]===s){e.stopImmediatePropagation(),e.preventDefault();const[s,r]=t[1],n=this.#t.get(s);this.#t.delete(s),n(await a.onsend(r))}})}send(t,...r){const n=this.#e(),{promise:a,resolve:o}=e();return this.#t.set(n,o),super.postMessage([s,[n,t]],...r),a}}const{notify:o,store:i}=Atomics,c=2*Int32Array.BYTES_PER_ELEMENT,u=({initByteLength:e=1024,maxByteLength:t=8192})=>new SharedArrayBuffer(c+e,{maxByteLength:c+t}),h=t=>(s,r)=>{const{promise:n,resolve:a}=e();return new t(s,r,a),n},m=(e,t,s)=>{const r=new Int32Array(e);return async({data:n})=>{const a=await t.ondata(n),u=a.length,h=c+a.buffer.byteLength;e.byteLength<h&&e.grow(h),r.set(a,2),s?(i(r,1,u),i(r,0,1),o(r,0)):(r[1]=u,r[0]=1)}},p=e=>{switch(typeof e){case"symbol":case"function":return!1}return!0},d=(e,t)=>{const s={};for(const e in t){const r=t[e];p(e)&&p(r)&&(s[e]=r)}return[e,s]},y=(e,t,s)=>{const r=new URL(e,location.href);return r.searchParams.set("reflected",t),[r,{...s,type:"module"}]};export{a as S,u as a,h as b,m as h,c as m,d as p,y as u};
|
package/dist/shared-DdAq4SGF.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{s}from"./sender-23_Enxlo.js";const{load:e,store:a,wait:t}=Atomics,o=(o,r)=>(addEventListener("message",r,{once:!0}),r=>o.then(([o,n,d])=>(postMessage(1),((s,o,r)=>(n,...d)=>(s.postMessage(n,...d),t(o,0,0),a(o,0,0),r.ondata(o.subarray(2,2+e(o,1)))))(d,new Int32Array(o),s({...n,...r})))));export{o as h};
|
package/dist/xhr-C97Ssg1V.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{w as e}from"./with-resolvers-CHEvl4oe.js";import{m as t,S as s,u as n,p as a,h as r}from"./shared-D9sRqO2H.js";import{S as o}from"./shared-array-buffer-cwdMr2mc.js";import{r as i}from"./shared-RFmxa5x4.js";import{S as c}from"./channel-BwDpH9fR.js";import"./i32-C78nBJH2.js";var l=({initByteLength:e=1024,maxByteLength:s=8192})=>new o(t+e,{maxByteLength:t+s});const d="async";let h=class extends s{constructor(e,t,s){const o=i(),c=new BroadcastChannel(o),h=l(t),p=new Int32Array(h),g=r(h,t,!1);c.addEventListener("message",async({data:[e,t]})=>{await g({data:t}),c.postMessage([e,p.slice(0,2+p[1])])}),super(...n(e,d,t)),super.addEventListener("message",()=>s(this),{once:!0}),super.postMessage(a(h,t).concat(o))}get channel(){return d}};const p=new Map,g=new BroadcastChannel(c);g.addEventListener("message",async({data:[e,t]})=>{if("request"===e){const[e,[s,n]]=t,a=p.get(n);if(a){const t=a.get(s);t&&(a.delete(s),g.postMessage(["response",[e,await t]]))}}});const{promise:u,resolve:m}=e();let f=!0;const v=(e,t)=>{let s,n=!0,{url:a}=t;e.getRegistration(a).then(s=>s??e.register(a,t)).then(function r(o){const{controller:i}=e;if(n=n&&!!i,s=o.installing||o.waiting||o.active,!s)return v(e,t);if("activated"===s.state){if(n){if(i.scriptURL===a)return m();o.unregister()}location.reload()}else s.addEventListener("statechange",()=>r(o),{once:!0})})};class w extends s{#e;constructor(t,s,o){if(f){f=!1;let{serviceWorker:e}=s;if(!e)return new h(t,s,o);"string"==typeof e&&(e={url:e}),e.url=new URL(e.url,location.href).href,v(navigator.serviceWorker,e)}const c=i(),d=new BroadcastChannel(c),g=l(s),u=new Map,m=new Int32Array(g),w=r(g,s,!1);p.set(c,u),d.addEventListener("message",async({data:[t,s]})=>{const{promise:n,resolve:a}=e();u.set(t,n),await w({data:s}),a(m.slice(0,2+m[1]))}),super(...n(t,"xhr",s)),super.addEventListener("message",()=>o(this),{once:!0}),super.postMessage(a(g,s).concat(c)),this.#e=c}terminate(){p.delete(this.#e),super.terminate()}get channel(){return"xhr"}}var y=(t,s)=>{const{promise:n,resolve:a}=e();return new w(t,s,a)instanceof h?n:u.then(()=>n)};export{y as default};
|