network-speed-js 1.0.2 → 1.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.md CHANGED
@@ -172,6 +172,20 @@ new NetworkSpeedSDK(options?: SpeedTestOptions)
172
172
  | `autoDetect` | `boolean` | `true` | 是否自动检测内外网 |
173
173
  | `timeout` | `number` | `10000` | 超时时间 (ms) |
174
174
  | `thresholds` | `object` | `{fast: 10, medium: 2}` | 网速评估阈值 (Mbps) |
175
+ | `resourceType` | `'image' \| 'fetch'` | `'image'` | 资源类型(见下方说明) |
176
+
177
+ **resourceType 说明:**
178
+
179
+ - `'image'`(默认):使用 Image 对象加载
180
+ - ✅ 不受跨域限制
181
+ - ✅ 适用于图片资源(.jpg、.png、.webp 等)
182
+ - ✅ 无需服务器配置 CORS
183
+ - ⚠️ 仅支持图片格式
184
+
185
+ - `'fetch'`:使用 fetch API 加载
186
+ - ✅ 支持任意类型资源(.bin、.json、.txt 等)
187
+ - ⚠️ 需要服务器配置 CORS
188
+ - ⚠️ 受跨域限制
175
189
 
176
190
  #### 方法
177
191
 
@@ -265,12 +279,34 @@ interface ResourceSpeedInfo {
265
279
 
266
280
  ## 💡 使用示例
267
281
 
268
- ### 1. 内外网自动检测
282
+ ### 1. 使用图片资源测速(默认,推荐)
283
+
284
+ ```typescript
285
+ const sdk = new NetworkSpeedSDK({
286
+ internetUrl: 'https://cdn.example.com/test-image.jpg',
287
+ // resourceType: 'image', // 默认值,可省略
288
+ });
289
+
290
+ const result = await sdk.test();
291
+ ```
292
+
293
+ ### 2. 使用非图片资源测速(需要 CORS)
294
+
295
+ ```typescript
296
+ const sdk = new NetworkSpeedSDK({
297
+ internetUrl: 'https://cdn.example.com/test-file.bin',
298
+ resourceType: 'fetch', // 使用 fetch API
299
+ });
300
+
301
+ const result = await sdk.test();
302
+ ```
303
+
304
+ ### 3. 内外网自动检测
269
305
 
270
306
  ```typescript
271
307
  const sdk = new NetworkSpeedSDK({
272
- intranetUrl: 'https://internal-cdn.company.com/test.bin',
273
- internetUrl: 'https://public-cdn.example.com/test.bin',
308
+ intranetUrl: 'https://internal-cdn.company.com/test.jpg',
309
+ internetUrl: 'https://public-cdn.example.com/test.jpg',
274
310
  autoDetect: true,
275
311
  });
276
312
 
@@ -278,7 +314,7 @@ const result = await sdk.test();
278
314
  console.log(result.isIntranet ? '内网环境' : '外网环境');
279
315
  ```
280
316
 
281
- ### 2. 首屏加载质量评估
317
+ ### 4. 首屏加载质量评估
282
318
 
283
319
  ```typescript
284
320
  const result = await sdk.test();
@@ -489,16 +525,18 @@ dd if=/dev/urandom of=speed-test.bin bs=1024 count=500
489
525
 
490
526
  **测速文件建议:**
491
527
  - 文件大小:200KB ~ 1MB
492
- - 文件类型:任意(.bin、.jpg、.png、.json、.txt 等)
528
+ - 文件类型:
529
+ - 图片格式(推荐):.jpg、.png、.webp(使用默认 `resourceType: 'image'`)
530
+ - 其他格式:.bin、.json、.txt(需设置 `resourceType: 'fetch'` 并配置 CORS)
493
531
  - 禁用缓存
494
- - 启用 CORS
532
+ - 启用 CORS(仅 fetch 模式需要)
495
533
  - 使用 CDN 分发
496
534
 
497
535
  **支持的资源类型:**
498
- - ✅ 二进制文件(.bin)
499
- - ✅ 图片文件(.jpg、.png、.webp)
500
- - ✅ 文本文件(.txt、.json
501
- - ✅ 任何可通过 HTTP 访问的资源
536
+ - ✅ 图片文件(.jpg、.png、.webp)- 默认模式,无需 CORS
537
+ - ✅ 二进制文件(.bin)- 需要 fetch 模式和 CORS
538
+ - ✅ 文本文件(.txt、.json)- 需要 fetch 模式和 CORS
539
+ - ✅ 任何可通过 HTTP 访问的资源 - 需要 fetch 模式和 CORS
502
540
 
503
541
  ### 配置项详解
504
542
 
@@ -18,6 +18,14 @@ export declare class SpeedTester {
18
18
  * 测试单个URL
19
19
  */
20
20
  private testSingleUrl;
21
+ /**
22
+ * 使用 Image 对象加载资源(默认方式,不受跨域限制)
23
+ */
24
+ private loadWithImage;
25
+ /**
26
+ * 使用 fetch API 加载资源(需要 CORS 支持)
27
+ */
28
+ private loadWithFetch;
21
29
  /**
22
30
  * 监听特定资源的性能数据
23
31
  */
@@ -1 +1 @@
1
- {"version":3,"file":"speed-tester.d.ts","sourceRoot":"","sources":["../../src/core/speed-tester.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,eAAe,EACf,gBAAgB,EAChB,wBAAwB,EACzB,MAAM,SAAS,CAAC;AAGjB;;GAEG;AACH,qBAAa,WAAW;IACtB,OAAO,CAAC,OAAO,CAA6B;IAC5C,OAAO,CAAC,QAAQ,CAAoC;gBAExC,OAAO,GAAE,gBAAqB;IAU1C;;OAEG;IACG,IAAI,IAAI,OAAO,CAAC,eAAe,CAAC;IAQtC;;OAEG;YACW,kBAAkB;IAehC;;OAEG;IACH,OAAO,CAAC,aAAa;IAuErB;;OAEG;IACH,eAAe,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,wBAAwB,GAAG,MAAM,IAAI;IAkBnF;;OAEG;IACH,aAAa,CAAC,OAAO,EAAE,OAAO,CAAC,gBAAgB,CAAC,GAAG,IAAI;IAIvD;;OAEG;IACH,OAAO,IAAI,IAAI;CAMhB"}
1
+ {"version":3,"file":"speed-tester.d.ts","sourceRoot":"","sources":["../../src/core/speed-tester.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,eAAe,EACf,gBAAgB,EAChB,wBAAwB,EACzB,MAAM,SAAS,CAAC;AAGjB;;GAEG;AACH,qBAAa,WAAW;IACtB,OAAO,CAAC,OAAO,CAA6B;IAC5C,OAAO,CAAC,QAAQ,CAAoC;gBAExC,OAAO,GAAE,gBAAqB;IAW1C;;OAEG;IACG,IAAI,IAAI,OAAO,CAAC,eAAe,CAAC;IAQtC;;OAEG;YACW,kBAAkB;IAehC;;OAEG;IACH,OAAO,CAAC,aAAa;IA0DrB;;OAEG;IACH,OAAO,CAAC,aAAa;IAqBrB;;OAEG;IACH,OAAO,CAAC,aAAa;IA4BrB;;OAEG;IACH,eAAe,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,wBAAwB,GAAG,MAAM,IAAI;IAkBnF;;OAEG;IACH,aAAa,CAAC,OAAO,EAAE,OAAO,CAAC,gBAAgB,CAAC,GAAG,IAAI;IAIvD;;OAEG;IACH,OAAO,IAAI,IAAI;CAMhB"}
@@ -32,6 +32,10 @@ export interface ResourceSpeedInfo {
32
32
  /** 传输大小 (bytes) */
33
33
  transferSize: number;
34
34
  }
35
+ /**
36
+ * 资源类型
37
+ */
38
+ export type ResourceType = 'image' | 'fetch';
35
39
  /**
36
40
  * SDK配置选项
37
41
  */
@@ -49,6 +53,12 @@ export interface SpeedTestOptions {
49
53
  fast: number;
50
54
  medium: number;
51
55
  };
56
+ /**
57
+ * 资源类型
58
+ * - 'image': 使用 Image 对象加载(默认,不受跨域限制,适用于图片资源)
59
+ * - 'fetch': 使用 fetch API 加载(需要服务器支持 CORS,适用于任意资源)
60
+ */
61
+ resourceType?: ResourceType;
52
62
  }
53
63
  /**
54
64
  * Performance Observer 回调参数
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/core/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,kBAAkB;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,kBAAkB;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa;IACb,WAAW,EAAE,MAAM,GAAG,QAAQ,GAAG,MAAM,GAAG,SAAS,CAAC;IACpD,YAAY;IACZ,UAAU,EAAE,OAAO,CAAC;IACpB,gBAAgB;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,mBAAmB;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,cAAc;IACd,WAAW,EAAE,MAAM,CAAC;CACrB;AAED;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,WAAW;IACX,IAAI,EAAE,MAAM,CAAC;IACb,kBAAkB;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,kBAAkB;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,gBAAgB;IAChB,YAAY,EAAE,MAAM,CAAC;IACrB,mBAAmB;IACnB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,gBAAgB;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,gBAAgB;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,gBAAgB;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,gBAAgB;IAChB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,oBAAoB;IACpB,UAAU,CAAC,EAAE;QACX,IAAI,EAAE,MAAM,CAAC;QACb,MAAM,EAAE,MAAM,CAAC;KAChB,CAAC;CACH;AAED;;GAEG;AACH,MAAM,WAAW,wBAAwB;IACvC,CAAC,KAAK,EAAE,yBAAyB,GAAG,IAAI,CAAC;CAC1C"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/core/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,kBAAkB;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,kBAAkB;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa;IACb,WAAW,EAAE,MAAM,GAAG,QAAQ,GAAG,MAAM,GAAG,SAAS,CAAC;IACpD,YAAY;IACZ,UAAU,EAAE,OAAO,CAAC;IACpB,gBAAgB;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,mBAAmB;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,cAAc;IACd,WAAW,EAAE,MAAM,CAAC;CACrB;AAED;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,WAAW;IACX,IAAI,EAAE,MAAM,CAAC;IACb,kBAAkB;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,kBAAkB;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,gBAAgB;IAChB,YAAY,EAAE,MAAM,CAAC;IACrB,mBAAmB;IACnB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED;;GAEG;AACH,MAAM,MAAM,YAAY,GAAG,OAAO,GAAG,OAAO,CAAC;AAE7C;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,gBAAgB;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,gBAAgB;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,gBAAgB;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,gBAAgB;IAChB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,oBAAoB;IACpB,UAAU,CAAC,EAAE;QACX,IAAI,EAAE,MAAM,CAAC;QACb,MAAM,EAAE,MAAM,CAAC;KAChB,CAAC;IACF;;;;OAIG;IACH,YAAY,CAAC,EAAE,YAAY,CAAC;CAC7B;AAED;;GAEG;AACH,MAAM,WAAW,wBAAwB;IACvC,CAAC,KAAK,EAAE,yBAAyB,GAAG,IAAI,CAAC;CAC1C"}
@@ -1,11 +1,11 @@
1
- function f(t) {
1
+ function l(t) {
2
2
  const e = t.responseEnd - t.responseStart;
3
3
  if (e <= 0 || t.transferSize === 0)
4
4
  return null;
5
- const r = t.transferSize * 8 / e / 1e3, n = t.transferSize / e;
5
+ const s = t.transferSize * 8 / e / 1e3, n = t.transferSize / e;
6
6
  return {
7
7
  name: t.name,
8
- speedMbps: Number(r.toFixed(2)),
8
+ speedMbps: Number(s.toFixed(2)),
9
9
  speedKBps: Number(n.toFixed(2)),
10
10
  downloadTime: Number(e.toFixed(2)),
11
11
  transferSize: t.transferSize
@@ -14,17 +14,17 @@ function f(t) {
14
14
  function p() {
15
15
  return performance.getEntriesByType("resource").filter(
16
16
  (e) => e instanceof PerformanceResourceTiming && e.transferSize > 0
17
- ).map(f).filter((e) => e !== null);
17
+ ).map(l).filter((e) => e !== null);
18
18
  }
19
- function m(t) {
19
+ function h(t) {
20
20
  performance.getEntriesByName(t).forEach(() => {
21
21
  performance.clearResourceTimings();
22
22
  });
23
23
  }
24
- function h(t, e = { fast: 10, medium: 2 }) {
24
+ function m(t, e = { fast: 10, medium: 2 }) {
25
25
  return t >= e.fast ? "fast" : t >= e.medium ? "medium" : t > 0 ? "slow" : "unknown";
26
26
  }
27
- class b {
27
+ class T {
28
28
  options;
29
29
  observer = null;
30
30
  constructor(e = {}) {
@@ -33,7 +33,8 @@ class b {
33
33
  internetUrl: e.internetUrl || "",
34
34
  timeout: e.timeout || 1e4,
35
35
  autoDetect: e.autoDetect ?? !0,
36
- thresholds: e.thresholds || { fast: 10, medium: 2 }
36
+ thresholds: e.thresholds || { fast: 10, medium: 2 },
37
+ resourceType: e.resourceType || "image"
37
38
  };
38
39
  }
39
40
  /**
@@ -57,57 +58,74 @@ class b {
57
58
  /**
58
59
  * 测试单个URL
59
60
  */
60
- testSingleUrl(e, r) {
61
- return new Promise((n, c) => {
62
- const o = `${e}?t=${Date.now()}`;
63
- m(o);
64
- const u = new PerformanceObserver((s) => {
65
- for (const a of s.getEntries())
66
- if (a.entryType === "resource" && a.name.includes(e)) {
67
- const i = f(a);
61
+ testSingleUrl(e, s) {
62
+ return new Promise((n, o) => {
63
+ const r = `${e}?t=${Date.now()}`;
64
+ h(r);
65
+ const c = new PerformanceObserver((d) => {
66
+ for (const u of d.getEntries())
67
+ if (u.entryType === "resource" && u.name.includes(e)) {
68
+ const i = l(u);
68
69
  if (i) {
69
- const d = {
70
+ const f = {
70
71
  speedMbps: i.speedMbps,
71
72
  speedKBps: i.speedKBps,
72
- networkType: h(
73
+ networkType: m(
73
74
  i.speedMbps,
74
75
  this.options.thresholds
75
76
  ),
76
- isIntranet: r,
77
+ isIntranet: s,
77
78
  duration: i.downloadTime,
78
79
  transferSize: i.transferSize,
79
80
  resourceUrl: e
80
81
  };
81
- u.disconnect(), n(d);
82
+ c.disconnect(), n(f);
82
83
  }
83
84
  }
84
85
  });
85
- u.observe({ entryTypes: ["resource"] });
86
- const l = setTimeout(() => {
87
- u.disconnect(), c(new Error(`测速超时: ${e}`));
86
+ c.observe({ entryTypes: ["resource"] });
87
+ const a = setTimeout(() => {
88
+ c.disconnect(), o(new Error(`测速超时: ${e}`));
88
89
  }, this.options.timeout);
89
- fetch(o, {
90
- method: "GET",
91
- cache: "no-store"
92
- // 禁用缓存
93
- }).then((s) => {
94
- if (!s.ok)
95
- throw new Error(`HTTP error! status: ${s.status}`);
96
- return s.blob();
97
- }).then(() => {
98
- clearTimeout(l);
99
- }).catch((s) => {
100
- clearTimeout(l), u.disconnect(), c(new Error(`资源加载失败: ${s.message}`));
101
- });
90
+ this.options.resourceType === "fetch" ? this.loadWithFetch(r, a, c, o) : this.loadWithImage(r, a, c, o);
91
+ });
92
+ }
93
+ /**
94
+ * 使用 Image 对象加载资源(默认方式,不受跨域限制)
95
+ */
96
+ loadWithImage(e, s, n, o) {
97
+ const r = new Image();
98
+ r.onload = () => {
99
+ clearTimeout(s);
100
+ }, r.onerror = () => {
101
+ clearTimeout(s), n.disconnect(), o(new Error(`资源加载失败: ${e}`));
102
+ }, r.src = e;
103
+ }
104
+ /**
105
+ * 使用 fetch API 加载资源(需要 CORS 支持)
106
+ */
107
+ loadWithFetch(e, s, n, o) {
108
+ fetch(e, {
109
+ method: "GET",
110
+ cache: "no-store"
111
+ // 禁用缓存
112
+ }).then((r) => {
113
+ if (!r.ok)
114
+ throw new Error(`HTTP error! status: ${r.status}`);
115
+ return r.blob();
116
+ }).then(() => {
117
+ clearTimeout(s);
118
+ }).catch((r) => {
119
+ clearTimeout(s), n.disconnect(), o(new Error(`资源加载失败: ${r.message}`));
102
120
  });
103
121
  }
104
122
  /**
105
123
  * 监听特定资源的性能数据
106
124
  */
107
- observeResource(e, r) {
108
- const n = new PerformanceObserver((c) => {
109
- for (const o of c.getEntries())
110
- o.entryType === "resource" && o.name.includes(e) && r(o);
125
+ observeResource(e, s) {
126
+ const n = new PerformanceObserver((o) => {
127
+ for (const r of o.getEntries())
128
+ r.entryType === "resource" && r.name.includes(e) && s(r);
111
129
  });
112
130
  return n.observe({ entryTypes: ["resource"] }), () => n.disconnect();
113
131
  }
@@ -124,10 +142,10 @@ class b {
124
142
  this.observer && (this.observer.disconnect(), this.observer = null);
125
143
  }
126
144
  }
127
- class S {
145
+ class y {
128
146
  tester;
129
147
  constructor(e = {}) {
130
- this.tester = new b(e);
148
+ this.tester = new T(e);
131
149
  }
132
150
  /**
133
151
  * 执行网速测试
@@ -144,8 +162,8 @@ class S {
144
162
  /**
145
163
  * 监听特定资源的性能数据
146
164
  */
147
- observeResource(e, r) {
148
- return this.tester.observeResource(e, r);
165
+ observeResource(e, s) {
166
+ return this.tester.observeResource(e, s);
149
167
  }
150
168
  /**
151
169
  * 更新配置
@@ -160,13 +178,13 @@ class S {
160
178
  this.tester.destroy();
161
179
  }
162
180
  }
163
- function y(t) {
164
- return new S(t);
181
+ function b(t) {
182
+ return new y(t);
165
183
  }
166
184
  export {
167
- S as NetworkSpeedSDK,
168
- f as calcSpeedByResource,
169
- y as createNetworkSpeedSDK,
170
- h as evaluateNetworkType,
185
+ y as NetworkSpeedSDK,
186
+ l as calcSpeedByResource,
187
+ b as createNetworkSpeedSDK,
188
+ m as evaluateNetworkType,
171
189
  p as getAllResourcesSpeeds
172
190
  };
@@ -1 +1 @@
1
- (function(r,s){typeof exports=="object"&&typeof module<"u"?s(exports):typeof define=="function"&&define.amd?define(["exports"],s):(r=typeof globalThis<"u"?globalThis:r||self,s(r.NetworkSpeedJS={}))})(this,(function(r){"use strict";function s(t){const e=t.responseEnd-t.responseStart;if(e<=0||t.transferSize===0)return null;const n=t.transferSize*8/e/1e3,i=t.transferSize/e;return{name:t.name,speedMbps:Number(n.toFixed(2)),speedKBps:Number(i.toFixed(2)),downloadTime:Number(e.toFixed(2)),transferSize:t.transferSize}}function f(){return performance.getEntriesByType("resource").filter(e=>e instanceof PerformanceResourceTiming&&e.transferSize>0).map(s).filter(e=>e!==null)}function S(t){performance.getEntriesByName(t).forEach(()=>{performance.clearResourceTimings()})}function p(t,e={fast:10,medium:2}){return t>=e.fast?"fast":t>=e.medium?"medium":t>0?"slow":"unknown"}class y{options;observer=null;constructor(e={}){this.options={intranetUrl:e.intranetUrl||"",internetUrl:e.internetUrl||"",timeout:e.timeout||1e4,autoDetect:e.autoDetect??!0,thresholds:e.thresholds||{fast:10,medium:2}}}async test(){return this.options.autoDetect?this.testWithAutoDetect():this.testSingleUrl(this.options.internetUrl,!1)}async testWithAutoDetect(){if(this.options.intranetUrl)try{return await this.testSingleUrl(this.options.intranetUrl,!0)}catch{console.log("内网测速失败,切换到外网测速")}return this.testSingleUrl(this.options.internetUrl,!1)}testSingleUrl(e,n){return new Promise((i,a)=>{const c=`${e}?t=${Date.now()}`;S(c);const l=new PerformanceObserver(o=>{for(const d of o.getEntries())if(d.entryType==="resource"&&d.name.includes(e)){const u=s(d);if(u){const w={speedMbps:u.speedMbps,speedKBps:u.speedKBps,networkType:p(u.speedMbps,this.options.thresholds),isIntranet:n,duration:u.downloadTime,transferSize:u.transferSize,resourceUrl:e};l.disconnect(),i(w)}}});l.observe({entryTypes:["resource"]});const h=setTimeout(()=>{l.disconnect(),a(new Error(`测速超时: ${e}`))},this.options.timeout);fetch(c,{method:"GET",cache:"no-store"}).then(o=>{if(!o.ok)throw new Error(`HTTP error! status: ${o.status}`);return o.blob()}).then(()=>{clearTimeout(h)}).catch(o=>{clearTimeout(h),l.disconnect(),a(new Error(`资源加载失败: ${o.message}`))})})}observeResource(e,n){const i=new PerformanceObserver(a=>{for(const c of a.getEntries())c.entryType==="resource"&&c.name.includes(e)&&n(c)});return i.observe({entryTypes:["resource"]}),()=>i.disconnect()}updateOptions(e){this.options={...this.options,...e}}destroy(){this.observer&&(this.observer.disconnect(),this.observer=null)}}class m{tester;constructor(e={}){this.tester=new y(e)}async test(){return this.tester.test()}getAllResourcesSpeeds(){return f()}observeResource(e,n){return this.tester.observeResource(e,n)}updateOptions(e){this.tester.updateOptions(e)}destroy(){this.tester.destroy()}}function b(t){return new m(t)}r.NetworkSpeedSDK=m,r.calcSpeedByResource=s,r.createNetworkSpeedSDK=b,r.evaluateNetworkType=p,r.getAllResourcesSpeeds=f,Object.defineProperty(r,Symbol.toStringTag,{value:"Module"})}));
1
+ (function(n,c){typeof exports=="object"&&typeof module<"u"?c(exports):typeof define=="function"&&define.amd?define(["exports"],c):(n=typeof globalThis<"u"?globalThis:n||self,c(n.NetworkSpeedJS={}))})(this,(function(n){"use strict";function c(t){const e=t.responseEnd-t.responseStart;if(e<=0||t.transferSize===0)return null;const s=t.transferSize*8/e/1e3,o=t.transferSize/e;return{name:t.name,speedMbps:Number(s.toFixed(2)),speedKBps:Number(o.toFixed(2)),downloadTime:Number(e.toFixed(2)),transferSize:t.transferSize}}function d(){return performance.getEntriesByType("resource").filter(e=>e instanceof PerformanceResourceTiming&&e.transferSize>0).map(c).filter(e=>e!==null)}function m(t){performance.getEntriesByName(t).forEach(()=>{performance.clearResourceTimings()})}function f(t,e={fast:10,medium:2}){return t>=e.fast?"fast":t>=e.medium?"medium":t>0?"slow":"unknown"}class S{options;observer=null;constructor(e={}){this.options={intranetUrl:e.intranetUrl||"",internetUrl:e.internetUrl||"",timeout:e.timeout||1e4,autoDetect:e.autoDetect??!0,thresholds:e.thresholds||{fast:10,medium:2},resourceType:e.resourceType||"image"}}async test(){return this.options.autoDetect?this.testWithAutoDetect():this.testSingleUrl(this.options.internetUrl,!1)}async testWithAutoDetect(){if(this.options.intranetUrl)try{return await this.testSingleUrl(this.options.intranetUrl,!0)}catch{console.log("内网测速失败,切换到外网测速")}return this.testSingleUrl(this.options.internetUrl,!1)}testSingleUrl(e,s){return new Promise((o,i)=>{const r=`${e}?t=${Date.now()}`;m(r);const a=new PerformanceObserver(T=>{for(const l of T.getEntries())if(l.entryType==="resource"&&l.name.includes(e)){const u=c(l);if(u){const w={speedMbps:u.speedMbps,speedKBps:u.speedKBps,networkType:f(u.speedMbps,this.options.thresholds),isIntranet:s,duration:u.downloadTime,transferSize:u.transferSize,resourceUrl:e};a.disconnect(),o(w)}}});a.observe({entryTypes:["resource"]});const h=setTimeout(()=>{a.disconnect(),i(new Error(`测速超时: ${e}`))},this.options.timeout);this.options.resourceType==="fetch"?this.loadWithFetch(r,h,a,i):this.loadWithImage(r,h,a,i)})}loadWithImage(e,s,o,i){const r=new Image;r.onload=()=>{clearTimeout(s)},r.onerror=()=>{clearTimeout(s),o.disconnect(),i(new Error(`资源加载失败: ${e}`))},r.src=e}loadWithFetch(e,s,o,i){fetch(e,{method:"GET",cache:"no-store"}).then(r=>{if(!r.ok)throw new Error(`HTTP error! status: ${r.status}`);return r.blob()}).then(()=>{clearTimeout(s)}).catch(r=>{clearTimeout(s),o.disconnect(),i(new Error(`资源加载失败: ${r.message}`))})}observeResource(e,s){const o=new PerformanceObserver(i=>{for(const r of i.getEntries())r.entryType==="resource"&&r.name.includes(e)&&s(r)});return o.observe({entryTypes:["resource"]}),()=>o.disconnect()}updateOptions(e){this.options={...this.options,...e}}destroy(){this.observer&&(this.observer.disconnect(),this.observer=null)}}class p{tester;constructor(e={}){this.tester=new S(e)}async test(){return this.tester.test()}getAllResourcesSpeeds(){return d()}observeResource(e,s){return this.tester.observeResource(e,s)}updateOptions(e){this.tester.updateOptions(e)}destroy(){this.tester.destroy()}}function y(t){return new p(t)}n.NetworkSpeedSDK=p,n.calcSpeedByResource=c,n.createNetworkSpeedSDK=y,n.evaluateNetworkType=f,n.getAllResourcesSpeeds=d,Object.defineProperty(n,Symbol.toStringTag,{value:"Module"})}));
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "network-speed-js",
3
3
  "private": false,
4
4
  "description": "A framework-agnostic network speed testing SDK based on Performance API with intranet/internet auto-detection support",
5
- "version": "1.0.2",
5
+ "version": "1.0.3",
6
6
  "author": "Sunny-117",
7
7
  "license": "MIT",
8
8
  "keywords": [