request-iframe 0.0.1 → 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.
Files changed (96) hide show
  1. package/README.CN.md +271 -12
  2. package/README.md +268 -11
  3. package/library/__tests__/channel.test.ts +420 -0
  4. package/library/__tests__/debug.test.ts +588 -0
  5. package/library/__tests__/dispatcher.test.ts +481 -0
  6. package/library/__tests__/interceptors.test.ts +22 -0
  7. package/library/__tests__/requestIframe.test.ts +2317 -99
  8. package/library/__tests__/server.test.ts +738 -0
  9. package/library/api/client.d.js +5 -0
  10. package/library/api/client.d.ts.map +1 -1
  11. package/library/api/client.js +11 -6
  12. package/library/api/server.d.js +5 -0
  13. package/library/api/server.d.ts +4 -3
  14. package/library/api/server.d.ts.map +1 -1
  15. package/library/api/server.js +25 -7
  16. package/library/constants/index.d.js +36 -0
  17. package/library/constants/index.d.ts +14 -4
  18. package/library/constants/index.d.ts.map +1 -1
  19. package/library/constants/index.js +15 -7
  20. package/library/constants/messages.d.js +5 -0
  21. package/library/constants/messages.d.ts +35 -0
  22. package/library/constants/messages.d.ts.map +1 -1
  23. package/library/constants/messages.js +36 -1
  24. package/library/core/client-server.d.ts +101 -0
  25. package/library/core/client-server.d.ts.map +1 -0
  26. package/library/core/client-server.js +266 -0
  27. package/library/core/client.d.js +5 -0
  28. package/library/core/client.d.ts +38 -6
  29. package/library/core/client.d.ts.map +1 -1
  30. package/library/core/client.js +198 -24
  31. package/library/core/request.d.js +5 -0
  32. package/library/core/response.d.js +5 -0
  33. package/library/core/response.d.ts +5 -1
  34. package/library/core/response.d.ts.map +1 -1
  35. package/library/core/response.js +85 -70
  36. package/library/core/server-client.d.js +5 -0
  37. package/library/core/server-client.d.ts +3 -1
  38. package/library/core/server-client.d.ts.map +1 -1
  39. package/library/core/server-client.js +19 -9
  40. package/library/core/server.d.js +5 -0
  41. package/library/core/server.d.ts +11 -3
  42. package/library/core/server.d.ts.map +1 -1
  43. package/library/core/server.js +112 -54
  44. package/library/index.d.ts +1 -1
  45. package/library/index.js +2 -2
  46. package/library/interceptors/index.d.js +5 -0
  47. package/library/interceptors/index.d.ts +4 -0
  48. package/library/interceptors/index.d.ts.map +1 -1
  49. package/library/interceptors/index.js +7 -0
  50. package/library/message/channel.d.js +5 -0
  51. package/library/message/channel.d.ts +3 -1
  52. package/library/message/channel.d.ts.map +1 -1
  53. package/library/message/dispatcher.d.js +5 -0
  54. package/library/message/dispatcher.d.ts +7 -2
  55. package/library/message/dispatcher.d.ts.map +1 -1
  56. package/library/message/dispatcher.js +47 -2
  57. package/library/message/index.d.js +25 -0
  58. package/library/stream/file-stream.d.js +4 -0
  59. package/library/stream/file-stream.d.ts +5 -0
  60. package/library/stream/file-stream.d.ts.map +1 -1
  61. package/library/stream/file-stream.js +41 -12
  62. package/library/stream/index.d.js +58 -0
  63. package/library/stream/readable-stream.d.js +5 -0
  64. package/library/stream/readable-stream.d.ts.map +1 -1
  65. package/library/stream/readable-stream.js +32 -30
  66. package/library/stream/types.d.js +5 -0
  67. package/library/stream/types.d.ts +18 -0
  68. package/library/stream/types.d.ts.map +1 -1
  69. package/library/stream/writable-stream.d.js +5 -0
  70. package/library/stream/writable-stream.d.ts +1 -0
  71. package/library/stream/writable-stream.d.ts.map +1 -1
  72. package/library/stream/writable-stream.js +7 -2
  73. package/library/types/index.d.js +5 -0
  74. package/library/types/index.d.ts +79 -19
  75. package/library/types/index.d.ts.map +1 -1
  76. package/library/utils/cache.d.js +5 -0
  77. package/library/utils/cache.d.ts +24 -0
  78. package/library/utils/cache.d.ts.map +1 -1
  79. package/library/utils/cache.js +76 -0
  80. package/library/utils/cookie.d.js +5 -0
  81. package/library/utils/debug.d.js +5 -0
  82. package/library/utils/debug.d.ts.map +1 -1
  83. package/library/utils/debug.js +382 -20
  84. package/library/utils/index.d.js +94 -0
  85. package/library/utils/index.d.ts +5 -0
  86. package/library/utils/index.d.ts.map +1 -1
  87. package/library/utils/index.js +14 -1
  88. package/library/utils/path-match.d.js +5 -0
  89. package/library/utils/protocol.d.js +5 -0
  90. package/package.json +16 -2
  91. package/react/library/__tests__/index.test.d.ts +2 -0
  92. package/react/library/__tests__/index.test.d.ts.map +1 -0
  93. package/react/library/__tests__/index.test.tsx +770 -0
  94. package/react/library/index.d.ts +118 -0
  95. package/react/library/index.d.ts.map +1 -0
  96. package/react/library/index.js +232 -0
package/README.CN.md CHANGED
@@ -2,6 +2,8 @@
2
2
 
3
3
  像发送 HTTP 请求一样与 iframe 通信!基于 `postMessage` 实现的 iframe 跨域通信库。
4
4
 
5
+ > 🌐 **Languages**: [English](./README.md) | [中文](./README.CN.md)
6
+
5
7
  <p align="center">
6
8
  <img src="https://img.shields.io/badge/TypeScript-Ready-blue" alt="TypeScript Ready">
7
9
  <img src="https://img.shields.io/badge/API-Express%20Like-green" alt="Express Like API">
@@ -32,6 +34,13 @@
32
34
  - [追踪模式](#追踪模式)
33
35
  - [多语言支持](#多语言支持)
34
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
+ - [最佳实践](#最佳实践)
35
44
  - [错误处理](#错误处理)
36
45
  - [FAQ](#faq)
37
46
  - [开发](#开发)
@@ -165,7 +174,7 @@ request-iframe 采用三阶段超时策略,智能适应不同场景:
165
174
 
166
175
  ```typescript
167
176
  client.send('/api/getData', data, {
168
- ackTimeout: 500, // 阶段1:等待 ACK 的超时时间(默认 500ms
177
+ ackTimeout: 1000, // 阶段1:等待 ACK 的超时时间(默认 1000ms
169
178
  timeout: 5000, // 阶段2:请求超时时间(默认 5s)
170
179
  asyncTimeout: 120000 // 阶段3:异步请求超时时间(默认 120s)
171
180
  });
@@ -205,7 +214,7 @@ client.send('/api/getData', data, {
205
214
 
206
215
  | 阶段 | 超时时间 | 场景 |
207
216
  |------|----------|------|
208
- | ackTimeout | 较短(500ms) | 快速检测 Server 是否在线,避免长时间等待不可达的 iframe |
217
+ | ackTimeout | 较短(1000ms) | 快速检测 Server 是否在线,避免长时间等待不可达的 iframe。从 500ms 增加到 1000ms,以适应性能较差的环境或浏览器繁忙的场景 |
209
218
  | timeout | 中等(5s) | 适用于简单的同步处理,如读取数据、参数校验等 |
210
219
  | asyncTimeout | 较长(120s) | 适用于复杂异步操作,如文件处理、批量操作、第三方 API 调用等 |
211
220
 
@@ -802,7 +811,7 @@ setMessages({
802
811
  | `target` | `HTMLIFrameElement \| Window` | 目标 iframe 元素或 window 对象 |
803
812
  | `options.secretKey` | `string` | 消息隔离标识(可选) |
804
813
  | `options.trace` | `boolean` | 是否开启追踪模式(可选) |
805
- | `options.ackTimeout` | `number` | 全局默认 ACK 确认超时(ms),默认 500 |
814
+ | `options.ackTimeout` | `number` | 全局默认 ACK 确认超时(ms),默认 1000 |
806
815
  | `options.timeout` | `number` | 全局默认请求超时(ms),默认 5000 |
807
816
  | `options.asyncTimeout` | `number` | 全局默认异步超时(ms),默认 120000 |
808
817
 
@@ -835,7 +844,7 @@ await client.send('/api/longTask', {}, {
835
844
  |------|------|------|
836
845
  | `options.secretKey` | `string` | 消息隔离标识(可选) |
837
846
  | `options.trace` | `boolean` | 是否开启追踪模式(可选) |
838
- | `options.ackTimeout` | `number` | 等待客户端确认超时(ms),默认 5000 |
847
+ | `options.ackTimeout` | `number` | 等待客户端确认超时(ms),默认 1000 |
839
848
 
840
849
  **返回值:** `RequestIframeServer`
841
850
 
@@ -851,7 +860,7 @@ await client.send('/api/longTask', {}, {
851
860
  |------|------|------|
852
861
  | `path` | `string` | 请求路径 |
853
862
  | `body` | `object` | 请求数据(可选) |
854
- | `options.ackTimeout` | `number` | ACK 确认超时(ms),默认 500 |
863
+ | `options.ackTimeout` | `number` | ACK 确认超时(ms),默认 1000 |
855
864
  | `options.timeout` | `number` | 请求超时(ms),默认 5000 |
856
865
  | `options.asyncTimeout` | `number` | 异步超时(ms),默认 120000 |
857
866
  | `options.headers` | `object` | 请求 headers(可选) |
@@ -862,16 +871,11 @@ await client.send('/api/longTask', {}, {
862
871
 
863
872
  ```typescript
864
873
  interface Response<T = any> {
865
- data: T; // 响应数据
874
+ data: T; // 响应数据(自动解析的文件流为 File/Blob)
866
875
  status: number; // 状态码
867
876
  statusText: string; // 状态文本
868
877
  requestId: string; // 请求 ID
869
878
  headers?: Record<string, string | string[]>; // 响应 headers(Set-Cookie 为数组)
870
- fileData?: { // 文件数据(如果有)
871
- content: string; // base64 编码内容
872
- mimeType?: string;
873
- fileName?: string;
874
- };
875
879
  stream?: IIframeReadableStream<T>; // 流响应(如果有)
876
880
  }
877
881
  ```
@@ -982,7 +986,262 @@ server.use(['/a', '/b'], (req, res, next) => { ... });
982
986
 
983
987
  销毁 Server 实例,移除所有监听器。
984
988
 
985
- ### ServerRequest 对象
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
+ ## 错误处理
986
1245
 
987
1246
  ```typescript
988
1247
  interface ServerRequest {
package/README.md CHANGED
@@ -2,6 +2,8 @@
2
2
 
3
3
  Communicate with iframes like sending HTTP requests! A cross-origin iframe communication library based on `postMessage`.
4
4
 
5
+ > 🌐 **Languages**: [English](./README.md) | [中文](./README.CN.md)
6
+
5
7
  <p align="center">
6
8
  <img src="https://img.shields.io/badge/TypeScript-Ready-blue" alt="TypeScript Ready">
7
9
  <img src="https://img.shields.io/badge/API-Express%20Like-green" alt="Express Like API">
@@ -32,6 +34,13 @@ Communicate with iframes like sending HTTP requests! A cross-origin iframe commu
32
34
  - [Trace Mode](#trace-mode)
33
35
  - [Internationalization](#internationalization)
34
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)
35
44
  - [Error Handling](#error-handling)
36
45
  - [FAQ](#faq)
37
46
  - [Development](#development)
@@ -249,7 +258,7 @@ request-iframe uses a three-stage timeout strategy to intelligently adapt to dif
249
258
 
250
259
  ```typescript
251
260
  client.send('/api/getData', data, {
252
- ackTimeout: 500, // Stage 1: ACK timeout (default 500ms)
261
+ ackTimeout: 1000, // Stage 1: ACK timeout (default 1000ms)
253
262
  timeout: 5000, // Stage 2: Request timeout (default 5s)
254
263
  asyncTimeout: 120000 // Stage 3: Async request timeout (default 120s)
255
264
  });
@@ -289,7 +298,7 @@ Send REQUEST
289
298
 
290
299
  | Stage | Timeout | Scenario |
291
300
  |-------|---------|----------|
292
- | ackTimeout | Short (500ms) | Quickly detect if Server is online, avoid long waits for unreachable iframes |
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 |
293
302
  | timeout | Medium (5s) | Suitable for simple synchronous processing, like reading data, parameter validation |
294
303
  | asyncTimeout | Long (120s) | Suitable for complex async operations, like file processing, batch operations, third-party API calls |
295
304
 
@@ -710,7 +719,7 @@ Create a Client instance.
710
719
  | `target` | `HTMLIFrameElement \| Window` | Target iframe element or window object |
711
720
  | `options.secretKey` | `string` | Message isolation identifier (optional) |
712
721
  | `options.trace` | `boolean` | Whether to enable trace mode (optional) |
713
- | `options.ackTimeout` | `number` | Global default ACK acknowledgment timeout (ms), default 500 |
722
+ | `options.ackTimeout` | `number` | Global default ACK acknowledgment timeout (ms), default 1000 |
714
723
  | `options.timeout` | `number` | Global default request timeout (ms), default 5000 |
715
724
  | `options.asyncTimeout` | `number` | Global default async timeout (ms), default 120000 |
716
725
 
@@ -726,7 +735,7 @@ Create a Server instance.
726
735
  |-----------|------|-------------|
727
736
  | `options.secretKey` | `string` | Message isolation identifier (optional) |
728
737
  | `options.trace` | `boolean` | Whether to enable trace mode (optional) |
729
- | `options.ackTimeout` | `number` | Wait for client acknowledgment timeout (ms), default 5000 |
738
+ | `options.ackTimeout` | `number` | Wait for client acknowledgment timeout (ms), default 1000 |
730
739
 
731
740
  **Returns:** `RequestIframeServer`
732
741
 
@@ -742,7 +751,7 @@ Send a request.
742
751
  |-----------|------|-------------|
743
752
  | `path` | `string` | Request path |
744
753
  | `body` | `object` | Request data (optional) |
745
- | `options.ackTimeout` | `number` | ACK acknowledgment timeout (ms), default 500 |
754
+ | `options.ackTimeout` | `number` | ACK acknowledgment timeout (ms), default 1000 |
746
755
  | `options.timeout` | `number` | Request timeout (ms), default 5000 |
747
756
  | `options.asyncTimeout` | `number` | Async timeout (ms), default 120000 |
748
757
  | `options.headers` | `object` | Request headers (optional) |
@@ -753,16 +762,11 @@ Send a request.
753
762
 
754
763
  ```typescript
755
764
  interface Response<T = any> {
756
- data: T; // Response data
765
+ data: T; // Response data (File/Blob for auto-resolved file streams)
757
766
  status: number; // Status code
758
767
  statusText: string; // Status text
759
768
  requestId: string; // Request ID
760
769
  headers?: Record<string, string | string[]>; // Response headers (Set-Cookie is array)
761
- fileData?: { // File data (if any)
762
- content: string; // base64-encoded content
763
- mimeType?: string;
764
- fileName?: string;
765
- };
766
770
  stream?: IIframeReadableStream<T>; // Stream response (if any)
767
771
  }
768
772
  ```
@@ -838,6 +842,259 @@ Destroy Server instance, remove all listeners.
838
842
 
839
843
  ---
840
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
+
841
1098
  ## Error Handling
842
1099
 
843
1100
  ### Error Codes