reflected 0.0.0 → 0.0.1

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.
Files changed (104) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +127 -0
  3. package/dist/async-Cn7CWifh.js +1 -0
  4. package/dist/async.js +1 -0
  5. package/dist/broadcast-BPNfR9nK.js +1 -0
  6. package/dist/broadcast-CwqVSk2p.js +1 -0
  7. package/dist/broadcast.js +1 -0
  8. package/dist/channel-BwDpH9fR.js +1 -0
  9. package/dist/i32-C78nBJH2.js +1 -0
  10. package/dist/index.js +1 -0
  11. package/dist/message-CgNarJIa.js +1 -0
  12. package/dist/message-YPvgpfHS.js +1 -0
  13. package/dist/message.js +1 -0
  14. package/dist/sender-23_Enxlo.js +1 -0
  15. package/dist/shared-D9sRqO2H.js +1 -0
  16. package/dist/shared-DdAq4SGF.js +1 -0
  17. package/dist/shared-RFmxa5x4.js +1 -0
  18. package/dist/shared-array-buffer-cwdMr2mc.js +1 -0
  19. package/dist/sw.js +1 -0
  20. package/dist/with-resolvers-CHEvl4oe.js +1 -0
  21. package/dist/xhr-C97Ssg1V.js +1 -0
  22. package/dist/xhr-CYct95wr.js +1 -0
  23. package/dist/xhr.js +1 -0
  24. package/package.json +86 -9
  25. package/rollup.js +59 -0
  26. package/src/async.js +8 -0
  27. package/src/broadcast.js +8 -0
  28. package/src/channel.js +1 -0
  29. package/src/index.js +30 -10
  30. package/src/main/async.js +29 -0
  31. package/src/main/broadcast.js +21 -0
  32. package/src/main/message.js +20 -0
  33. package/src/main/sab.js +12 -0
  34. package/src/main/sender.js +38 -0
  35. package/src/main/shared.js +74 -16
  36. package/src/main/xhr.js +104 -0
  37. package/src/message.js +8 -0
  38. package/src/service/listeners.js +37 -0
  39. package/src/service/worker.js +5 -0
  40. package/src/shared.js +10 -0
  41. package/src/worker/async.js +38 -0
  42. package/src/worker/{firefox.js → broadcast.js} +4 -1
  43. package/src/worker/{chrome.js → message.js} +4 -1
  44. package/src/worker/sender.js +16 -0
  45. package/src/worker/shared.js +26 -5
  46. package/src/worker/xhr.js +38 -0
  47. package/src/xhr.js +8 -0
  48. package/test/index.html +8 -9
  49. package/test/index.js +35 -0
  50. package/test/mini-coi.js +28 -0
  51. package/test/readme.html +66 -0
  52. package/test/readme.js +26 -0
  53. package/test/sw.js +1 -0
  54. package/test/worker.js +15 -3
  55. package/types/async.d.ts +3 -0
  56. package/types/broadcast.d.ts +21 -0
  57. package/types/channel.d.ts +2 -0
  58. package/types/index.d.ts +5 -0
  59. package/types/main/async.d.ts +7 -0
  60. package/types/main/broadcast.d.ts +20 -0
  61. package/types/main/message.d.ts +20 -0
  62. package/types/main/sab.d.ts +5 -0
  63. package/types/main/sender.d.ts +5 -0
  64. package/types/main/shared.d.ts +44 -0
  65. package/types/main/xhr.d.ts +2 -0
  66. package/types/message.d.ts +21 -0
  67. package/types/reflected/src/async.d.ts +3 -0
  68. package/types/reflected/src/broadcast.d.ts +3 -0
  69. package/types/reflected/src/channel.d.ts +2 -0
  70. package/types/reflected/src/index.d.ts +3 -0
  71. package/types/reflected/src/main/async.d.ts +7 -0
  72. package/types/reflected/src/main/broadcast.d.ts +2 -0
  73. package/types/reflected/src/main/message.d.ts +2 -0
  74. package/types/reflected/src/main/sab.d.ts +5 -0
  75. package/types/reflected/src/main/sender.d.ts +5 -0
  76. package/types/reflected/src/main/shared.d.ts +44 -0
  77. package/types/reflected/src/main/xhr.d.ts +2 -0
  78. package/types/reflected/src/message.d.ts +3 -0
  79. package/types/reflected/src/service/listeners.d.ts +3 -0
  80. package/types/reflected/src/service/worker.d.ts +1 -0
  81. package/types/reflected/src/shared.d.ts +1 -0
  82. package/types/reflected/src/worker/async.d.ts +3 -0
  83. package/types/reflected/src/worker/broadcast.d.ts +3 -0
  84. package/types/reflected/src/worker/message.d.ts +3 -0
  85. package/types/reflected/src/worker/sender.d.ts +2 -0
  86. package/types/reflected/src/worker/shared.d.ts +11 -0
  87. package/types/reflected/src/worker/xhr.d.ts +3 -0
  88. package/types/reflected/src/xhr.d.ts +3 -0
  89. package/types/service/listeners.d.ts +3 -0
  90. package/types/service/worker.d.ts +1 -0
  91. package/types/shared.d.ts +1 -0
  92. package/types/weak-id/i32.d.ts +2 -0
  93. package/types/worker/async.d.ts +3 -0
  94. package/types/worker/broadcast.d.ts +3 -0
  95. package/types/worker/message.d.ts +3 -0
  96. package/types/worker/sender.d.ts +2 -0
  97. package/types/worker/shared.d.ts +11 -0
  98. package/types/worker/xhr.d.ts +3 -0
  99. package/types/xhr.d.ts +3 -0
  100. package/src/main/chrome.js +0 -19
  101. package/src/main/fallback.js +0 -29
  102. package/src/main/firefox.js +0 -19
  103. package/src/main/shared-id.js +0 -1
  104. package/src/worker/fallback.js +0 -1
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright © 2026-today, Andrea Giammarchi, @WebReflection
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the “Software”), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software
10
+ is furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included
13
+ in all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21
+ IN THE SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,127 @@
1
+ # reflected
2
+
3
+ A primitive to allow workers to call synchronously any functionality exposed on the main thread.
4
+
5
+ This module uses 3 synchronous strategies + 1 asynchronous fallback:
6
+
7
+ * **message** based on *SharedArrayBuffer* and *MessageChannel*, the fastest and most reliable "*channel strategy*" that requires headers to enable [Cross Origin Isolation](https://developer.mozilla.org/en-US/docs/Web/API/Window/crossOriginIsolated) on the page.
8
+ * **broadcast** also based on *SharedArrayBuffer* but with *BroadcastChannel* instead to satisfy a long standing [Firefox bug](https://bugzilla.mozilla.org/show_bug.cgi?id=1752287). This also requires headers to enable [Cross Origin Isolation](https://developer.mozilla.org/en-US/docs/Web/API/Window/crossOriginIsolated) on the page.
9
+ * **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
+ * **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
+
12
+ All strategies are automatically detected through the default/main `import` but all dedicated strategies can be retrieved directly, example:
13
+
14
+ * `import reflect from 'reflected'` will decide automatically which strategy should be used.
15
+ * `import reflect from 'reflected/message'` will return the right *message* based module on the main thread and the worker mode within the worker.
16
+ * `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
+ * `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
+
19
+ Swap `message` with `broadcast`, `xhr` or `async`, and all exports will work equally well accordingly to the chosen "*channel strategy*".
20
+
21
+ ### Worker Thread API
22
+
23
+ ```js
24
+ // file: worker.js (always loaded as module)
25
+ import reflect from 'https://esm.run/reflected';
26
+
27
+ // ℹ️ must await the initialization
28
+ const reflected = await reflect({
29
+ // receives the returned data from the main thread.
30
+ // use this helper to transform such data into something
31
+ // that the worker can use/understand after invoke
32
+ // ⚠️ must be synchronous and it's invoked synchronously
33
+ ondata(response:Int32Array) {
34
+ return response.length ? response[0] : undefined;
35
+ },
36
+
37
+ // receives the data from the main thread when
38
+ // `worker.send(payload, ...rest)` is invoked.
39
+ // use this helper to transform the main thread request
40
+ // into something compatible with structuredClone algorithm
41
+ // ℹ️ works even if synchronous but it's resolved asynchronously
42
+ async onsend(payload) {
43
+ const data = await fetch('./data.json').then(r => r.json());
44
+ return process(payload, data);
45
+ },
46
+ });
47
+
48
+ // retrive the result of `test_sum(1, 2, 3)`
49
+ // directly from the main thread.
50
+ // only the async channel variant would need to await
51
+ const value = reflected({
52
+ invoke: 'test_sum',
53
+ args: [1, 2, 3]
54
+ });
55
+
56
+ console.log(value); // 6
57
+ ```
58
+
59
+ ### Main Thread API
60
+
61
+ ```js
62
+ // file: index.js as module
63
+ import reflect from 'https://esm.run/reflected';
64
+
65
+ function test_sum(...args:number[]) {
66
+ let i = 0;
67
+ while (args.length)
68
+ i += args.pop();
69
+ return i;
70
+ }
71
+
72
+ // ℹ️ must await the initialization
73
+ const worker = await reflect(
74
+ // Worker scriptURL
75
+ './worker.js',
76
+ // Worker options + required utilities / helpers
77
+ // ℹ️ type is enforced to be 'module' due top-level await
78
+ {
79
+ // invoked when the worker asks to synchronize a call
80
+ // and it mmust return an Int32Array reference to populate
81
+ // the SharedArrayBuffer and notify/unlock the worker
82
+ // ℹ️ works even if synchronous but it's resolved asynchronously
83
+ // ⚠️ the worker is not responsive until this returns so
84
+ // be sure you handle errors gracefully to still provide
85
+ // a result the worker can consume out of the shared buffer!
86
+ async ondata(payload:unknown) {
87
+ const { invoke, args } = payload;
88
+
89
+ if (invoke === 'test_sum') {
90
+ // just demoing this can be async too
91
+ const value = await test_sum(...args);
92
+ return new Int32Array([value]);
93
+ }
94
+
95
+ // errors should still be Int32Array but
96
+ // it is trivial to return no result
97
+ return new Int32Array(0);
98
+ },
99
+
100
+ // *optional* helper to process data returned from the worker when
101
+ // the main thread `await worker.send(payload, ...rest)` operation
102
+ // is invoked. If not present, whatever payload the worker returned
103
+ // will be directly returned as invoke result, just like in here.
104
+ // ℹ️ works even if synchronous but it's resolved asynchronously
105
+ onsend(payload:unknown) {
106
+ return payload;
107
+ },
108
+
109
+ // optional: the initial SharedArrayBuffer length
110
+ initByteLength: 1024,
111
+
112
+ // optional: the max possible SharedArrayBuffer growth
113
+ maxByteLength: 8192,
114
+
115
+ // optional: the service worker as fallback
116
+ // * if it's a string, it's used to register it
117
+ // * if it's an object, it's used to initialize it
118
+ // but it must contain a `url` field to register it
119
+ // ℹ️ if already registered it will not try to register it
120
+ serviceWorker: undefined,
121
+ }
122
+ );
123
+
124
+ const value = await worker.send({ any: 'payload' });
125
+ ```
126
+
127
+ Test [live](https://webreflection.github.io/reflected/test/readme.html) or read the [main thread](./test/readme.html) and [worker thread](./test/readme.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-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/async.js ADDED
@@ -0,0 +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="eac8422c-ea74-4190-9b8c-7966827497f0";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),g="async";var y=(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.ondata(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,g,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 g}});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]})=>{const[s,a]=c.get(e);n.set(t,0),s(r.ondata(n.subarray(2,2+n[1])))}),(e,...s)=>{const{promise:n,resolve:r}=t(),i=o();return c.set(i,[r,s]),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})))):y;export{w as channel,f as default};
@@ -0,0 +1 @@
1
+ import{b as s,S as e,a,h as r,u as t,p as n}from"./shared-D9sRqO2H.js";import{r as o}from"./shared-RFmxa5x4.js";import"./with-resolvers-CHEvl4oe.js";import"./i32-C78nBJH2.js";import"./channel-BwDpH9fR.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};
@@ -0,0 +1 @@
1
+ import{w as r}from"./with-resolvers-CHEvl4oe.js";import{h as s}from"./shared-DdAq4SGF.js";import"./sender-23_Enxlo.js";import"./channel-BwDpH9fR.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};
@@ -0,0 +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="eac8422c-ea74-4190-9b8c-7966827497f0";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),h="broadcast";var m=(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.ondata(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,h,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 h}});const{isArray:y}=Array;const{load:g,store:l,wait:w}=Atomics,{promise:f,resolve:v}=t(),L="broadcast";var A=((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.ondata(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}))))))(f,({data:[e,t,s]})=>v([e,t,new BroadcastChannel(s)])),b="importScripts"in globalThis?A:m;export{L as channel,b as default};
@@ -0,0 +1 @@
1
+ var a="eac8422c-ea74-4190-9b8c-7966827497f0";export{a as S};
@@ -0,0 +1 @@
1
+ var r=r=>{const n=new Int32Array(1);return()=>n[0]++};export{r as i};
package/dist/index.js ADDED
@@ -0,0 +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-CgNarJIa.js"):"broadcast"===i?import("./broadcast-CwqVSk2p.js"):"xhr"===i?import("./xhr-CYct95wr.js"):import("./async-Cn7CWifh.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-BPNfR9nK.js")).default):(e="message",t=(await import("./message-YPvgpfHS.js")).default):navigator.serviceWorker?(e="xhr",t=(await import("./xhr-C97Ssg1V.js")).default):(e="fallback",t=()=>{});var r=t;export{e as channel,r as default};
@@ -0,0 +1 @@
1
+ import{w as s}from"./with-resolvers-CHEvl4oe.js";import{h as r}from"./shared-DdAq4SGF.js";import"./sender-23_Enxlo.js";import"./channel-BwDpH9fR.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};
@@ -0,0 +1 @@
1
+ import{b as s,S as e,a as t,h as r,u as a,p as n}from"./shared-D9sRqO2H.js";import"./with-resolvers-CHEvl4oe.js";import"./i32-C78nBJH2.js";import"./channel-BwDpH9fR.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};
@@ -0,0 +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="eac8422c-ea74-4190-9b8c-7966827497f0";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:i}=Atomics,c=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(c+e,{maxByteLength:c+t}))(t);n.addEventListener(u,((e,t)=>{const s=new Int32Array(e);return async({data:n})=>{const r=await t.ondata(n),a=r.length,d=c+r.buffer.byteLength;e.byteLength<d&&e.grow(d),s.set(r,2),i(s,1,a),i(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:g}=Array;const{load:h,store:m,wait:y}=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),y(t,0,0),m(t,0,0),s.ondata(t.subarray(2,2+h(t,1)))))(r,new Int32Array(e),(e=>(addEventListener("message",async t=>{const{data:n}=t;if(g(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};
@@ -0,0 +1 @@
1
+ import{S as a}from"./channel-BwDpH9fR.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{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};
@@ -0,0 +1 @@
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};
@@ -0,0 +1 @@
1
+ let t=!0;try{crypto.randomUUID()}catch(o){t=!1}const o=t?()=>crypto.randomUUID():()=>(Date.now()+Math.random()).toString(36);export{o as r};
@@ -0,0 +1 @@
1
+ let{SharedArrayBuffer:e}=globalThis,r=!0;try{new e(4,{maxByteLength:8})}catch(a){r=!1,e=class extends ArrayBuffer{get growable(){return super.resizable}grow(e){super.resize(e)}}}export{e as S,r as n};
package/dist/sw.js ADDED
@@ -0,0 +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 r;do{r=e(s())}while(n.has(r));const a=t();return n.set(r,a),[r,a.promise]},(e,t,s)=>{const r=n.get(e);n.delete(e),s?r?.reject(s):r?.resolve(t)}]})(),{protocol:r,host:a,pathname:o}=location,i=`${r}//${a}${o}`,c=new BroadcastChannel("eac8422c-ea74-4190-9b8c-7966827497f0");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());
@@ -0,0 +1 @@
1
+ var e=(Promise.withResolvers||function(){var e,r,s=new this((s,i)=>{e=s,r=i});return{resolve:e,reject:r,promise:s}}).bind(Promise);export{e as w};
@@ -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-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};
@@ -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-23_Enxlo.js";import"./channel-BwDpH9fR.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.ondata(t.subarray(2,2+t[1]))}})(p,new Int32Array(o),t({...a,...e}))));export{p as channel,i as default};
package/dist/xhr.js ADDED
@@ -0,0 +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="eac8422c-ea74-4190-9b8c-7966827497f0";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.ondata(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 f=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 m=new Map,v=new BroadcastChannel(n);v.addEventListener("message",async({data:[e,t]})=>{if("request"===e){const[e,[s,n]]=t,r=m.get(n);if(r){const t=r.get(s);t&&(r.delete(s),v.postMessage(["response",[e,await t]]))}}});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 A=class extends o{#s;constructor(e,s,n){if(E){E=!1;let{serviceWorker:t}=s;if(!t)return new f(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);m.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(){m.delete(this.#s),super.terminate()}get channel(){return"xhr"}};const{isArray:b}=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.ondata(t.subarray(2,2+t[1]))}})(a,new Int32Array(t),(e=>(addEventListener("message",async t=>{const{data:s}=t;if(b(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 A(e,s,r)instanceof f?n:L.then(()=>n)};export{P as channel,T as default};
package/package.json CHANGED
@@ -1,16 +1,93 @@
1
1
  {
2
2
  "name": "reflected",
3
- "version": "0.0.0",
4
- "description": "",
5
- "main": "index.js",
3
+ "version": "0.0.1",
4
+ "description": "A primitive to allow workers to call synchronously any functionality exposed on the main thread.",
5
+ "exports": {
6
+ ".": {
7
+ "import": "./src/index.js",
8
+ "types": "./types/index.d.ts"
9
+ },
10
+ "./async": {
11
+ "import": "./src/async.js",
12
+ "types": "./types/async.d.ts"
13
+ },
14
+ "./broadcast": {
15
+ "import": "./src/broadcast.js",
16
+ "types": "./types/broadcast.d.ts"
17
+ },
18
+ "./message": {
19
+ "import": "./src/message.js",
20
+ "types": "./types/message.d.ts"
21
+ },
22
+ "./xhr": {
23
+ "import": "./src/xhr.js",
24
+ "types": "./types/xhr.d.ts"
25
+ },
26
+ "./main/async": {
27
+ "import": "./src/main/async.js",
28
+ "types": "./types/main/async.d.ts"
29
+ },
30
+ "./main/broadcast": {
31
+ "import": "./src/main/broadcast.js",
32
+ "types": "./types/main/broadcast.d.ts"
33
+ },
34
+ "./main/message": {
35
+ "import": "./src/main/message.js",
36
+ "types": "./types/main/message.d.ts"
37
+ },
38
+ "./main/xhr": {
39
+ "import": "./src/main/xhr.js",
40
+ "types": "./types/main/xhr.d.ts"
41
+ },
42
+ "./worker/async": {
43
+ "import": "./src/worker/async.js",
44
+ "types": "./types/worker/async.d.ts"
45
+ },
46
+ "./worker/broadcast": {
47
+ "import": "./src/worker/broadcast.js",
48
+ "types": "./types/worker/broadcast.d.ts"
49
+ },
50
+ "./worker/message": {
51
+ "import": "./src/worker/message.js",
52
+ "types": "./types/worker/message.d.ts"
53
+ },
54
+ "./worker/xhr": {
55
+ "import": "./src/worker/xhr.js",
56
+ "types": "./types/worker/xhr.d.ts"
57
+ },
58
+ "./package.json": "./package.json"
59
+ },
6
60
  "scripts": {
7
- "test": "echo \"Error: no test specified\" && exit 1"
61
+ "build": "rm -rf dist && rollup -c rollup.js && cp dist/sw.js test/sw.js && npm run types",
62
+ "types": "tsc --allowJs --checkJs --lib dom,esnext --module nodeNext --target esnext -d --emitDeclarationOnly --outDir ./types ./src/*.js ./src/*/*.js"
8
63
  },
9
64
  "keywords": [],
10
- "author": "",
11
- "license": "ISC",
12
- "type": "commonjs",
65
+ "author": "Andrea Giammarchi",
66
+ "license": "MIT",
67
+ "type": "module",
68
+ "types": "./types/index.d.ts",
69
+ "main": "./dist/index.js",
70
+ "module": "./src/index.js",
13
71
  "dependencies": {
14
- "@webreflection/utils": "^0.1.2"
15
- }
72
+ "@webreflection/utils": "^0.1.2",
73
+ "next-resolver": "^0.1.6",
74
+ "typescript": "^5.9.3",
75
+ "weak-id": "^0.2.1"
76
+ },
77
+ "devDependencies": {
78
+ "@rollup/plugin-node-resolve": "^16.0.3",
79
+ "@rollup/plugin-terser": "^0.4.4",
80
+ "rollup": "^4.57.1"
81
+ },
82
+ "directories": {
83
+ "test": "test"
84
+ },
85
+ "repository": {
86
+ "type": "git",
87
+ "url": "git+https://github.com/WebReflection/reflected.git"
88
+ },
89
+ "bugs": {
90
+ "url": "https://github.com/WebReflection/reflected/issues"
91
+ },
92
+ "homepage": "https://github.com/WebReflection/reflected#readme"
16
93
  }
package/rollup.js ADDED
@@ -0,0 +1,59 @@
1
+ import { nodeResolve } from '@rollup/plugin-node-resolve';
2
+ import terser from '@rollup/plugin-terser';
3
+
4
+ import { writeFileSync } from 'fs';
5
+
6
+ writeFileSync(`./src/channel.js`, `export default '${crypto.randomUUID()}';`);
7
+
8
+ const plugins = [nodeResolve()].concat(process.env.NO_MIN ? [] : [terser()]);
9
+
10
+ export default [
11
+ {
12
+ plugins,
13
+ input: './src/index.js',
14
+ output: {
15
+ esModule: true,
16
+ dir: './dist'
17
+ }
18
+ },
19
+ {
20
+ plugins,
21
+ input: './src/broadcast.js',
22
+ output: {
23
+ esModule: true,
24
+ dir: './dist'
25
+ }
26
+ },
27
+ {
28
+ plugins,
29
+ input: './src/message.js',
30
+ output: {
31
+ esModule: true,
32
+ dir: './dist'
33
+ }
34
+ },
35
+ {
36
+ plugins,
37
+ input: './src/xhr.js',
38
+ output: {
39
+ esModule: true,
40
+ dir: './dist'
41
+ }
42
+ },
43
+ {
44
+ plugins,
45
+ input: './src/async.js',
46
+ output: {
47
+ esModule: true,
48
+ dir: './dist'
49
+ }
50
+ },
51
+ {
52
+ plugins,
53
+ input: './src/service/worker.js',
54
+ output: {
55
+ esModule: true,
56
+ file: './dist/sw.js'
57
+ }
58
+ },
59
+ ];
package/src/async.js ADDED
@@ -0,0 +1,8 @@
1
+ import main from './main/async.js';
2
+ import worker from './worker/async.js';
3
+
4
+ export { channel } from './worker/async.js';
5
+
6
+ export default (
7
+ 'importScripts' in globalThis ? worker : main
8
+ );
@@ -0,0 +1,8 @@
1
+ import main from './main/broadcast.js';
2
+ import worker from './worker/broadcast.js';
3
+
4
+ export { channel } from './worker/broadcast.js';
5
+
6
+ export default (
7
+ 'importScripts' in globalThis ? worker : main
8
+ );
package/src/channel.js ADDED
@@ -0,0 +1 @@
1
+ export default 'eac8422c-ea74-4190-9b8c-7966827497f0';
package/src/index.js CHANGED
@@ -1,20 +1,29 @@
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';
1
+ import withResolvers from '@webreflection/utils/with-resolvers';
2
+ import { native } from '@webreflection/utils/shared-array-buffer';
3
3
 
4
+ /** @type {string} */
5
+ let channel;
6
+
7
+ /** @type {Function} */
4
8
  let module;
5
9
 
6
10
  if ('importScripts' in globalThis) {
7
11
  let get;
8
12
  const { promise, resolve } = withResolvers();
13
+ // @ts-ignore
9
14
  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');
15
+ channel = reflected;
16
+ if (reflected === 'message') get = import(/* webpackIgnore: true */'./worker/message.js');
17
+ else if (reflected === 'broadcast') get = import(/* webpackIgnore: true */'./worker/broadcast.js');
18
+ else if (reflected === 'xhr') get = import(/* webpackIgnore: true */'./worker/xhr.js');
19
+ else get = import(/* webpackIgnore: true */'./worker/async.js');
13
20
  module = async options => {
14
21
  const { data, ports } = await promise;
15
22
  const { default: reflect } = await get;
16
23
  const event = new Event('message');
24
+ // @ts-ignore
17
25
  event.data = data;
26
+ // @ts-ignore
18
27
  event.ports = ports;
19
28
  dispatchEvent(event);
20
29
  return reflect(options);
@@ -22,13 +31,24 @@ if ('importScripts' in globalThis) {
22
31
  addEventListener('message', resolve, { once: true });
23
32
  }
24
33
  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;
34
+ if ('InstallTrigger' in globalThis) {
35
+ channel = 'broadcast';
36
+ module = (await import(/* webpackIgnore: true */'./main/broadcast.js')).default;
37
+ }
38
+ else {
39
+ channel = 'message';
40
+ module = (await import(/* webpackIgnore: true */'./main/message.js')).default;
41
+ }
42
+ }
43
+ else if (navigator.serviceWorker) {
44
+ channel = 'xhr';
45
+ module = (await import(/* webpackIgnore: true */'./main/xhr.js')).default;
29
46
  }
30
47
  else {
31
- module = (await import('./main/fallback.js')).default;
48
+ channel = 'fallback';
49
+ module = () => {};
32
50
  }
33
51
 
52
+ export { channel };
53
+
34
54
  export default module;
@@ -0,0 +1,29 @@
1
+ import Sender from './sender.js';
2
+ import SAB from './sab.js';
3
+ import { bootstrap, handler, post, url } from './shared.js';
4
+ import { randomUUID } from '../shared.js';
5
+
6
+ const CHANNEL = 'async';
7
+
8
+ export class Worker extends Sender {
9
+ constructor(scriptURL, options, resolve) {
10
+ const channel = randomUUID();
11
+ const bc = new BroadcastChannel(channel);
12
+ const sab = SAB(options);
13
+ const i32a = new Int32Array(sab);
14
+ const handle = handler(sab, options, false);
15
+ bc.addEventListener('message', async ({ data: [id, data] }) => {
16
+ await handle({ data });
17
+ bc.postMessage([id, i32a.slice(0, 2 + i32a[1])]);
18
+ });
19
+ super(...url(scriptURL, CHANNEL, options));
20
+ super.addEventListener('message', () => resolve(this), { once: true });
21
+ super.postMessage(post(sab, options).concat(channel));
22
+ }
23
+
24
+ get channel() {
25
+ return CHANNEL;
26
+ }
27
+ };
28
+
29
+ export default bootstrap(Worker);
@@ -0,0 +1,21 @@
1
+ import Sender from './sender.js';
2
+ import { SAB, bootstrap, handler, post, url } from './shared.js';
3
+ import { randomUUID } from '../shared.js';
4
+
5
+ const CHANNEL = 'broadcast';
6
+
7
+ export default bootstrap(class Worker extends Sender {
8
+ constructor(scriptURL, options, resolve) {
9
+ const channel = randomUUID();
10
+ const bc = new BroadcastChannel(channel);
11
+ const sab = SAB(options);
12
+ bc.addEventListener('message', handler(sab, options, true));
13
+ super(...url(scriptURL, CHANNEL, options));
14
+ super.addEventListener('message', () => resolve(this), { once: true });
15
+ super.postMessage(post(sab, options).concat(channel));
16
+ }
17
+
18
+ get channel() {
19
+ return CHANNEL;
20
+ }
21
+ });
@@ -0,0 +1,20 @@
1
+ import Sender from './sender.js';
2
+ import { SAB, bootstrap, handler, post, url } from './shared.js';
3
+
4
+ const CHANNEL = 'message';
5
+
6
+ export default bootstrap(class Worker extends Sender {
7
+ constructor(scriptURL, options, resolve) {
8
+ const { port1, port2 } = new MessageChannel;
9
+ const sab = SAB(options);
10
+ port1.addEventListener(CHANNEL, handler(sab, options, true));
11
+ port1.start();
12
+ super(...url(scriptURL, CHANNEL, options));
13
+ super.addEventListener(CHANNEL, () => resolve(this), { once: true });
14
+ super.postMessage(post(sab, options), [port2]);
15
+ }
16
+
17
+ get channel() {
18
+ return CHANNEL;
19
+ }
20
+ });
@@ -0,0 +1,12 @@
1
+ import { SharedArrayBuffer } from '@webreflection/utils/shared-array-buffer';
2
+
3
+ import { minByteLength } from './shared.js';
4
+
5
+ export default ({
6
+ initByteLength = 1024,
7
+ maxByteLength = (1024 * 8)
8
+ }) =>
9
+ new SharedArrayBuffer(
10
+ minByteLength + initByteLength,
11
+ { maxByteLength: minByteLength + maxByteLength }
12
+ );
@@ -0,0 +1,38 @@
1
+ import withResolvers from '@webreflection/utils/with-resolvers';
2
+ import i32 from 'weak-id/i32';
3
+
4
+ import SHARED_CHANNEL from '../channel.js';
5
+
6
+ const { isArray } = Array;
7
+
8
+ const onsend = value => value;
9
+
10
+ export default class Sender extends Worker {
11
+ #next;
12
+ #requests;
13
+ constructor(scriptURL, options) {
14
+ super(scriptURL, options);
15
+ this.#next = i32();
16
+ this.#requests = new Map;
17
+ if (!options.onsend) options.onsend = onsend;
18
+ super.addEventListener('message', async event => {
19
+ const { data } = event;
20
+ if (isArray(data) && data[0] === SHARED_CHANNEL) {
21
+ event.stopImmediatePropagation();
22
+ event.preventDefault();
23
+ const [id, payload] = data[1];
24
+ const resolve = this.#requests.get(id);
25
+ this.#requests.delete(id);
26
+ resolve(await options.onsend(payload));
27
+ }
28
+ });
29
+ }
30
+
31
+ send(payload, ...rest) {
32
+ const id = this.#next();
33
+ const { promise, resolve } = withResolvers();
34
+ this.#requests.set(id, resolve);
35
+ super.postMessage([SHARED_CHANNEL, [id, payload]], ...rest);
36
+ return promise;
37
+ }
38
+ }