request-iframe 0.0.2 → 0.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.CN.md +269 -12
- package/README.md +266 -11
- package/library/__tests__/channel.test.ts +420 -0
- package/library/__tests__/debug.test.ts +588 -0
- package/library/__tests__/dispatcher.test.ts +481 -0
- package/library/__tests__/requestIframe.test.ts +2127 -99
- package/library/__tests__/server.test.ts +738 -0
- package/library/api/client.d.ts.map +1 -1
- package/library/api/client.js +11 -6
- package/library/api/server.d.ts +4 -3
- package/library/api/server.d.ts.map +1 -1
- package/library/api/server.js +25 -7
- package/library/constants/index.d.ts +14 -4
- package/library/constants/index.d.ts.map +1 -1
- package/library/constants/index.js +15 -7
- package/library/constants/messages.d.ts +35 -0
- package/library/constants/messages.d.ts.map +1 -1
- package/library/constants/messages.js +36 -1
- package/library/core/client-server.d.ts +101 -0
- package/library/core/client-server.d.ts.map +1 -0
- package/library/core/client-server.js +266 -0
- package/library/core/client.d.ts +22 -6
- package/library/core/client.d.ts.map +1 -1
- package/library/core/client.js +159 -24
- package/library/core/request.d.ts.map +1 -1
- package/library/core/response.d.ts +5 -1
- package/library/core/response.d.ts.map +1 -1
- package/library/core/response.js +85 -70
- package/library/core/server-client.d.ts +3 -1
- package/library/core/server-client.d.ts.map +1 -1
- package/library/core/server-client.js +19 -9
- package/library/core/server.d.ts +9 -1
- package/library/core/server.d.ts.map +1 -1
- package/library/core/server.js +96 -52
- package/library/index.d.ts +1 -1
- package/library/index.js +2 -2
- package/library/interceptors/index.d.ts.map +1 -1
- package/library/message/channel.d.ts +3 -1
- package/library/message/channel.d.ts.map +1 -1
- package/library/message/dispatcher.d.ts +7 -2
- package/library/message/dispatcher.d.ts.map +1 -1
- package/library/message/dispatcher.js +47 -2
- package/library/message/index.d.ts.map +1 -1
- package/library/stream/file-stream.d.ts +5 -0
- package/library/stream/file-stream.d.ts.map +1 -1
- package/library/stream/file-stream.js +41 -12
- package/library/stream/index.d.ts.map +1 -1
- package/library/stream/readable-stream.d.ts.map +1 -1
- package/library/stream/readable-stream.js +32 -30
- package/library/stream/types.d.ts +18 -0
- package/library/stream/types.d.ts.map +1 -1
- package/library/stream/writable-stream.d.ts +1 -0
- package/library/stream/writable-stream.d.ts.map +1 -1
- package/library/stream/writable-stream.js +7 -2
- package/library/types/index.d.ts +80 -28
- package/library/types/index.d.ts.map +1 -1
- package/library/utils/cache.d.ts +24 -0
- package/library/utils/cache.d.ts.map +1 -1
- package/library/utils/cache.js +76 -0
- package/library/utils/cookie.d.ts.map +1 -1
- package/library/utils/debug.d.ts.map +1 -1
- package/library/utils/debug.js +382 -20
- package/library/utils/index.d.ts +5 -0
- package/library/utils/index.d.ts.map +1 -1
- package/library/utils/index.js +14 -1
- package/library/utils/path-match.d.ts.map +1 -1
- package/library/utils/protocol.d.ts.map +1 -1
- package/package.json +3 -1
- package/react/library/__tests__/index.test.tsx +238 -267
- package/react/library/index.d.ts +4 -3
- package/react/library/index.d.ts.map +1 -1
- package/react/library/index.js +167 -158
package/README.CN.md
CHANGED
|
@@ -34,6 +34,13 @@
|
|
|
34
34
|
- [追踪模式](#追踪模式)
|
|
35
35
|
- [多语言支持](#多语言支持)
|
|
36
36
|
- [API 参考](#api-参考)
|
|
37
|
+
- [React Hooks](#react-hooks)
|
|
38
|
+
- [useClient](#useclienttargetfnorref-options-deps)
|
|
39
|
+
- [useServer](#useserveroptions-deps)
|
|
40
|
+
- [useServerHandler](#useserverhandlerserver-path-handler-deps)
|
|
41
|
+
- [useServerHandlerMap](#useserverhandlermapserver-map-deps)
|
|
42
|
+
- [完整示例](#完整示例)
|
|
43
|
+
- [最佳实践](#最佳实践)
|
|
37
44
|
- [错误处理](#错误处理)
|
|
38
45
|
- [FAQ](#faq)
|
|
39
46
|
- [开发](#开发)
|
|
@@ -167,7 +174,7 @@ request-iframe 采用三阶段超时策略,智能适应不同场景:
|
|
|
167
174
|
|
|
168
175
|
```typescript
|
|
169
176
|
client.send('/api/getData', data, {
|
|
170
|
-
ackTimeout:
|
|
177
|
+
ackTimeout: 1000, // 阶段1:等待 ACK 的超时时间(默认 1000ms)
|
|
171
178
|
timeout: 5000, // 阶段2:请求超时时间(默认 5s)
|
|
172
179
|
asyncTimeout: 120000 // 阶段3:异步请求超时时间(默认 120s)
|
|
173
180
|
});
|
|
@@ -207,7 +214,7 @@ client.send('/api/getData', data, {
|
|
|
207
214
|
|
|
208
215
|
| 阶段 | 超时时间 | 场景 |
|
|
209
216
|
|------|----------|------|
|
|
210
|
-
| ackTimeout | 较短(
|
|
217
|
+
| ackTimeout | 较短(1000ms) | 快速检测 Server 是否在线,避免长时间等待不可达的 iframe。从 500ms 增加到 1000ms,以适应性能较差的环境或浏览器繁忙的场景 |
|
|
211
218
|
| timeout | 中等(5s) | 适用于简单的同步处理,如读取数据、参数校验等 |
|
|
212
219
|
| asyncTimeout | 较长(120s) | 适用于复杂异步操作,如文件处理、批量操作、第三方 API 调用等 |
|
|
213
220
|
|
|
@@ -804,7 +811,7 @@ setMessages({
|
|
|
804
811
|
| `target` | `HTMLIFrameElement \| Window` | 目标 iframe 元素或 window 对象 |
|
|
805
812
|
| `options.secretKey` | `string` | 消息隔离标识(可选) |
|
|
806
813
|
| `options.trace` | `boolean` | 是否开启追踪模式(可选) |
|
|
807
|
-
| `options.ackTimeout` | `number` | 全局默认 ACK 确认超时(ms),默认
|
|
814
|
+
| `options.ackTimeout` | `number` | 全局默认 ACK 确认超时(ms),默认 1000 |
|
|
808
815
|
| `options.timeout` | `number` | 全局默认请求超时(ms),默认 5000 |
|
|
809
816
|
| `options.asyncTimeout` | `number` | 全局默认异步超时(ms),默认 120000 |
|
|
810
817
|
|
|
@@ -837,7 +844,7 @@ await client.send('/api/longTask', {}, {
|
|
|
837
844
|
|------|------|------|
|
|
838
845
|
| `options.secretKey` | `string` | 消息隔离标识(可选) |
|
|
839
846
|
| `options.trace` | `boolean` | 是否开启追踪模式(可选) |
|
|
840
|
-
| `options.ackTimeout` | `number` | 等待客户端确认超时(ms),默认
|
|
847
|
+
| `options.ackTimeout` | `number` | 等待客户端确认超时(ms),默认 1000 |
|
|
841
848
|
|
|
842
849
|
**返回值:** `RequestIframeServer`
|
|
843
850
|
|
|
@@ -853,7 +860,7 @@ await client.send('/api/longTask', {}, {
|
|
|
853
860
|
|------|------|------|
|
|
854
861
|
| `path` | `string` | 请求路径 |
|
|
855
862
|
| `body` | `object` | 请求数据(可选) |
|
|
856
|
-
| `options.ackTimeout` | `number` | ACK 确认超时(ms),默认
|
|
863
|
+
| `options.ackTimeout` | `number` | ACK 确认超时(ms),默认 1000 |
|
|
857
864
|
| `options.timeout` | `number` | 请求超时(ms),默认 5000 |
|
|
858
865
|
| `options.asyncTimeout` | `number` | 异步超时(ms),默认 120000 |
|
|
859
866
|
| `options.headers` | `object` | 请求 headers(可选) |
|
|
@@ -864,16 +871,11 @@ await client.send('/api/longTask', {}, {
|
|
|
864
871
|
|
|
865
872
|
```typescript
|
|
866
873
|
interface Response<T = any> {
|
|
867
|
-
data: T; //
|
|
874
|
+
data: T; // 响应数据(自动解析的文件流为 File/Blob)
|
|
868
875
|
status: number; // 状态码
|
|
869
876
|
statusText: string; // 状态文本
|
|
870
877
|
requestId: string; // 请求 ID
|
|
871
878
|
headers?: Record<string, string | string[]>; // 响应 headers(Set-Cookie 为数组)
|
|
872
|
-
fileData?: { // 文件数据(如果有)
|
|
873
|
-
content: string; // base64 编码内容
|
|
874
|
-
mimeType?: string;
|
|
875
|
-
fileName?: string;
|
|
876
|
-
};
|
|
877
879
|
stream?: IIframeReadableStream<T>; // 流响应(如果有)
|
|
878
880
|
}
|
|
879
881
|
```
|
|
@@ -984,7 +986,262 @@ server.use(['/a', '/b'], (req, res, next) => { ... });
|
|
|
984
986
|
|
|
985
987
|
销毁 Server 实例,移除所有监听器。
|
|
986
988
|
|
|
987
|
-
|
|
989
|
+
---
|
|
990
|
+
|
|
991
|
+
## React Hooks
|
|
992
|
+
|
|
993
|
+
request-iframe 提供了 React hooks,方便在 React 应用中使用。从 `request-iframe/react` 导入 hooks:
|
|
994
|
+
|
|
995
|
+
```typescript
|
|
996
|
+
import { useClient, useServer, useServerHandler, useServerHandlerMap } from 'request-iframe/react';
|
|
997
|
+
```
|
|
998
|
+
|
|
999
|
+
### useClient(targetFnOrRef, options?, deps?)
|
|
1000
|
+
|
|
1001
|
+
用于使用 request-iframe client 的 React hook。
|
|
1002
|
+
|
|
1003
|
+
**参数:**
|
|
1004
|
+
|
|
1005
|
+
| 参数 | 类型 | 说明 |
|
|
1006
|
+
|------|------|------|
|
|
1007
|
+
| `targetFnOrRef` | `(() => HTMLIFrameElement \| Window \| null) \| RefObject<HTMLIFrameElement \| Window>` | 返回 iframe 元素或 Window 对象的函数,或 React ref 对象 |
|
|
1008
|
+
| `options` | `RequestIframeClientOptions` | Client 选项(可选) |
|
|
1009
|
+
| `deps` | `readonly unknown[]` | 依赖数组(可选,当依赖变化时重新创建 client) |
|
|
1010
|
+
|
|
1011
|
+
**返回值:** `RequestIframeClient | null`
|
|
1012
|
+
|
|
1013
|
+
**示例:**
|
|
1014
|
+
|
|
1015
|
+
```tsx
|
|
1016
|
+
import { useClient } from 'request-iframe/react';
|
|
1017
|
+
import { useRef } from 'react';
|
|
1018
|
+
|
|
1019
|
+
const MyComponent = () => {
|
|
1020
|
+
const iframeRef = useRef<HTMLIFrameElement>(null);
|
|
1021
|
+
const client = useClient(iframeRef, { secretKey: 'my-app' });
|
|
1022
|
+
|
|
1023
|
+
const handleClick = async () => {
|
|
1024
|
+
if (client) {
|
|
1025
|
+
const response = await client.send('/api/data', { id: 1 });
|
|
1026
|
+
console.log(response.data);
|
|
1027
|
+
}
|
|
1028
|
+
};
|
|
1029
|
+
|
|
1030
|
+
return (
|
|
1031
|
+
<div>
|
|
1032
|
+
<iframe ref={iframeRef} src="/iframe.html" />
|
|
1033
|
+
<button onClick={handleClick}>发送请求</button>
|
|
1034
|
+
</div>
|
|
1035
|
+
);
|
|
1036
|
+
};
|
|
1037
|
+
```
|
|
1038
|
+
|
|
1039
|
+
**使用函数而不是 ref:**
|
|
1040
|
+
|
|
1041
|
+
```tsx
|
|
1042
|
+
const MyComponent = () => {
|
|
1043
|
+
const iframeRef = useRef<HTMLIFrameElement>(null);
|
|
1044
|
+
const client = useClient(() => iframeRef.current, { secretKey: 'my-app' });
|
|
1045
|
+
// ...
|
|
1046
|
+
};
|
|
1047
|
+
```
|
|
1048
|
+
|
|
1049
|
+
### useServer(options?, deps?)
|
|
1050
|
+
|
|
1051
|
+
用于使用 request-iframe server 的 React hook。
|
|
1052
|
+
|
|
1053
|
+
**参数:**
|
|
1054
|
+
|
|
1055
|
+
| 参数 | 类型 | 说明 |
|
|
1056
|
+
|------|------|------|
|
|
1057
|
+
| `options` | `RequestIframeServerOptions` | Server 选项(可选) |
|
|
1058
|
+
| `deps` | `readonly unknown[]` | 依赖数组(可选,当依赖变化时重新创建 server) |
|
|
1059
|
+
|
|
1060
|
+
**返回值:** `RequestIframeServer | null`
|
|
1061
|
+
|
|
1062
|
+
**示例:**
|
|
1063
|
+
|
|
1064
|
+
```tsx
|
|
1065
|
+
import { useServer } from 'request-iframe/react';
|
|
1066
|
+
|
|
1067
|
+
const MyComponent = () => {
|
|
1068
|
+
const server = useServer({ secretKey: 'my-app' });
|
|
1069
|
+
|
|
1070
|
+
useEffect(() => {
|
|
1071
|
+
if (!server) return;
|
|
1072
|
+
|
|
1073
|
+
const off = server.on('/api/data', (req, res) => {
|
|
1074
|
+
res.send({ data: 'Hello' });
|
|
1075
|
+
});
|
|
1076
|
+
|
|
1077
|
+
return off; // 组件卸载时清理
|
|
1078
|
+
}, [server]);
|
|
1079
|
+
|
|
1080
|
+
return <div>Server 组件</div>;
|
|
1081
|
+
};
|
|
1082
|
+
```
|
|
1083
|
+
|
|
1084
|
+
### useServerHandler(server, path, handler, deps?)
|
|
1085
|
+
|
|
1086
|
+
用于注册单个 server handler 的 React hook,自动处理清理和闭包问题。
|
|
1087
|
+
|
|
1088
|
+
**参数:**
|
|
1089
|
+
|
|
1090
|
+
| 参数 | 类型 | 说明 |
|
|
1091
|
+
|------|------|------|
|
|
1092
|
+
| `server` | `RequestIframeServer \| null` | Server 实例(来自 `useServer`) |
|
|
1093
|
+
| `path` | `string` | 路由路径 |
|
|
1094
|
+
| `handler` | `ServerHandler` | 处理函数 |
|
|
1095
|
+
| `deps` | `readonly unknown[]` | 依赖数组(可选,当依赖变化时重新注册) |
|
|
1096
|
+
|
|
1097
|
+
**示例:**
|
|
1098
|
+
|
|
1099
|
+
```tsx
|
|
1100
|
+
import { useServer, useServerHandler } from 'request-iframe/react';
|
|
1101
|
+
import { useState } from 'react';
|
|
1102
|
+
|
|
1103
|
+
const MyComponent = () => {
|
|
1104
|
+
const server = useServer();
|
|
1105
|
+
const [userId, setUserId] = useState(1);
|
|
1106
|
+
|
|
1107
|
+
// Handler 自动使用最新的 userId 值
|
|
1108
|
+
useServerHandler(server, '/api/user', (req, res) => {
|
|
1109
|
+
res.send({ userId, data: 'Hello' });
|
|
1110
|
+
}, [userId]); // 当 userId 变化时重新注册
|
|
1111
|
+
|
|
1112
|
+
return <div>Server 组件</div>;
|
|
1113
|
+
};
|
|
1114
|
+
```
|
|
1115
|
+
|
|
1116
|
+
**关键特性:**
|
|
1117
|
+
- 自动处理闭包问题 - 始终使用依赖项的最新值
|
|
1118
|
+
- 组件卸载或依赖变化时自动取消注册 handler
|
|
1119
|
+
- 无需手动管理 handler 的注册/清理
|
|
1120
|
+
|
|
1121
|
+
### useServerHandlerMap(server, map, deps?)
|
|
1122
|
+
|
|
1123
|
+
用于批量注册多个 server handlers 的 React hook,自动处理清理。
|
|
1124
|
+
|
|
1125
|
+
**参数:**
|
|
1126
|
+
|
|
1127
|
+
| 参数 | 类型 | 说明 |
|
|
1128
|
+
|------|------|------|
|
|
1129
|
+
| `server` | `RequestIframeServer \| null` | Server 实例(来自 `useServer`) |
|
|
1130
|
+
| `map` | `Record<string, ServerHandler>` | 路由路径和处理函数的映射 |
|
|
1131
|
+
| `deps` | `readonly unknown[]` | 依赖数组(可选,当依赖变化时重新注册) |
|
|
1132
|
+
|
|
1133
|
+
**示例:**
|
|
1134
|
+
|
|
1135
|
+
```tsx
|
|
1136
|
+
import { useServer, useServerHandlerMap } from 'request-iframe/react';
|
|
1137
|
+
import { useState } from 'react';
|
|
1138
|
+
|
|
1139
|
+
const MyComponent = () => {
|
|
1140
|
+
const server = useServer();
|
|
1141
|
+
const [userId, setUserId] = useState(1);
|
|
1142
|
+
|
|
1143
|
+
// 一次性注册多个 handlers
|
|
1144
|
+
useServerHandlerMap(server, {
|
|
1145
|
+
'/api/user': (req, res) => {
|
|
1146
|
+
res.send({ userId, data: '用户数据' });
|
|
1147
|
+
},
|
|
1148
|
+
'/api/posts': (req, res) => {
|
|
1149
|
+
res.send({ userId, data: '文章数据' });
|
|
1150
|
+
}
|
|
1151
|
+
}, [userId]); // 当 userId 变化时重新注册所有 handlers
|
|
1152
|
+
|
|
1153
|
+
return <div>Server 组件</div>;
|
|
1154
|
+
};
|
|
1155
|
+
```
|
|
1156
|
+
|
|
1157
|
+
**关键特性:**
|
|
1158
|
+
- 批量注册多个 handlers
|
|
1159
|
+
- 自动处理闭包问题 - 始终使用依赖项的最新值
|
|
1160
|
+
- 组件卸载或依赖变化时自动取消注册所有 handlers
|
|
1161
|
+
- 高效 - 仅在 map 的键变化时重新注册
|
|
1162
|
+
|
|
1163
|
+
### 完整示例
|
|
1164
|
+
|
|
1165
|
+
以下是一个完整的示例,展示如何在真实应用中使用 React hooks:
|
|
1166
|
+
|
|
1167
|
+
```tsx
|
|
1168
|
+
import { useClient, useServer, useServerHandler } from 'request-iframe/react';
|
|
1169
|
+
import { useRef, useState } from 'react';
|
|
1170
|
+
|
|
1171
|
+
// 父组件(Client)
|
|
1172
|
+
const ParentComponent = () => {
|
|
1173
|
+
const iframeRef = useRef<HTMLIFrameElement>(null);
|
|
1174
|
+
const client = useClient(iframeRef, { secretKey: 'my-app' });
|
|
1175
|
+
const [data, setData] = useState(null);
|
|
1176
|
+
|
|
1177
|
+
const fetchData = async () => {
|
|
1178
|
+
if (!client) return;
|
|
1179
|
+
|
|
1180
|
+
try {
|
|
1181
|
+
const response = await client.send('/api/data', { id: 1 });
|
|
1182
|
+
setData(response.data);
|
|
1183
|
+
} catch (error) {
|
|
1184
|
+
console.error('请求失败:', error);
|
|
1185
|
+
}
|
|
1186
|
+
};
|
|
1187
|
+
|
|
1188
|
+
return (
|
|
1189
|
+
<div>
|
|
1190
|
+
<iframe ref={iframeRef} src="/iframe.html" />
|
|
1191
|
+
<button onClick={fetchData}>获取数据</button>
|
|
1192
|
+
{data && <pre>{JSON.stringify(data, null, 2)}</pre>}
|
|
1193
|
+
</div>
|
|
1194
|
+
);
|
|
1195
|
+
};
|
|
1196
|
+
|
|
1197
|
+
// Iframe 组件(Server)
|
|
1198
|
+
const IframeComponent = () => {
|
|
1199
|
+
const server = useServer({ secretKey: 'my-app' });
|
|
1200
|
+
const [userId, setUserId] = useState(1);
|
|
1201
|
+
|
|
1202
|
+
// 注册 handler,自动清理
|
|
1203
|
+
useServerHandler(server, '/api/data', async (req, res) => {
|
|
1204
|
+
// Handler 始终使用最新的 userId 值
|
|
1205
|
+
const userData = await fetchUserData(userId);
|
|
1206
|
+
res.send(userData);
|
|
1207
|
+
}, [userId]);
|
|
1208
|
+
|
|
1209
|
+
return (
|
|
1210
|
+
<div>
|
|
1211
|
+
<p>用户 ID: {userId}</p>
|
|
1212
|
+
<button onClick={() => setUserId(userId + 1)}>增加</button>
|
|
1213
|
+
</div>
|
|
1214
|
+
);
|
|
1215
|
+
};
|
|
1216
|
+
```
|
|
1217
|
+
|
|
1218
|
+
### 最佳实践
|
|
1219
|
+
|
|
1220
|
+
1. **始终检查 null**:Client 和 server hooks 在初始时或目标不可用时可能返回 `null`:
|
|
1221
|
+
```tsx
|
|
1222
|
+
const client = useClient(iframeRef);
|
|
1223
|
+
if (!client) return null; // 处理 null 情况
|
|
1224
|
+
```
|
|
1225
|
+
|
|
1226
|
+
2. **使用依赖数组**:向 hooks 传递依赖项,确保 handlers 使用最新值:
|
|
1227
|
+
```tsx
|
|
1228
|
+
useServerHandler(server, '/api/data', (req, res) => {
|
|
1229
|
+
res.send({ userId }); // 始终使用最新的 userId
|
|
1230
|
+
}, [userId]); // 当 userId 变化时重新注册
|
|
1231
|
+
```
|
|
1232
|
+
|
|
1233
|
+
3. **自动清理**:Hooks 在组件卸载时自动清理,但你也可以手动取消注册:
|
|
1234
|
+
```tsx
|
|
1235
|
+
useEffect(() => {
|
|
1236
|
+
if (!server) return;
|
|
1237
|
+
const off = server.on('/api/data', handler);
|
|
1238
|
+
return off; // 手动清理(可选,hooks 会自动处理)
|
|
1239
|
+
}, [server]);
|
|
1240
|
+
```
|
|
1241
|
+
|
|
1242
|
+
---
|
|
1243
|
+
|
|
1244
|
+
## 错误处理
|
|
988
1245
|
|
|
989
1246
|
```typescript
|
|
990
1247
|
interface ServerRequest {
|
package/README.md
CHANGED
|
@@ -34,6 +34,13 @@ Communicate with iframes like sending HTTP requests! A cross-origin iframe commu
|
|
|
34
34
|
- [Trace Mode](#trace-mode)
|
|
35
35
|
- [Internationalization](#internationalization)
|
|
36
36
|
- [API Reference](#api-reference)
|
|
37
|
+
- [React Hooks](#react-hooks)
|
|
38
|
+
- [useClient](#useclienttargetfnorref-options-deps)
|
|
39
|
+
- [useServer](#useserveroptions-deps)
|
|
40
|
+
- [useServerHandler](#useserverhandlerserver-path-handler-deps)
|
|
41
|
+
- [useServerHandlerMap](#useserverhandlermapserver-map-deps)
|
|
42
|
+
- [Complete Example](#complete-example)
|
|
43
|
+
- [Best Practices](#best-practices)
|
|
37
44
|
- [Error Handling](#error-handling)
|
|
38
45
|
- [FAQ](#faq)
|
|
39
46
|
- [Development](#development)
|
|
@@ -251,7 +258,7 @@ request-iframe uses a three-stage timeout strategy to intelligently adapt to dif
|
|
|
251
258
|
|
|
252
259
|
```typescript
|
|
253
260
|
client.send('/api/getData', data, {
|
|
254
|
-
ackTimeout:
|
|
261
|
+
ackTimeout: 1000, // Stage 1: ACK timeout (default 1000ms)
|
|
255
262
|
timeout: 5000, // Stage 2: Request timeout (default 5s)
|
|
256
263
|
asyncTimeout: 120000 // Stage 3: Async request timeout (default 120s)
|
|
257
264
|
});
|
|
@@ -291,7 +298,7 @@ Send REQUEST
|
|
|
291
298
|
|
|
292
299
|
| Stage | Timeout | Scenario |
|
|
293
300
|
|-------|---------|----------|
|
|
294
|
-
| ackTimeout | Short (
|
|
301
|
+
| ackTimeout | Short (1000ms) | Quickly detect if Server is online, avoid long waits for unreachable iframes. Increased from 500ms to accommodate slower environments or busy browsers |
|
|
295
302
|
| timeout | Medium (5s) | Suitable for simple synchronous processing, like reading data, parameter validation |
|
|
296
303
|
| asyncTimeout | Long (120s) | Suitable for complex async operations, like file processing, batch operations, third-party API calls |
|
|
297
304
|
|
|
@@ -712,7 +719,7 @@ Create a Client instance.
|
|
|
712
719
|
| `target` | `HTMLIFrameElement \| Window` | Target iframe element or window object |
|
|
713
720
|
| `options.secretKey` | `string` | Message isolation identifier (optional) |
|
|
714
721
|
| `options.trace` | `boolean` | Whether to enable trace mode (optional) |
|
|
715
|
-
| `options.ackTimeout` | `number` | Global default ACK acknowledgment timeout (ms), default
|
|
722
|
+
| `options.ackTimeout` | `number` | Global default ACK acknowledgment timeout (ms), default 1000 |
|
|
716
723
|
| `options.timeout` | `number` | Global default request timeout (ms), default 5000 |
|
|
717
724
|
| `options.asyncTimeout` | `number` | Global default async timeout (ms), default 120000 |
|
|
718
725
|
|
|
@@ -728,7 +735,7 @@ Create a Server instance.
|
|
|
728
735
|
|-----------|------|-------------|
|
|
729
736
|
| `options.secretKey` | `string` | Message isolation identifier (optional) |
|
|
730
737
|
| `options.trace` | `boolean` | Whether to enable trace mode (optional) |
|
|
731
|
-
| `options.ackTimeout` | `number` | Wait for client acknowledgment timeout (ms), default
|
|
738
|
+
| `options.ackTimeout` | `number` | Wait for client acknowledgment timeout (ms), default 1000 |
|
|
732
739
|
|
|
733
740
|
**Returns:** `RequestIframeServer`
|
|
734
741
|
|
|
@@ -744,7 +751,7 @@ Send a request.
|
|
|
744
751
|
|-----------|------|-------------|
|
|
745
752
|
| `path` | `string` | Request path |
|
|
746
753
|
| `body` | `object` | Request data (optional) |
|
|
747
|
-
| `options.ackTimeout` | `number` | ACK acknowledgment timeout (ms), default
|
|
754
|
+
| `options.ackTimeout` | `number` | ACK acknowledgment timeout (ms), default 1000 |
|
|
748
755
|
| `options.timeout` | `number` | Request timeout (ms), default 5000 |
|
|
749
756
|
| `options.asyncTimeout` | `number` | Async timeout (ms), default 120000 |
|
|
750
757
|
| `options.headers` | `object` | Request headers (optional) |
|
|
@@ -755,16 +762,11 @@ Send a request.
|
|
|
755
762
|
|
|
756
763
|
```typescript
|
|
757
764
|
interface Response<T = any> {
|
|
758
|
-
data: T; // Response data
|
|
765
|
+
data: T; // Response data (File/Blob for auto-resolved file streams)
|
|
759
766
|
status: number; // Status code
|
|
760
767
|
statusText: string; // Status text
|
|
761
768
|
requestId: string; // Request ID
|
|
762
769
|
headers?: Record<string, string | string[]>; // Response headers (Set-Cookie is array)
|
|
763
|
-
fileData?: { // File data (if any)
|
|
764
|
-
content: string; // base64-encoded content
|
|
765
|
-
mimeType?: string;
|
|
766
|
-
fileName?: string;
|
|
767
|
-
};
|
|
768
770
|
stream?: IIframeReadableStream<T>; // Stream response (if any)
|
|
769
771
|
}
|
|
770
772
|
```
|
|
@@ -840,6 +842,259 @@ Destroy Server instance, remove all listeners.
|
|
|
840
842
|
|
|
841
843
|
---
|
|
842
844
|
|
|
845
|
+
## React Hooks
|
|
846
|
+
|
|
847
|
+
request-iframe provides React hooks for easy integration in React applications. Import hooks from `request-iframe/react`:
|
|
848
|
+
|
|
849
|
+
```typescript
|
|
850
|
+
import { useClient, useServer, useServerHandler, useServerHandlerMap } from 'request-iframe/react';
|
|
851
|
+
```
|
|
852
|
+
|
|
853
|
+
### useClient(targetFnOrRef, options?, deps?)
|
|
854
|
+
|
|
855
|
+
React hook for using request-iframe client.
|
|
856
|
+
|
|
857
|
+
**Parameters:**
|
|
858
|
+
|
|
859
|
+
| Parameter | Type | Description |
|
|
860
|
+
|-----------|------|-------------|
|
|
861
|
+
| `targetFnOrRef` | `(() => HTMLIFrameElement \| Window \| null) \| RefObject<HTMLIFrameElement \| Window>` | Function that returns iframe element or Window object, or a React ref object |
|
|
862
|
+
| `options` | `RequestIframeClientOptions` | Client options (optional) |
|
|
863
|
+
| `deps` | `readonly unknown[]` | Dependency array (optional, for re-creating client when dependencies change) |
|
|
864
|
+
|
|
865
|
+
**Returns:** `RequestIframeClient | null`
|
|
866
|
+
|
|
867
|
+
**Example:**
|
|
868
|
+
|
|
869
|
+
```tsx
|
|
870
|
+
import { useClient } from 'request-iframe/react';
|
|
871
|
+
import { useRef } from 'react';
|
|
872
|
+
|
|
873
|
+
const MyComponent = () => {
|
|
874
|
+
const iframeRef = useRef<HTMLIFrameElement>(null);
|
|
875
|
+
const client = useClient(iframeRef, { secretKey: 'my-app' });
|
|
876
|
+
|
|
877
|
+
const handleClick = async () => {
|
|
878
|
+
if (client) {
|
|
879
|
+
const response = await client.send('/api/data', { id: 1 });
|
|
880
|
+
console.log(response.data);
|
|
881
|
+
}
|
|
882
|
+
};
|
|
883
|
+
|
|
884
|
+
return (
|
|
885
|
+
<div>
|
|
886
|
+
<iframe ref={iframeRef} src="/iframe.html" />
|
|
887
|
+
<button onClick={handleClick}>Send Request</button>
|
|
888
|
+
</div>
|
|
889
|
+
);
|
|
890
|
+
};
|
|
891
|
+
```
|
|
892
|
+
|
|
893
|
+
**Using function instead of ref:**
|
|
894
|
+
|
|
895
|
+
```tsx
|
|
896
|
+
const MyComponent = () => {
|
|
897
|
+
const iframeRef = useRef<HTMLIFrameElement>(null);
|
|
898
|
+
const client = useClient(() => iframeRef.current, { secretKey: 'my-app' });
|
|
899
|
+
// ...
|
|
900
|
+
};
|
|
901
|
+
```
|
|
902
|
+
|
|
903
|
+
### useServer(options?, deps?)
|
|
904
|
+
|
|
905
|
+
React hook for using request-iframe server.
|
|
906
|
+
|
|
907
|
+
**Parameters:**
|
|
908
|
+
|
|
909
|
+
| Parameter | Type | Description |
|
|
910
|
+
|-----------|------|-------------|
|
|
911
|
+
| `options` | `RequestIframeServerOptions` | Server options (optional) |
|
|
912
|
+
| `deps` | `readonly unknown[]` | Dependency array (optional, for re-creating server when dependencies change) |
|
|
913
|
+
|
|
914
|
+
**Returns:** `RequestIframeServer | null`
|
|
915
|
+
|
|
916
|
+
**Example:**
|
|
917
|
+
|
|
918
|
+
```tsx
|
|
919
|
+
import { useServer } from 'request-iframe/react';
|
|
920
|
+
|
|
921
|
+
const MyComponent = () => {
|
|
922
|
+
const server = useServer({ secretKey: 'my-app' });
|
|
923
|
+
|
|
924
|
+
useEffect(() => {
|
|
925
|
+
if (!server) return;
|
|
926
|
+
|
|
927
|
+
const off = server.on('/api/data', (req, res) => {
|
|
928
|
+
res.send({ data: 'Hello' });
|
|
929
|
+
});
|
|
930
|
+
|
|
931
|
+
return off; // Cleanup on unmount
|
|
932
|
+
}, [server]);
|
|
933
|
+
|
|
934
|
+
return <div>Server Component</div>;
|
|
935
|
+
};
|
|
936
|
+
```
|
|
937
|
+
|
|
938
|
+
### useServerHandler(server, path, handler, deps?)
|
|
939
|
+
|
|
940
|
+
React hook for registering a single server handler with automatic cleanup and closure handling.
|
|
941
|
+
|
|
942
|
+
**Parameters:**
|
|
943
|
+
|
|
944
|
+
| Parameter | Type | Description |
|
|
945
|
+
|-----------|------|-------------|
|
|
946
|
+
| `server` | `RequestIframeServer \| null` | Server instance (from `useServer`) |
|
|
947
|
+
| `path` | `string` | Route path |
|
|
948
|
+
| `handler` | `ServerHandler` | Handler function |
|
|
949
|
+
| `deps` | `readonly unknown[]` | Dependency array (optional, for re-registering when dependencies change) |
|
|
950
|
+
|
|
951
|
+
**Example:**
|
|
952
|
+
|
|
953
|
+
```tsx
|
|
954
|
+
import { useServer, useServerHandler } from 'request-iframe/react';
|
|
955
|
+
import { useState } from 'react';
|
|
956
|
+
|
|
957
|
+
const MyComponent = () => {
|
|
958
|
+
const server = useServer();
|
|
959
|
+
const [userId, setUserId] = useState(1);
|
|
960
|
+
|
|
961
|
+
// Handler automatically uses latest userId value
|
|
962
|
+
useServerHandler(server, '/api/user', (req, res) => {
|
|
963
|
+
res.send({ userId, data: 'Hello' });
|
|
964
|
+
}, [userId]); // Re-register when userId changes
|
|
965
|
+
|
|
966
|
+
return <div>Server Component</div>;
|
|
967
|
+
};
|
|
968
|
+
```
|
|
969
|
+
|
|
970
|
+
**Key Features:**
|
|
971
|
+
- Automatically handles closure issues - always uses latest values from dependencies
|
|
972
|
+
- Automatically unregisters handler on unmount or when dependencies change
|
|
973
|
+
- No need to manually manage handler registration/cleanup
|
|
974
|
+
|
|
975
|
+
### useServerHandlerMap(server, map, deps?)
|
|
976
|
+
|
|
977
|
+
React hook for registering multiple server handlers at once with automatic cleanup.
|
|
978
|
+
|
|
979
|
+
**Parameters:**
|
|
980
|
+
|
|
981
|
+
| Parameter | Type | Description |
|
|
982
|
+
|-----------|------|-------------|
|
|
983
|
+
| `server` | `RequestIframeServer \| null` | Server instance (from `useServer`) |
|
|
984
|
+
| `map` | `Record<string, ServerHandler>` | Map of route paths and handler functions |
|
|
985
|
+
| `deps` | `readonly unknown[]` | Dependency array (optional, for re-registering when dependencies change) |
|
|
986
|
+
|
|
987
|
+
**Example:**
|
|
988
|
+
|
|
989
|
+
```tsx
|
|
990
|
+
import { useServer, useServerHandlerMap } from 'request-iframe/react';
|
|
991
|
+
import { useState } from 'react';
|
|
992
|
+
|
|
993
|
+
const MyComponent = () => {
|
|
994
|
+
const server = useServer();
|
|
995
|
+
const [userId, setUserId] = useState(1);
|
|
996
|
+
|
|
997
|
+
// Register multiple handlers at once
|
|
998
|
+
useServerHandlerMap(server, {
|
|
999
|
+
'/api/user': (req, res) => {
|
|
1000
|
+
res.send({ userId, data: 'User data' });
|
|
1001
|
+
},
|
|
1002
|
+
'/api/posts': (req, res) => {
|
|
1003
|
+
res.send({ userId, data: 'Posts data' });
|
|
1004
|
+
}
|
|
1005
|
+
}, [userId]); // Re-register all handlers when userId changes
|
|
1006
|
+
|
|
1007
|
+
return <div>Server Component</div>;
|
|
1008
|
+
};
|
|
1009
|
+
```
|
|
1010
|
+
|
|
1011
|
+
**Key Features:**
|
|
1012
|
+
- Batch registration of multiple handlers
|
|
1013
|
+
- Automatically handles closure issues - always uses latest values from dependencies
|
|
1014
|
+
- Automatically unregisters all handlers on unmount or when dependencies change
|
|
1015
|
+
- Efficient - only re-registers when map keys change
|
|
1016
|
+
|
|
1017
|
+
### Complete Example
|
|
1018
|
+
|
|
1019
|
+
Here's a complete example showing how to use React hooks in a real application:
|
|
1020
|
+
|
|
1021
|
+
```tsx
|
|
1022
|
+
import { useClient, useServer, useServerHandler } from 'request-iframe/react';
|
|
1023
|
+
import { useRef, useState } from 'react';
|
|
1024
|
+
|
|
1025
|
+
// Parent Component (Client)
|
|
1026
|
+
const ParentComponent = () => {
|
|
1027
|
+
const iframeRef = useRef<HTMLIFrameElement>(null);
|
|
1028
|
+
const client = useClient(iframeRef, { secretKey: 'my-app' });
|
|
1029
|
+
const [data, setData] = useState(null);
|
|
1030
|
+
|
|
1031
|
+
const fetchData = async () => {
|
|
1032
|
+
if (!client) return;
|
|
1033
|
+
|
|
1034
|
+
try {
|
|
1035
|
+
const response = await client.send('/api/data', { id: 1 });
|
|
1036
|
+
setData(response.data);
|
|
1037
|
+
} catch (error) {
|
|
1038
|
+
console.error('Request failed:', error);
|
|
1039
|
+
}
|
|
1040
|
+
};
|
|
1041
|
+
|
|
1042
|
+
return (
|
|
1043
|
+
<div>
|
|
1044
|
+
<iframe ref={iframeRef} src="/iframe.html" />
|
|
1045
|
+
<button onClick={fetchData}>Fetch Data</button>
|
|
1046
|
+
{data && <pre>{JSON.stringify(data, null, 2)}</pre>}
|
|
1047
|
+
</div>
|
|
1048
|
+
);
|
|
1049
|
+
};
|
|
1050
|
+
|
|
1051
|
+
// Iframe Component (Server)
|
|
1052
|
+
const IframeComponent = () => {
|
|
1053
|
+
const server = useServer({ secretKey: 'my-app' });
|
|
1054
|
+
const [userId, setUserId] = useState(1);
|
|
1055
|
+
|
|
1056
|
+
// Register handler with automatic cleanup
|
|
1057
|
+
useServerHandler(server, '/api/data', async (req, res) => {
|
|
1058
|
+
// Handler always uses latest userId value
|
|
1059
|
+
const userData = await fetchUserData(userId);
|
|
1060
|
+
res.send(userData);
|
|
1061
|
+
}, [userId]);
|
|
1062
|
+
|
|
1063
|
+
return (
|
|
1064
|
+
<div>
|
|
1065
|
+
<p>User ID: {userId}</p>
|
|
1066
|
+
<button onClick={() => setUserId(userId + 1)}>Increment</button>
|
|
1067
|
+
</div>
|
|
1068
|
+
);
|
|
1069
|
+
};
|
|
1070
|
+
```
|
|
1071
|
+
|
|
1072
|
+
### Best Practices
|
|
1073
|
+
|
|
1074
|
+
1. **Always check for null**: Client and server hooks may return `null` initially or when target is unavailable:
|
|
1075
|
+
```tsx
|
|
1076
|
+
const client = useClient(iframeRef);
|
|
1077
|
+
if (!client) return null; // Handle null case
|
|
1078
|
+
```
|
|
1079
|
+
|
|
1080
|
+
2. **Use dependency arrays**: Pass dependencies to hooks to ensure handlers use latest values:
|
|
1081
|
+
```tsx
|
|
1082
|
+
useServerHandler(server, '/api/data', (req, res) => {
|
|
1083
|
+
res.send({ userId }); // Always uses latest userId
|
|
1084
|
+
}, [userId]); // Re-register when userId changes
|
|
1085
|
+
```
|
|
1086
|
+
|
|
1087
|
+
3. **Cleanup is automatic**: Hooks automatically clean up on unmount, but you can also manually unregister:
|
|
1088
|
+
```tsx
|
|
1089
|
+
useEffect(() => {
|
|
1090
|
+
if (!server) return;
|
|
1091
|
+
const off = server.on('/api/data', handler);
|
|
1092
|
+
return off; // Manual cleanup (optional, hooks do this automatically)
|
|
1093
|
+
}, [server]);
|
|
1094
|
+
```
|
|
1095
|
+
|
|
1096
|
+
---
|
|
1097
|
+
|
|
843
1098
|
## Error Handling
|
|
844
1099
|
|
|
845
1100
|
### Error Codes
|