network-speed-js 1.0.1 → 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,42 +1,40 @@
1
- var m = Object.defineProperty;
2
- var h = (t, e, r) => e in t ? m(t, e, { enumerable: !0, configurable: !0, writable: !0, value: r }) : t[e] = r;
3
- var a = (t, e, r) => h(t, typeof e != "symbol" ? e + "" : e, r);
4
- function d(t) {
1
+ function l(t) {
5
2
  const e = t.responseEnd - t.responseStart;
6
3
  if (e <= 0 || t.transferSize === 0)
7
4
  return null;
8
- const r = t.transferSize * 8 / e / 1e3, n = t.transferSize / e;
5
+ const s = t.transferSize * 8 / e / 1e3, n = t.transferSize / e;
9
6
  return {
10
7
  name: t.name,
11
- speedMbps: Number(r.toFixed(2)),
8
+ speedMbps: Number(s.toFixed(2)),
12
9
  speedKBps: Number(n.toFixed(2)),
13
10
  downloadTime: Number(e.toFixed(2)),
14
11
  transferSize: t.transferSize
15
12
  };
16
13
  }
17
- function b() {
14
+ function p() {
18
15
  return performance.getEntriesByType("resource").filter(
19
16
  (e) => e instanceof PerformanceResourceTiming && e.transferSize > 0
20
- ).map(d).filter((e) => e !== null);
17
+ ).map(l).filter((e) => e !== null);
21
18
  }
22
- function S(t) {
19
+ function h(t) {
23
20
  performance.getEntriesByName(t).forEach(() => {
24
21
  performance.clearResourceTimings();
25
22
  });
26
23
  }
27
- function w(t, e = { fast: 10, medium: 2 }) {
24
+ function m(t, e = { fast: 10, medium: 2 }) {
28
25
  return t >= e.fast ? "fast" : t >= e.medium ? "medium" : t > 0 ? "slow" : "unknown";
29
26
  }
30
- class y {
27
+ class T {
28
+ options;
29
+ observer = null;
31
30
  constructor(e = {}) {
32
- a(this, "options");
33
- a(this, "observer", null);
34
31
  this.options = {
35
32
  intranetUrl: e.intranetUrl || "",
36
33
  internetUrl: e.internetUrl || "",
37
34
  timeout: e.timeout || 1e4,
38
35
  autoDetect: e.autoDetect ?? !0,
39
- thresholds: e.thresholds || { fast: 10, medium: 2 }
36
+ thresholds: e.thresholds || { fast: 10, medium: 2 },
37
+ resourceType: e.resourceType || "image"
40
38
  };
41
39
  }
42
40
  /**
@@ -60,57 +58,74 @@ class y {
60
58
  /**
61
59
  * 测试单个URL
62
60
  */
63
- testSingleUrl(e, r) {
64
- return new Promise((n, c) => {
65
- const o = `${e}?t=${Date.now()}`;
66
- S(o);
67
- const u = new PerformanceObserver((s) => {
68
- for (const l of s.getEntries())
69
- if (l.entryType === "resource" && l.name.includes(e)) {
70
- const i = d(l);
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);
71
69
  if (i) {
72
- const p = {
70
+ const f = {
73
71
  speedMbps: i.speedMbps,
74
72
  speedKBps: i.speedKBps,
75
- networkType: w(
73
+ networkType: m(
76
74
  i.speedMbps,
77
75
  this.options.thresholds
78
76
  ),
79
- isIntranet: r,
77
+ isIntranet: s,
80
78
  duration: i.downloadTime,
81
79
  transferSize: i.transferSize,
82
80
  resourceUrl: e
83
81
  };
84
- u.disconnect(), n(p);
82
+ c.disconnect(), n(f);
85
83
  }
86
84
  }
87
85
  });
88
- u.observe({ entryTypes: ["resource"] });
89
- const f = setTimeout(() => {
90
- u.disconnect(), c(new Error(`测速超时: ${e}`));
86
+ c.observe({ entryTypes: ["resource"] });
87
+ const a = setTimeout(() => {
88
+ c.disconnect(), o(new Error(`测速超时: ${e}`));
91
89
  }, this.options.timeout);
92
- fetch(o, {
93
- method: "GET",
94
- cache: "no-store"
95
- // 禁用缓存
96
- }).then((s) => {
97
- if (!s.ok)
98
- throw new Error(`HTTP error! status: ${s.status}`);
99
- return s.blob();
100
- }).then(() => {
101
- clearTimeout(f);
102
- }).catch((s) => {
103
- clearTimeout(f), u.disconnect(), c(new Error(`资源加载失败: ${s.message}`));
104
- });
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}`));
105
120
  });
106
121
  }
107
122
  /**
108
123
  * 监听特定资源的性能数据
109
124
  */
110
- observeResource(e, r) {
111
- const n = new PerformanceObserver((c) => {
112
- for (const o of c.getEntries())
113
- 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);
114
129
  });
115
130
  return n.observe({ entryTypes: ["resource"] }), () => n.disconnect();
116
131
  }
@@ -127,10 +142,10 @@ class y {
127
142
  this.observer && (this.observer.disconnect(), this.observer = null);
128
143
  }
129
144
  }
130
- class T {
145
+ class y {
146
+ tester;
131
147
  constructor(e = {}) {
132
- a(this, "tester");
133
- this.tester = new y(e);
148
+ this.tester = new T(e);
134
149
  }
135
150
  /**
136
151
  * 执行网速测试
@@ -142,13 +157,13 @@ class T {
142
157
  * 获取所有已加载资源的速度信息
143
158
  */
144
159
  getAllResourcesSpeeds() {
145
- return b();
160
+ return p();
146
161
  }
147
162
  /**
148
163
  * 监听特定资源的性能数据
149
164
  */
150
- observeResource(e, r) {
151
- return this.tester.observeResource(e, r);
165
+ observeResource(e, s) {
166
+ return this.tester.observeResource(e, s);
152
167
  }
153
168
  /**
154
169
  * 更新配置
@@ -163,13 +178,13 @@ class T {
163
178
  this.tester.destroy();
164
179
  }
165
180
  }
166
- function U(t) {
167
- return new T(t);
181
+ function b(t) {
182
+ return new y(t);
168
183
  }
169
184
  export {
170
- T as NetworkSpeedSDK,
171
- d as calcSpeedByResource,
172
- U as createNetworkSpeedSDK,
173
- w as evaluateNetworkType,
174
- b as getAllResourcesSpeeds
185
+ y as NetworkSpeedSDK,
186
+ l as calcSpeedByResource,
187
+ b as createNetworkSpeedSDK,
188
+ m as evaluateNetworkType,
189
+ p as getAllResourcesSpeeds
175
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";var g=Object.defineProperty;var v=(r,s,n)=>s in r?g(r,s,{enumerable:!0,configurable:!0,writable:!0,value:n}):r[s]=n;var f=(r,s,n)=>v(r,typeof s!="symbol"?s+"":s,n);function s(t){const e=t.responseEnd-t.responseStart;if(e<=0||t.transferSize===0)return null;const o=t.transferSize*8/e/1e3,c=t.transferSize/e;return{name:t.name,speedMbps:Number(o.toFixed(2)),speedKBps:Number(c.toFixed(2)),downloadTime:Number(e.toFixed(2)),transferSize:t.transferSize}}function n(){return performance.getEntriesByType("resource").filter(e=>e instanceof PerformanceResourceTiming&&e.transferSize>0).map(s).filter(e=>e!==null)}function y(t){performance.getEntriesByName(t).forEach(()=>{performance.clearResourceTimings()})}function m(t,e={fast:10,medium:2}){return t>=e.fast?"fast":t>=e.medium?"medium":t>0?"slow":"unknown"}class b{constructor(e={}){f(this,"options");f(this,"observer",null);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,o){return new Promise((c,l)=>{const u=`${e}?t=${Date.now()}`;y(u);const d=new PerformanceObserver(i=>{for(const p of i.getEntries())if(p.entryType==="resource"&&p.name.includes(e)){const a=s(p);if(a){const T={speedMbps:a.speedMbps,speedKBps:a.speedKBps,networkType:m(a.speedMbps,this.options.thresholds),isIntranet:o,duration:a.downloadTime,transferSize:a.transferSize,resourceUrl:e};d.disconnect(),c(T)}}});d.observe({entryTypes:["resource"]});const S=setTimeout(()=>{d.disconnect(),l(new Error(`测速超时: ${e}`))},this.options.timeout);fetch(u,{method:"GET",cache:"no-store"}).then(i=>{if(!i.ok)throw new Error(`HTTP error! status: ${i.status}`);return i.blob()}).then(()=>{clearTimeout(S)}).catch(i=>{clearTimeout(S),d.disconnect(),l(new Error(`资源加载失败: ${i.message}`))})})}observeResource(e,o){const c=new PerformanceObserver(l=>{for(const u of l.getEntries())u.entryType==="resource"&&u.name.includes(e)&&o(u)});return c.observe({entryTypes:["resource"]}),()=>c.disconnect()}updateOptions(e){this.options={...this.options,...e}}destroy(){this.observer&&(this.observer.disconnect(),this.observer=null)}}class h{constructor(e={}){f(this,"tester");this.tester=new b(e)}async test(){return this.tester.test()}getAllResourcesSpeeds(){return n()}observeResource(e,o){return this.tester.observeResource(e,o)}updateOptions(e){this.tester.updateOptions(e)}destroy(){this.tester.destroy()}}function w(t){return new h(t)}r.NetworkSpeedSDK=h,r.calcSpeedByResource=s,r.createNetworkSpeedSDK=w,r.evaluateNetworkType=m,r.getAllResourcesSpeeds=n,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.1",
5
+ "version": "1.0.3",
6
6
  "author": "Sunny-117",
7
7
  "license": "MIT",
8
8
  "keywords": [
@@ -28,9 +28,9 @@
28
28
  ],
29
29
  "exports": {
30
30
  ".": {
31
+ "types": "./dist/index.d.ts",
31
32
  "import": "./dist/network-speed-js.js",
32
- "require": "./dist/network-speed-js.umd.js",
33
- "types": "./dist/index.d.ts"
33
+ "require": "./dist/network-speed-js.umd.js"
34
34
  }
35
35
  },
36
36
  "scripts": {
@@ -51,10 +51,10 @@
51
51
  },
52
52
  "devDependencies": {
53
53
  "@types/node": "^25.0.10",
54
- "@vitejs/plugin-vue": "^4.5.2",
55
- "typescript": "^5.2.2",
56
- "vite": "^5.0.8",
57
- "vue-tsc": "^1.8.25"
54
+ "@vitejs/plugin-vue": "^6.0.3",
55
+ "typescript": "^5.9.3",
56
+ "vite": "^7.3.1",
57
+ "vue-tsc": "^3.2.3"
58
58
  },
59
59
  "repository": {
60
60
  "type": "git",