request-iframe 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.
- package/QUICKSTART.CN.md +269 -0
- package/QUICKSTART.md +269 -0
- package/README.CN.md +1369 -0
- package/README.md +1016 -0
- package/library/__tests__/interceptors.test.ts +124 -0
- package/library/__tests__/requestIframe.test.ts +2216 -0
- package/library/__tests__/stream.test.ts +650 -0
- package/library/__tests__/utils.test.ts +433 -0
- package/library/api/client.d.ts +16 -0
- package/library/api/client.d.ts.map +1 -0
- package/library/api/client.js +72 -0
- package/library/api/server.d.ts +16 -0
- package/library/api/server.d.ts.map +1 -0
- package/library/api/server.js +44 -0
- package/library/constants/index.d.ts +209 -0
- package/library/constants/index.d.ts.map +1 -0
- package/library/constants/index.js +260 -0
- package/library/constants/messages.d.ts +80 -0
- package/library/constants/messages.d.ts.map +1 -0
- package/library/constants/messages.js +123 -0
- package/library/core/client.d.ts +99 -0
- package/library/core/client.d.ts.map +1 -0
- package/library/core/client.js +440 -0
- package/library/core/message-handler.d.ts +110 -0
- package/library/core/message-handler.d.ts.map +1 -0
- package/library/core/message-handler.js +320 -0
- package/library/core/request-response.d.ts +59 -0
- package/library/core/request-response.d.ts.map +1 -0
- package/library/core/request-response.js +337 -0
- package/library/core/request.d.ts +17 -0
- package/library/core/request.d.ts.map +1 -0
- package/library/core/request.js +34 -0
- package/library/core/response.d.ts +51 -0
- package/library/core/response.d.ts.map +1 -0
- package/library/core/response.js +323 -0
- package/library/core/server-base.d.ts +86 -0
- package/library/core/server-base.d.ts.map +1 -0
- package/library/core/server-base.js +257 -0
- package/library/core/server-client.d.ts +99 -0
- package/library/core/server-client.d.ts.map +1 -0
- package/library/core/server-client.js +256 -0
- package/library/core/server.d.ts +82 -0
- package/library/core/server.d.ts.map +1 -0
- package/library/core/server.js +338 -0
- package/library/index.d.ts +16 -0
- package/library/index.d.ts.map +1 -0
- package/library/index.js +211 -0
- package/library/interceptors/index.d.ts +41 -0
- package/library/interceptors/index.d.ts.map +1 -0
- package/library/interceptors/index.js +126 -0
- package/library/message/channel.d.ts +107 -0
- package/library/message/channel.d.ts.map +1 -0
- package/library/message/channel.js +184 -0
- package/library/message/dispatcher.d.ts +119 -0
- package/library/message/dispatcher.d.ts.map +1 -0
- package/library/message/dispatcher.js +249 -0
- package/library/message/index.d.ts +5 -0
- package/library/message/index.d.ts.map +1 -0
- package/library/message/index.js +25 -0
- package/library/stream/file-stream.d.ts +48 -0
- package/library/stream/file-stream.d.ts.map +1 -0
- package/library/stream/file-stream.js +240 -0
- package/library/stream/index.d.ts +15 -0
- package/library/stream/index.d.ts.map +1 -0
- package/library/stream/index.js +83 -0
- package/library/stream/readable-stream.d.ts +83 -0
- package/library/stream/readable-stream.d.ts.map +1 -0
- package/library/stream/readable-stream.js +249 -0
- package/library/stream/types.d.ts +165 -0
- package/library/stream/types.d.ts.map +1 -0
- package/library/stream/types.js +5 -0
- package/library/stream/writable-stream.d.ts +60 -0
- package/library/stream/writable-stream.d.ts.map +1 -0
- package/library/stream/writable-stream.js +348 -0
- package/library/types/index.d.ts +408 -0
- package/library/types/index.d.ts.map +1 -0
- package/library/types/index.js +5 -0
- package/library/utils/cache.d.ts +19 -0
- package/library/utils/cache.d.ts.map +1 -0
- package/library/utils/cache.js +83 -0
- package/library/utils/cookie.d.ts +117 -0
- package/library/utils/cookie.d.ts.map +1 -0
- package/library/utils/cookie.js +365 -0
- package/library/utils/debug.d.ts +11 -0
- package/library/utils/debug.d.ts.map +1 -0
- package/library/utils/debug.js +162 -0
- package/library/utils/index.d.ts +13 -0
- package/library/utils/index.d.ts.map +1 -0
- package/library/utils/index.js +132 -0
- package/library/utils/path-match.d.ts +17 -0
- package/library/utils/path-match.d.ts.map +1 -0
- package/library/utils/path-match.js +90 -0
- package/library/utils/protocol.d.ts +61 -0
- package/library/utils/protocol.d.ts.map +1 -0
- package/library/utils/protocol.js +169 -0
- package/package.json +58 -0
package/QUICKSTART.CN.md
ADDED
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
# 快速开始
|
|
2
|
+
|
|
3
|
+
5 分钟上手 request-iframe,像发送 HTTP 请求一样与 iframe 通信!
|
|
4
|
+
|
|
5
|
+
## 安装
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install request-iframe
|
|
9
|
+
# 或
|
|
10
|
+
yarn add request-iframe
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## 场景说明
|
|
14
|
+
|
|
15
|
+
假设你有一个父页面需要和嵌入的 iframe 通信:
|
|
16
|
+
|
|
17
|
+
```
|
|
18
|
+
┌─────────────────────────────────────────┐
|
|
19
|
+
│ 父页面 (parent.html) │
|
|
20
|
+
│ ┌───────────────────────────────────┐ │
|
|
21
|
+
│ │ iframe (child.html) │ │
|
|
22
|
+
│ │ │ │
|
|
23
|
+
│ │ 需要获取 iframe 内的数据 │ │
|
|
24
|
+
│ │ │ │
|
|
25
|
+
│ └───────────────────────────────────┘ │
|
|
26
|
+
└─────────────────────────────────────────┘
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Step 1: 父页面创建 Client
|
|
30
|
+
|
|
31
|
+
```typescript
|
|
32
|
+
// parent.html
|
|
33
|
+
import { requestIframeClient } from 'request-iframe';
|
|
34
|
+
|
|
35
|
+
// 获取 iframe 元素
|
|
36
|
+
const iframe = document.getElementById('my-iframe') as HTMLIFrameElement;
|
|
37
|
+
|
|
38
|
+
// 创建 client(用于发送请求)
|
|
39
|
+
const client = requestIframeClient(iframe, {
|
|
40
|
+
secretKey: 'my-app' // 消息隔离标识,需要和 iframe 内保持一致
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
// 发送请求并等待响应
|
|
44
|
+
async function getUserInfo(userId: number) {
|
|
45
|
+
const response = await client.send('/api/getUserInfo', { userId });
|
|
46
|
+
return response.data;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// 使用
|
|
50
|
+
const user = await getUserInfo(123);
|
|
51
|
+
console.log(user); // { name: 'Tom', age: 18 }
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Step 2: iframe 内创建 Server
|
|
55
|
+
|
|
56
|
+
```typescript
|
|
57
|
+
// child.html (iframe 内)
|
|
58
|
+
import { requestIframeServer } from 'request-iframe';
|
|
59
|
+
|
|
60
|
+
// 创建 server(用于接收请求)
|
|
61
|
+
const server = requestIframeServer({
|
|
62
|
+
secretKey: 'my-app' // 必须和父页面的 client 保持一致!
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
// 注册请求处理器
|
|
66
|
+
server.on('/api/getUserInfo', (req, res) => {
|
|
67
|
+
const { userId } = req.body;
|
|
68
|
+
|
|
69
|
+
// 模拟从数据库获取用户信息
|
|
70
|
+
const user = { name: 'Tom', age: 18 };
|
|
71
|
+
|
|
72
|
+
// 返回响应
|
|
73
|
+
res.send(user);
|
|
74
|
+
});
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
**就是这么简单!** 🎉
|
|
78
|
+
|
|
79
|
+
---
|
|
80
|
+
|
|
81
|
+
## 更多示例
|
|
82
|
+
|
|
83
|
+
### 异步处理
|
|
84
|
+
|
|
85
|
+
```typescript
|
|
86
|
+
// Server 端
|
|
87
|
+
server.on('/api/fetchData', async (req, res) => {
|
|
88
|
+
// 异步操作(如网络请求)
|
|
89
|
+
const data = await fetch('https://api.example.com/data').then(r => r.json());
|
|
90
|
+
res.send(data);
|
|
91
|
+
});
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
当处理器返回 Promise 时,框架会自动:
|
|
95
|
+
1. 通知 Client 这是异步任务
|
|
96
|
+
2. 将超时时间从 5 秒切换到 120 秒
|
|
97
|
+
|
|
98
|
+
### 错误处理
|
|
99
|
+
|
|
100
|
+
```typescript
|
|
101
|
+
// Server 端
|
|
102
|
+
server.on('/api/getData', (req, res) => {
|
|
103
|
+
if (!req.body.id) {
|
|
104
|
+
return res.status(400).send({ error: '缺少 id 参数' });
|
|
105
|
+
}
|
|
106
|
+
res.send({ data: '...' });
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
// Client 端
|
|
110
|
+
try {
|
|
111
|
+
const response = await client.send('/api/getData', {});
|
|
112
|
+
} catch (error) {
|
|
113
|
+
if (error.response?.status === 400) {
|
|
114
|
+
console.error('参数错误:', error.response.data.error);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### 添加鉴权
|
|
120
|
+
|
|
121
|
+
```typescript
|
|
122
|
+
// Server 端添加中间件
|
|
123
|
+
server.use((req, res, next) => {
|
|
124
|
+
const token = req.headers['authorization'];
|
|
125
|
+
|
|
126
|
+
if (!token || !isValidToken(token)) {
|
|
127
|
+
return res.status(401).send({ error: '未授权' });
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
next(); // 继续执行
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
// Client 端添加请求拦截器
|
|
134
|
+
client.interceptors.request.use((config) => {
|
|
135
|
+
config.headers = {
|
|
136
|
+
...config.headers,
|
|
137
|
+
'Authorization': `Bearer ${getToken()}`
|
|
138
|
+
};
|
|
139
|
+
return config;
|
|
140
|
+
});
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
### 批量注册接口
|
|
144
|
+
|
|
145
|
+
```typescript
|
|
146
|
+
server.map({
|
|
147
|
+
'/api/users/list': (req, res) => {
|
|
148
|
+
res.send([{ id: 1, name: 'Tom' }, { id: 2, name: 'Jerry' }]);
|
|
149
|
+
},
|
|
150
|
+
'/api/users/create': async (req, res) => {
|
|
151
|
+
const user = await createUser(req.body);
|
|
152
|
+
res.status(201).send(user);
|
|
153
|
+
},
|
|
154
|
+
'/api/users/delete': async (req, res) => {
|
|
155
|
+
await deleteUser(req.body.id);
|
|
156
|
+
res.send({ success: true });
|
|
157
|
+
}
|
|
158
|
+
});
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
### 文件下载
|
|
162
|
+
|
|
163
|
+
```typescript
|
|
164
|
+
// Server 端
|
|
165
|
+
server.on('/api/download', async (req, res) => {
|
|
166
|
+
const content = '这是文件内容';
|
|
167
|
+
await res.sendFile(content, {
|
|
168
|
+
mimeType: 'text/plain',
|
|
169
|
+
fileName: 'example.txt'
|
|
170
|
+
});
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
// Client 端
|
|
174
|
+
const response = await client.send('/api/download', {});
|
|
175
|
+
if (response.fileData) {
|
|
176
|
+
// 创建下载链接
|
|
177
|
+
const blob = new Blob(
|
|
178
|
+
[atob(response.fileData.content)],
|
|
179
|
+
{ type: response.fileData.mimeType }
|
|
180
|
+
);
|
|
181
|
+
const url = URL.createObjectURL(blob);
|
|
182
|
+
|
|
183
|
+
// 触发下载
|
|
184
|
+
const a = document.createElement('a');
|
|
185
|
+
a.href = url;
|
|
186
|
+
a.download = response.fileData.fileName || 'download';
|
|
187
|
+
a.click();
|
|
188
|
+
}
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
### 调试模式
|
|
192
|
+
|
|
193
|
+
开启 trace 模式查看详细日志:
|
|
194
|
+
|
|
195
|
+
```typescript
|
|
196
|
+
const client = requestIframeClient(iframe, {
|
|
197
|
+
secretKey: 'my-app',
|
|
198
|
+
trace: true // 开启调试日志
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
const server = requestIframeServer({
|
|
202
|
+
secretKey: 'my-app',
|
|
203
|
+
trace: true
|
|
204
|
+
});
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
控制台会输出:
|
|
208
|
+
```
|
|
209
|
+
[request-iframe] [INFO] 📤 Request Start { path: '/api/getData', body: {...} }
|
|
210
|
+
[request-iframe] [INFO] 📨 ACK Received { requestId: 'req_xxx' }
|
|
211
|
+
[request-iframe] [INFO] ✅ Request Success { status: 200, data: {...} }
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
---
|
|
215
|
+
|
|
216
|
+
## 常见问题
|
|
217
|
+
|
|
218
|
+
### Q: 为什么请求一直超时?
|
|
219
|
+
|
|
220
|
+
检查以下几点:
|
|
221
|
+
1. iframe 是否已加载完成
|
|
222
|
+
2. Client 和 Server 的 `secretKey` 是否一致
|
|
223
|
+
3. Server 的处理器路径是否正确
|
|
224
|
+
|
|
225
|
+
### Q: 如何在 iframe 内向父页面发送请求?
|
|
226
|
+
|
|
227
|
+
```typescript
|
|
228
|
+
// iframe 内
|
|
229
|
+
const client = requestIframeClient(window.parent, { secretKey: 'reverse' });
|
|
230
|
+
await client.send('/notify', { event: 'ready' });
|
|
231
|
+
|
|
232
|
+
// 父页面
|
|
233
|
+
const server = requestIframeServer({ secretKey: 'reverse' });
|
|
234
|
+
server.on('/notify', (req, res) => {
|
|
235
|
+
console.log('iframe 已就绪');
|
|
236
|
+
res.send({ ok: true });
|
|
237
|
+
});
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
### Q: 支持 TypeScript 吗?
|
|
241
|
+
|
|
242
|
+
完全支持!所有 API 都有完整的类型定义。
|
|
243
|
+
|
|
244
|
+
```typescript
|
|
245
|
+
import {
|
|
246
|
+
requestIframeClient,
|
|
247
|
+
requestIframeServer,
|
|
248
|
+
Response,
|
|
249
|
+
ServerRequest,
|
|
250
|
+
ServerResponse
|
|
251
|
+
} from 'request-iframe';
|
|
252
|
+
|
|
253
|
+
// 泛型支持
|
|
254
|
+
interface User {
|
|
255
|
+
id: number;
|
|
256
|
+
name: string;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
const response = await client.send<User>('/api/user', { id: 1 });
|
|
260
|
+
console.log(response.data.name); // TypeScript 知道这是 string
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
---
|
|
264
|
+
|
|
265
|
+
## 下一步
|
|
266
|
+
|
|
267
|
+
- 查看 [README.CN.md](./README.CN.md) 了解完整 API(中文)
|
|
268
|
+
- 查看 [README.md](./README.md) 了解完整 API(English)
|
|
269
|
+
- 查看 [src/__tests__](./src/__tests__) 目录下的测试用例获取更多示例
|
package/QUICKSTART.md
ADDED
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
# Quick Start
|
|
2
|
+
|
|
3
|
+
Get started with request-iframe in 5 minutes - communicate with iframes like sending HTTP requests!
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install request-iframe
|
|
9
|
+
# or
|
|
10
|
+
yarn add request-iframe
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Scenario
|
|
14
|
+
|
|
15
|
+
Suppose you have a parent page that needs to communicate with an embedded iframe:
|
|
16
|
+
|
|
17
|
+
```
|
|
18
|
+
┌─────────────────────────────────────────┐
|
|
19
|
+
│ Parent Page (parent.html) │
|
|
20
|
+
│ ┌───────────────────────────────────┐ │
|
|
21
|
+
│ │ iframe (child.html) │ │
|
|
22
|
+
│ │ │ │
|
|
23
|
+
│ │ Need to get data from iframe │ │
|
|
24
|
+
│ │ │ │
|
|
25
|
+
│ └───────────────────────────────────┘ │
|
|
26
|
+
└─────────────────────────────────────────┘
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Step 1: Create Client in Parent Page
|
|
30
|
+
|
|
31
|
+
```typescript
|
|
32
|
+
// parent.html
|
|
33
|
+
import { requestIframeClient } from 'request-iframe';
|
|
34
|
+
|
|
35
|
+
// Get iframe element
|
|
36
|
+
const iframe = document.getElementById('my-iframe') as HTMLIFrameElement;
|
|
37
|
+
|
|
38
|
+
// Create client (for sending requests)
|
|
39
|
+
const client = requestIframeClient(iframe, {
|
|
40
|
+
secretKey: 'my-app' // Message isolation identifier, must match iframe
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
// Send request and wait for response
|
|
44
|
+
async function getUserInfo(userId: number) {
|
|
45
|
+
const response = await client.send('/api/getUserInfo', { userId });
|
|
46
|
+
return response.data;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Use
|
|
50
|
+
const user = await getUserInfo(123);
|
|
51
|
+
console.log(user); // { name: 'Tom', age: 18 }
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Step 2: Create Server in iframe
|
|
55
|
+
|
|
56
|
+
```typescript
|
|
57
|
+
// child.html (inside iframe)
|
|
58
|
+
import { requestIframeServer } from 'request-iframe';
|
|
59
|
+
|
|
60
|
+
// Create server (for receiving requests)
|
|
61
|
+
const server = requestIframeServer({
|
|
62
|
+
secretKey: 'my-app' // Must match parent page's client!
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
// Register request handler
|
|
66
|
+
server.on('/api/getUserInfo', (req, res) => {
|
|
67
|
+
const { userId } = req.body;
|
|
68
|
+
|
|
69
|
+
// Simulate fetching user info from database
|
|
70
|
+
const user = { name: 'Tom', age: 18 };
|
|
71
|
+
|
|
72
|
+
// Return response
|
|
73
|
+
res.send(user);
|
|
74
|
+
});
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
**That's it!** 🎉
|
|
78
|
+
|
|
79
|
+
---
|
|
80
|
+
|
|
81
|
+
## More Examples
|
|
82
|
+
|
|
83
|
+
### Async Processing
|
|
84
|
+
|
|
85
|
+
```typescript
|
|
86
|
+
// Server side
|
|
87
|
+
server.on('/api/fetchData', async (req, res) => {
|
|
88
|
+
// Async operation (like network request)
|
|
89
|
+
const data = await fetch('https://api.example.com/data').then(r => r.json());
|
|
90
|
+
res.send(data);
|
|
91
|
+
});
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
When the handler returns a Promise, the framework automatically:
|
|
95
|
+
1. Notifies Client this is an async task
|
|
96
|
+
2. Switches timeout from 5 seconds to 120 seconds
|
|
97
|
+
|
|
98
|
+
### Error Handling
|
|
99
|
+
|
|
100
|
+
```typescript
|
|
101
|
+
// Server side
|
|
102
|
+
server.on('/api/getData', (req, res) => {
|
|
103
|
+
if (!req.body.id) {
|
|
104
|
+
return res.status(400).send({ error: 'Missing id parameter' });
|
|
105
|
+
}
|
|
106
|
+
res.send({ data: '...' });
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
// Client side
|
|
110
|
+
try {
|
|
111
|
+
const response = await client.send('/api/getData', {});
|
|
112
|
+
} catch (error) {
|
|
113
|
+
if (error.response?.status === 400) {
|
|
114
|
+
console.error('Parameter error:', error.response.data.error);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### Add Authentication
|
|
120
|
+
|
|
121
|
+
```typescript
|
|
122
|
+
// Server side: Add middleware
|
|
123
|
+
server.use((req, res, next) => {
|
|
124
|
+
const token = req.headers['authorization'];
|
|
125
|
+
|
|
126
|
+
if (!token || !isValidToken(token)) {
|
|
127
|
+
return res.status(401).send({ error: 'Unauthorized' });
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
next(); // Continue execution
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
// Client side: Add request interceptor
|
|
134
|
+
client.interceptors.request.use((config) => {
|
|
135
|
+
config.headers = {
|
|
136
|
+
...config.headers,
|
|
137
|
+
'Authorization': `Bearer ${getToken()}`
|
|
138
|
+
};
|
|
139
|
+
return config;
|
|
140
|
+
});
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
### Batch Register Interfaces
|
|
144
|
+
|
|
145
|
+
```typescript
|
|
146
|
+
server.map({
|
|
147
|
+
'/api/users/list': (req, res) => {
|
|
148
|
+
res.send([{ id: 1, name: 'Tom' }, { id: 2, name: 'Jerry' }]);
|
|
149
|
+
},
|
|
150
|
+
'/api/users/create': async (req, res) => {
|
|
151
|
+
const user = await createUser(req.body);
|
|
152
|
+
res.status(201).send(user);
|
|
153
|
+
},
|
|
154
|
+
'/api/users/delete': async (req, res) => {
|
|
155
|
+
await deleteUser(req.body.id);
|
|
156
|
+
res.send({ success: true });
|
|
157
|
+
}
|
|
158
|
+
});
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
### File Download
|
|
162
|
+
|
|
163
|
+
```typescript
|
|
164
|
+
// Server side
|
|
165
|
+
server.on('/api/download', async (req, res) => {
|
|
166
|
+
const content = 'This is file content';
|
|
167
|
+
await res.sendFile(content, {
|
|
168
|
+
mimeType: 'text/plain',
|
|
169
|
+
fileName: 'example.txt'
|
|
170
|
+
});
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
// Client side
|
|
174
|
+
const response = await client.send('/api/download', {});
|
|
175
|
+
if (response.fileData) {
|
|
176
|
+
// Create download link
|
|
177
|
+
const blob = new Blob(
|
|
178
|
+
[atob(response.fileData.content)],
|
|
179
|
+
{ type: response.fileData.mimeType }
|
|
180
|
+
);
|
|
181
|
+
const url = URL.createObjectURL(blob);
|
|
182
|
+
|
|
183
|
+
// Trigger download
|
|
184
|
+
const a = document.createElement('a');
|
|
185
|
+
a.href = url;
|
|
186
|
+
a.download = response.fileData.fileName || 'download';
|
|
187
|
+
a.click();
|
|
188
|
+
}
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
### Debug Mode
|
|
192
|
+
|
|
193
|
+
Enable trace mode to view detailed logs:
|
|
194
|
+
|
|
195
|
+
```typescript
|
|
196
|
+
const client = requestIframeClient(iframe, {
|
|
197
|
+
secretKey: 'my-app',
|
|
198
|
+
trace: true // Enable debug logs
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
const server = requestIframeServer({
|
|
202
|
+
secretKey: 'my-app',
|
|
203
|
+
trace: true
|
|
204
|
+
});
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
Console will output:
|
|
208
|
+
```
|
|
209
|
+
[request-iframe] [INFO] 📤 Request Start { path: '/api/getData', body: {...} }
|
|
210
|
+
[request-iframe] [INFO] 📨 ACK Received { requestId: 'req_xxx' }
|
|
211
|
+
[request-iframe] [INFO] ✅ Request Success { status: 200, data: {...} }
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
---
|
|
215
|
+
|
|
216
|
+
## Common Questions
|
|
217
|
+
|
|
218
|
+
### Q: Why does the request keep timing out?
|
|
219
|
+
|
|
220
|
+
Check the following:
|
|
221
|
+
1. Is the iframe fully loaded?
|
|
222
|
+
2. Do Client and Server have the same `secretKey`?
|
|
223
|
+
3. Is the Server handler path correct?
|
|
224
|
+
|
|
225
|
+
### Q: How to send requests from iframe to parent page?
|
|
226
|
+
|
|
227
|
+
```typescript
|
|
228
|
+
// Inside iframe
|
|
229
|
+
const client = requestIframeClient(window.parent, { secretKey: 'reverse' });
|
|
230
|
+
await client.send('/notify', { event: 'ready' });
|
|
231
|
+
|
|
232
|
+
// Parent page
|
|
233
|
+
const server = requestIframeServer({ secretKey: 'reverse' });
|
|
234
|
+
server.on('/notify', (req, res) => {
|
|
235
|
+
console.log('iframe is ready');
|
|
236
|
+
res.send({ ok: true });
|
|
237
|
+
});
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
### Q: Does it support TypeScript?
|
|
241
|
+
|
|
242
|
+
Fully supported! All APIs have complete type definitions.
|
|
243
|
+
|
|
244
|
+
```typescript
|
|
245
|
+
import {
|
|
246
|
+
requestIframeClient,
|
|
247
|
+
requestIframeServer,
|
|
248
|
+
Response,
|
|
249
|
+
ServerRequest,
|
|
250
|
+
ServerResponse
|
|
251
|
+
} from 'request-iframe';
|
|
252
|
+
|
|
253
|
+
// Generic support
|
|
254
|
+
interface User {
|
|
255
|
+
id: number;
|
|
256
|
+
name: string;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
const response = await client.send<User>('/api/user', { id: 1 });
|
|
260
|
+
console.log(response.data.name); // TypeScript knows this is string
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
---
|
|
264
|
+
|
|
265
|
+
## Next Steps
|
|
266
|
+
|
|
267
|
+
- View [README.md](./README.md) for complete API documentation (English)
|
|
268
|
+
- View [README.CN.md](./README.CN.md) for complete API documentation (中文)
|
|
269
|
+
- Check [src/__tests__](./src/__tests__) directory for more examples from test cases
|