request-iframe 0.2.0 → 0.2.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.
- package/QUICKSTART.CN.md +33 -11
- package/QUICKSTART.md +33 -11
- package/README.CN.md +157 -44
- package/README.md +159 -41
- package/cdn/request-iframe.umd.js +4814 -4026
- package/cdn/request-iframe.umd.js.map +1 -1
- package/cdn/request-iframe.umd.min.js +2 -2
- package/cdn/request-iframe.umd.min.js.map +1 -1
- package/esm/api/client.js +45 -22
- package/esm/api/endpoint.js +30 -13
- package/esm/api/server.js +22 -13
- package/esm/constants/warn-once.js +7 -1
- package/esm/endpoint/index.js +1 -2
- package/esm/endpoint/infra/inbox.js +5 -4
- package/esm/endpoint/infra/outbox.js +8 -8
- package/esm/endpoint/stream/file-auto-resolve.js +9 -8
- package/esm/impl/client.js +3 -2
- package/esm/impl/response.js +4 -2
- package/esm/impl/server.js +8 -6
- package/esm/message/channel.js +15 -3
- package/esm/message/dispatcher.js +27 -0
- package/esm/stream/file-stream.js +311 -72
- package/esm/stream/writable-stream.js +21 -4
- package/esm/utils/blob.js +17 -0
- package/esm/utils/debug-lazy.js +76 -0
- package/esm/utils/logger.js +33 -1
- package/esm/utils/strict-mode.js +85 -0
- package/esm/utils/warn-once.js +30 -0
- package/esm/utils/warnings.js +47 -0
- package/library/api/client.d.ts.map +1 -1
- package/library/api/client.js +45 -22
- package/library/api/endpoint.d.ts.map +1 -1
- package/library/api/endpoint.js +30 -13
- package/library/api/server.d.ts.map +1 -1
- package/library/api/server.js +22 -13
- package/library/constants/warn-once.d.ts +6 -0
- package/library/constants/warn-once.d.ts.map +1 -1
- package/library/constants/warn-once.js +7 -1
- package/library/endpoint/index.d.ts +0 -1
- package/library/endpoint/index.d.ts.map +1 -1
- package/library/endpoint/index.js +1 -8
- package/library/endpoint/infra/inbox.d.ts.map +1 -1
- package/library/endpoint/infra/inbox.js +4 -3
- package/library/endpoint/infra/outbox.d.ts +2 -0
- package/library/endpoint/infra/outbox.d.ts.map +1 -1
- package/library/endpoint/infra/outbox.js +7 -7
- package/library/endpoint/stream/file-auto-resolve.d.ts +1 -1
- package/library/endpoint/stream/file-auto-resolve.d.ts.map +1 -1
- package/library/endpoint/stream/file-auto-resolve.js +8 -8
- package/library/impl/client.d.ts +2 -0
- package/library/impl/client.d.ts.map +1 -1
- package/library/impl/client.js +3 -2
- package/library/impl/response.d.ts.map +1 -1
- package/library/impl/response.js +4 -2
- package/library/impl/server.d.ts.map +1 -1
- package/library/impl/server.js +7 -5
- package/library/message/channel.d.ts +2 -2
- package/library/message/channel.d.ts.map +1 -1
- package/library/message/channel.js +15 -3
- package/library/message/dispatcher.d.ts.map +1 -1
- package/library/message/dispatcher.js +27 -0
- package/library/stream/file-stream.d.ts +70 -5
- package/library/stream/file-stream.d.ts.map +1 -1
- package/library/stream/file-stream.js +310 -70
- package/library/stream/types.d.ts +2 -0
- package/library/stream/types.d.ts.map +1 -1
- package/library/stream/writable-stream.d.ts.map +1 -1
- package/library/stream/writable-stream.js +21 -4
- package/library/types/index.d.ts +38 -0
- package/library/types/index.d.ts.map +1 -1
- package/library/utils/blob.d.ts +7 -0
- package/library/utils/blob.d.ts.map +1 -1
- package/library/utils/blob.js +18 -0
- package/library/utils/debug-lazy.d.ts +26 -0
- package/library/utils/debug-lazy.d.ts.map +1 -0
- package/library/utils/debug-lazy.js +85 -0
- package/library/utils/logger.d.ts +20 -0
- package/library/utils/logger.d.ts.map +1 -1
- package/library/utils/logger.js +34 -1
- package/library/utils/strict-mode.d.ts +37 -0
- package/library/utils/strict-mode.d.ts.map +1 -0
- package/library/utils/strict-mode.js +94 -0
- package/library/utils/warn-once.d.ts +9 -0
- package/library/utils/warn-once.d.ts.map +1 -0
- package/library/utils/warn-once.js +36 -0
- package/library/utils/warnings.d.ts +48 -0
- package/library/utils/warnings.d.ts.map +1 -0
- package/library/utils/warnings.js +54 -0
- package/package.json +1 -1
- package/esm/endpoint/stream/file-writable.js +0 -105
- package/library/endpoint/stream/file-writable.d.ts +0 -33
- package/library/endpoint/stream/file-writable.d.ts.map +0 -1
- package/library/endpoint/stream/file-writable.js +0 -115
package/README.md
CHANGED
|
@@ -125,13 +125,20 @@ Example (using unpkg):
|
|
|
125
125
|
```typescript
|
|
126
126
|
import { requestIframeClient } from 'request-iframe';
|
|
127
127
|
|
|
128
|
-
|
|
129
|
-
const iframe = document.querySelector('iframe')
|
|
128
|
+
/** Get iframe element */
|
|
129
|
+
const iframe = document.querySelector('iframe') as HTMLIFrameElement;
|
|
130
|
+
|
|
131
|
+
/** Prefer waiting iframe load so contentWindow is ready */
|
|
132
|
+
await new Promise<void>((resolve) => iframe.addEventListener('load', () => resolve(), { once: true }));
|
|
130
133
|
|
|
131
|
-
|
|
132
|
-
|
|
134
|
+
/**
|
|
135
|
+
* Create client (safer + less boilerplate default)
|
|
136
|
+
* - strict: true defaults targetOrigin/allowedOrigins to window.location.origin (same-origin only)
|
|
137
|
+
* - For cross-origin, explicitly configure targetOrigin + allowedOrigins/validateOrigin
|
|
138
|
+
*/
|
|
139
|
+
const client = requestIframeClient(iframe, { secretKey: 'my-app', strict: true });
|
|
133
140
|
|
|
134
|
-
|
|
141
|
+
/** Send request (just like axios) */
|
|
135
142
|
const response = await client.send('/api/getUserInfo', { userId: 123 });
|
|
136
143
|
console.log(response.data); // { name: 'Tom', age: 18 }
|
|
137
144
|
```
|
|
@@ -141,10 +148,15 @@ console.log(response.data); // { name: 'Tom', age: 18 }
|
|
|
141
148
|
```typescript
|
|
142
149
|
import { requestIframeServer } from 'request-iframe';
|
|
143
150
|
|
|
144
|
-
|
|
145
|
-
|
|
151
|
+
/**
|
|
152
|
+
* Create server
|
|
153
|
+
* - Strongly recommended to configure allowedOrigins / validateOrigin in production
|
|
154
|
+
* - This snippet assumes a same-origin demo (parent origin === iframe origin)
|
|
155
|
+
* For cross-origin, replace with the real parent origin (e.g. 'https://parent.example.com')
|
|
156
|
+
*/
|
|
157
|
+
const server = requestIframeServer({ secretKey: 'my-app', strict: true });
|
|
146
158
|
|
|
147
|
-
|
|
159
|
+
/** Register handler (just like express) */
|
|
148
160
|
server.on('/api/getUserInfo', (req, res) => {
|
|
149
161
|
const { userId } = req.body;
|
|
150
162
|
res.send({ name: 'Tom', age: 18 });
|
|
@@ -169,8 +181,8 @@ That's it! 🎉
|
|
|
169
181
|
In micro-frontend architecture, the main application needs to communicate with child application iframes:
|
|
170
182
|
|
|
171
183
|
```typescript
|
|
172
|
-
|
|
173
|
-
const client = requestIframeClient(iframe, { secretKey: 'main-app' });
|
|
184
|
+
/** Main application (parent page, same-origin default: strict: true) */
|
|
185
|
+
const client = requestIframeClient(iframe, { secretKey: 'main-app', strict: true });
|
|
174
186
|
|
|
175
187
|
// Get user info from child application
|
|
176
188
|
const userInfoResponse = await client.send('/api/user/info', {});
|
|
@@ -185,8 +197,8 @@ await client.send('/api/data/refresh', { timestamp: Date.now() });
|
|
|
185
197
|
When integrating third-party components, isolate via iframe while maintaining communication:
|
|
186
198
|
|
|
187
199
|
```typescript
|
|
188
|
-
|
|
189
|
-
const client = requestIframeClient(thirdPartyIframe, { secretKey: 'widget' });
|
|
200
|
+
/** Parent page (same-origin default: strict: true) */
|
|
201
|
+
const client = requestIframeClient(thirdPartyIframe, { secretKey: 'widget', strict: true });
|
|
190
202
|
|
|
191
203
|
// Configure component
|
|
192
204
|
await client.send('/config', {
|
|
@@ -194,14 +206,16 @@ await client.send('/config', {
|
|
|
194
206
|
language: 'en-US'
|
|
195
207
|
});
|
|
196
208
|
|
|
197
|
-
|
|
198
|
-
const server = requestIframeServer({ secretKey: 'widget' });
|
|
209
|
+
/** Listen to component events (via reverse communication) */
|
|
210
|
+
const server = requestIframeServer({ secretKey: 'widget', strict: true });
|
|
199
211
|
server.on('/event', (req, res) => {
|
|
200
212
|
console.log('Component event:', req.body);
|
|
201
213
|
res.send({ received: true });
|
|
202
214
|
});
|
|
203
215
|
```
|
|
204
216
|
|
|
217
|
+
> If the third-party iframe is **cross-origin**, explicitly configure `targetOrigin` and `allowedOrigins/validateOrigin` (see the security section).
|
|
218
|
+
|
|
205
219
|
### Popup / New Window (Window Communication)
|
|
206
220
|
|
|
207
221
|
`request-iframe` also works with a `Window` target (not only an iframe).
|
|
@@ -209,19 +223,25 @@ server.on('/event', (req, res) => {
|
|
|
209
223
|
**Important**: you must have a real `Window` reference (e.g. returned by `window.open()`, or available via `window.opener` / `event.source`). You cannot send to an arbitrary browser tab by URL.
|
|
210
224
|
|
|
211
225
|
```typescript
|
|
212
|
-
|
|
226
|
+
/** Parent page: open a new tab/window */
|
|
213
227
|
const child = window.open('https://child.example.com/page.html', '_blank');
|
|
214
228
|
if (!child) throw new Error('Popup blocked');
|
|
215
229
|
|
|
216
|
-
|
|
230
|
+
/** Parent -> child */
|
|
231
|
+
const targetOrigin = 'https://child.example.com';
|
|
217
232
|
const client = requestIframeClient(child, {
|
|
218
233
|
secretKey: 'popup-demo',
|
|
219
|
-
targetOrigin
|
|
234
|
+
targetOrigin, // strongly recommended (avoid '*')
|
|
235
|
+
allowedOrigins: [targetOrigin]
|
|
220
236
|
});
|
|
221
237
|
await client.send('/api/ping', { from: 'parent' });
|
|
222
238
|
|
|
223
|
-
|
|
224
|
-
|
|
239
|
+
/**
|
|
240
|
+
* Child page: create server
|
|
241
|
+
* Note: allowedOrigins should be the real parent origin (example value below).
|
|
242
|
+
*/
|
|
243
|
+
const parentOrigin = 'https://parent.example.com';
|
|
244
|
+
const server = requestIframeServer({ secretKey: 'popup-demo', allowedOrigins: [parentOrigin] });
|
|
225
245
|
server.on('/api/ping', (req, res) => res.send({ ok: true, echo: req.body }));
|
|
226
246
|
```
|
|
227
247
|
|
|
@@ -230,8 +250,12 @@ server.on('/api/ping', (req, res) => res.send({ ok: true, echo: req.body }));
|
|
|
230
250
|
When iframe and parent page are on different origins, use request-iframe to securely fetch data:
|
|
231
251
|
|
|
232
252
|
```typescript
|
|
233
|
-
|
|
234
|
-
|
|
253
|
+
/**
|
|
254
|
+
* Inside iframe (different origin)
|
|
255
|
+
* Note: allowedOrigins should be the real parent origin (example value below).
|
|
256
|
+
*/
|
|
257
|
+
const parentOrigin = 'https://parent.example.com';
|
|
258
|
+
const server = requestIframeServer({ secretKey: 'data-api', allowedOrigins: [parentOrigin] });
|
|
235
259
|
|
|
236
260
|
server.on('/api/data', async (req, res) => {
|
|
237
261
|
// Fetch data from same-origin API (iframe can access same-origin resources)
|
|
@@ -239,8 +263,9 @@ server.on('/api/data', async (req, res) => {
|
|
|
239
263
|
res.send(data);
|
|
240
264
|
});
|
|
241
265
|
|
|
242
|
-
|
|
243
|
-
const
|
|
266
|
+
/** Parent page (cross-origin) */
|
|
267
|
+
const targetOrigin = new URL(iframe.src).origin;
|
|
268
|
+
const client = requestIframeClient(iframe, { secretKey: 'data-api', targetOrigin, allowedOrigins: [targetOrigin] });
|
|
244
269
|
const response = await client.send('/api/data', {});
|
|
245
270
|
const data = response.data; // Successfully fetch cross-origin data
|
|
246
271
|
```
|
|
@@ -740,6 +765,17 @@ import {
|
|
|
740
765
|
isIframeFileReadableStream
|
|
741
766
|
} from 'request-iframe';
|
|
742
767
|
|
|
768
|
+
/**
|
|
769
|
+
* How to choose (data stream vs file/byte stream)
|
|
770
|
+
*
|
|
771
|
+
* - IframeWritableStream / IframeReadableStream:
|
|
772
|
+
* For application-level data (objects/strings), relies on structured clone.
|
|
773
|
+
* - IframeFileWritableStream / IframeFileReadableStream:
|
|
774
|
+
* For byte sequences (files/binary/UTF-8 text files). Chunks represent bytes.
|
|
775
|
+
* - Text file recommended: IframeFileWritableStream.fromText(...) + fileStream.readAsText()
|
|
776
|
+
* - Binary recommended: yield Uint8Array/ArrayBuffer (transferables when possible)
|
|
777
|
+
*/
|
|
778
|
+
|
|
743
779
|
// Server side: Send data stream using iterator
|
|
744
780
|
server.on('/api/stream', async (req, res) => {
|
|
745
781
|
const stream = new IframeWritableStream({
|
|
@@ -762,6 +798,19 @@ server.on('/api/stream', async (req, res) => {
|
|
|
762
798
|
await res.sendStream(stream);
|
|
763
799
|
});
|
|
764
800
|
|
|
801
|
+
// Server side: Option 2 (recommended) - create a file stream from Blob/File via from()
|
|
802
|
+
server.on('/api/fileStream2', async (req, res) => {
|
|
803
|
+
const blob = new Blob([/* file bytes */], { type: 'application/octet-stream' });
|
|
804
|
+
const stream = await IframeFileWritableStream.from({
|
|
805
|
+
content: blob,
|
|
806
|
+
fileName: 'large-file.bin',
|
|
807
|
+
mimeType: 'application/octet-stream',
|
|
808
|
+
chunked: true,
|
|
809
|
+
chunkSize: 256 * 1024
|
|
810
|
+
});
|
|
811
|
+
await res.sendStream(stream);
|
|
812
|
+
});
|
|
813
|
+
|
|
765
814
|
// Client side: Receive stream data
|
|
766
815
|
const response = await client.send('/api/stream', {}, { streamTimeout: 10000 });
|
|
767
816
|
|
|
@@ -795,6 +844,28 @@ if (isIframeReadableStream(response.stream)) {
|
|
|
795
844
|
}
|
|
796
845
|
```
|
|
797
846
|
|
|
847
|
+
#### Text file convenience
|
|
848
|
+
|
|
849
|
+
```typescript
|
|
850
|
+
// Server side: send a UTF-8 text file
|
|
851
|
+
server.on('/api/textFile', async (req, res) => {
|
|
852
|
+
const stream = await IframeFileWritableStream.fromText({
|
|
853
|
+
text: 'hello',
|
|
854
|
+
fileName: 'hello.txt',
|
|
855
|
+
chunked: true,
|
|
856
|
+
chunkSize: 64 * 1024
|
|
857
|
+
});
|
|
858
|
+
await res.sendStream(stream);
|
|
859
|
+
});
|
|
860
|
+
|
|
861
|
+
// Client side: read as UTF-8 text
|
|
862
|
+
const resp = await client.send('/api/textFile', {});
|
|
863
|
+
if (isIframeFileReadableStream(resp.stream)) {
|
|
864
|
+
const text = await resp.stream.readAsText();
|
|
865
|
+
console.log(text);
|
|
866
|
+
}
|
|
867
|
+
```
|
|
868
|
+
|
|
798
869
|
#### Client → Server (Client sends stream to server)
|
|
799
870
|
|
|
800
871
|
```typescript
|
|
@@ -849,11 +920,11 @@ server.on('/api/uploadStream', async (req, res) => {
|
|
|
849
920
|
| Type | Description |
|
|
850
921
|
|------|-------------|
|
|
851
922
|
| `IframeWritableStream` | Writer/producer stream: **created by whichever side is sending the stream** (server→client response stream, or client→server request stream) |
|
|
852
|
-
| `IframeFileWritableStream` | File writer/producer stream (
|
|
923
|
+
| `IframeFileWritableStream` | File writer/producer stream (supports `Uint8Array`/`ArrayBuffer` chunks; uses transferables when possible) |
|
|
853
924
|
| `IframeReadableStream` | Reader/consumer stream for receiving regular data (regardless of which side sent it) |
|
|
854
|
-
| `IframeFileReadableStream` | File reader/consumer stream (
|
|
925
|
+
| `IframeFileReadableStream` | File reader/consumer stream (supports binary chunks) |
|
|
855
926
|
|
|
856
|
-
> **Note**: File
|
|
927
|
+
> **Note**: File stream chunks represent **bytes**. This version transfers file chunks as binary (`ArrayBuffer`/`Uint8Array`) and will use transferables when possible to reduce copying. If you manually yield a `string` chunk, it will be encoded into bytes using **UTF-8**; for binary data, yield `Uint8Array/ArrayBuffer` directly. For large files, prefer **chunked** file streams (`chunked: true`) and keep chunk sizes moderate (e.g. 256KB–1MB).
|
|
857
928
|
|
|
858
929
|
**Stream timeouts:**
|
|
859
930
|
- `options.streamTimeout` (request option): client-side stream idle timeout while consuming `response.stream` (data/file streams). When triggered, the client performs a heartbeat check (by default `client.isConnect()`); if not alive, the stream fails as disconnected.
|
|
@@ -964,6 +1035,7 @@ Create a Client instance.
|
|
|
964
1035
|
| `target` | `HTMLIFrameElement \| Window` | Target iframe element or window object |
|
|
965
1036
|
| `options.secretKey` | `string` | Message isolation identifier (optional) |
|
|
966
1037
|
| `options.trace` | `boolean \| 'trace' \| 'info' \| 'warn' \| 'error' \| 'silent'` | Trace/log level (optional). Default logs are warn/error only |
|
|
1038
|
+
| `options.strict` | `boolean` | Strict mode (recommended for same-origin defaults). If you did not explicitly configure `targetOrigin/allowedOrigins/validateOrigin`, defaults are constrained to `window.location.origin` (same-origin only). **Note: strict is NOT a cross-origin security configuration**; for cross-origin you must explicitly set `targetOrigin` + `allowedOrigins/validateOrigin`. |
|
|
967
1039
|
| `options.targetOrigin` | `string` | Override postMessage targetOrigin for sending (optional). If `target` is a `Window`, default is `*`. |
|
|
968
1040
|
| `options.ackTimeout` | `number` | Global default ACK acknowledgment timeout (ms), default 1000 |
|
|
969
1041
|
| `options.timeout` | `number` | Global default request timeout (ms), default 5000 |
|
|
@@ -978,7 +1050,8 @@ Create a Client instance.
|
|
|
978
1050
|
**Notes about `target: Window`:**
|
|
979
1051
|
- **You must have a `Window` reference** (e.g. from `window.open()`, `window.opener`, or `MessageEvent.source`).
|
|
980
1052
|
- You **cannot** communicate with an arbitrary browser tab by URL.
|
|
981
|
-
- For security, prefer setting a strict `targetOrigin` and configure `allowedOrigins` / `validateOrigin`.
|
|
1053
|
+
- For security, prefer setting a strict `targetOrigin` (avoid `*`) and configure `allowedOrigins` / `validateOrigin`.
|
|
1054
|
+
- `strict: true` only constrains defaults to the current origin (same-origin only); it does **not** automatically make a cross-origin setup secure.
|
|
982
1055
|
|
|
983
1056
|
**Production configuration template:**
|
|
984
1057
|
|
|
@@ -1049,6 +1122,7 @@ Create a Server instance.
|
|
|
1049
1122
|
|-----------|------|-------------|
|
|
1050
1123
|
| `options.secretKey` | `string` | Message isolation identifier (optional) |
|
|
1051
1124
|
| `options.trace` | `boolean \| 'trace' \| 'info' \| 'warn' \| 'error' \| 'silent'` | Trace/log level (optional). Default logs are warn/error only |
|
|
1125
|
+
| `options.strict` | `boolean` | Strict mode (recommended for same-origin defaults). If you did not explicitly configure `allowedOrigins/validateOrigin`, it defaults to `allowedOrigins: [window.location.origin]` (same-origin only). **Note: strict is NOT a cross-origin security configuration**; for cross-origin you must explicitly configure allowlists/validators. |
|
|
1052
1126
|
| `options.ackTimeout` | `number` | Wait for client acknowledgment timeout (ms), default 1000 |
|
|
1053
1127
|
| `options.maxConcurrentRequestsPerClient` | `number` | Max concurrent in-flight requests per client (per origin + creatorId). Default Infinity |
|
|
1054
1128
|
| `options.allowedOrigins` | `string \| RegExp \| Array<string \| RegExp>` | Allowlist for incoming message origins (optional, recommended for production) |
|
|
@@ -1660,13 +1734,35 @@ try {
|
|
|
1660
1734
|
`secretKey` is used for message isolation. When there are multiple iframes or multiple request-iframe instances on a page, using different `secretKey` values can prevent message cross-talk:
|
|
1661
1735
|
|
|
1662
1736
|
```typescript
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1737
|
+
/**
|
|
1738
|
+
* Communication for iframe A
|
|
1739
|
+
* - Parent page should allowlist iframe A origin
|
|
1740
|
+
* - Inside iframe A should allowlist parent origin
|
|
1741
|
+
*/
|
|
1742
|
+
const iframeASrc = iframeA.getAttribute('src');
|
|
1743
|
+
if (!iframeASrc) throw new Error('iframeA src is empty');
|
|
1744
|
+
const iframeAOrigin = new URL(iframeASrc, window.location.href).origin;
|
|
1745
|
+
const clientA = requestIframeClient(iframeA, {
|
|
1746
|
+
secretKey: 'app-a',
|
|
1747
|
+
targetOrigin: iframeAOrigin,
|
|
1748
|
+
allowedOrigins: [iframeAOrigin]
|
|
1749
|
+
});
|
|
1750
|
+
const parentOrigin = 'https://parent.com';
|
|
1751
|
+
const serverA = requestIframeServer({ secretKey: 'app-a', allowedOrigins: [parentOrigin] });
|
|
1666
1752
|
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1753
|
+
/**
|
|
1754
|
+
* Communication for iframe B
|
|
1755
|
+
* - Same pattern as iframe A
|
|
1756
|
+
*/
|
|
1757
|
+
const iframeBSrc = iframeB.getAttribute('src');
|
|
1758
|
+
if (!iframeBSrc) throw new Error('iframeB src is empty');
|
|
1759
|
+
const iframeBOrigin = new URL(iframeBSrc, window.location.href).origin;
|
|
1760
|
+
const clientB = requestIframeClient(iframeB, {
|
|
1761
|
+
secretKey: 'app-b',
|
|
1762
|
+
targetOrigin: iframeBOrigin,
|
|
1763
|
+
allowedOrigins: [iframeBOrigin]
|
|
1764
|
+
});
|
|
1765
|
+
const serverB = requestIframeServer({ secretKey: 'app-b', allowedOrigins: [parentOrigin] });
|
|
1670
1766
|
```
|
|
1671
1767
|
|
|
1672
1768
|
### 2. Why is ACK acknowledgment needed?
|
|
@@ -1681,14 +1777,28 @@ ACK mechanism is similar to TCP handshake, used for:
|
|
|
1681
1777
|
`postMessage` itself supports cross-origin communication, request-iframe handles it automatically:
|
|
1682
1778
|
|
|
1683
1779
|
```typescript
|
|
1684
|
-
|
|
1685
|
-
|
|
1780
|
+
/**
|
|
1781
|
+
* Parent page (https://parent.com)
|
|
1782
|
+
* - targetOrigin/allowedOrigins should be the iframe origin
|
|
1783
|
+
*/
|
|
1784
|
+
const iframeSrc = iframe.getAttribute('src');
|
|
1785
|
+
if (!iframeSrc) throw new Error('iframe src is empty');
|
|
1786
|
+
const childOrigin = new URL(iframeSrc, window.location.href).origin;
|
|
1787
|
+
const client = requestIframeClient(iframe, {
|
|
1788
|
+
secretKey: 'my-app',
|
|
1789
|
+
targetOrigin: childOrigin,
|
|
1790
|
+
allowedOrigins: [childOrigin]
|
|
1791
|
+
});
|
|
1686
1792
|
|
|
1687
|
-
|
|
1688
|
-
|
|
1793
|
+
/**
|
|
1794
|
+
* Inside iframe (https://child.com)
|
|
1795
|
+
* - allowedOrigins should be the real parent origin
|
|
1796
|
+
*/
|
|
1797
|
+
const parentOrigin = 'https://parent.com';
|
|
1798
|
+
const server = requestIframeServer({ secretKey: 'my-app', allowedOrigins: [parentOrigin] });
|
|
1689
1799
|
```
|
|
1690
1800
|
|
|
1691
|
-
Just ensure both sides use the same `secretKey
|
|
1801
|
+
Just ensure both sides use the same `secretKey`, and configure `targetOrigin` / `allowedOrigins` correctly.
|
|
1692
1802
|
|
|
1693
1803
|
### 4. Can Server actively push messages?
|
|
1694
1804
|
|
|
@@ -1699,9 +1809,17 @@ For bidirectional communication, you have 2 options:
|
|
|
1699
1809
|
- Use `requestIframeEndpoint()` on both sides (recommended) so each side has **send + handle** in one object
|
|
1700
1810
|
|
|
1701
1811
|
```typescript
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1812
|
+
/**
|
|
1813
|
+
* Inside iframe
|
|
1814
|
+
* - For Window targets, always set a strict targetOrigin and allowlist it.
|
|
1815
|
+
*/
|
|
1816
|
+
const parentOrigin = 'https://parent.com';
|
|
1817
|
+
const server = requestIframeServer({ secretKey: 'my-app', allowedOrigins: [parentOrigin] });
|
|
1818
|
+
const client = requestIframeClient(window.parent, {
|
|
1819
|
+
secretKey: 'my-app-reverse',
|
|
1820
|
+
targetOrigin: parentOrigin,
|
|
1821
|
+
allowedOrigins: [parentOrigin]
|
|
1822
|
+
});
|
|
1705
1823
|
|
|
1706
1824
|
// Actively send message to parent page
|
|
1707
1825
|
await client.send('/notify', { event: 'data-changed' });
|