@tarojs/plugin-http 3.8.0-canary.0 → 4.0.0-alpha.2

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/LICENSE CHANGED
@@ -158,3 +158,17 @@ MIT (stencil-vue2-output-target):
158
158
  The following files embed [stencil-vue2-output-target](https://github.com/diondree/stencil-vue2-output-target) MIT:
159
159
  `/packages/taro-components-library-vue2/src/vue-component-lib/utils.ts`
160
160
  See `/LICENSE` for details of the license.
161
+
162
+ ==================
163
+
164
+ MIT (weui):
165
+ The following files embed [stencil-vue2-output-target](https://github.com/Tencent/weui) MIT:
166
+ `/packages/taro-components/src/components/*.scss`
167
+ See `/LICENSE.txt` for details of the license.
168
+
169
+ ==================
170
+
171
+ Apache-2.0 (intersection-observer):
172
+ The following files embed [intersection-observer](https://github.com/GoogleChromeLabs/intersection-observer) Apache-2.0:
173
+ `/packages/taro-api/src/polyfill/intersection-observer.ts`
174
+ See `/LICENSE.txt` for details of the license.
package/dist/runtime.d.ts CHANGED
@@ -58,7 +58,10 @@ declare class XMLHttpRequest extends Events {
58
58
  // 欺骗一些库让其认为是原生的xhr
59
59
  static toString(): string;
60
60
  toString(): string;
61
- // 事件
61
+ // 事件正常流转: loadstart => progress(可能多次) => load => loadend
62
+ // error 流转: loadstart => error => loadend
63
+ // abort 流转: loadstart => abort => loadend
64
+ // web在线测试: https://developer.mozilla.org/zh-CN/play
62
65
  /** 当 request 被停止时触发,例如当程序调用 XMLHttpRequest.abort() 时 */
63
66
  onabort: ((e: XMLHttpRequestEvent) => void) | null;
64
67
  /** 当 request 遭遇错误时触发 */
package/dist/runtime.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { parseUrl, Events, createEvent, window, document } from '@tarojs/runtime';
2
2
  export { document } from '@tarojs/runtime';
3
- import { isString, isFunction, isWebPlatform } from '@tarojs/shared';
4
3
  import { setStorage, getStorageSync, request } from '@tarojs/taro';
4
+ import { isString, isFunction } from '@tarojs/shared';
5
5
 
6
6
  /******************************************************************************
7
7
  Copyright (c) Microsoft Corporation.
@@ -17,7 +17,7 @@ LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
17
17
  OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
18
18
  PERFORMANCE OF THIS SOFTWARE.
19
19
  ***************************************************************************** */
20
- /* global Reflect, Promise */
20
+ /* global Reflect, Promise, SuppressedError, Symbol */
21
21
 
22
22
 
23
23
  function __classPrivateFieldGet(receiver, state, kind, f) {
@@ -31,7 +31,12 @@ function __classPrivateFieldSet(receiver, state, value, kind, f) {
31
31
  if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
32
32
  if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
33
33
  return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
34
- }
34
+ }
35
+
36
+ typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
37
+ var e = new Error(message);
38
+ return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
39
+ };
35
40
 
36
41
  var _Cookie_map;
37
42
  const STORAGE_KEY = 'PAGE_COOKIE';
@@ -406,7 +411,10 @@ class XMLHttpRequest extends Events {
406
411
  _XMLHttpRequest_timeout.set(this, void 0);
407
412
  _XMLHttpRequest_withCredentials.set(this, void 0);
408
413
  _XMLHttpRequest_requestTask.set(this, void 0);
409
- // 事件
414
+ // 事件正常流转: loadstart => progress(可能多次) => load => loadend
415
+ // error 流转: loadstart => error => loadend
416
+ // abort 流转: loadstart => abort => loadend
417
+ // web在线测试: https://developer.mozilla.org/zh-CN/play
410
418
  /** 当 request 被停止时触发,例如当程序调用 XMLHttpRequest.abort() 时 */
411
419
  this.onabort = null;
412
420
  /** 当 request 遭遇错误时触发 */
@@ -577,6 +585,7 @@ _XMLHttpRequest_method = new WeakMap(), _XMLHttpRequest_url = new WeakMap(), _XM
577
585
  // 头信息
578
586
  const header = Object.assign({}, __classPrivateFieldGet(this, _XMLHttpRequest_header, "f"));
579
587
  // https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Cookies
588
+ // @ts-ignore
580
589
  header.cookie = window.document.$$cookie;
581
590
  if (!this.withCredentials) {
582
591
  // 不同源,要求 withCredentials 为 true 才携带 cookie
@@ -606,7 +615,7 @@ _XMLHttpRequest_method = new WeakMap(), _XMLHttpRequest_url = new WeakMap(), _XM
606
615
  __classPrivateFieldGet(this, _XMLHttpRequest_instances, "m", _XMLHttpRequest_callReadyStateChange).call(this, XMLHttpRequest.HEADERS_RECEIVED);
607
616
  if (ENABLE_COOKIE) {
608
617
  // 处理 set-cookie
609
- const setCookieStr = header['Set-Cookie'];
618
+ const setCookieStr = this.getResponseHeader('set-cookie');
610
619
  if (setCookieStr && typeof setCookieStr === 'string') {
611
620
  let start = 0;
612
621
  let startSplit = 0;
@@ -634,17 +643,40 @@ _XMLHttpRequest_method = new WeakMap(), _XMLHttpRequest_url = new WeakMap(), _XM
634
643
  // 处理返回数据
635
644
  if (data) {
636
645
  __classPrivateFieldGet(this, _XMLHttpRequest_instances, "m", _XMLHttpRequest_callReadyStateChange).call(this, XMLHttpRequest.LOADING);
637
- const loadstartEvent = createXMLHttpRequestEvent('loadstart', this, header['Content-Length']);
646
+ const contentLength = Number(this.getResponseHeader('content-length') || 0);
647
+ const loadstartEvent = createXMLHttpRequestEvent('loadstart', this, contentLength);
638
648
  this.trigger('loadstart', loadstartEvent);
639
649
  isFunction(this.onloadstart) && this.onloadstart(loadstartEvent);
640
650
  __classPrivateFieldSet(this, _XMLHttpRequest_response, data, "f");
641
- const loadEvent = createXMLHttpRequestEvent('load', this, header['Content-Length']);
651
+ const loadEvent = createXMLHttpRequestEvent('load', this, contentLength);
642
652
  this.trigger('load', loadEvent);
643
653
  isFunction(this.onload) && this.onload(loadEvent);
644
654
  }
645
655
  }, _XMLHttpRequest_requestFail = function _XMLHttpRequest_requestFail(err) {
656
+ // 微信小程序,无论接口返回200还是其他,响应无论是否有错误,都会进入 success 回调;只有类似超时这种请求错误才会进入 fail 回调
657
+ //
658
+ /**
659
+ * 阿里系小程序,接口返回非200状态码,会进入 fail 回调, 此时 err 对象结构如下(当错误码为 14 或 19 时,会多返回 status、data、headers。可通过这些字段获取服务端相关错误信息):
660
+ {
661
+ data: "{\"code\": 401,\"msg\":\"登录过期,请重新登录\"}"
662
+ error: 19
663
+ errorMessage: "http status error"
664
+ headers: {date: 'Mon, 14 Aug 2023 08:54:58 GMT', content-type: 'application/json;charset=UTF-8', content-length: '52', connection: 'close', access-control-allow-credentials: 'true', …}
665
+ originalData: "{\"code\": 401,\"msg\":\"登录过期,请重新登录\"}"
666
+ status: 401
667
+ }
668
+ */
669
+ // 统一行为,能正常响应的,都算 success.
670
+ if (err.status) {
671
+ __classPrivateFieldGet(this, _XMLHttpRequest_instances, "m", _XMLHttpRequest_requestSuccess).call(this, {
672
+ data: err,
673
+ statusCode: err.status,
674
+ header: err.headers
675
+ });
676
+ return;
677
+ }
646
678
  __classPrivateFieldSet(this, _XMLHttpRequest_status, 0, "f");
647
- __classPrivateFieldSet(this, _XMLHttpRequest_statusText, err.errMsg, "f");
679
+ __classPrivateFieldSet(this, _XMLHttpRequest_statusText, err.errMsg || err.errorMessage, "f");
648
680
  const errorEvent = createXMLHttpRequestEvent('error', this, 0);
649
681
  this.trigger('error', errorEvent);
650
682
  isFunction(this.onerror) && this.onerror(errorEvent);
@@ -652,7 +684,8 @@ _XMLHttpRequest_method = new WeakMap(), _XMLHttpRequest_url = new WeakMap(), _XM
652
684
  __classPrivateFieldSet(this, _XMLHttpRequest_requestTask, null, "f");
653
685
  __classPrivateFieldGet(this, _XMLHttpRequest_instances, "m", _XMLHttpRequest_callReadyStateChange).call(this, XMLHttpRequest.DONE);
654
686
  if (__classPrivateFieldGet(this, _XMLHttpRequest_status, "f")) {
655
- const loadendEvent = createXMLHttpRequestEvent('loadend', this, __classPrivateFieldGet(this, _XMLHttpRequest_header, "f")['Content-Length']);
687
+ const contentLength = Number(this.getResponseHeader('content-length') || 0);
688
+ const loadendEvent = createXMLHttpRequestEvent('loadend', this, contentLength);
656
689
  this.trigger('loadend', loadendEvent);
657
690
  isFunction(this.onloadend) && this.onloadend(loadendEvent);
658
691
  }
@@ -663,7 +696,7 @@ XMLHttpRequest.HEADERS_RECEIVED = 2;
663
696
  XMLHttpRequest.LOADING = 3;
664
697
  XMLHttpRequest.DONE = 4;
665
698
 
666
- if (!isWebPlatform()) {
699
+ if (process.env.TARO_PLATFORM !== 'web') {
667
700
  if (ENABLE_COOKIE) {
668
701
  const _cookie = createCookieInstance();
669
702
  Object.defineProperties(document, {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tarojs/plugin-http",
3
- "version": "3.8.0-canary.0",
3
+ "version": "4.0.0-alpha.2",
4
4
  "description": "Taro 小程序端支持使用 web 请求 的插件",
5
5
  "main": "index.js",
6
6
  "files": [
@@ -23,12 +23,12 @@
23
23
  },
24
24
  "homepage": "https://github.com/NervJS/taro#readme",
25
25
  "dependencies": {
26
- "@tarojs/runtime": "3.8.0-canary.0",
27
- "@tarojs/service": "3.8.0-canary.0",
28
- "@tarojs/shared": "3.8.0-canary.0"
26
+ "@tarojs/shared": "4.0.0-alpha.2",
27
+ "@tarojs/service": "4.0.0-alpha.2",
28
+ "@tarojs/runtime": "4.0.0-alpha.2"
29
29
  },
30
30
  "devDependencies": {
31
- "@rollup/plugin-json": "^4.1.0",
31
+ "@rollup/plugin-json": "^6.1.0",
32
32
  "@rollup/plugin-node-resolve": "^15.0.1",
33
33
  "jest": "^29.3.1",
34
34
  "jest-cli": "^29.3.1",
@@ -37,13 +37,13 @@
37
37
  "rollup-plugin-node-externals": "^5.0.0",
38
38
  "rollup-plugin-ts": "^3.0.2",
39
39
  "ts-jest": "^29.0.5",
40
- "tslib": "^2.5.0",
40
+ "tslib": "^2.6.2",
41
41
  "typescript": "^4.7.4"
42
42
  },
43
43
  "scripts": {
44
44
  "dev": "rollup -c -w --bundleConfigAsCjs",
45
45
  "build": "rollup -c --bundleConfigAsCjs",
46
46
  "test": "jest",
47
- "test:ci": "jest --ci -i --coverage false"
47
+ "test:ci": "jest --ci -i --coverage --silent"
48
48
  }
49
49
  }
@@ -3,7 +3,7 @@ describe('DOM', () => {
3
3
  const runtime = require('../../dist/runtime')
4
4
  const document = runtime.document
5
5
  global.document = runtime.document
6
-
6
+
7
7
  describe('document', () => {
8
8
 
9
9
  it('document setCookie', async () => {
@@ -15,6 +15,5 @@ describe('DOM', () => {
15
15
  'aaa=1111-2222-33-444-abcdefgasd; bbb=23123-aswe-4a7a-a740-f55dfd296b1d; ccc=69asd3d81234668942'
16
16
  )
17
17
  })
18
-
19
18
  })
20
19
  })
@@ -0,0 +1,101 @@
1
+ import { window } from '@tarojs/runtime'
2
+ import { request, RequestTask } from '@tarojs/taro'
3
+
4
+ import { XMLHttpRequest, XMLHttpRequestEvent } from '../../dist/runtime'
5
+
6
+ jest.mock('@tarojs/taro', () => {
7
+ return {
8
+ request: jest.fn(),
9
+ }
10
+ })
11
+
12
+ const requestMock = jest.mocked(request)
13
+
14
+ describe('XMLHttpRequest', () => {
15
+ it('大写header key', async () => {
16
+ const data = 'success'
17
+ // 模拟实现 request 函数
18
+ requestMock.mockImplementation((opt) => {
19
+ if (opt.success) {
20
+ const header = { 'Content-Length': data.length.toString() }
21
+ opt.success({ data: data as any, header, statusCode: 200, errMsg: '' })
22
+ }
23
+ if (opt.complete) {
24
+ opt.complete({ errMsg: '' })
25
+ }
26
+ return {} as RequestTask<any>
27
+ })
28
+ // 发起请求
29
+ const xhr = new XMLHttpRequest()
30
+ xhr.open('GET', 'localhost')
31
+ const req = new Promise<XMLHttpRequestEvent>((resolve, reject) => {
32
+ xhr.addEventListener('load', resolve)
33
+ xhr.addEventListener('error', reject)
34
+ })
35
+ xhr.send('')
36
+ await req
37
+ // 检查是否能正常读取大写的header
38
+ expect(xhr.status).toBe(200)
39
+ expect(data.length).toBeGreaterThan(0)
40
+ expect(xhr.getResponseHeader('Content-length')).toBe(data.length.toString())
41
+ expect(xhr.getResponseHeader('content-length')).toBe(data.length.toString())
42
+ expect(xhr.getResponseHeader('Content-Length')).toBe(data.length.toString())
43
+ })
44
+ it('小写header key', async () => {
45
+ const data = 'success'
46
+ // 模拟实现 request 函数
47
+ requestMock.mockImplementation((opt) => {
48
+ if (opt.success) {
49
+ const header = { 'content-length': data.length.toString() }
50
+ opt.success({ data: data as any, header, statusCode: 200, errMsg: '' })
51
+ }
52
+ if (opt.complete) {
53
+ opt.complete({ errMsg: '' })
54
+ }
55
+ return {} as RequestTask<any>
56
+ })
57
+ const xhr = new XMLHttpRequest()
58
+ xhr.open('GET', 'localhost')
59
+ const req = new Promise<XMLHttpRequestEvent>((resolve, reject) => {
60
+ xhr.addEventListener('load', resolve)
61
+ xhr.addEventListener('error', reject)
62
+ })
63
+ xhr.send('')
64
+ await req
65
+ // 检查是否能成功读取小写的header
66
+ expect(xhr.status).toBe(200)
67
+ expect(xhr.responseText).toBe(data)
68
+ expect(xhr.getResponseHeader('Content-length')).toBe(data.length.toString())
69
+ expect(xhr.getResponseHeader('content-length')).toBe(data.length.toString())
70
+ expect(xhr.getResponseHeader('Content-Length')).toBe(data.length.toString())
71
+ })
72
+
73
+ it('set-cookie', async () => {
74
+ const data = 'success'
75
+ // 模拟实现 request 函数
76
+ requestMock.mockImplementation((opt) => {
77
+ if (opt.success) {
78
+ const header = { 'set-cookie': 'aaa=bbb; domain=taro.com' }
79
+ opt.success({ data: data as any, header, statusCode: 200, errMsg: '' })
80
+ }
81
+ if (opt.complete) {
82
+ opt.complete({ errMsg: '' })
83
+ }
84
+ return {} as RequestTask<any>
85
+ })
86
+ // 发送一个请求
87
+ const xhr = new XMLHttpRequest()
88
+ xhr.open('GET', 'localhost')
89
+ const req = new Promise<XMLHttpRequestEvent>((resolve, reject) => {
90
+ xhr.addEventListener('load', resolve)
91
+ xhr.addEventListener('error', reject)
92
+ })
93
+ // 在发送请求之前cookie为空
94
+ expect(window.document.cookie.length).toBe(0)
95
+ xhr.send('')
96
+ // 等待请求加载完成
97
+ await req
98
+ // 检查是否能读取set-cookie
99
+ expect(window.document.cookie.length).not.toBe(0)
100
+ })
101
+ })
@@ -83,7 +83,7 @@ function createXMLHttpRequestEvent (event: string, target:XMLHttpRequest, loaded
83
83
  })
84
84
  } catch (err) {
85
85
  // no handler
86
- }
86
+ }
87
87
  return e
88
88
  }
89
89
 
@@ -118,7 +118,10 @@ export class XMLHttpRequest extends Events {
118
118
  #withCredentials: boolean
119
119
  #requestTask: null | Taro.RequestTask<any>
120
120
 
121
- // 事件
121
+ // 事件正常流转: loadstart => progress(可能多次) => load => loadend
122
+ // error 流转: loadstart => error => loadend
123
+ // abort 流转: loadstart => abort => loadend
124
+ // web在线测试: https://developer.mozilla.org/zh-CN/play
122
125
 
123
126
  /** 当 request 被停止时触发,例如当程序调用 XMLHttpRequest.abort() 时 */
124
127
  onabort: ((e: XMLHttpRequestEvent) => void) | null = null
@@ -223,6 +226,7 @@ export class XMLHttpRequest extends Events {
223
226
  // 头信息
224
227
  const header = Object.assign({}, this.#header)
225
228
  // https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Cookies
229
+ // @ts-ignore
226
230
  header.cookie = window.document.$$cookie
227
231
  if (!this.withCredentials) {
228
232
  // 不同源,要求 withCredentials 为 true 才携带 cookie
@@ -259,7 +263,7 @@ export class XMLHttpRequest extends Events {
259
263
 
260
264
  if (ENABLE_COOKIE) {
261
265
  // 处理 set-cookie
262
- const setCookieStr = header['Set-Cookie']
266
+ const setCookieStr = this.getResponseHeader('set-cookie')
263
267
 
264
268
  if (setCookieStr && typeof setCookieStr === 'string') {
265
269
  let start = 0
@@ -294,12 +298,13 @@ export class XMLHttpRequest extends Events {
294
298
  // 处理返回数据
295
299
  if (data) {
296
300
  this.#callReadyStateChange(XMLHttpRequest.LOADING)
297
- const loadstartEvent = createXMLHttpRequestEvent('loadstart', this, header['Content-Length'])
301
+ const contentLength = Number(this.getResponseHeader('content-length') || 0)
302
+ const loadstartEvent = createXMLHttpRequestEvent('loadstart', this, contentLength)
298
303
  this.trigger('loadstart', loadstartEvent)
299
304
  isFunction(this.onloadstart) && this.onloadstart(loadstartEvent)
300
305
  this.#response = data
301
306
 
302
- const loadEvent = createXMLHttpRequestEvent('load', this, header['Content-Length'])
307
+ const loadEvent = createXMLHttpRequestEvent('load', this, contentLength)
303
308
  this.trigger('load', loadEvent)
304
309
  isFunction(this.onload) && this.onload(loadEvent)
305
310
  }
@@ -309,8 +314,30 @@ export class XMLHttpRequest extends Events {
309
314
  * 请求失败
310
315
  */
311
316
  #requestFail (err) {
317
+ // 微信小程序,无论接口返回200还是其他,响应无论是否有错误,都会进入 success 回调;只有类似超时这种请求错误才会进入 fail 回调
318
+ //
319
+ /**
320
+ * 阿里系小程序,接口返回非200状态码,会进入 fail 回调, 此时 err 对象结构如下(当错误码为 14 或 19 时,会多返回 status、data、headers。可通过这些字段获取服务端相关错误信息):
321
+ {
322
+ data: "{\"code\": 401,\"msg\":\"登录过期,请重新登录\"}"
323
+ error: 19
324
+ errorMessage: "http status error"
325
+ headers: {date: 'Mon, 14 Aug 2023 08:54:58 GMT', content-type: 'application/json;charset=UTF-8', content-length: '52', connection: 'close', access-control-allow-credentials: 'true', …}
326
+ originalData: "{\"code\": 401,\"msg\":\"登录过期,请重新登录\"}"
327
+ status: 401
328
+ }
329
+ */
330
+ // 统一行为,能正常响应的,都算 success.
331
+ if (err.status) {
332
+ this.#requestSuccess({
333
+ data: err,
334
+ statusCode: err.status,
335
+ header: err.headers
336
+ })
337
+ return
338
+ }
312
339
  this.#status = 0
313
- this.#statusText = err.errMsg
340
+ this.#statusText = err.errMsg || err.errorMessage
314
341
  const errorEvent = createXMLHttpRequestEvent('error', this, 0)
315
342
  this.trigger('error', errorEvent)
316
343
  isFunction(this.onerror) && this.onerror(errorEvent)
@@ -324,7 +351,8 @@ export class XMLHttpRequest extends Events {
324
351
  this.#callReadyStateChange(XMLHttpRequest.DONE)
325
352
 
326
353
  if (this.#status) {
327
- const loadendEvent = createXMLHttpRequestEvent('loadend', this, this.#header['Content-Length'])
354
+ const contentLength = Number(this.getResponseHeader('content-length') || 0)
355
+ const loadendEvent = createXMLHttpRequestEvent('loadend', this, contentLength)
328
356
  this.trigger('loadend', loadendEvent)
329
357
  isFunction(this.onloadend) && this.onloadend(loadendEvent)
330
358
  }
@@ -1,15 +1,14 @@
1
1
  import { document, window } from '@tarojs/runtime'
2
- import { isWebPlatform } from '@tarojs/shared'
3
2
 
4
3
  import { Cookie, createCookieInstance } from './Cookie'
5
4
  import { type XMLHttpRequestEvent, XMLHttpRequest } from './XMLHttpRequest'
6
5
 
7
6
  declare const ENABLE_COOKIE: boolean
8
7
 
9
- if (!isWebPlatform()) {
8
+ if (process.env.TARO_PLATFORM !== 'web') {
10
9
  if (ENABLE_COOKIE) {
11
10
  const _cookie = createCookieInstance()
12
-
11
+
13
12
  Object.defineProperties(document, {
14
13
  URL: {
15
14
  get () {