request-iframe 0.0.5 → 0.1.0

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 (142) hide show
  1. package/README.CN.md +54 -7
  2. package/README.md +64 -11
  3. package/esm/api/client.js +79 -0
  4. package/esm/api/server.js +59 -0
  5. package/esm/constants/index.js +257 -0
  6. package/esm/constants/messages.js +155 -0
  7. package/esm/core/client-server.js +329 -0
  8. package/esm/core/client.js +873 -0
  9. package/esm/core/request.js +27 -0
  10. package/esm/core/response.js +451 -0
  11. package/esm/core/server.js +767 -0
  12. package/esm/index.js +21 -0
  13. package/esm/interceptors/index.js +122 -0
  14. package/esm/message/channel.js +181 -0
  15. package/esm/message/dispatcher.js +380 -0
  16. package/esm/message/index.js +2 -0
  17. package/esm/stream/file-stream.js +289 -0
  18. package/esm/stream/index.js +44 -0
  19. package/esm/stream/readable-stream.js +500 -0
  20. package/esm/stream/stream-core.js +91 -0
  21. package/esm/stream/types.js +1 -0
  22. package/esm/stream/writable-stream.js +582 -0
  23. package/esm/types/index.js +1 -0
  24. package/esm/utils/ack-meta.js +53 -0
  25. package/esm/utils/cache.js +147 -0
  26. package/esm/utils/cookie.js +352 -0
  27. package/esm/utils/debug.js +521 -0
  28. package/esm/utils/error.js +27 -0
  29. package/esm/utils/index.js +178 -0
  30. package/esm/utils/origin.js +28 -0
  31. package/esm/utils/path-match.js +148 -0
  32. package/esm/utils/protocol.js +157 -0
  33. package/library/api/client.d.ts.map +1 -1
  34. package/library/api/client.js +8 -1
  35. package/library/api/server.d.ts.map +1 -1
  36. package/library/api/server.js +4 -1
  37. package/library/constants/index.d.ts +25 -1
  38. package/library/constants/index.d.ts.map +1 -1
  39. package/library/constants/index.js +30 -5
  40. package/library/constants/messages.d.ts +5 -0
  41. package/library/constants/messages.d.ts.map +1 -1
  42. package/library/constants/messages.js +5 -0
  43. package/library/core/client-server.d.ts +3 -2
  44. package/library/core/client-server.d.ts.map +1 -1
  45. package/library/core/client-server.js +51 -4
  46. package/library/core/client.d.ts +4 -1
  47. package/library/core/client.d.ts.map +1 -1
  48. package/library/core/client.js +74 -31
  49. package/library/core/response.d.ts +21 -3
  50. package/library/core/response.d.ts.map +1 -1
  51. package/library/core/response.js +46 -6
  52. package/library/core/server.d.ts +28 -1
  53. package/library/core/server.d.ts.map +1 -1
  54. package/library/core/server.js +180 -19
  55. package/library/message/channel.d.ts +6 -0
  56. package/library/message/channel.d.ts.map +1 -1
  57. package/library/message/dispatcher.d.ts +22 -0
  58. package/library/message/dispatcher.d.ts.map +1 -1
  59. package/library/message/dispatcher.js +92 -0
  60. package/library/stream/file-stream.d.ts +4 -0
  61. package/library/stream/file-stream.d.ts.map +1 -1
  62. package/library/stream/file-stream.js +61 -33
  63. package/library/stream/index.d.ts.map +1 -1
  64. package/library/stream/index.js +2 -0
  65. package/library/stream/readable-stream.d.ts +30 -11
  66. package/library/stream/readable-stream.d.ts.map +1 -1
  67. package/library/stream/readable-stream.js +329 -73
  68. package/library/stream/stream-core.d.ts +44 -0
  69. package/library/stream/stream-core.d.ts.map +1 -0
  70. package/library/stream/stream-core.js +98 -0
  71. package/library/stream/types.d.ts +90 -3
  72. package/library/stream/types.d.ts.map +1 -1
  73. package/library/stream/writable-stream.d.ts +40 -12
  74. package/library/stream/writable-stream.d.ts.map +1 -1
  75. package/library/stream/writable-stream.js +391 -195
  76. package/library/types/index.d.ts +70 -3
  77. package/library/types/index.d.ts.map +1 -1
  78. package/library/utils/ack-meta.d.ts +2 -0
  79. package/library/utils/ack-meta.d.ts.map +1 -0
  80. package/library/utils/ack-meta.js +59 -0
  81. package/library/utils/index.d.ts +1 -0
  82. package/library/utils/index.d.ts.map +1 -1
  83. package/library/utils/index.js +16 -0
  84. package/library/utils/origin.d.ts +14 -0
  85. package/library/utils/origin.d.ts.map +1 -0
  86. package/library/utils/origin.js +34 -0
  87. package/package.json +31 -7
  88. package/react/README.md +16 -0
  89. package/react/esm/index.js +284 -0
  90. package/react/library/index.d.ts +1 -1
  91. package/react/library/index.d.ts.map +1 -1
  92. package/react/library/index.js +7 -4
  93. package/react/package.json +24 -2
  94. package/library/__tests__/channel.test.ts +0 -432
  95. package/library/__tests__/coverage-branches.test.ts +0 -356
  96. package/library/__tests__/debug.test.ts +0 -610
  97. package/library/__tests__/dispatcher.test.ts +0 -485
  98. package/library/__tests__/interceptors.test.ts +0 -146
  99. package/library/__tests__/requestIframe.test.ts +0 -5590
  100. package/library/__tests__/server.test.ts +0 -738
  101. package/library/__tests__/stream.test.ts +0 -726
  102. package/library/__tests__/utils.test.ts +0 -473
  103. package/library/api/client.d.js +0 -5
  104. package/library/api/server.d.js +0 -5
  105. package/library/constants/index.d.js +0 -36
  106. package/library/constants/messages.d.js +0 -5
  107. package/library/core/client.d.js +0 -5
  108. package/library/core/message-handler.d.ts +0 -110
  109. package/library/core/message-handler.d.ts.map +0 -1
  110. package/library/core/message-handler.js +0 -320
  111. package/library/core/request-response.d.ts +0 -59
  112. package/library/core/request-response.d.ts.map +0 -1
  113. package/library/core/request-response.js +0 -337
  114. package/library/core/request.d.js +0 -5
  115. package/library/core/response.d.js +0 -5
  116. package/library/core/server-base.d.ts +0 -86
  117. package/library/core/server-base.d.ts.map +0 -1
  118. package/library/core/server-base.js +0 -257
  119. package/library/core/server-client.d.js +0 -5
  120. package/library/core/server-client.d.ts +0 -101
  121. package/library/core/server-client.d.ts.map +0 -1
  122. package/library/core/server-client.js +0 -266
  123. package/library/core/server.d.js +0 -5
  124. package/library/interceptors/index.d.js +0 -5
  125. package/library/message/channel.d.js +0 -5
  126. package/library/message/dispatcher.d.js +0 -5
  127. package/library/message/index.d.js +0 -25
  128. package/library/stream/file-stream.d.js +0 -4
  129. package/library/stream/index.d.js +0 -58
  130. package/library/stream/readable-stream.d.js +0 -5
  131. package/library/stream/types.d.js +0 -5
  132. package/library/stream/writable-stream.d.js +0 -5
  133. package/library/types/index.d.js +0 -5
  134. package/library/utils/cache.d.js +0 -5
  135. package/library/utils/cookie.d.js +0 -5
  136. package/library/utils/debug.d.js +0 -5
  137. package/library/utils/index.d.js +0 -94
  138. package/library/utils/path-match.d.js +0 -5
  139. package/library/utils/protocol.d.js +0 -5
  140. package/react/library/__tests__/index.test.d.ts +0 -2
  141. package/react/library/__tests__/index.test.d.ts.map +0 -1
  142. package/react/library/__tests__/index.test.tsx +0 -792
@@ -0,0 +1,27 @@
1
+ import _slicedToArray from "@babel/runtime/helpers/esm/slicedToArray";
2
+ import "core-js/modules/es.object.entries.js";
3
+ /**
4
+ * ServerRequest implementation
5
+ */
6
+ export class ServerRequestImpl {
7
+ constructor(data, context, response, params = {}) {
8
+ this.body = data.body;
9
+ // headers may contain array values (e.g., Set-Cookie), simplified to string here
10
+ this.headers = {};
11
+ if (data.headers) {
12
+ for (var _i = 0, _Object$entries = Object.entries(data.headers); _i < _Object$entries.length; _i++) {
13
+ var _Object$entries$_i = _slicedToArray(_Object$entries[_i], 2),
14
+ key = _Object$entries$_i[0],
15
+ value = _Object$entries$_i[1];
16
+ this.headers[key] = Array.isArray(value) ? value.join(', ') : value;
17
+ }
18
+ }
19
+ this.cookies = data.cookies || {};
20
+ this.path = data.path || '';
21
+ this.params = params;
22
+ this.requestId = data.requestId;
23
+ this.origin = context.origin;
24
+ this.source = context.source;
25
+ this.res = response;
26
+ }
27
+ }
@@ -0,0 +1,451 @@
1
+ import _asyncToGenerator from "@babel/runtime/helpers/esm/asyncToGenerator";
2
+ import _defineProperty from "@babel/runtime/helpers/esm/defineProperty";
3
+ import _regeneratorRuntime from "@babel/runtime/regenerator";
4
+ function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
5
+ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
6
+ import "core-js/modules/es.array.iterator.js";
7
+ import "core-js/modules/es.array.filter.js";
8
+ import "core-js/modules/es.object.get-own-property-descriptors.js";
9
+ import "core-js/modules/es.promise.js";
10
+ import "core-js/modules/web.dom-collections.for-each.js";
11
+ import "core-js/modules/web.dom-collections.iterator.js";
12
+ import { createPostMessage, createSetCookie, createClearCookie, detectContentType, blobToBase64 } from '../utils';
13
+ import { MessageType, HttpStatus, HttpHeader, getStatusText, MessageRole, ErrorCode } from '../constants';
14
+ import { IframeFileWritableStream, isIframeWritableStream } from '../stream';
15
+ import { isAckMetaEqual } from '../utils/ack-meta';
16
+
17
+ /**
18
+ * Callback waiting for client acknowledgment
19
+ */
20
+
21
+ /**
22
+ * ServerResponse implementation
23
+ */
24
+ export class ServerResponseImpl {
25
+ constructor(requestId, path, secretKey, targetWindow, targetOrigin, channel, serverId, targetId, options) {
26
+ _defineProperty(this, "statusCode", HttpStatus.OK);
27
+ _defineProperty(this, "headers", {});
28
+ _defineProperty(this, "_sent", false);
29
+ this.requestId = requestId;
30
+ this.path = path;
31
+ this.secretKey = secretKey;
32
+ this.targetWindow = targetWindow;
33
+ this.targetOrigin = targetOrigin;
34
+ this.channel = channel;
35
+ this.serverId = serverId;
36
+ this.targetId = targetId;
37
+ this.registerStreamHandler = options === null || options === void 0 ? void 0 : options.registerStreamHandler;
38
+ this.unregisterStreamHandler = options === null || options === void 0 ? void 0 : options.unregisterStreamHandler;
39
+ this.heartbeat = options === null || options === void 0 ? void 0 : options.heartbeat;
40
+ this.onSentCallback = options === null || options === void 0 ? void 0 : options.onSent;
41
+ }
42
+
43
+ /**
44
+ * Send message via channel
45
+ */
46
+ sendMessage(message) {
47
+ // Window check is handled in MessageDispatcher (via channel.send -> dispatcher.send)
48
+ this.channel.send(this.targetWindow, message, this.targetOrigin);
49
+ }
50
+
51
+ /**
52
+ * Check if header exists (case-insensitive)
53
+ */
54
+ hasHeader(name) {
55
+ var lower = name.toLowerCase();
56
+ return Object.keys(this.headers).some(k => k.toLowerCase() === lower);
57
+ }
58
+
59
+ /**
60
+ * Detect data type and return appropriate Content-Type
61
+ * Returns null if Content-Type should not be auto-set
62
+ */
63
+ detectContentType(data) {
64
+ return detectContentType(data, {
65
+ checkStream: true,
66
+ isIframeWritableStream
67
+ });
68
+ }
69
+
70
+ /**
71
+ * Auto set Content-Type based on data type (only if user not set)
72
+ */
73
+ ensureContentTypeIfNeeded(data) {
74
+ if (this.hasHeader(HttpHeader.CONTENT_TYPE)) return;
75
+ var contentType = this.detectContentType(data);
76
+ if (contentType) {
77
+ this.setHeader(HttpHeader.CONTENT_TYPE, contentType);
78
+ }
79
+ }
80
+
81
+ /**
82
+ * Set callback waiting for client acknowledgment
83
+ */
84
+ _setOnAckCallback(callback) {
85
+ this.onAckCallback = callback;
86
+ }
87
+
88
+ /**
89
+ * Trigger client acknowledgment callback
90
+ */
91
+ _triggerAck(received, ackMeta) {
92
+ if (this.onAckCallback) {
93
+ this.onAckCallback(received, ackMeta);
94
+ this.onAckCallback = undefined;
95
+ }
96
+ }
97
+
98
+ /**
99
+ * Mark response as sent (and trigger onSent callback once).
100
+ */
101
+ markSent() {
102
+ if (this._sent) return;
103
+ this._sent = true;
104
+ if (this.onSentCallback) {
105
+ var cb = this.onSentCallback;
106
+ this.onSentCallback = undefined;
107
+ cb();
108
+ }
109
+ }
110
+
111
+ /**
112
+ * Internal: mark as sent for manual error responses.
113
+ */
114
+ _markSent() {
115
+ this.markSent();
116
+ }
117
+
118
+ /**
119
+ * Internal method: send raw data (used by send after type detection)
120
+ */
121
+ _sendRaw(data, options) {
122
+ var _options$requireAck;
123
+ if (this._sent) return Promise.resolve(false);
124
+ this.markSent();
125
+ var requireAck = (_options$requireAck = options === null || options === void 0 ? void 0 : options.requireAck) !== null && _options$requireAck !== void 0 ? _options$requireAck : false;
126
+ var expectedAckMeta = options === null || options === void 0 ? void 0 : options.ackMeta;
127
+ try {
128
+ // If acknowledgment not required, send directly and return true
129
+ if (!requireAck) {
130
+ this.sendMessage(createPostMessage(MessageType.RESPONSE, this.requestId, {
131
+ path: this.path,
132
+ secretKey: this.secretKey,
133
+ data,
134
+ status: this.statusCode,
135
+ statusText: getStatusText(this.statusCode),
136
+ headers: this.headers,
137
+ requireAck: false,
138
+ role: MessageRole.SERVER,
139
+ creatorId: this.serverId,
140
+ targetId: this.targetId
141
+ }));
142
+ return Promise.resolve(true);
143
+ }
144
+
145
+ // Acknowledgment required, wait for client response
146
+ return new Promise((resolve, reject) => {
147
+ try {
148
+ this._setOnAckCallback((received, receivedAckMeta) => {
149
+ if (!received) {
150
+ resolve(false);
151
+ return;
152
+ }
153
+ if (expectedAckMeta !== undefined && !isAckMetaEqual(expectedAckMeta, receivedAckMeta)) {
154
+ resolve(false);
155
+ return;
156
+ }
157
+ resolve(true);
158
+ });
159
+ this.sendMessage(createPostMessage(MessageType.RESPONSE, this.requestId, {
160
+ path: this.path,
161
+ secretKey: this.secretKey,
162
+ data,
163
+ status: this.statusCode,
164
+ statusText: getStatusText(this.statusCode),
165
+ headers: this.headers,
166
+ requireAck: true,
167
+ ackMeta: expectedAckMeta,
168
+ role: MessageRole.SERVER,
169
+ creatorId: this.serverId,
170
+ targetId: this.targetId
171
+ }));
172
+ } catch (error) {
173
+ // If window is closed, reject immediately
174
+ if ((error === null || error === void 0 ? void 0 : error.code) === ErrorCode.TARGET_WINDOW_CLOSED) {
175
+ reject(error);
176
+ } else {
177
+ throw error;
178
+ }
179
+ }
180
+ });
181
+ } catch (error) {
182
+ // If window is closed, return rejected promise
183
+ if ((error === null || error === void 0 ? void 0 : error.code) === ErrorCode.TARGET_WINDOW_CLOSED) {
184
+ return Promise.reject(error);
185
+ }
186
+ throw error;
187
+ }
188
+ }
189
+
190
+ /**
191
+ * Universal send method - automatically detects data type and calls appropriate method
192
+ * - If data is IframeWritableStream, calls sendStream
193
+ * - If data is File/Blob, calls sendFile
194
+ * - Otherwise, sends as regular data with auto-detected Content-Type
195
+ */
196
+ send(data, options) {
197
+ var _this = this;
198
+ return _asyncToGenerator(/*#__PURE__*/_regeneratorRuntime.mark(function _callee() {
199
+ var fileOptions;
200
+ return _regeneratorRuntime.wrap(function (_context) {
201
+ while (1) switch (_context.prev = _context.next) {
202
+ case 0:
203
+ if (!_this._sent) {
204
+ _context.next = 1;
205
+ break;
206
+ }
207
+ return _context.abrupt("return", Promise.resolve(false));
208
+ case 1:
209
+ if (!isIframeWritableStream(data)) {
210
+ _context.next = 3;
211
+ break;
212
+ }
213
+ _context.next = 2;
214
+ return _this.sendStream(data);
215
+ case 2:
216
+ return _context.abrupt("return", true);
217
+ case 3:
218
+ if (!(typeof File !== 'undefined' && data instanceof File || typeof Blob !== 'undefined' && data instanceof Blob)) {
219
+ _context.next = 4;
220
+ break;
221
+ }
222
+ // Extract options for sendFile
223
+ fileOptions = _objectSpread({
224
+ requireAck: options === null || options === void 0 ? void 0 : options.requireAck
225
+ }, typeof File !== 'undefined' && data instanceof File ? {
226
+ mimeType: data.type,
227
+ fileName: data.name
228
+ } : {});
229
+ return _context.abrupt("return", _this.sendFile(data, fileOptions));
230
+ case 4:
231
+ // For other types, auto-detect and set Content-Type, then send
232
+ _this.ensureContentTypeIfNeeded(data);
233
+ return _context.abrupt("return", _this._sendRaw(data, options));
234
+ case 5:
235
+ case "end":
236
+ return _context.stop();
237
+ }
238
+ }, _callee);
239
+ }))();
240
+ }
241
+ json(data, options) {
242
+ return this.send(data, options);
243
+ }
244
+ sendFile(content, options) {
245
+ var _this2 = this;
246
+ return _asyncToGenerator(/*#__PURE__*/_regeneratorRuntime.mark(function _callee3() {
247
+ var mimeType, fileName, fileContent, stream;
248
+ return _regeneratorRuntime.wrap(function (_context3) {
249
+ while (1) switch (_context3.prev = _context3.next) {
250
+ case 0:
251
+ if (!_this2._sent) {
252
+ _context3.next = 1;
253
+ break;
254
+ }
255
+ return _context3.abrupt("return", false);
256
+ case 1:
257
+ mimeType = (options === null || options === void 0 ? void 0 : options.mimeType) || 'application/octet-stream';
258
+ fileName = options === null || options === void 0 ? void 0 : options.fileName;
259
+ if (!(typeof content === 'string')) {
260
+ _context3.next = 2;
261
+ break;
262
+ }
263
+ // If it's a plain string, convert to base64
264
+ fileContent = btoa(unescape(encodeURIComponent(content)));
265
+ _context3.next = 6;
266
+ break;
267
+ case 2:
268
+ if (!(content instanceof File)) {
269
+ _context3.next = 4;
270
+ break;
271
+ }
272
+ mimeType = content.type || mimeType;
273
+ fileName = fileName || content.name;
274
+ _context3.next = 3;
275
+ return blobToBase64(content);
276
+ case 3:
277
+ fileContent = _context3.sent;
278
+ _context3.next = 6;
279
+ break;
280
+ case 4:
281
+ _context3.next = 5;
282
+ return blobToBase64(content);
283
+ case 5:
284
+ fileContent = _context3.sent;
285
+ case 6:
286
+ // Set file-related headers
287
+ _this2.setHeader(HttpHeader.CONTENT_TYPE, mimeType);
288
+ if (fileName) {
289
+ _this2.setHeader(HttpHeader.CONTENT_DISPOSITION, `attachment; filename="${fileName}"`);
290
+ } else {
291
+ _this2.setHeader(HttpHeader.CONTENT_DISPOSITION, 'attachment');
292
+ }
293
+
294
+ // Create file stream with autoResolve enabled
295
+ stream = new IframeFileWritableStream({
296
+ filename: fileName || 'file',
297
+ mimeType,
298
+ chunked: false,
299
+ // File is sent in one chunk
300
+ autoResolve: true,
301
+ // Client will automatically resolve to fileData
302
+ next: function () {
303
+ var _next = _asyncToGenerator(/*#__PURE__*/_regeneratorRuntime.mark(function _callee2() {
304
+ return _regeneratorRuntime.wrap(function (_context2) {
305
+ while (1) switch (_context2.prev = _context2.next) {
306
+ case 0:
307
+ return _context2.abrupt("return", {
308
+ data: fileContent,
309
+ done: true
310
+ });
311
+ case 1:
312
+ case "end":
313
+ return _context2.stop();
314
+ }
315
+ }, _callee2);
316
+ }));
317
+ function next() {
318
+ return _next.apply(this, arguments);
319
+ }
320
+ return next;
321
+ }()
322
+ }); // Send stream (this will handle the requireAck logic internally and set _sent)
323
+ _context3.next = 7;
324
+ return _this2.sendStream(stream);
325
+ case 7:
326
+ return _context3.abrupt("return", true);
327
+ case 8:
328
+ case "end":
329
+ return _context3.stop();
330
+ }
331
+ }, _callee3);
332
+ }))();
333
+ }
334
+
335
+ /**
336
+ * Send stream response
337
+ * Bind stream to current request context and start stream transmission
338
+ */
339
+ sendStream(stream) {
340
+ var _this3 = this;
341
+ return _asyncToGenerator(/*#__PURE__*/_regeneratorRuntime.mark(function _callee4() {
342
+ return _regeneratorRuntime.wrap(function (_context4) {
343
+ while (1) switch (_context4.prev = _context4.next) {
344
+ case 0:
345
+ if (!_this3._sent) {
346
+ _context4.next = 1;
347
+ break;
348
+ }
349
+ return _context4.abrupt("return");
350
+ case 1:
351
+ _this3.markSent();
352
+
353
+ // Window check is handled in MessageDispatcher when stream sends messages
354
+
355
+ // Bind stream to request context
356
+ stream._bind({
357
+ requestId: _this3.requestId,
358
+ targetWindow: _this3.targetWindow,
359
+ targetOrigin: _this3.targetOrigin,
360
+ secretKey: _this3.secretKey,
361
+ channel: _this3.channel,
362
+ registerStreamHandler: _this3.registerStreamHandler,
363
+ unregisterStreamHandler: _this3.unregisterStreamHandler,
364
+ heartbeat: _this3.heartbeat,
365
+ serverId: _this3.serverId,
366
+ targetId: _this3.targetId
367
+ });
368
+
369
+ // Start stream transmission
370
+ _context4.next = 2;
371
+ return stream.start();
372
+ case 2:
373
+ case "end":
374
+ return _context4.stop();
375
+ }
376
+ }, _callee4);
377
+ }))();
378
+ }
379
+ status(code) {
380
+ this.statusCode = code;
381
+ return this;
382
+ }
383
+ setHeader(name, value) {
384
+ // Consistent with Express, returns void
385
+ // Special handling for Set-Cookie, keep as array
386
+ if (name.toLowerCase() === HttpHeader.SET_COOKIE.toLowerCase()) {
387
+ var existing = this.headers[HttpHeader.SET_COOKIE];
388
+ if (Array.isArray(value)) {
389
+ this.headers[HttpHeader.SET_COOKIE] = Array.isArray(existing) ? [...existing, ...value] : value;
390
+ } else {
391
+ var newValue = String(value);
392
+ if (Array.isArray(existing)) {
393
+ existing.push(newValue);
394
+ } else {
395
+ this.headers[HttpHeader.SET_COOKIE] = [newValue];
396
+ }
397
+ }
398
+ } else if (Array.isArray(value)) {
399
+ this.headers[name] = value.join(', ');
400
+ } else {
401
+ this.headers[name] = String(value);
402
+ }
403
+ }
404
+ set(name, value) {
405
+ // Chainable version, compatible with Express res.set
406
+ this.setHeader(name, value);
407
+ return this;
408
+ }
409
+ cookie(name, value, options) {
410
+ /**
411
+ * Set Cookie (similar to HTTP Set-Cookie)
412
+ * Generate Set-Cookie string and add to headers[HttpHeader.SET_COOKIE] array
413
+ * Client will parse and save to cookie storage upon receiving
414
+ */
415
+ var setCookieStr = createSetCookie(name, value, {
416
+ path: options === null || options === void 0 ? void 0 : options.path,
417
+ expires: options === null || options === void 0 ? void 0 : options.expires,
418
+ maxAge: options === null || options === void 0 ? void 0 : options.maxAge,
419
+ httpOnly: options === null || options === void 0 ? void 0 : options.httpOnly,
420
+ secure: options === null || options === void 0 ? void 0 : options.secure,
421
+ sameSite: (options === null || options === void 0 ? void 0 : options.sameSite) === true ? 'Strict' : (options === null || options === void 0 ? void 0 : options.sameSite) === false ? undefined : options === null || options === void 0 ? void 0 : options.sameSite
422
+ });
423
+
424
+ // Add to Set-Cookie header array
425
+ var existing = this.headers[HttpHeader.SET_COOKIE];
426
+ if (Array.isArray(existing)) {
427
+ existing.push(setCookieStr);
428
+ } else {
429
+ this.headers[HttpHeader.SET_COOKIE] = [setCookieStr];
430
+ }
431
+ return this;
432
+ }
433
+ clearCookie(name, options) {
434
+ /**
435
+ * Clear specified Cookie
436
+ * Generate an expired Set-Cookie string, client will delete this cookie upon receiving
437
+ */
438
+ var setCookieStr = createClearCookie(name, {
439
+ path: options === null || options === void 0 ? void 0 : options.path
440
+ });
441
+
442
+ // Add to Set-Cookie header array
443
+ var existing = this.headers[HttpHeader.SET_COOKIE];
444
+ if (Array.isArray(existing)) {
445
+ existing.push(setCookieStr);
446
+ } else {
447
+ this.headers[HttpHeader.SET_COOKIE] = [setCookieStr];
448
+ }
449
+ return this;
450
+ }
451
+ }