reflected 0.0.0 → 0.0.2

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 (106) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +144 -0
  3. package/dist/async-DF1WaSCr.js +1 -0
  4. package/dist/async.js +1 -0
  5. package/dist/broadcast-D0xibjmN.js +1 -0
  6. package/dist/broadcast-Dc7wAEE6.js +1 -0
  7. package/dist/broadcast.js +1 -0
  8. package/dist/channel-CdS9bLt4.js +1 -0
  9. package/dist/i32-C78nBJH2.js +1 -0
  10. package/dist/index.js +1 -0
  11. package/dist/message-D7s0I-EX.js +1 -0
  12. package/dist/message-DLH7cJo6.js +1 -0
  13. package/dist/message.js +1 -0
  14. package/dist/sender-BMLGKAss.js +1 -0
  15. package/dist/shared-C_kd5Il-.js +1 -0
  16. package/dist/shared-MkVjuKUg.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-D5y-AocB.js +1 -0
  22. package/dist/xhr-DAG_4oRo.js +1 -0
  23. package/dist/xhr.js +1 -0
  24. package/package.json +91 -10
  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 +33 -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 +101 -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 +28 -7
  46. package/src/worker/xhr.js +38 -0
  47. package/src/xhr.js +8 -0
  48. package/test/README/index.html +11 -0
  49. package/test/README/index.js +54 -0
  50. package/test/README/mini-coi.js +28 -0
  51. package/test/README/worker.js +26 -0
  52. package/test/index.html +8 -9
  53. package/test/index.js +35 -0
  54. package/test/mini-coi.js +28 -0
  55. package/test/sw.js +1 -0
  56. package/test/worker.js +16 -4
  57. package/types/async.d.ts +3 -0
  58. package/types/broadcast.d.ts +21 -0
  59. package/types/channel.d.ts +2 -0
  60. package/types/index.d.ts +9 -0
  61. package/types/main/async.d.ts +7 -0
  62. package/types/main/broadcast.d.ts +20 -0
  63. package/types/main/message.d.ts +20 -0
  64. package/types/main/sab.d.ts +5 -0
  65. package/types/main/sender.d.ts +5 -0
  66. package/types/main/shared.d.ts +44 -0
  67. package/types/main/xhr.d.ts +2 -0
  68. package/types/message.d.ts +21 -0
  69. package/types/reflected/src/async.d.ts +3 -0
  70. package/types/reflected/src/broadcast.d.ts +3 -0
  71. package/types/reflected/src/channel.d.ts +2 -0
  72. package/types/reflected/src/index.d.ts +3 -0
  73. package/types/reflected/src/main/async.d.ts +7 -0
  74. package/types/reflected/src/main/broadcast.d.ts +2 -0
  75. package/types/reflected/src/main/message.d.ts +2 -0
  76. package/types/reflected/src/main/sab.d.ts +5 -0
  77. package/types/reflected/src/main/sender.d.ts +5 -0
  78. package/types/reflected/src/main/shared.d.ts +44 -0
  79. package/types/reflected/src/main/xhr.d.ts +2 -0
  80. package/types/reflected/src/message.d.ts +3 -0
  81. package/types/reflected/src/service/listeners.d.ts +3 -0
  82. package/types/reflected/src/service/worker.d.ts +1 -0
  83. package/types/reflected/src/shared.d.ts +1 -0
  84. package/types/reflected/src/worker/async.d.ts +3 -0
  85. package/types/reflected/src/worker/broadcast.d.ts +3 -0
  86. package/types/reflected/src/worker/message.d.ts +3 -0
  87. package/types/reflected/src/worker/sender.d.ts +2 -0
  88. package/types/reflected/src/worker/shared.d.ts +11 -0
  89. package/types/reflected/src/worker/xhr.d.ts +3 -0
  90. package/types/reflected/src/xhr.d.ts +3 -0
  91. package/types/service/listeners.d.ts +3 -0
  92. package/types/service/worker.d.ts +1 -0
  93. package/types/shared.d.ts +1 -0
  94. package/types/weak-id/i32.d.ts +2 -0
  95. package/types/worker/async.d.ts +3 -0
  96. package/types/worker/broadcast.d.ts +3 -0
  97. package/types/worker/message.d.ts +3 -0
  98. package/types/worker/sender.d.ts +2 -0
  99. package/types/worker/shared.d.ts +11 -0
  100. package/types/worker/xhr.d.ts +3 -0
  101. package/types/xhr.d.ts +3 -0
  102. package/src/main/chrome.js +0 -19
  103. package/src/main/fallback.js +0 -29
  104. package/src/main/firefox.js +0 -19
  105. package/src/main/shared-id.js +0 -1
  106. 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,144 @@
1
+ # reflected
2
+
3
+ A primitive to allow workers to call **synchronously** any functionality exposed on the main thread.
4
+
5
+ ## Strategies
6
+
7
+ This module uses 3 synchronous strategies + 1 asynchronous fallback:
8
+
9
+ * **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.
10
+ * **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.
11
+ * **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.
12
+ * **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.
13
+
14
+ All strategies are automatically detected through the default/main `import` but all dedicated strategies can be retrieved directly, for example:
15
+
16
+ * `import reflect from 'reflected'` will decide automatically which strategy should be used.
17
+ * `import reflect from 'reflected/message'` will return the right *message* based module on the main thread and the worker mode within the worker.
18
+ * `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.
19
+ * `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.
20
+
21
+ Swap `message` with `broadcast`, `xhr` or `async`, and all exports will work equally well according to the chosen "*channel strategy*".
22
+
23
+ | Import | Use case |
24
+ |--------|----------|
25
+ | `reflected` | Auto-pick strategy (main + worker) |
26
+ | `reflected/message` \| `broadcast` \| `xhr` \| `async` | Specific strategy, context-aware |
27
+ | `reflected/main/<strategy>` | Main thread only (smaller bundle) |
28
+ | `reflected/worker/<strategy>` | Worker only (smaller bundle) |
29
+
30
+ **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.
31
+
32
+ ### Worker Thread API
33
+
34
+ ```js
35
+ // file: worker.js (always loaded as module)
36
+ import reflect from 'https://esm.run/reflected';
37
+
38
+ // ℹ️ must await the initialization
39
+ const reflected = await reflect({
40
+ // receives the returned data from the main thread.
41
+ // use this helper to transform such data into something
42
+ // that the worker can use/understand after invoke
43
+ // ⚠️ must be synchronous and it's invoked synchronously
44
+ onsync(response:Int32Array) {
45
+ return response.length ? response[0] : undefined;
46
+ },
47
+
48
+ // receives the data from the main thread when
49
+ // `worker.send(payload, ...rest)` is invoked.
50
+ // use this helper to transform the main thread request
51
+ // into something compatible with structuredClone algorithm
52
+ // ℹ️ works even if synchronous but it's resolved asynchronously
53
+ async onsend(payload) {
54
+ const data = await fetch('./data.json').then(r => r.json());
55
+ return process(payload, data);
56
+ },
57
+ });
58
+
59
+ // retrieve the result of `test_sum(1, 2, 3)`
60
+ // directly from the main thread.
61
+ // only the async channel variant would need to await
62
+ const value = reflected({
63
+ invoke: 'test_sum',
64
+ args: [1, 2, 3]
65
+ });
66
+
67
+ console.log(value); // 6
68
+ ```
69
+
70
+ ### Main Thread API
71
+
72
+ ```js
73
+ // file: index.js as module
74
+ import reflect from 'https://esm.run/reflected';
75
+
76
+ function test_sum(...args:number[]) {
77
+ let i = 0;
78
+ while (args.length)
79
+ i += args.pop();
80
+ return i;
81
+ }
82
+
83
+ // ℹ️ must await the initialization
84
+ const worker = await reflect(
85
+ // Worker scriptURL
86
+ './worker.js',
87
+ // Worker options + required utilities / helpers
88
+ // ℹ️ type is enforced to be 'module' due to top-level await
89
+ {
90
+ // invoked when the worker asks to synchronize a call
91
+ // and it must return an Int32Array reference to populate
92
+ // the SharedArrayBuffer and notify/unlock the worker
93
+ // ℹ️ works even if synchronous but it's resolved asynchronously
94
+ // ⚠️ the worker is not responsive until this returns so
95
+ // be sure you handle errors gracefully to still provide
96
+ // a result the worker can consume out of the shared buffer!
97
+ async onsync(payload:unknown) {
98
+ const { invoke, args } = payload;
99
+
100
+ if (invoke === 'test_sum') {
101
+ // just demoing this can be async too
102
+ const value = await test_sum(...args);
103
+ return new Int32Array([value]);
104
+ }
105
+
106
+ // errors should still be Int32Array but
107
+ // it is trivial to return no result
108
+ return new Int32Array(0);
109
+ },
110
+
111
+ // *optional* helper to process data returned from the worker when
112
+ // the main thread `await worker.send(payload, ...rest)` operation
113
+ // is invoked. If not present, whatever payload the worker returned
114
+ // will be directly returned as invoke result, just like in here.
115
+ // ℹ️ works even if synchronous but it's resolved asynchronously
116
+ onsend(payload:unknown) {
117
+ return payload;
118
+ },
119
+
120
+ // optional: the initial SharedArrayBuffer length
121
+ initByteLength: 1024,
122
+
123
+ // optional: the max possible SharedArrayBuffer growth
124
+ maxByteLength: 8192,
125
+
126
+ // optional: the service worker as fallback
127
+ // * if it's a string, it's used to register it
128
+ // * if it's an object, it's used to initialize it
129
+ // but it must contain a `url` field to register it
130
+ // ℹ️ if already registered it will not try to register it
131
+ serviceWorker: undefined,
132
+ }
133
+ );
134
+
135
+ const value = await worker.send({ any: 'payload' });
136
+ ```
137
+
138
+ ### Extras
139
+
140
+ - **Named export `channel`:** After initialization, `import reflect, { channel } from 'reflected'` gives the active strategy name (`'message'`, `'broadcast'`, `'xhr'`, or `'async'`).
141
+ - **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.
142
+ - **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)
143
+
144
+ 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 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="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};
@@ -0,0 +1 @@
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};
@@ -0,0 +1 @@
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};
@@ -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="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};
@@ -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-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};
@@ -0,0 +1 @@
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};
@@ -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-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};
@@ -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="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};
@@ -0,0 +1 @@
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};
@@ -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 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());
@@ -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{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 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="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,16 +1,97 @@
1
1
  {
2
2
  "name": "reflected",
3
- "version": "0.0.0",
4
- "description": "",
5
- "main": "index.js",
3
+ "version": "0.0.2",
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
- "keywords": [],
10
- "author": "",
11
- "license": "ISC",
12
- "type": "commonjs",
64
+ "keywords": [
65
+ "worker",
66
+ "SharedArrayBuffer",
67
+ "synchronous"
68
+ ],
69
+ "author": "Andrea Giammarchi (webreflection)",
70
+ "license": "MIT",
71
+ "type": "module",
72
+ "types": "./types/index.d.ts",
73
+ "main": "./dist/index.js",
74
+ "module": "./src/index.js",
13
75
  "dependencies": {
14
- "@webreflection/utils": "^0.1.2"
15
- }
76
+ "@webreflection/utils": "^0.1.2",
77
+ "next-resolver": "^0.1.6",
78
+ "typescript": "^5.9.3",
79
+ "weak-id": "^0.2.1"
80
+ },
81
+ "devDependencies": {
82
+ "@rollup/plugin-node-resolve": "^16.0.3",
83
+ "@rollup/plugin-terser": "^0.4.4",
84
+ "rollup": "^4.57.1"
85
+ },
86
+ "directories": {
87
+ "test": "test"
88
+ },
89
+ "repository": {
90
+ "type": "git",
91
+ "url": "git+https://github.com/WebReflection/reflected.git"
92
+ },
93
+ "bugs": {
94
+ "url": "https://github.com/WebReflection/reflected/issues"
95
+ },
96
+ "homepage": "https://github.com/WebReflection/reflected#readme"
16
97
  }
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
+ if (process.env.NEW_CHANNEL)
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 'fc260aad-4404-43b8-ae9d-2c06554bb294';
package/src/index.js CHANGED
@@ -1,20 +1,32 @@
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
+ /** @typedef {import('./main/shared.js').Options} MainOptions */
5
+ /** @typedef {import('./worker/shared.js').Options} WorkerOptions */
6
+
7
+ /** @type {string} */
8
+ let channel;
9
+
10
+ /** @type {Function} */
4
11
  let module;
5
12
 
6
13
  if ('importScripts' in globalThis) {
7
14
  let get;
8
15
  const { promise, resolve } = withResolvers();
16
+ // @ts-ignore
9
17
  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');
18
+ channel = reflected;
19
+ if (reflected === 'message') get = import(/* webpackIgnore: true */'./worker/message.js');
20
+ else if (reflected === 'broadcast') get = import(/* webpackIgnore: true */'./worker/broadcast.js');
21
+ else if (reflected === 'xhr') get = import(/* webpackIgnore: true */'./worker/xhr.js');
22
+ else get = import(/* webpackIgnore: true */'./worker/async.js');
13
23
  module = async options => {
14
24
  const { data, ports } = await promise;
15
25
  const { default: reflect } = await get;
16
26
  const event = new Event('message');
27
+ // @ts-ignore
17
28
  event.data = data;
29
+ // @ts-ignore
18
30
  event.ports = ports;
19
31
  dispatchEvent(event);
20
32
  return reflect(options);
@@ -22,13 +34,24 @@ if ('importScripts' in globalThis) {
22
34
  addEventListener('message', resolve, { once: true });
23
35
  }
24
36
  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;
37
+ if ('InstallTrigger' in globalThis) {
38
+ channel = 'broadcast';
39
+ module = (await import(/* webpackIgnore: true */'./main/broadcast.js')).default;
40
+ }
41
+ else {
42
+ channel = 'message';
43
+ module = (await import(/* webpackIgnore: true */'./main/message.js')).default;
44
+ }
45
+ }
46
+ else if (navigator.serviceWorker) {
47
+ channel = 'xhr';
48
+ module = (await import(/* webpackIgnore: true */'./main/xhr.js')).default;
29
49
  }
30
50
  else {
31
- module = (await import('./main/fallback.js')).default;
51
+ channel = 'fallback';
52
+ module = () => {};
32
53
  }
33
54
 
55
+ export { channel };
56
+
34
57
  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, payload] }) => {
16
+ await handle({ data: payload });
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
+ );