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
|
@@ -0,0 +1,420 @@
|
|
|
1
|
+
import { MessageChannel, ChannelType, MessageContext } from '../message/channel';
|
|
2
|
+
import { createPostMessage } from '../utils';
|
|
3
|
+
import { MessageType, MessageRole } from '../constants';
|
|
4
|
+
|
|
5
|
+
describe('MessageChannel', () => {
|
|
6
|
+
let channel: MessageChannel;
|
|
7
|
+
let mockReceiver: jest.Mock;
|
|
8
|
+
|
|
9
|
+
beforeEach(() => {
|
|
10
|
+
mockReceiver = jest.fn();
|
|
11
|
+
channel = new MessageChannel();
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
afterEach(() => {
|
|
15
|
+
channel.destroy();
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
describe('constructor', () => {
|
|
19
|
+
it('should create channel with default type', () => {
|
|
20
|
+
expect(channel.type).toBe(ChannelType.POST_MESSAGE);
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it('should create channel with secretKey', () => {
|
|
24
|
+
const channelWithKey = new MessageChannel('test-key');
|
|
25
|
+
expect(channelWithKey.secretKey).toBe('test-key');
|
|
26
|
+
channelWithKey.destroy();
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it('should add message event listener', () => {
|
|
30
|
+
const addEventListenerSpy = jest.spyOn(window, 'addEventListener');
|
|
31
|
+
const testChannel = new MessageChannel();
|
|
32
|
+
expect(addEventListenerSpy).toHaveBeenCalledWith('message', expect.any(Function));
|
|
33
|
+
testChannel.destroy();
|
|
34
|
+
addEventListenerSpy.mockRestore();
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
describe('addReceiver and removeReceiver', () => {
|
|
39
|
+
it('should add receiver callback', () => {
|
|
40
|
+
channel.addReceiver(mockReceiver);
|
|
41
|
+
|
|
42
|
+
const message = createPostMessage(MessageType.REQUEST, 'req123', {
|
|
43
|
+
path: 'test'
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
window.dispatchEvent(
|
|
47
|
+
new MessageEvent('message', {
|
|
48
|
+
data: message,
|
|
49
|
+
origin: 'https://example.com'
|
|
50
|
+
})
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
// Wait for async message handling
|
|
54
|
+
setTimeout(() => {
|
|
55
|
+
expect(mockReceiver).toHaveBeenCalled();
|
|
56
|
+
}, 10);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it('should remove receiver callback', () => {
|
|
60
|
+
channel.addReceiver(mockReceiver);
|
|
61
|
+
channel.removeReceiver(mockReceiver);
|
|
62
|
+
|
|
63
|
+
const message = createPostMessage(MessageType.REQUEST, 'req123', {
|
|
64
|
+
path: 'test'
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
window.dispatchEvent(
|
|
68
|
+
new MessageEvent('message', {
|
|
69
|
+
data: message,
|
|
70
|
+
origin: 'https://example.com'
|
|
71
|
+
})
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
// Wait for async message handling
|
|
75
|
+
setTimeout(() => {
|
|
76
|
+
expect(mockReceiver).not.toHaveBeenCalled();
|
|
77
|
+
}, 10);
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it('should support multiple receivers', () => {
|
|
81
|
+
const receiver1 = jest.fn();
|
|
82
|
+
const receiver2 = jest.fn();
|
|
83
|
+
|
|
84
|
+
channel.addReceiver(receiver1);
|
|
85
|
+
channel.addReceiver(receiver2);
|
|
86
|
+
|
|
87
|
+
const message = createPostMessage(MessageType.REQUEST, 'req123', {
|
|
88
|
+
path: 'test'
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
window.dispatchEvent(
|
|
92
|
+
new MessageEvent('message', {
|
|
93
|
+
data: message,
|
|
94
|
+
origin: 'https://example.com'
|
|
95
|
+
})
|
|
96
|
+
);
|
|
97
|
+
|
|
98
|
+
// Wait for async message handling
|
|
99
|
+
setTimeout(() => {
|
|
100
|
+
expect(receiver1).toHaveBeenCalled();
|
|
101
|
+
expect(receiver2).toHaveBeenCalled();
|
|
102
|
+
}, 10);
|
|
103
|
+
});
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
describe('reference counting', () => {
|
|
107
|
+
it('should increment reference count', () => {
|
|
108
|
+
expect(channel.getRefCount()).toBe(0);
|
|
109
|
+
channel.addRef();
|
|
110
|
+
expect(channel.getRefCount()).toBe(1);
|
|
111
|
+
channel.addRef();
|
|
112
|
+
expect(channel.getRefCount()).toBe(2);
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
it('should decrement reference count', () => {
|
|
116
|
+
channel.addRef();
|
|
117
|
+
channel.addRef();
|
|
118
|
+
expect(channel.release()).toBe(1);
|
|
119
|
+
expect(channel.release()).toBe(0);
|
|
120
|
+
});
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
describe('message filtering', () => {
|
|
124
|
+
it('should filter messages by secretKey', () => {
|
|
125
|
+
const channelWithKey = new MessageChannel('test-key');
|
|
126
|
+
channelWithKey.addReceiver(mockReceiver);
|
|
127
|
+
|
|
128
|
+
// Message with matching secretKey
|
|
129
|
+
const validMessage = createPostMessage(MessageType.REQUEST, 'req123', {
|
|
130
|
+
path: 'test',
|
|
131
|
+
secretKey: 'test-key'
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
window.dispatchEvent(
|
|
135
|
+
new MessageEvent('message', {
|
|
136
|
+
data: validMessage,
|
|
137
|
+
origin: 'https://example.com'
|
|
138
|
+
})
|
|
139
|
+
);
|
|
140
|
+
|
|
141
|
+
// Message with different secretKey
|
|
142
|
+
const invalidMessage = createPostMessage(MessageType.REQUEST, 'req124', {
|
|
143
|
+
path: 'test',
|
|
144
|
+
secretKey: 'other-key'
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
window.dispatchEvent(
|
|
148
|
+
new MessageEvent('message', {
|
|
149
|
+
data: invalidMessage,
|
|
150
|
+
origin: 'https://example.com'
|
|
151
|
+
})
|
|
152
|
+
);
|
|
153
|
+
|
|
154
|
+
setTimeout(() => {
|
|
155
|
+
expect(mockReceiver).toHaveBeenCalledTimes(1);
|
|
156
|
+
expect(mockReceiver).toHaveBeenCalledWith(
|
|
157
|
+
validMessage,
|
|
158
|
+
expect.objectContaining({
|
|
159
|
+
origin: 'https://example.com'
|
|
160
|
+
})
|
|
161
|
+
);
|
|
162
|
+
}, 10);
|
|
163
|
+
|
|
164
|
+
channelWithKey.destroy();
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
it('should filter messages without secretKey when channel has secretKey', () => {
|
|
168
|
+
const channelWithKey = new MessageChannel('test-key');
|
|
169
|
+
channelWithKey.addReceiver(mockReceiver);
|
|
170
|
+
|
|
171
|
+
// Message without secretKey
|
|
172
|
+
const message = createPostMessage(MessageType.REQUEST, 'req123', {
|
|
173
|
+
path: 'test'
|
|
174
|
+
});
|
|
175
|
+
delete (message as any).secretKey;
|
|
176
|
+
|
|
177
|
+
window.dispatchEvent(
|
|
178
|
+
new MessageEvent('message', {
|
|
179
|
+
data: message,
|
|
180
|
+
origin: 'https://example.com'
|
|
181
|
+
})
|
|
182
|
+
);
|
|
183
|
+
|
|
184
|
+
setTimeout(() => {
|
|
185
|
+
expect(mockReceiver).not.toHaveBeenCalled();
|
|
186
|
+
}, 10);
|
|
187
|
+
|
|
188
|
+
channelWithKey.destroy();
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
it('should accept messages without secretKey when channel has no secretKey', () => {
|
|
192
|
+
channel.addReceiver(mockReceiver);
|
|
193
|
+
|
|
194
|
+
const message = createPostMessage(MessageType.REQUEST, 'req123', {
|
|
195
|
+
path: 'test'
|
|
196
|
+
});
|
|
197
|
+
delete (message as any).secretKey;
|
|
198
|
+
|
|
199
|
+
window.dispatchEvent(
|
|
200
|
+
new MessageEvent('message', {
|
|
201
|
+
data: message,
|
|
202
|
+
origin: 'https://example.com'
|
|
203
|
+
})
|
|
204
|
+
);
|
|
205
|
+
|
|
206
|
+
setTimeout(() => {
|
|
207
|
+
expect(mockReceiver).toHaveBeenCalled();
|
|
208
|
+
}, 10);
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
it('should ignore invalid postMessage format', () => {
|
|
212
|
+
channel.addReceiver(mockReceiver);
|
|
213
|
+
|
|
214
|
+
window.dispatchEvent(
|
|
215
|
+
new MessageEvent('message', {
|
|
216
|
+
data: { invalid: 'message' },
|
|
217
|
+
origin: 'https://example.com'
|
|
218
|
+
})
|
|
219
|
+
);
|
|
220
|
+
|
|
221
|
+
setTimeout(() => {
|
|
222
|
+
expect(mockReceiver).not.toHaveBeenCalled();
|
|
223
|
+
}, 10);
|
|
224
|
+
});
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
describe('send', () => {
|
|
228
|
+
it('should send message to target window', () => {
|
|
229
|
+
const targetWindow = {
|
|
230
|
+
postMessage: jest.fn()
|
|
231
|
+
} as any;
|
|
232
|
+
|
|
233
|
+
const message = createPostMessage(MessageType.REQUEST, 'req123', {
|
|
234
|
+
path: 'test'
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
channel.send(targetWindow, message, 'https://example.com');
|
|
238
|
+
|
|
239
|
+
expect(targetWindow.postMessage).toHaveBeenCalledWith(
|
|
240
|
+
message,
|
|
241
|
+
'https://example.com'
|
|
242
|
+
);
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
it('should use default origin * when not specified', () => {
|
|
246
|
+
const targetWindow = {
|
|
247
|
+
postMessage: jest.fn()
|
|
248
|
+
} as any;
|
|
249
|
+
|
|
250
|
+
const message = createPostMessage(MessageType.REQUEST, 'req123', {
|
|
251
|
+
path: 'test'
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
channel.send(targetWindow, message);
|
|
255
|
+
|
|
256
|
+
expect(targetWindow.postMessage).toHaveBeenCalledWith(
|
|
257
|
+
message,
|
|
258
|
+
'*'
|
|
259
|
+
);
|
|
260
|
+
});
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
describe('sendMessage', () => {
|
|
264
|
+
it('should create and send message', () => {
|
|
265
|
+
const targetWindow = {
|
|
266
|
+
postMessage: jest.fn()
|
|
267
|
+
} as any;
|
|
268
|
+
|
|
269
|
+
channel.sendMessage(
|
|
270
|
+
targetWindow,
|
|
271
|
+
'https://example.com',
|
|
272
|
+
MessageType.REQUEST,
|
|
273
|
+
'req123',
|
|
274
|
+
{
|
|
275
|
+
path: 'test',
|
|
276
|
+
body: { param: 'value' }
|
|
277
|
+
}
|
|
278
|
+
);
|
|
279
|
+
|
|
280
|
+
expect(targetWindow.postMessage).toHaveBeenCalledWith(
|
|
281
|
+
expect.objectContaining({
|
|
282
|
+
__requestIframe__: 1,
|
|
283
|
+
type: 'request',
|
|
284
|
+
requestId: 'req123',
|
|
285
|
+
path: 'test',
|
|
286
|
+
body: { param: 'value' },
|
|
287
|
+
secretKey: undefined
|
|
288
|
+
}),
|
|
289
|
+
'https://example.com'
|
|
290
|
+
);
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
it('should include secretKey in message', () => {
|
|
294
|
+
const channelWithKey = new MessageChannel('test-key');
|
|
295
|
+
const targetWindow = {
|
|
296
|
+
postMessage: jest.fn()
|
|
297
|
+
} as any;
|
|
298
|
+
|
|
299
|
+
channelWithKey.sendMessage(
|
|
300
|
+
targetWindow,
|
|
301
|
+
'https://example.com',
|
|
302
|
+
MessageType.REQUEST,
|
|
303
|
+
'req123',
|
|
304
|
+
{ path: 'test' }
|
|
305
|
+
);
|
|
306
|
+
|
|
307
|
+
expect(targetWindow.postMessage).toHaveBeenCalledWith(
|
|
308
|
+
expect.objectContaining({
|
|
309
|
+
secretKey: 'test-key'
|
|
310
|
+
}),
|
|
311
|
+
'https://example.com'
|
|
312
|
+
);
|
|
313
|
+
|
|
314
|
+
channelWithKey.destroy();
|
|
315
|
+
});
|
|
316
|
+
});
|
|
317
|
+
|
|
318
|
+
describe('prefixPath', () => {
|
|
319
|
+
it('should add secretKey prefix when secretKey exists', () => {
|
|
320
|
+
const channelWithKey = new MessageChannel('test-key');
|
|
321
|
+
expect(channelWithKey.prefixPath('test')).toBe('test-key:test');
|
|
322
|
+
channelWithKey.destroy();
|
|
323
|
+
});
|
|
324
|
+
|
|
325
|
+
it('should return path as-is when no secretKey', () => {
|
|
326
|
+
expect(channel.prefixPath('test')).toBe('test');
|
|
327
|
+
});
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
describe('extractContext', () => {
|
|
331
|
+
it('should extract context from MessageEvent', () => {
|
|
332
|
+
channel.addReceiver(mockReceiver);
|
|
333
|
+
|
|
334
|
+
const sourceWindow = window;
|
|
335
|
+
const message = createPostMessage(MessageType.REQUEST, 'req123', {
|
|
336
|
+
path: 'test'
|
|
337
|
+
});
|
|
338
|
+
|
|
339
|
+
window.dispatchEvent(
|
|
340
|
+
new MessageEvent('message', {
|
|
341
|
+
data: message,
|
|
342
|
+
origin: 'https://example.com',
|
|
343
|
+
source: sourceWindow
|
|
344
|
+
})
|
|
345
|
+
);
|
|
346
|
+
|
|
347
|
+
setTimeout(() => {
|
|
348
|
+
expect(mockReceiver).toHaveBeenCalledWith(
|
|
349
|
+
message,
|
|
350
|
+
expect.objectContaining({
|
|
351
|
+
source: sourceWindow,
|
|
352
|
+
origin: 'https://example.com'
|
|
353
|
+
})
|
|
354
|
+
);
|
|
355
|
+
}, 10);
|
|
356
|
+
});
|
|
357
|
+
});
|
|
358
|
+
|
|
359
|
+
describe('error handling', () => {
|
|
360
|
+
it('should handle receiver errors gracefully', () => {
|
|
361
|
+
const errorReceiver = jest.fn(() => {
|
|
362
|
+
throw new Error('Receiver error');
|
|
363
|
+
});
|
|
364
|
+
const consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation();
|
|
365
|
+
|
|
366
|
+
channel.addReceiver(errorReceiver);
|
|
367
|
+
channel.addReceiver(mockReceiver);
|
|
368
|
+
|
|
369
|
+
const message = createPostMessage(MessageType.REQUEST, 'req123', {
|
|
370
|
+
path: 'test'
|
|
371
|
+
});
|
|
372
|
+
|
|
373
|
+
window.dispatchEvent(
|
|
374
|
+
new MessageEvent('message', {
|
|
375
|
+
data: message,
|
|
376
|
+
origin: 'https://example.com'
|
|
377
|
+
})
|
|
378
|
+
);
|
|
379
|
+
|
|
380
|
+
setTimeout(() => {
|
|
381
|
+
expect(errorReceiver).toHaveBeenCalled();
|
|
382
|
+
expect(mockReceiver).toHaveBeenCalled(); // Other receivers should still be called
|
|
383
|
+
expect(consoleErrorSpy).toHaveBeenCalledWith(
|
|
384
|
+
'[request-iframe] Receiver error:',
|
|
385
|
+
expect.any(Error)
|
|
386
|
+
);
|
|
387
|
+
consoleErrorSpy.mockRestore();
|
|
388
|
+
}, 10);
|
|
389
|
+
});
|
|
390
|
+
});
|
|
391
|
+
|
|
392
|
+
describe('destroy', () => {
|
|
393
|
+
it('should remove event listener', () => {
|
|
394
|
+
const removeEventListenerSpy = jest.spyOn(window, 'removeEventListener');
|
|
395
|
+
channel.destroy();
|
|
396
|
+
expect(removeEventListenerSpy).toHaveBeenCalledWith('message', expect.any(Function));
|
|
397
|
+
removeEventListenerSpy.mockRestore();
|
|
398
|
+
});
|
|
399
|
+
|
|
400
|
+
it('should clear receivers', () => {
|
|
401
|
+
channel.addReceiver(mockReceiver);
|
|
402
|
+
channel.destroy();
|
|
403
|
+
|
|
404
|
+
const message = createPostMessage(MessageType.REQUEST, 'req123', {
|
|
405
|
+
path: 'test'
|
|
406
|
+
});
|
|
407
|
+
|
|
408
|
+
window.dispatchEvent(
|
|
409
|
+
new MessageEvent('message', {
|
|
410
|
+
data: message,
|
|
411
|
+
origin: 'https://example.com'
|
|
412
|
+
})
|
|
413
|
+
);
|
|
414
|
+
|
|
415
|
+
setTimeout(() => {
|
|
416
|
+
expect(mockReceiver).not.toHaveBeenCalled();
|
|
417
|
+
}, 10);
|
|
418
|
+
});
|
|
419
|
+
});
|
|
420
|
+
});
|