request-iframe 0.0.6 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (95) hide show
  1. package/README.CN.md +220 -21
  2. package/README.md +221 -24
  3. package/esm/api/client.js +80 -0
  4. package/esm/api/server.js +61 -0
  5. package/esm/constants/index.js +289 -0
  6. package/esm/constants/messages.js +157 -0
  7. package/esm/core/client-server.js +294 -0
  8. package/esm/core/client.js +873 -0
  9. package/esm/core/request.js +27 -0
  10. package/esm/core/response.js +459 -0
  11. package/esm/core/server.js +776 -0
  12. package/esm/index.js +21 -0
  13. package/esm/interceptors/index.js +122 -0
  14. package/esm/message/channel.js +182 -0
  15. package/esm/message/dispatcher.js +418 -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 +539 -0
  20. package/esm/stream/stream-core.js +204 -0
  21. package/esm/stream/types.js +1 -0
  22. package/esm/stream/writable-stream.js +836 -0
  23. package/esm/types/index.js +1 -0
  24. package/esm/utils/ack.js +36 -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 +180 -0
  30. package/esm/utils/origin.js +30 -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 +13 -5
  35. package/library/api/server.d.ts.map +1 -1
  36. package/library/api/server.js +6 -1
  37. package/library/constants/index.d.ts +59 -4
  38. package/library/constants/index.d.ts.map +1 -1
  39. package/library/constants/index.js +67 -9
  40. package/library/constants/messages.d.ts +8 -1
  41. package/library/constants/messages.d.ts.map +1 -1
  42. package/library/constants/messages.js +8 -1
  43. package/library/core/client-server.d.ts +7 -15
  44. package/library/core/client-server.d.ts.map +1 -1
  45. package/library/core/client-server.js +56 -44
  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 +55 -7
  52. package/library/core/server.d.ts +34 -3
  53. package/library/core/server.d.ts.map +1 -1
  54. package/library/core/server.js +191 -21
  55. package/library/message/channel.d.ts +6 -0
  56. package/library/message/channel.d.ts.map +1 -1
  57. package/library/message/channel.js +2 -1
  58. package/library/message/dispatcher.d.ts +32 -0
  59. package/library/message/dispatcher.d.ts.map +1 -1
  60. package/library/message/dispatcher.js +131 -1
  61. package/library/stream/file-stream.d.ts +4 -0
  62. package/library/stream/file-stream.d.ts.map +1 -1
  63. package/library/stream/file-stream.js +61 -33
  64. package/library/stream/index.d.ts.map +1 -1
  65. package/library/stream/index.js +2 -0
  66. package/library/stream/readable-stream.d.ts +30 -11
  67. package/library/stream/readable-stream.d.ts.map +1 -1
  68. package/library/stream/readable-stream.js +368 -73
  69. package/library/stream/stream-core.d.ts +65 -0
  70. package/library/stream/stream-core.d.ts.map +1 -0
  71. package/library/stream/stream-core.js +211 -0
  72. package/library/stream/types.d.ts +203 -3
  73. package/library/stream/types.d.ts.map +1 -1
  74. package/library/stream/writable-stream.d.ts +59 -13
  75. package/library/stream/writable-stream.d.ts.map +1 -1
  76. package/library/stream/writable-stream.js +647 -197
  77. package/library/types/index.d.ts +70 -4
  78. package/library/types/index.d.ts.map +1 -1
  79. package/library/utils/ack.d.ts +2 -0
  80. package/library/utils/ack.d.ts.map +1 -0
  81. package/library/utils/ack.js +44 -0
  82. package/library/utils/debug.js +1 -1
  83. package/library/utils/index.d.ts +1 -0
  84. package/library/utils/index.d.ts.map +1 -1
  85. package/library/utils/index.js +19 -2
  86. package/library/utils/origin.d.ts +14 -0
  87. package/library/utils/origin.d.ts.map +1 -0
  88. package/library/utils/origin.js +35 -0
  89. package/package.json +30 -7
  90. package/react/README.md +16 -0
  91. package/react/esm/index.js +284 -0
  92. package/react/library/index.d.ts +1 -1
  93. package/react/library/index.d.ts.map +1 -1
  94. package/react/library/index.js +3 -3
  95. package/react/package.json +24 -2
@@ -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,459 @@
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, generateRequestId } from '../utils';
13
+ import { MessageType, HttpStatus, HttpHeader, getStatusText, MessageRole, ErrorCode } from '../constants';
14
+ import { IframeFileWritableStream, isIframeWritableStream } from '../stream';
15
+ import { isAckMatch } from '../utils/ack';
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, ack) {
92
+ if (this.onAckCallback) {
93
+ this.onAckCallback(received, ack);
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, _ack;
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
+ /**
127
+ * When requireAck is enabled, attach a unique ack payload by default so ACK can be
128
+ * unambiguously associated with this send.
129
+ *
130
+ * NOTE: ack is an internal reserved field (not part of public API).
131
+ */
132
+ var expectedAck = (_ack = options === null || options === void 0 ? void 0 : options.ack) !== null && _ack !== void 0 ? _ack : requireAck ? {
133
+ id: generateRequestId()
134
+ } : undefined;
135
+ try {
136
+ // If acknowledgment not required, send directly and return true
137
+ if (!requireAck) {
138
+ this.sendMessage(createPostMessage(MessageType.RESPONSE, this.requestId, {
139
+ path: this.path,
140
+ secretKey: this.secretKey,
141
+ data,
142
+ status: this.statusCode,
143
+ statusText: getStatusText(this.statusCode),
144
+ headers: this.headers,
145
+ requireAck: false,
146
+ role: MessageRole.SERVER,
147
+ creatorId: this.serverId,
148
+ targetId: this.targetId
149
+ }));
150
+ return Promise.resolve(true);
151
+ }
152
+
153
+ // Acknowledgment required, wait for client response
154
+ return new Promise((resolve, reject) => {
155
+ try {
156
+ this._setOnAckCallback((received, receivedAckMeta) => {
157
+ if (!received) {
158
+ resolve(false);
159
+ return;
160
+ }
161
+ if (expectedAck !== undefined && !isAckMatch(expectedAck, receivedAckMeta)) {
162
+ resolve(false);
163
+ return;
164
+ }
165
+ resolve(true);
166
+ });
167
+ this.sendMessage(createPostMessage(MessageType.RESPONSE, this.requestId, {
168
+ path: this.path,
169
+ secretKey: this.secretKey,
170
+ data,
171
+ status: this.statusCode,
172
+ statusText: getStatusText(this.statusCode),
173
+ headers: this.headers,
174
+ requireAck: true,
175
+ ack: expectedAck,
176
+ role: MessageRole.SERVER,
177
+ creatorId: this.serverId,
178
+ targetId: this.targetId
179
+ }));
180
+ } catch (error) {
181
+ // If window is closed, reject immediately
182
+ if ((error === null || error === void 0 ? void 0 : error.code) === ErrorCode.TARGET_WINDOW_CLOSED) {
183
+ reject(error);
184
+ } else {
185
+ throw error;
186
+ }
187
+ }
188
+ });
189
+ } catch (error) {
190
+ // If window is closed, return rejected promise
191
+ if ((error === null || error === void 0 ? void 0 : error.code) === ErrorCode.TARGET_WINDOW_CLOSED) {
192
+ return Promise.reject(error);
193
+ }
194
+ throw error;
195
+ }
196
+ }
197
+
198
+ /**
199
+ * Universal send method - automatically detects data type and calls appropriate method
200
+ * - If data is IframeWritableStream, calls sendStream
201
+ * - If data is File/Blob, calls sendFile
202
+ * - Otherwise, sends as regular data with auto-detected Content-Type
203
+ */
204
+ send(data, options) {
205
+ var _this = this;
206
+ return _asyncToGenerator(/*#__PURE__*/_regeneratorRuntime.mark(function _callee() {
207
+ var fileOptions;
208
+ return _regeneratorRuntime.wrap(function (_context) {
209
+ while (1) switch (_context.prev = _context.next) {
210
+ case 0:
211
+ if (!_this._sent) {
212
+ _context.next = 1;
213
+ break;
214
+ }
215
+ return _context.abrupt("return", Promise.resolve(false));
216
+ case 1:
217
+ if (!isIframeWritableStream(data)) {
218
+ _context.next = 3;
219
+ break;
220
+ }
221
+ _context.next = 2;
222
+ return _this.sendStream(data);
223
+ case 2:
224
+ return _context.abrupt("return", true);
225
+ case 3:
226
+ if (!(typeof File !== 'undefined' && data instanceof File || typeof Blob !== 'undefined' && data instanceof Blob)) {
227
+ _context.next = 4;
228
+ break;
229
+ }
230
+ // Extract options for sendFile
231
+ fileOptions = _objectSpread({
232
+ requireAck: options === null || options === void 0 ? void 0 : options.requireAck
233
+ }, typeof File !== 'undefined' && data instanceof File ? {
234
+ mimeType: data.type,
235
+ fileName: data.name
236
+ } : {});
237
+ return _context.abrupt("return", _this.sendFile(data, fileOptions));
238
+ case 4:
239
+ // For other types, auto-detect and set Content-Type, then send
240
+ _this.ensureContentTypeIfNeeded(data);
241
+ return _context.abrupt("return", _this._sendRaw(data, options));
242
+ case 5:
243
+ case "end":
244
+ return _context.stop();
245
+ }
246
+ }, _callee);
247
+ }))();
248
+ }
249
+ json(data, options) {
250
+ return this.send(data, options);
251
+ }
252
+ sendFile(content, options) {
253
+ var _this2 = this;
254
+ return _asyncToGenerator(/*#__PURE__*/_regeneratorRuntime.mark(function _callee3() {
255
+ var mimeType, fileName, fileContent, stream;
256
+ return _regeneratorRuntime.wrap(function (_context3) {
257
+ while (1) switch (_context3.prev = _context3.next) {
258
+ case 0:
259
+ if (!_this2._sent) {
260
+ _context3.next = 1;
261
+ break;
262
+ }
263
+ return _context3.abrupt("return", false);
264
+ case 1:
265
+ mimeType = (options === null || options === void 0 ? void 0 : options.mimeType) || 'application/octet-stream';
266
+ fileName = options === null || options === void 0 ? void 0 : options.fileName;
267
+ if (!(typeof content === 'string')) {
268
+ _context3.next = 2;
269
+ break;
270
+ }
271
+ // If it's a plain string, convert to base64
272
+ fileContent = btoa(unescape(encodeURIComponent(content)));
273
+ _context3.next = 6;
274
+ break;
275
+ case 2:
276
+ if (!(content instanceof File)) {
277
+ _context3.next = 4;
278
+ break;
279
+ }
280
+ mimeType = content.type || mimeType;
281
+ fileName = fileName || content.name;
282
+ _context3.next = 3;
283
+ return blobToBase64(content);
284
+ case 3:
285
+ fileContent = _context3.sent;
286
+ _context3.next = 6;
287
+ break;
288
+ case 4:
289
+ _context3.next = 5;
290
+ return blobToBase64(content);
291
+ case 5:
292
+ fileContent = _context3.sent;
293
+ case 6:
294
+ // Set file-related headers
295
+ _this2.setHeader(HttpHeader.CONTENT_TYPE, mimeType);
296
+ if (fileName) {
297
+ _this2.setHeader(HttpHeader.CONTENT_DISPOSITION, `attachment; filename="${fileName}"`);
298
+ } else {
299
+ _this2.setHeader(HttpHeader.CONTENT_DISPOSITION, 'attachment');
300
+ }
301
+
302
+ // Create file stream with autoResolve enabled
303
+ stream = new IframeFileWritableStream({
304
+ filename: fileName || 'file',
305
+ mimeType,
306
+ chunked: false,
307
+ // File is sent in one chunk
308
+ autoResolve: true,
309
+ // Client will automatically resolve to fileData
310
+ next: function () {
311
+ var _next = _asyncToGenerator(/*#__PURE__*/_regeneratorRuntime.mark(function _callee2() {
312
+ return _regeneratorRuntime.wrap(function (_context2) {
313
+ while (1) switch (_context2.prev = _context2.next) {
314
+ case 0:
315
+ return _context2.abrupt("return", {
316
+ data: fileContent,
317
+ done: true
318
+ });
319
+ case 1:
320
+ case "end":
321
+ return _context2.stop();
322
+ }
323
+ }, _callee2);
324
+ }));
325
+ function next() {
326
+ return _next.apply(this, arguments);
327
+ }
328
+ return next;
329
+ }()
330
+ }); // Send stream (this will handle the requireAck logic internally and set _sent)
331
+ _context3.next = 7;
332
+ return _this2.sendStream(stream);
333
+ case 7:
334
+ return _context3.abrupt("return", true);
335
+ case 8:
336
+ case "end":
337
+ return _context3.stop();
338
+ }
339
+ }, _callee3);
340
+ }))();
341
+ }
342
+
343
+ /**
344
+ * Send stream response
345
+ * Bind stream to current request context and start stream transmission
346
+ */
347
+ sendStream(stream) {
348
+ var _this3 = this;
349
+ return _asyncToGenerator(/*#__PURE__*/_regeneratorRuntime.mark(function _callee4() {
350
+ return _regeneratorRuntime.wrap(function (_context4) {
351
+ while (1) switch (_context4.prev = _context4.next) {
352
+ case 0:
353
+ if (!_this3._sent) {
354
+ _context4.next = 1;
355
+ break;
356
+ }
357
+ return _context4.abrupt("return");
358
+ case 1:
359
+ _this3.markSent();
360
+
361
+ // Window check is handled in MessageDispatcher when stream sends messages
362
+
363
+ // Bind stream to request context
364
+ stream._bind({
365
+ requestId: _this3.requestId,
366
+ targetWindow: _this3.targetWindow,
367
+ targetOrigin: _this3.targetOrigin,
368
+ secretKey: _this3.secretKey,
369
+ channel: _this3.channel,
370
+ registerStreamHandler: _this3.registerStreamHandler,
371
+ unregisterStreamHandler: _this3.unregisterStreamHandler,
372
+ heartbeat: _this3.heartbeat,
373
+ serverId: _this3.serverId,
374
+ targetId: _this3.targetId
375
+ });
376
+
377
+ // Start stream transmission
378
+ _context4.next = 2;
379
+ return stream.start();
380
+ case 2:
381
+ case "end":
382
+ return _context4.stop();
383
+ }
384
+ }, _callee4);
385
+ }))();
386
+ }
387
+ status(code) {
388
+ this.statusCode = code;
389
+ return this;
390
+ }
391
+ setHeader(name, value) {
392
+ // Consistent with Express, returns void
393
+ // Special handling for Set-Cookie, keep as array
394
+ if (name.toLowerCase() === HttpHeader.SET_COOKIE.toLowerCase()) {
395
+ var existing = this.headers[HttpHeader.SET_COOKIE];
396
+ if (Array.isArray(value)) {
397
+ this.headers[HttpHeader.SET_COOKIE] = Array.isArray(existing) ? [...existing, ...value] : value;
398
+ } else {
399
+ var newValue = String(value);
400
+ if (Array.isArray(existing)) {
401
+ existing.push(newValue);
402
+ } else {
403
+ this.headers[HttpHeader.SET_COOKIE] = [newValue];
404
+ }
405
+ }
406
+ } else if (Array.isArray(value)) {
407
+ this.headers[name] = value.join(', ');
408
+ } else {
409
+ this.headers[name] = String(value);
410
+ }
411
+ }
412
+ set(name, value) {
413
+ // Chainable version, compatible with Express res.set
414
+ this.setHeader(name, value);
415
+ return this;
416
+ }
417
+ cookie(name, value, options) {
418
+ /**
419
+ * Set Cookie (similar to HTTP Set-Cookie)
420
+ * Generate Set-Cookie string and add to headers[HttpHeader.SET_COOKIE] array
421
+ * Client will parse and save to cookie storage upon receiving
422
+ */
423
+ var setCookieStr = createSetCookie(name, value, {
424
+ path: options === null || options === void 0 ? void 0 : options.path,
425
+ expires: options === null || options === void 0 ? void 0 : options.expires,
426
+ maxAge: options === null || options === void 0 ? void 0 : options.maxAge,
427
+ httpOnly: options === null || options === void 0 ? void 0 : options.httpOnly,
428
+ secure: options === null || options === void 0 ? void 0 : options.secure,
429
+ 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
430
+ });
431
+
432
+ // Add to Set-Cookie header array
433
+ var existing = this.headers[HttpHeader.SET_COOKIE];
434
+ if (Array.isArray(existing)) {
435
+ existing.push(setCookieStr);
436
+ } else {
437
+ this.headers[HttpHeader.SET_COOKIE] = [setCookieStr];
438
+ }
439
+ return this;
440
+ }
441
+ clearCookie(name, options) {
442
+ /**
443
+ * Clear specified Cookie
444
+ * Generate an expired Set-Cookie string, client will delete this cookie upon receiving
445
+ */
446
+ var setCookieStr = createClearCookie(name, {
447
+ path: options === null || options === void 0 ? void 0 : options.path
448
+ });
449
+
450
+ // Add to Set-Cookie header array
451
+ var existing = this.headers[HttpHeader.SET_COOKIE];
452
+ if (Array.isArray(existing)) {
453
+ existing.push(setCookieStr);
454
+ } else {
455
+ this.headers[HttpHeader.SET_COOKIE] = [setCookieStr];
456
+ }
457
+ return this;
458
+ }
459
+ }