network-speed-js 1.0.4 → 1.0.6

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
@@ -1,7 +1,7 @@
1
- # Network Speed SDK
2
-
3
1
  <div align="center">
4
2
 
3
+ # Network Speed SDK
4
+
5
5
  一个基于 **Performance API** 的现代化网速测试 SDK,支持内外网自动检测、资源监听和完整的 TS 类型支持。
6
6
 
7
7
  **框架无关 · 开箱即用 · 准确可靠**
@@ -13,8 +13,12 @@
13
13
 
14
14
  ---
15
15
 
16
+ <div align="center">
17
+
16
18
  ## 🎯 核心亮点
17
19
 
20
+ </div>
21
+
18
22
  ### 从 Axios 到 Performance API 的技术升级
19
23
 
20
24
  本项目从 **v0.x(Axios 拦截器方案)** 完全重构为 **v1.0(Performance API 方案)**,实现了测速准确度和功能的质的飞跃。
@@ -33,8 +37,12 @@
33
37
 
34
38
  ---
35
39
 
40
+ <div align="center">
41
+
36
42
  ## ✨ 特性
37
43
 
44
+ </div>
45
+
38
46
  - 🚀 **基于 Performance API** - 使用浏览器原生 API,准确测量真实下载速度
39
47
  - 🔄 **内外网自动检测** - 智能切换内网/外网测速资源
40
48
  - 📊 **完整的性能数据** - 提供速度、耗时、传输大小等详细信息
@@ -43,21 +51,49 @@
43
51
  - 🌐 **框架无关** - 可用于 Vue、React、Angular 或原生 JavaScript 项目
44
52
  - 📦 **轻量级** - 零依赖,体积小巧
45
53
 
54
+ <div align="center">
55
+
46
56
  ## 📦 安装
47
57
 
58
+ </div>
59
+
48
60
  ```bash
49
61
  npm install network-speed-js
50
62
  ```
51
63
 
64
+ <div align="center">
65
+
52
66
  ## 🚀 快速开始
53
67
 
68
+ </div>
69
+
54
70
  ### 原生 JavaScript / TypeScript
55
71
 
72
+ **方式一:使用图片资源测速(推荐,默认模式,无需CORS)**
73
+
56
74
  ```typescript
57
75
  import { NetworkSpeedSDK } from 'network-speed-js';
58
76
 
59
77
  const sdk = new NetworkSpeedSDK({
60
- internetUrl: 'https://cdn.example.com/test.bin',
78
+ internetImageUrl: 'https://cdn.example.com/test-image.jpg',
79
+ // 可选:内网图片URL
80
+ intranetImageUrl: 'https://internal-cdn.com/test-image.jpg',
81
+ });
82
+
83
+ const result = await sdk.test();
84
+ console.log(`网速: ${result.speedMbps} Mbps`);
85
+ ```
86
+
87
+ **方式二:使用任意资源测速(需要CORS支持)**
88
+
89
+ ```typescript
90
+ import { NetworkSpeedSDK } from 'network-speed-js';
91
+
92
+ const sdk = new NetworkSpeedSDK({
93
+ internetUrl: 'https://cdn.example.com/test-file.bin',
94
+ useFetch: true, // 启用fetch模式
95
+ // 可选:内网资源URL
96
+ intranetUrl: 'https://internal-cdn.com/test-file.bin',
61
97
  });
62
98
 
63
99
  const result = await sdk.test();
@@ -84,7 +120,7 @@ const result = ref(null);
84
120
  const testSpeed = async () => {
85
121
  loading.value = true;
86
122
  const sdk = new NetworkSpeedSDK({
87
- internetUrl: 'https://cdn.example.com/test.bin',
123
+ internetImageUrl: 'https://cdn.example.com/test-image.jpg',
88
124
  });
89
125
  result.value = await sdk.test();
90
126
  loading.value = false;
@@ -105,7 +141,7 @@ function SpeedTest() {
105
141
  const testSpeed = async () => {
106
142
  setLoading(true);
107
143
  const sdk = new NetworkSpeedSDK({
108
- internetUrl: 'https://cdn.example.com/test.bin',
144
+ internetImageUrl: 'https://cdn.example.com/test-image.jpg',
109
145
  });
110
146
  const data = await sdk.test();
111
147
  setResult(data);
@@ -145,7 +181,7 @@ export class SpeedTestComponent {
145
181
  async testSpeed() {
146
182
  this.loading = true;
147
183
  const sdk = new NetworkSpeedSDK({
148
- internetUrl: 'https://cdn.example.com/test.bin',
184
+ internetImageUrl: 'https://cdn.example.com/test-image.jpg',
149
185
  });
150
186
  this.result = await sdk.test();
151
187
  this.loading = false;
@@ -153,8 +189,12 @@ export class SpeedTestComponent {
153
189
  }
154
190
  ```
155
191
 
192
+ <div align="center">
193
+
156
194
  ## 📖 API 文档
157
195
 
196
+ </div>
197
+
158
198
  ### NetworkSpeedSDK
159
199
 
160
200
  #### 构造函数
@@ -163,29 +203,63 @@ export class SpeedTestComponent {
163
203
  new NetworkSpeedSDK(options?: SpeedTestOptions)
164
204
  ```
165
205
 
166
- **配置选项:**
206
+ **参数说明:**
207
+ - `options`(可选):配置选项
208
+ - 如果传入配置,可以执行测速
209
+ - 如果不传配置,只能使用工具函数(`getAllResourcesSpeeds`、`observeResource`)
167
210
 
168
- | 参数 | 类型 | 默认值 | 说明 |
169
- |------|------|--------|------|
170
- | `intranetUrl` | `string` | `''` | 内网测速资源URL |
171
- | `internetUrl` | `string` | `''` | 外网测速资源URL(必填) |
172
- | `autoDetect` | `boolean` | `true` | 是否自动检测内外网 |
173
- | `timeout` | `number` | `10000` | 超时时间 (ms) |
174
- | `thresholds` | `object` | `{fast: 10, medium: 2}` | 网速评估阈值 (Mbps) |
175
- | `resourceType` | `'image' \| 'fetch'` | `'image'` | 资源类型(见下方说明) |
211
+ **配置选项(两种模式):**
176
212
 
177
- **resourceType 说明:**
213
+ **模式一:图片模式(默认,推荐)**
178
214
 
179
- - `'image'`(默认):使用 Image 对象加载
180
- - 不受跨域限制
181
- -适用于图片资源(.jpg、.png、.webp 等)
182
- - 无需服务器配置 CORS
183
- - ⚠️ 仅支持图片格式
215
+ ```typescript
216
+ interface ImageSpeedTestOptions {
217
+ internetImageUrl: string; // 必填:外网图片URL
218
+ intranetImageUrl?: string; // 可选:内网图片URL
219
+ timeout?: number; // 可选:超时时间 (ms),默认 10000
220
+ autoDetect?: boolean; // 可选:是否自动检测内外网,默认 true
221
+ thresholds?: { // 可选:网速评估阈值 (Mbps)
222
+ fast: number; // 默认 10
223
+ medium: number; // 默认 2
224
+ };
225
+ }
226
+ ```
184
227
 
185
- - `'fetch'`:使用 fetch API 加载
186
- - ✅ 支持任意类型资源(.bin、.json、.txt 等)
187
- - ⚠️ 需要服务器配置 CORS
188
- - ⚠️ 受跨域限制
228
+ **模式二:Fetch模式(支持任意资源,需要CORS)**
229
+
230
+ ```typescript
231
+ interface FetchSpeedTestOptions {
232
+ internetUrl: string; // ✅ 必填:外网资源URL
233
+ useFetch: true; // ✅ 必填:启用fetch模式
234
+ intranetUrl?: string; // 可选:内网资源URL
235
+ timeout?: number; // 可选:超时时间 (ms),默认 10000
236
+ autoDetect?: boolean; // 可选:是否自动检测内外网,默认 true
237
+ thresholds?: { // 可选:网速评估阈值 (Mbps)
238
+ fast: number; // 默认 10
239
+ medium: number; // 默认 2
240
+ };
241
+ }
242
+ ```
243
+
244
+ **使用示例:**
245
+
246
+ ```typescript
247
+ // ✅ 图片模式(只需要图片URL)
248
+ const sdk1 = new NetworkSpeedSDK({
249
+ internetImageUrl: 'https://cdn.example.com/test.jpg',
250
+ });
251
+
252
+ // ✅ Fetch模式(需要URL + useFetch: true)
253
+ const sdk2 = new NetworkSpeedSDK({
254
+ internetUrl: 'https://cdn.example.com/test.bin',
255
+ useFetch: true,
256
+ });
257
+
258
+ // ✅ 无参数实例化(仅用于工具函数)
259
+ const sdk3 = new NetworkSpeedSDK();
260
+ const speeds = sdk3.getAllResourcesSpeeds(); // ✅ 可以使用
261
+ await sdk3.test(); // ❌ 会抛出错误:SDK未配置
262
+ ```
189
263
 
190
264
  #### 方法
191
265
 
@@ -277,14 +351,17 @@ interface ResourceSpeedInfo {
277
351
  }
278
352
  ```
279
353
 
354
+ <div align="center">
355
+
280
356
  ## 💡 使用示例
281
357
 
358
+ </div>
359
+
282
360
  ### 1. 使用图片资源测速(默认,推荐)
283
361
 
284
362
  ```typescript
285
363
  const sdk = new NetworkSpeedSDK({
286
- internetUrl: 'https://cdn.example.com/test-image.jpg',
287
- // resourceType: 'image', // 默认值,可省略
364
+ internetImageUrl: 'https://cdn.example.com/test-image.jpg',
288
365
  });
289
366
 
290
367
  const result = await sdk.test();
@@ -295,7 +372,7 @@ const result = await sdk.test();
295
372
  ```typescript
296
373
  const sdk = new NetworkSpeedSDK({
297
374
  internetUrl: 'https://cdn.example.com/test-file.bin',
298
- resourceType: 'fetch', // 使用 fetch API
375
+ useFetch: true, // 必须启用fetch模式
299
376
  });
300
377
 
301
378
  const result = await sdk.test();
@@ -304,9 +381,18 @@ const result = await sdk.test();
304
381
  ### 3. 内外网自动检测
305
382
 
306
383
  ```typescript
384
+ // 图片模式
307
385
  const sdk = new NetworkSpeedSDK({
308
- intranetUrl: 'https://internal-cdn.company.com/test.jpg',
309
- internetUrl: 'https://public-cdn.example.com/test.jpg',
386
+ intranetImageUrl: 'https://internal-cdn.company.com/test.jpg',
387
+ internetImageUrl: 'https://public-cdn.example.com/test.jpg',
388
+ autoDetect: true,
389
+ });
390
+
391
+ // 或 Fetch模式
392
+ const sdk = new NetworkSpeedSDK({
393
+ intranetUrl: 'https://internal-cdn.company.com/test.bin',
394
+ internetUrl: 'https://public-cdn.example.com/test.bin',
395
+ useFetch: true,
310
396
  autoDetect: true,
311
397
  });
312
398
 
@@ -417,8 +503,12 @@ const monitor = new NetworkMonitor();
417
503
  setInterval(() => monitor.monitor(), 60000); // 每分钟检测
418
504
  ```
419
505
 
506
+ <div align="center">
507
+
420
508
  ## 🎨 框架集成
421
509
 
510
+ </div>
511
+
422
512
  ### Vue 3 自定义 Hook
423
513
 
424
514
  ```typescript
@@ -494,8 +584,12 @@ export function useNetworkSpeed(options = {}) {
494
584
  }
495
585
  ```
496
586
 
587
+ <div align="center">
588
+
497
589
  ## ⚙️ 配置指南
498
590
 
591
+ </div>
592
+
499
593
  ### 测速资源准备
500
594
 
501
595
  #### 服务端配置(Nginx)
@@ -579,8 +673,12 @@ dd if=/dev/urandom of=speed-test.bin bs=1024 count=500
579
673
  }
580
674
  ```
581
675
 
676
+ <div align="center">
677
+
582
678
  ## 🔍 核心概念
583
679
 
680
+ </div>
681
+
584
682
  ### Performance API 是什么?
585
683
 
586
684
  Performance API 是浏览器提供的原生性能监控接口,可以精确测量资源加载的各个阶段耗时。
@@ -602,8 +700,12 @@ Performance API 是浏览器提供的原生性能监控接口,可以精确测
602
700
  2. 如果内网资源超时或失败,自动切换到外网资源
603
701
  3. 根据成功的资源判断当前网络环境
604
702
 
703
+ <div align="center">
704
+
605
705
  ## 📊 Performance API vs 其他方案
606
706
 
707
+ </div>
708
+
607
709
  | 方案 | 准确度 | 可控性 | 复杂度 | 推荐 |
608
710
  |------|--------|--------|--------|------|
609
711
  | Performance API | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐ | ⭐⭐ | ✅ 主力 |
@@ -642,8 +744,12 @@ const size = entry.transferSize; // 真实传输字节数
642
744
  - ✅ 可识别缓存命中
643
745
  - ✅ 提供完整的加载时序
644
746
 
747
+ <div align="center">
748
+
645
749
  ## ❓ 常见问题
646
750
 
751
+ </div>
752
+
647
753
  ### Q1: 测速结果不准确?
648
754
 
649
755
  **可能原因:**
@@ -718,8 +824,12 @@ if (connection) {
718
824
  }
719
825
  ```
720
826
 
827
+ <div align="center">
828
+
721
829
  ## 🚀 性能优化
722
830
 
831
+ </div>
832
+
723
833
  ### 1. 避免频繁测速
724
834
 
725
835
  ```typescript
@@ -783,8 +893,12 @@ async function testSpeed() {
783
893
  }
784
894
  ```
785
895
 
896
+ <div align="center">
897
+
786
898
  ## ⚠️ 注意事项
787
899
 
900
+ </div>
901
+
788
902
  ### Performance API 能做什么
789
903
 
790
904
  ✅ 基于真实资源加载评估下载速度
@@ -799,8 +913,12 @@ async function testSpeed() {
799
913
  ❌ 测量 RTT 抖动
800
914
  ❌ 脱离真实请求独立测速
801
915
 
916
+ <div align="center">
917
+
802
918
  ## 🤝 贡献
803
919
 
920
+ </div>
921
+
804
922
  欢迎提交 Issue 和 Pull Request!
805
923
 
806
924
  ### 本地开发
@@ -839,12 +957,20 @@ network-speed-js/
839
957
  └── README.md # 项目文档
840
958
  ```
841
959
 
960
+ <div align="center">
961
+
842
962
  ## 📄 License
843
963
 
964
+ </div>
965
+
844
966
  MIT License
845
967
 
968
+ <div align="center">
969
+
846
970
  ## 🔗 相关链接
847
971
 
972
+ </div>
973
+
848
974
  - [更新日志 (CHANGELOG)](./CHANGELOG.md)
849
975
  - [Performance API 文档](https://developer.mozilla.org/en-US/docs/Web/API/Performance_API)
850
976
  - [PerformanceResourceTiming](https://developer.mozilla.org/en-US/docs/Web/API/PerformanceResourceTiming)
@@ -4,23 +4,51 @@ import type { SpeedTestOptions, SpeedTestResult, ResourceSpeedInfo } from './typ
4
4
  */
5
5
  export declare class NetworkSpeedSDK {
6
6
  private tester;
7
+ /**
8
+ * 创建SDK实例
9
+ * @param options 配置选项(可选,如果不传则只能使用工具函数,不能执行测速)
10
+ *
11
+ * @example
12
+ * // 图片模式(默认,推荐)
13
+ * const sdk = new NetworkSpeedSDK({
14
+ * internetImageUrl: 'https://cdn.example.com/test.jpg',
15
+ * });
16
+ *
17
+ * @example
18
+ * // Fetch模式(需要CORS)
19
+ * const sdk = new NetworkSpeedSDK({
20
+ * internetUrl: 'https://cdn.example.com/test.bin',
21
+ * useFetch: true,
22
+ * });
23
+ *
24
+ * @example
25
+ * // 仅使用工具函数
26
+ * const sdk = new NetworkSpeedSDK();
27
+ * const speeds = sdk.getAllResourcesSpeeds();
28
+ */
7
29
  constructor(options?: SpeedTestOptions);
8
30
  /**
9
31
  * 执行网速测试
32
+ * @throws {Error} 如果SDK未配置
10
33
  */
11
34
  test(): Promise<SpeedTestResult>;
12
35
  /**
13
36
  * 获取所有已加载资源的速度信息
37
+ * @returns 资源速度信息数组
14
38
  */
15
39
  getAllResourcesSpeeds(): ResourceSpeedInfo[];
16
40
  /**
17
41
  * 监听特定资源的性能数据
42
+ * @param urlPattern URL匹配模式
43
+ * @param callback 回调函数
44
+ * @returns 停止监听的函数
18
45
  */
19
46
  observeResource(urlPattern: string, callback: (entry: PerformanceResourceTiming) => void): () => void;
20
47
  /**
21
48
  * 更新配置
49
+ * @param options 新的配置选项
22
50
  */
23
- updateOptions(options: Partial<SpeedTestOptions>): void;
51
+ updateOptions(options: SpeedTestOptions): void;
24
52
  /**
25
53
  * 销毁SDK实例
26
54
  */
@@ -28,6 +56,13 @@ export declare class NetworkSpeedSDK {
28
56
  }
29
57
  /**
30
58
  * 创建SDK实例的工厂函数
59
+ * @param options 配置选项
60
+ * @returns SDK实例
61
+ *
62
+ * @example
63
+ * const sdk = createNetworkSpeedSDK({
64
+ * internetImageUrl: 'https://cdn.example.com/test.jpg',
65
+ * });
31
66
  */
32
67
  export declare function createNetworkSpeedSDK(options?: SpeedTestOptions): NetworkSpeedSDK;
33
68
  export default NetworkSpeedSDK;
@@ -1 +1 @@
1
- {"version":3,"file":"sdk.d.ts","sourceRoot":"","sources":["../../src/core/sdk.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,gBAAgB,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAC;AAEpF;;GAEG;AACH,qBAAa,eAAe;IAC1B,OAAO,CAAC,MAAM,CAAc;gBAEhB,OAAO,GAAE,gBAAqB;IAI1C;;OAEG;IACG,IAAI,IAAI,OAAO,CAAC,eAAe,CAAC;IAItC;;OAEG;IACH,qBAAqB,IAAI,iBAAiB,EAAE;IAI5C;;OAEG;IACH,eAAe,CACb,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,CAAC,KAAK,EAAE,yBAAyB,KAAK,IAAI,GACnD,MAAM,IAAI;IAIb;;OAEG;IACH,aAAa,CAAC,OAAO,EAAE,OAAO,CAAC,gBAAgB,CAAC,GAAG,IAAI;IAIvD;;OAEG;IACH,OAAO,IAAI,IAAI;CAGhB;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,CAAC,EAAE,gBAAgB,GAAG,eAAe,CAEjF;AAGD,eAAe,eAAe,CAAC"}
1
+ {"version":3,"file":"sdk.d.ts","sourceRoot":"","sources":["../../src/core/sdk.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,gBAAgB,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAC;AAEpF;;GAEG;AACH,qBAAa,eAAe;IAC1B,OAAO,CAAC,MAAM,CAA4B;IAE1C;;;;;;;;;;;;;;;;;;;;;OAqBG;gBACS,OAAO,CAAC,EAAE,gBAAgB;IAMtC;;;OAGG;IACG,IAAI,IAAI,OAAO,CAAC,eAAe,CAAC;IAatC;;;OAGG;IACH,qBAAqB,IAAI,iBAAiB,EAAE;IAI5C;;;;;OAKG;IACH,eAAe,CACb,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,CAAC,KAAK,EAAE,yBAAyB,KAAK,IAAI,GACnD,MAAM,IAAI;IAUb;;;OAGG;IACH,aAAa,CAAC,OAAO,EAAE,gBAAgB,GAAG,IAAI;IAK9C;;OAEG;IACH,OAAO,IAAI,IAAI;CAMhB;AAED;;;;;;;;;GASG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,CAAC,EAAE,gBAAgB,GAAG,eAAe,CAEjF;AAGD,eAAe,eAAe,CAAC"}
@@ -1,11 +1,25 @@
1
1
  import type { SpeedTestResult, SpeedTestOptions, PerformanceEntryCallback } from './types';
2
+ /**
3
+ * 内部标准化配置
4
+ */
5
+ interface NormalizedOptions {
6
+ intranetUrl: string;
7
+ internetUrl: string;
8
+ timeout: number;
9
+ autoDetect: boolean;
10
+ thresholds: {
11
+ fast: number;
12
+ medium: number;
13
+ };
14
+ useFetch: boolean;
15
+ }
2
16
  /**
3
17
  * 网速测试核心类
4
18
  */
5
19
  export declare class SpeedTester {
6
20
  private options;
7
21
  private observer;
8
- constructor(options?: SpeedTestOptions);
22
+ constructor(options: SpeedTestOptions);
9
23
  /**
10
24
  * 执行测速
11
25
  */
@@ -33,10 +47,11 @@ export declare class SpeedTester {
33
47
  /**
34
48
  * 更新配置
35
49
  */
36
- updateOptions(options: Partial<SpeedTestOptions>): void;
50
+ updateOptions(options: Partial<NormalizedOptions>): void;
37
51
  /**
38
52
  * 销毁实例
39
53
  */
40
54
  destroy(): void;
41
55
  }
56
+ export {};
42
57
  //# sourceMappingURL=speed-tester.d.ts.map
@@ -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;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"}
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,UAAU,iBAAiB;IACzB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,OAAO,CAAC;IACpB,UAAU,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IAC7C,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED;;GAEG;AACH,qBAAa,WAAW;IACtB,OAAO,CAAC,OAAO,CAAoB;IACnC,OAAO,CAAC,QAAQ,CAAoC;gBAExC,OAAO,EAAE,gBAAgB;IAgDrC;;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,iBAAiB,CAAC,GAAG,IAAI;IAIxD;;OAEG;IACH,OAAO,IAAI,IAAI;CAMhB"}
@@ -33,17 +33,9 @@ export interface ResourceSpeedInfo {
33
33
  transferSize: number;
34
34
  }
35
35
  /**
36
- * 资源类型
36
+ * 基础配置选项
37
37
  */
38
- export type ResourceType = 'image' | 'fetch';
39
- /**
40
- * SDK配置选项
41
- */
42
- export interface SpeedTestOptions {
43
- /** 内网测速资源URL */
44
- intranetUrl?: string;
45
- /** 外网测速资源URL */
46
- internetUrl?: string;
38
+ interface BaseSpeedTestOptions {
47
39
  /** 超时时间 (ms) */
48
40
  timeout?: number;
49
41
  /** 是否自动检测内外网 */
@@ -53,17 +45,36 @@ export interface SpeedTestOptions {
53
45
  fast: number;
54
46
  medium: number;
55
47
  };
56
- /**
57
- * 资源类型
58
- * - 'image': 使用 Image 对象加载(默认,不受跨域限制,适用于图片资源)
59
- * - 'fetch': 使用 fetch API 加载(需要服务器支持 CORS,适用于任意资源)
60
- */
61
- resourceType?: ResourceType;
62
48
  }
49
+ /**
50
+ * 图片模式配置(默认,不受跨域限制)
51
+ */
52
+ export interface ImageSpeedTestOptions extends BaseSpeedTestOptions {
53
+ /** 内网测速图片URL */
54
+ intranetImageUrl?: string;
55
+ /** 外网测速图片URL(必填) */
56
+ internetImageUrl: string;
57
+ }
58
+ /**
59
+ * Fetch模式配置(支持任意资源,需要CORS)
60
+ */
61
+ export interface FetchSpeedTestOptions extends BaseSpeedTestOptions {
62
+ /** 内网测速资源URL */
63
+ intranetUrl?: string;
64
+ /** 外网测速资源URL(必填) */
65
+ internetUrl: string;
66
+ /** 使用fetch模式 */
67
+ useFetch: true;
68
+ }
69
+ /**
70
+ * SDK配置选项(联合类型)
71
+ */
72
+ export type SpeedTestOptions = ImageSpeedTestOptions | FetchSpeedTestOptions;
63
73
  /**
64
74
  * Performance Observer 回调参数
65
75
  */
66
76
  export interface PerformanceEntryCallback {
67
77
  (entry: PerformanceResourceTiming): void;
68
78
  }
79
+ export {};
69
80
  //# sourceMappingURL=types.d.ts.map
@@ -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,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
+ {"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,UAAU,oBAAoB;IAC5B,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,qBAAsB,SAAQ,oBAAoB;IACjE,gBAAgB;IAChB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,oBAAoB;IACpB,gBAAgB,EAAE,MAAM,CAAC;CAC1B;AAED;;GAEG;AACH,MAAM,WAAW,qBAAsB,SAAQ,oBAAoB;IACjE,gBAAgB;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,oBAAoB;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,gBAAgB;IAChB,QAAQ,EAAE,IAAI,CAAC;CAChB;AAED;;GAEG;AACH,MAAM,MAAM,gBAAgB,GAAG,qBAAqB,GAAG,qBAAqB,CAAC;AAE7E;;GAEG;AACH,MAAM,WAAW,wBAAwB;IACvC,CAAC,KAAK,EAAE,yBAAyB,GAAG,IAAI,CAAC;CAC1C"}
@@ -1,41 +1,63 @@
1
- function l(t) {
1
+ function h(t) {
2
2
  const e = t.responseEnd - t.responseStart;
3
3
  if (e <= 0 || t.transferSize === 0)
4
4
  return null;
5
- const s = t.transferSize * 8 / e / 1e3, n = t.transferSize / e;
5
+ const r = t.transferSize * 8 / e / 1e3, n = t.transferSize / e;
6
6
  return {
7
7
  name: t.name,
8
- speedMbps: Number(s.toFixed(2)),
8
+ speedMbps: Number(r.toFixed(2)),
9
9
  speedKBps: Number(n.toFixed(2)),
10
10
  downloadTime: Number(e.toFixed(2)),
11
11
  transferSize: t.transferSize
12
12
  };
13
13
  }
14
- function p() {
14
+ function d() {
15
15
  return performance.getEntriesByType("resource").filter(
16
16
  (e) => e instanceof PerformanceResourceTiming && e.transferSize > 0
17
- ).map(l).filter((e) => e !== null);
17
+ ).map(h).filter((e) => e !== null);
18
18
  }
19
- function h(t) {
19
+ function p(t) {
20
20
  performance.getEntriesByName(t).forEach(() => {
21
21
  performance.clearResourceTimings();
22
22
  });
23
23
  }
24
- function m(t, e = { fast: 10, medium: 2 }) {
24
+ function w(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 T {
27
+ class l {
28
28
  options;
29
29
  observer = null;
30
- constructor(e = {}) {
31
- this.options = {
32
- intranetUrl: e.intranetUrl || "",
33
- internetUrl: e.internetUrl || "",
34
- timeout: e.timeout || 1e4,
35
- autoDetect: e.autoDetect ?? !0,
36
- thresholds: e.thresholds || { fast: 10, medium: 2 },
37
- resourceType: e.resourceType || "image"
38
- };
30
+ constructor(e) {
31
+ if ("useFetch" in e && e.useFetch) {
32
+ if (!e.internetUrl)
33
+ throw new Error("Fetch模式必须提供 internetUrl 参数");
34
+ if (!e.internetUrl.startsWith("http://") && !e.internetUrl.startsWith("https://"))
35
+ throw new Error("internetUrl 必须是完整的HTTP/HTTPS URL");
36
+ this.options = {
37
+ intranetUrl: e.intranetUrl || "",
38
+ internetUrl: e.internetUrl,
39
+ timeout: e.timeout || 1e4,
40
+ autoDetect: e.autoDetect ?? !0,
41
+ thresholds: e.thresholds || { fast: 10, medium: 2 },
42
+ useFetch: !0
43
+ };
44
+ } else {
45
+ const r = "internetImageUrl" in e ? e.internetImageUrl : "";
46
+ if (!r)
47
+ throw new Error("图片模式必须提供 internetImageUrl 参数");
48
+ if (!r.startsWith("http://") && !r.startsWith("https://"))
49
+ throw new Error("internetImageUrl 必须是完整的HTTP/HTTPS URL");
50
+ this.options = {
51
+ intranetUrl: "intranetImageUrl" in e && e.intranetImageUrl || "",
52
+ internetUrl: r,
53
+ timeout: e.timeout || 1e4,
54
+ autoDetect: e.autoDetect ?? !0,
55
+ thresholds: e.thresholds || { fast: 10, medium: 2 },
56
+ useFetch: !1
57
+ };
58
+ }
59
+ if (this.options.intranetUrl && !this.options.intranetUrl.startsWith("http://") && !this.options.intranetUrl.startsWith("https://"))
60
+ throw new Error("intranetUrl/intranetImageUrl 必须是完整的HTTP/HTTPS URL");
39
61
  }
40
62
  /**
41
63
  * 执行测速
@@ -58,74 +80,74 @@ class T {
58
80
  /**
59
81
  * 测试单个URL
60
82
  */
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);
69
- if (i) {
70
- const f = {
71
- speedMbps: i.speedMbps,
72
- speedKBps: i.speedKBps,
73
- networkType: m(
74
- i.speedMbps,
83
+ testSingleUrl(e, r) {
84
+ return new Promise((n, i) => {
85
+ const s = `${e}?t=${Date.now()}`;
86
+ p(s);
87
+ const c = new PerformanceObserver((f) => {
88
+ for (const a of f.getEntries())
89
+ if (a.entryType === "resource" && a.name.includes(e)) {
90
+ const o = h(a);
91
+ if (o) {
92
+ const m = {
93
+ speedMbps: o.speedMbps,
94
+ speedKBps: o.speedKBps,
95
+ networkType: w(
96
+ o.speedMbps,
75
97
  this.options.thresholds
76
98
  ),
77
- isIntranet: s,
78
- duration: i.downloadTime,
79
- transferSize: i.transferSize,
99
+ isIntranet: r,
100
+ duration: o.downloadTime,
101
+ transferSize: o.transferSize,
80
102
  resourceUrl: e
81
103
  };
82
- c.disconnect(), n(f);
104
+ c.disconnect(), n(m);
83
105
  }
84
106
  }
85
107
  });
86
108
  c.observe({ entryTypes: ["resource"] });
87
- const a = setTimeout(() => {
88
- c.disconnect(), o(new Error(`测速超时: ${e}`));
109
+ const u = setTimeout(() => {
110
+ c.disconnect(), i(new Error(`测速超时: ${e}`));
89
111
  }, this.options.timeout);
90
- this.options.resourceType === "fetch" ? this.loadWithFetch(r, a, c, o) : this.loadWithImage(r, a, c, o);
112
+ this.options.useFetch ? this.loadWithFetch(s, u, c, i) : this.loadWithImage(s, u, c, i);
91
113
  });
92
114
  }
93
115
  /**
94
116
  * 使用 Image 对象加载资源(默认方式,不受跨域限制)
95
117
  */
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;
118
+ loadWithImage(e, r, n, i) {
119
+ const s = new Image();
120
+ s.onload = () => {
121
+ clearTimeout(r);
122
+ }, s.onerror = () => {
123
+ clearTimeout(r), n.disconnect(), i(new Error(`图片加载失败: ${e}`));
124
+ }, s.src = e;
103
125
  }
104
126
  /**
105
127
  * 使用 fetch API 加载资源(需要 CORS 支持)
106
128
  */
107
- loadWithFetch(e, s, n, o) {
129
+ loadWithFetch(e, r, n, i) {
108
130
  fetch(e, {
109
131
  method: "GET",
110
132
  cache: "no-store"
111
133
  // 禁用缓存
112
- }).then((r) => {
113
- if (!r.ok)
114
- throw new Error(`HTTP error! status: ${r.status}`);
115
- return r.blob();
134
+ }).then((s) => {
135
+ if (!s.ok)
136
+ throw new Error(`HTTP error! status: ${s.status}`);
137
+ return s.blob();
116
138
  }).then(() => {
117
- clearTimeout(s);
118
- }).catch((r) => {
119
- clearTimeout(s), n.disconnect(), o(new Error(`资源加载失败: ${r.message}`));
139
+ clearTimeout(r);
140
+ }).catch((s) => {
141
+ clearTimeout(r), n.disconnect(), i(new Error(`资源加载失败: ${s.message}`));
120
142
  });
121
143
  }
122
144
  /**
123
145
  * 监听特定资源的性能数据
124
146
  */
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);
147
+ observeResource(e, r) {
148
+ const n = new PerformanceObserver((i) => {
149
+ for (const s of i.getEntries())
150
+ s.entryType === "resource" && s.name.includes(e) && r(s);
129
151
  });
130
152
  return n.observe({ entryTypes: ["resource"] }), () => n.disconnect();
131
153
  }
@@ -142,49 +164,87 @@ class T {
142
164
  this.observer && (this.observer.disconnect(), this.observer = null);
143
165
  }
144
166
  }
145
- class y {
146
- tester;
147
- constructor(e = {}) {
148
- this.tester = new T(e);
167
+ class U {
168
+ tester = null;
169
+ /**
170
+ * 创建SDK实例
171
+ * @param options 配置选项(可选,如果不传则只能使用工具函数,不能执行测速)
172
+ *
173
+ * @example
174
+ * // 图片模式(默认,推荐)
175
+ * const sdk = new NetworkSpeedSDK({
176
+ * internetImageUrl: 'https://cdn.example.com/test.jpg',
177
+ * });
178
+ *
179
+ * @example
180
+ * // Fetch模式(需要CORS)
181
+ * const sdk = new NetworkSpeedSDK({
182
+ * internetUrl: 'https://cdn.example.com/test.bin',
183
+ * useFetch: true,
184
+ * });
185
+ *
186
+ * @example
187
+ * // 仅使用工具函数
188
+ * const sdk = new NetworkSpeedSDK();
189
+ * const speeds = sdk.getAllResourcesSpeeds();
190
+ */
191
+ constructor(e) {
192
+ e && (this.tester = new l(e));
149
193
  }
150
194
  /**
151
195
  * 执行网速测试
196
+ * @throws {Error} 如果SDK未配置
152
197
  */
153
198
  async test() {
199
+ if (!this.tester)
200
+ throw new Error(
201
+ `SDK未配置,请在构造函数中传入配置选项。
202
+ 示例:
203
+ new NetworkSpeedSDK({ internetImageUrl: "https://cdn.example.com/test.jpg" })
204
+ 或:
205
+ new NetworkSpeedSDK({ internetUrl: "https://cdn.example.com/test.bin", useFetch: true })`
206
+ );
154
207
  return this.tester.test();
155
208
  }
156
209
  /**
157
210
  * 获取所有已加载资源的速度信息
211
+ * @returns 资源速度信息数组
158
212
  */
159
213
  getAllResourcesSpeeds() {
160
- return p();
214
+ return d();
161
215
  }
162
216
  /**
163
217
  * 监听特定资源的性能数据
218
+ * @param urlPattern URL匹配模式
219
+ * @param callback 回调函数
220
+ * @returns 停止监听的函数
164
221
  */
165
- observeResource(e, s) {
166
- return this.tester.observeResource(e, s);
222
+ observeResource(e, r) {
223
+ return this.tester || (this.tester = new l({
224
+ internetImageUrl: ""
225
+ })), this.tester.observeResource(e, r);
167
226
  }
168
227
  /**
169
228
  * 更新配置
229
+ * @param options 新的配置选项
170
230
  */
171
231
  updateOptions(e) {
172
- this.tester.updateOptions(e);
232
+ this.tester = new l(e);
173
233
  }
174
234
  /**
175
235
  * 销毁SDK实例
176
236
  */
177
237
  destroy() {
178
- this.tester.destroy();
238
+ this.tester && (this.tester.destroy(), this.tester = null);
179
239
  }
180
240
  }
181
- function b(t) {
182
- return new y(t);
241
+ function g(t) {
242
+ return new U(t);
183
243
  }
184
244
  export {
185
- y as NetworkSpeedSDK,
186
- l as calcSpeedByResource,
187
- b as createNetworkSpeedSDK,
188
- m as evaluateNetworkType,
189
- p as getAllResourcesSpeeds
245
+ U as NetworkSpeedSDK,
246
+ h as calcSpeedByResource,
247
+ g as createNetworkSpeedSDK,
248
+ w as evaluateNetworkType,
249
+ d as getAllResourcesSpeeds
190
250
  };
@@ -1 +1,5 @@
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"})}));
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 r=t.transferSize*8/e/1e3,i=t.transferSize/e;return{name:t.name,speedMbps:Number(r.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(c).filter(e=>e!==null)}function w(t){performance.getEntriesByName(t).forEach(()=>{performance.clearResourceTimings()})}function d(t,e={fast:10,medium:2}){return t>=e.fast?"fast":t>=e.medium?"medium":t>0?"slow":"unknown"}class l{options;observer=null;constructor(e){if("useFetch"in e&&e.useFetch){if(!e.internetUrl)throw new Error("Fetch模式必须提供 internetUrl 参数");if(!e.internetUrl.startsWith("http://")&&!e.internetUrl.startsWith("https://"))throw new Error("internetUrl 必须是完整的HTTP/HTTPS URL");this.options={intranetUrl:e.intranetUrl||"",internetUrl:e.internetUrl,timeout:e.timeout||1e4,autoDetect:e.autoDetect??!0,thresholds:e.thresholds||{fast:10,medium:2},useFetch:!0}}else{const r="internetImageUrl"in e?e.internetImageUrl:"";if(!r)throw new Error("图片模式必须提供 internetImageUrl 参数");if(!r.startsWith("http://")&&!r.startsWith("https://"))throw new Error("internetImageUrl 必须是完整的HTTP/HTTPS URL");this.options={intranetUrl:"intranetImageUrl"in e&&e.intranetImageUrl||"",internetUrl:r,timeout:e.timeout||1e4,autoDetect:e.autoDetect??!0,thresholds:e.thresholds||{fast:10,medium:2},useFetch:!1}}if(this.options.intranetUrl&&!this.options.intranetUrl.startsWith("http://")&&!this.options.intranetUrl.startsWith("https://"))throw new Error("intranetUrl/intranetImageUrl 必须是完整的HTTP/HTTPS URL")}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,r){return new Promise((i,o)=>{const s=`${e}?t=${Date.now()}`;w(s);const u=new PerformanceObserver(U=>{for(const h of U.getEntries())if(h.entryType==="resource"&&h.name.includes(e)){const a=c(h);if(a){const T={speedMbps:a.speedMbps,speedKBps:a.speedKBps,networkType:d(a.speedMbps,this.options.thresholds),isIntranet:r,duration:a.downloadTime,transferSize:a.transferSize,resourceUrl:e};u.disconnect(),i(T)}}});u.observe({entryTypes:["resource"]});const p=setTimeout(()=>{u.disconnect(),o(new Error(`测速超时: ${e}`))},this.options.timeout);this.options.useFetch?this.loadWithFetch(s,p,u,o):this.loadWithImage(s,p,u,o)})}loadWithImage(e,r,i,o){const s=new Image;s.onload=()=>{clearTimeout(r)},s.onerror=()=>{clearTimeout(r),i.disconnect(),o(new Error(`图片加载失败: ${e}`))},s.src=e}loadWithFetch(e,r,i,o){fetch(e,{method:"GET",cache:"no-store"}).then(s=>{if(!s.ok)throw new Error(`HTTP error! status: ${s.status}`);return s.blob()}).then(()=>{clearTimeout(r)}).catch(s=>{clearTimeout(r),i.disconnect(),o(new Error(`资源加载失败: ${s.message}`))})}observeResource(e,r){const i=new PerformanceObserver(o=>{for(const s of o.getEntries())s.entryType==="resource"&&s.name.includes(e)&&r(s)});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=null;constructor(e){e&&(this.tester=new l(e))}async test(){if(!this.tester)throw new Error(`SDK未配置,请在构造函数中传入配置选项。
2
+ 示例:
3
+ new NetworkSpeedSDK({ internetImageUrl: "https://cdn.example.com/test.jpg" })
4
+ 或:
5
+ new NetworkSpeedSDK({ internetUrl: "https://cdn.example.com/test.bin", useFetch: true })`);return this.tester.test()}getAllResourcesSpeeds(){return f()}observeResource(e,r){return this.tester||(this.tester=new l({internetImageUrl:""})),this.tester.observeResource(e,r)}updateOptions(e){this.tester=new l(e)}destroy(){this.tester&&(this.tester.destroy(),this.tester=null)}}function S(t){return new m(t)}n.NetworkSpeedSDK=m,n.calcSpeedByResource=c,n.createNetworkSpeedSDK=S,n.evaluateNetworkType=d,n.getAllResourcesSpeeds=f,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.4",
5
+ "version": "1.0.6",
6
6
  "author": "Sunny-117",
7
7
  "license": "MIT",
8
8
  "keywords": [