@juzi/file-box 1.7.20 → 1.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/README.md +4 -2
  2. package/dist/cjs/src/config.d.ts +5 -5
  3. package/dist/cjs/src/config.d.ts.map +1 -1
  4. package/dist/cjs/src/config.js +7 -9
  5. package/dist/cjs/src/config.js.map +1 -1
  6. package/dist/cjs/src/file-box.js +1 -1
  7. package/dist/cjs/src/file-box.js.map +1 -1
  8. package/dist/cjs/src/misc.d.ts.map +1 -1
  9. package/dist/cjs/src/misc.js +185 -69
  10. package/dist/cjs/src/misc.js.map +1 -1
  11. package/dist/cjs/src/misc.spec.js +26 -17
  12. package/dist/cjs/src/misc.spec.js.map +1 -1
  13. package/dist/cjs/src/version.d.ts.map +1 -1
  14. package/dist/cjs/src/version.js +1 -1
  15. package/dist/cjs/src/version.js.map +1 -1
  16. package/dist/cjs/tests/chunk-download.spec.js +62 -90
  17. package/dist/cjs/tests/chunk-download.spec.js.map +1 -1
  18. package/dist/cjs/tests/misc-error-handling.spec.js +134 -30
  19. package/dist/cjs/tests/misc-error-handling.spec.js.map +1 -1
  20. package/dist/cjs/tests/network-timeout.spec.js +101 -105
  21. package/dist/cjs/tests/network-timeout.spec.js.map +1 -1
  22. package/dist/esm/src/config.d.ts +5 -5
  23. package/dist/esm/src/config.d.ts.map +1 -1
  24. package/dist/esm/src/config.js +6 -8
  25. package/dist/esm/src/config.js.map +1 -1
  26. package/dist/esm/src/file-box.js +2 -2
  27. package/dist/esm/src/file-box.js.map +1 -1
  28. package/dist/esm/src/misc.d.ts.map +1 -1
  29. package/dist/esm/src/misc.js +187 -71
  30. package/dist/esm/src/misc.js.map +1 -1
  31. package/dist/esm/src/misc.spec.js +26 -17
  32. package/dist/esm/src/misc.spec.js.map +1 -1
  33. package/dist/esm/src/version.d.ts.map +1 -1
  34. package/dist/esm/src/version.js +1 -1
  35. package/dist/esm/src/version.js.map +1 -1
  36. package/dist/esm/tests/chunk-download.spec.js +62 -90
  37. package/dist/esm/tests/chunk-download.spec.js.map +1 -1
  38. package/dist/esm/tests/misc-error-handling.spec.js +134 -30
  39. package/dist/esm/tests/misc-error-handling.spec.js.map +1 -1
  40. package/dist/esm/tests/network-timeout.spec.js +103 -107
  41. package/dist/esm/tests/network-timeout.spec.js.map +1 -1
  42. package/package.json +2 -2
  43. package/src/config.ts +6 -12
  44. package/src/file-box.ts +2 -2
  45. package/src/misc.spec.ts +29 -14
  46. package/src/misc.ts +200 -75
  47. package/src/version.ts +1 -1
@@ -1,123 +1,119 @@
1
1
  #!/usr/bin/env -S node --no-warnings --loader ts-node/esm
2
2
  import { createServer } from 'http';
3
3
  import { setTimeout } from 'timers/promises';
4
- import { sinon, test } from 'tstest';
5
- import { HTTP_REQUEST_TIMEOUT, HTTP_RESPONSE_TIMEOUT } from '../src/config.js';
4
+ import { test } from 'tstest';
5
+ import { CONFIG } from '../src/config.js';
6
6
  import { FileBox } from '../src/mod.js';
7
- test('slow network stall HTTP_TIMEOUT', async (t) => {
8
- const sandbox = sinon.createSandbox();
9
- sandbox.useFakeTimers({
10
- now: Date.now(),
11
- shouldAdvanceTime: true,
12
- shouldClearNativeTimers: true,
13
- toFake: ['setTimeout', 'clearTimeout', 'setInterval', 'clearInterval', 'nextTick'],
7
+ test('HTTP timeout handling', async (t) => {
8
+ // 设置短超时用于快速测试
9
+ const originalRequestTimeout = CONFIG.HTTP_REQUEST_TIMEOUT;
10
+ const originalResponseTimeout = CONFIG.HTTP_RESPONSE_TIMEOUT;
11
+ CONFIG.HTTP_REQUEST_TIMEOUT = 200; // 200ms
12
+ CONFIG.HTTP_RESPONSE_TIMEOUT = 300; // 300ms
13
+ t.teardown(() => {
14
+ CONFIG.HTTP_REQUEST_TIMEOUT = originalRequestTimeout;
15
+ CONFIG.HTTP_RESPONSE_TIMEOUT = originalResponseTimeout;
14
16
  });
15
- t.jobs = 3;
16
- const port = Math.floor(Math.random() * (65535 - 49152 + 1)) + 49152;
17
- const URL = {
18
- NOT_TIMEOUT: '/not_timeout',
19
- READY: '/ready',
20
- TIMEOUT: '/timeout',
21
- };
22
- /* eslint @typescript-eslint/no-misused-promises:off */
23
- const server = createServer(async (req, res) => {
24
- res.write(Buffer.from('This is the first chunk of data.'));
25
- if (req.url === URL.NOT_TIMEOUT) {
26
- await setTimeout(HTTP_REQUEST_TIMEOUT * 0.5);
27
- res.write(Buffer.from('This is the second chunk of data.'));
28
- }
29
- else if (req.url === URL.READY) {
30
- await setTimeout(HTTP_REQUEST_TIMEOUT + 100);
31
- }
32
- else if (req.url === URL.TIMEOUT) {
33
- if (req.method === 'GET') {
34
- await setTimeout(HTTP_RESPONSE_TIMEOUT + 100);
17
+ await t.test('should complete download without timeout', async (t) => {
18
+ const testData = 'Test data for no timeout';
19
+ const server = createServer((req, res) => {
20
+ if (req.method === 'HEAD') {
21
+ res.writeHead(200, { 'Content-Length': String(testData.length) });
22
+ res.end();
23
+ return;
35
24
  }
36
- }
37
- // console.debug(`${new Date().toLocaleTimeString()} call res.end "${req.url}"`)
38
- res.end(Buffer.from('All data end.'));
39
- });
40
- const host = await new Promise((resolve) => {
41
- server.listen(port, '127.0.0.1', () => {
42
- const addr = server.address();
43
- // console.debug(`Server is listening on port ${JSON.stringify(addr)}`)
44
- resolve(`http://127.0.0.1:${addr.port}`);
25
+ // 服务器不支持 Range,直接返回 200
26
+ // 快速响应,不应该超时
27
+ res.writeHead(200, { 'Content-Length': String(testData.length) });
28
+ res.end(testData);
45
29
  });
46
- });
47
- t.teardown(() => {
48
- // console.debug('teardown')
49
- server.close();
50
- sandbox.restore();
51
- });
52
- /** eslint @typescript-eslint/no-floating-promises:off */
53
- t.test('should not timeout', async (t) => {
54
- const url = `${host}${URL.NOT_TIMEOUT}`;
55
- const dataSpy = sandbox.spy();
56
- const errorSpy = sandbox.spy();
57
- // console.debug(`${new Date().toLocaleTimeString()} Start request "${url}" ...`)
58
- const start = Date.now();
59
- const stream = await FileBox.fromUrl(url).toStream();
60
- stream.once('error', errorSpy).on('data', dataSpy);
61
- await sandbox.clock.tickAsync(1);
62
- t.ok(dataSpy.calledOnce, `should get chunk 1 (${Date.now() - start} passed)`);
63
- t.ok(errorSpy.notCalled, `should not get error (${Date.now() - start} passed)`);
64
- // FIXME: tickAsync does not work on socket timeout
65
30
  await new Promise((resolve) => {
66
- stream.once('error', resolve).on('close', resolve);
67
- // resolve(setTimeout(HTTP_REQUEST_TIMEOUT))
31
+ server.listen(0, '127.0.0.1', resolve);
32
+ });
33
+ const port = server.address().port;
34
+ t.teardown(() => { server.close(); });
35
+ const url = `http://127.0.0.1:${port}/test`;
36
+ const fileBox = FileBox.fromUrl(url);
37
+ const stream = await fileBox.toStream();
38
+ const chunks = [];
39
+ stream.on('data', (chunk) => chunks.push(chunk));
40
+ await new Promise((resolve, reject) => {
41
+ stream.on('end', resolve);
42
+ stream.on('error', reject);
68
43
  });
69
- await sandbox.clock.tickAsync(1);
70
- // await sandbox.clock.tickAsync(HTTP_RESPONSE_TIMEOUT)
71
- t.comment('recv data count:', dataSpy.callCount);
72
- t.comment('recv error count:', errorSpy.callCount);
73
- t.ok(dataSpy.calledThrice, `should get chunk 3 after TIMEOUT ${HTTP_REQUEST_TIMEOUT} (${Date.now() - start} passed)`);
74
- t.ok(errorSpy.notCalled, `should not get error after TIMEOUT ${HTTP_REQUEST_TIMEOUT} (${Date.now() - start} passed)`);
44
+ const result = Buffer.concat(chunks).toString();
45
+ t.equal(result, testData, 'should receive complete data');
75
46
  t.end();
76
- }).catch(t.threw);
77
- /** eslint @typescript-eslint/no-floating-promises:off */
78
- t.test('should timeout', async (t) => {
79
- const url = `${host}${URL.TIMEOUT}`;
80
- const dataSpy = sandbox.spy();
81
- const errorSpy = sandbox.spy();
82
- // console.debug(`${new Date().toLocaleTimeString()} Start request "${url}" ...`)
83
- const start = Date.now();
84
- const stream = await FileBox.fromUrl(url).toStream();
85
- stream.once('error', errorSpy).once('data', dataSpy);
86
- // .on('error', (e) => {
87
- // console.error(`on error for req "${url}":`, e.stack)
88
- // })
89
- // .on('data', (d: Buffer) => {
90
- // console.error(`on data for req "${url}":`, d.toString())
91
- // })
92
- await sandbox.clock.tickAsync(1);
93
- // t.comment('recv data count:', dataSpy.callCount)
94
- // t.comment('recv error count:', errorSpy.callCount)
95
- t.ok(dataSpy.calledOnce, `should get chunk 1 (${Date.now() - start} passed)`);
96
- t.ok(errorSpy.notCalled, `should not get error (${Date.now() - start} passed)`);
97
- // FIXME: tickAsync does not work on socket timeout
47
+ });
48
+ await t.test('should handle response timeout', async (t) => {
49
+ const server = createServer((req, res) => {
50
+ if (req.method === 'HEAD') {
51
+ res.writeHead(200, { 'Content-Length': '100' });
52
+ res.end();
53
+ return;
54
+ }
55
+ // 发送部分数据后停止,不调用 res.end()
56
+ // 让连接挂起,Socket 会在 HTTP_RESPONSE_TIMEOUT 后超时
57
+ res.writeHead(200, { 'Content-Length': '100' });
58
+ res.write('Partial data...');
59
+ // 不调用 res.end()
60
+ });
98
61
  await new Promise((resolve) => {
99
- stream.once('error', resolve).on('close', resolve);
100
- // resolve(setTimeout(HTTP_RESPONSE_TIMEOUT))
62
+ server.listen(0, '127.0.0.1', resolve);
101
63
  });
102
- await sandbox.clock.tickAsync(1);
103
- // await sandbox.clock.tickAsync(HTTP_RESPONSE_TIMEOUT)
104
- // t.comment('recv data count:', dataSpy.callCount)
105
- // t.comment('recv error count:', errorSpy.callCount)
106
- t.ok(errorSpy.calledOnce, `should get error after TIMEOUT ${HTTP_RESPONSE_TIMEOUT} (${Date.now() - start} passed)`);
64
+ const port = server.address().port;
65
+ t.teardown(() => { server.close(); });
66
+ const url = `http://127.0.0.1:${port}/timeout`;
67
+ try {
68
+ const fileBox = FileBox.fromUrl(url);
69
+ const stream = await fileBox.toStream();
70
+ const chunks = [];
71
+ await new Promise((resolve, reject) => {
72
+ stream.on('data', (chunk) => chunks.push(chunk));
73
+ stream.on('end', resolve);
74
+ stream.on('error', reject);
75
+ });
76
+ t.fail('should have thrown timeout error');
77
+ }
78
+ catch (error) {
79
+ const err = error;
80
+ t.ok(err.message.includes('timeout'), `should timeout with error: ${err.message}`);
81
+ }
107
82
  t.end();
108
- }).catch(t.threw);
109
- /** eslint @typescript-eslint/no-floating-promises:off */
110
- t.test('ready should timeout', async (t) => {
111
- const url = `${host}${URL.READY}`;
112
- const errorSpy = sandbox.spy();
113
- // console.debug(`${new Date().toLocaleTimeString()} Start request "${url}" ...`)
114
- const start = Date.now();
115
- const fileBox = FileBox.fromUrl(url);
116
- await fileBox.ready().catch(errorSpy);
117
- await sandbox.clock.tickAsync(1);
118
- // t.comment('recv error count:', errorSpy.callCount)
119
- t.ok(errorSpy.calledOnce, `should get error after TIMEOUT ${HTTP_REQUEST_TIMEOUT} (${Date.now() - start} passed)`);
83
+ });
84
+ await t.test('should handle request timeout', async (t) => {
85
+ let requestReceived = false;
86
+ /* eslint @typescript-eslint/no-misused-promises:off */
87
+ const server = createServer(async (req, res) => {
88
+ if (req.method === 'HEAD') {
89
+ res.writeHead(200, { 'Content-Length': '100' });
90
+ res.end();
91
+ return;
92
+ }
93
+ requestReceived = true;
94
+ // 延迟响应超过 HTTP_REQUEST_TIMEOUT
95
+ // 在发送任何数据之前延迟,触发 request timeout
96
+ await setTimeout(CONFIG.HTTP_REQUEST_TIMEOUT + 100);
97
+ res.writeHead(200, { 'Content-Length': '10' });
98
+ res.end('Too late');
99
+ });
100
+ await new Promise((resolve) => {
101
+ server.listen(0, '127.0.0.1', resolve);
102
+ });
103
+ const port = server.address().port;
104
+ t.teardown(() => { server.close(); });
105
+ const url = `http://127.0.0.1:${port}/request-timeout`;
106
+ try {
107
+ const fileBox = FileBox.fromUrl(url);
108
+ await fileBox.toStream();
109
+ t.fail('should have thrown timeout error');
110
+ }
111
+ catch (error) {
112
+ const err = error;
113
+ t.ok(requestReceived, 'should have received request');
114
+ t.ok(err.message.includes('timeout'), `should timeout with error: ${err.message}`);
115
+ }
120
116
  t.end();
121
- }).catch(t.threw);
117
+ });
122
118
  });
123
119
  //# sourceMappingURL=network-timeout.spec.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"network-timeout.spec.js","sourceRoot":"","sources":["../../../tests/network-timeout.spec.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"network-timeout.spec.js","sourceRoot":"","sources":["../../../tests/network-timeout.spec.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,YAAY,EAAE,MAAM,MAAM,CAAA;AAEnC,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAA;AAC5C,OAAO,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAA;AAE7B,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAA;AACzC,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAA;AAEvC,IAAI,CAAC,uBAAuB,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE;IACxC,cAAc;IACd,MAAM,sBAAsB,GAAG,MAAM,CAAC,oBAAoB,CAAA;IAC1D,MAAM,uBAAuB,GAAG,MAAM,CAAC,qBAAqB,CAAA;IAC5D,MAAM,CAAC,oBAAoB,GAAG,GAAG,CAAA,CAAG,QAAQ;IAC5C,MAAM,CAAC,qBAAqB,GAAG,GAAG,CAAA,CAAE,QAAQ;IAE5C,CAAC,CAAC,QAAQ,CAAC,GAAG,EAAE;QACd,MAAM,CAAC,oBAAoB,GAAG,sBAAsB,CAAA;QACpD,MAAM,CAAC,qBAAqB,GAAG,uBAAuB,CAAA;IACxD,CAAC,CAAC,CAAA;IAEF,MAAM,CAAC,CAAC,IAAI,CAAC,0CAA0C,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE;QACnE,MAAM,QAAQ,GAAG,0BAA0B,CAAA;QAE3C,MAAM,MAAM,GAAG,YAAY,CAAC,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE;YACvC,IAAI,GAAG,CAAC,MAAM,KAAK,MAAM,EAAE;gBACzB,GAAG,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,gBAAgB,EAAE,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,CAAA;gBACjE,GAAG,CAAC,GAAG,EAAE,CAAA;gBACT,OAAM;aACP;YAED,wBAAwB;YACxB,aAAa;YACb,GAAG,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,gBAAgB,EAAE,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,CAAA;YACjE,GAAG,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAA;QACnB,CAAC,CAAC,CAAA;QAEF,MAAM,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE;YAClC,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,WAAW,EAAE,OAAO,CAAC,CAAA;QACxC,CAAC,CAAC,CAAA;QAEF,MAAM,IAAI,GAAI,MAAM,CAAC,OAAO,EAAkB,CAAC,IAAI,CAAA;QACnD,CAAC,CAAC,QAAQ,CAAC,GAAG,EAAE,GAAG,MAAM,CAAC,KAAK,EAAE,CAAA,CAAC,CAAC,CAAC,CAAA;QAEpC,MAAM,GAAG,GAAG,oBAAoB,IAAI,OAAO,CAAA;QAC3C,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,CAAA;QACpC,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,QAAQ,EAAE,CAAA;QAEvC,MAAM,MAAM,GAAa,EAAE,CAAA;QAC3B,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAa,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAA;QAExD,MAAM,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YAC1C,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,CAAC,CAAA;YACzB,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC,CAAA;QAC5B,CAAC,CAAC,CAAA;QAEF,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,QAAQ,EAAE,CAAA;QAC/C,CAAC,CAAC,KAAK,CAAC,MAAM,EAAE,QAAQ,EAAE,8BAA8B,CAAC,CAAA;QACzD,CAAC,CAAC,GAAG,EAAE,CAAA;IACT,CAAC,CAAC,CAAA;IAEF,MAAM,CAAC,CAAC,IAAI,CAAC,gCAAgC,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE;QACzD,MAAM,MAAM,GAAG,YAAY,CAAC,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE;YACvC,IAAI,GAAG,CAAC,MAAM,KAAK,MAAM,EAAE;gBACzB,GAAG,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,gBAAgB,EAAE,KAAK,EAAE,CAAC,CAAA;gBAC/C,GAAG,CAAC,GAAG,EAAE,CAAA;gBACT,OAAM;aACP;YAED,0BAA0B;YAC1B,4CAA4C;YAC5C,GAAG,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,gBAAgB,EAAE,KAAK,EAAE,CAAC,CAAA;YAC/C,GAAG,CAAC,KAAK,CAAC,iBAAiB,CAAC,CAAA;YAC5B,gBAAgB;QAClB,CAAC,CAAC,CAAA;QAEF,MAAM,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE;YAClC,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,WAAW,EAAE,OAAO,CAAC,CAAA;QACxC,CAAC,CAAC,CAAA;QAEF,MAAM,IAAI,GAAI,MAAM,CAAC,OAAO,EAAkB,CAAC,IAAI,CAAA;QACnD,CAAC,CAAC,QAAQ,CAAC,GAAG,EAAE,GAAG,MAAM,CAAC,KAAK,EAAE,CAAA,CAAC,CAAC,CAAC,CAAA;QAEpC,MAAM,GAAG,GAAG,oBAAoB,IAAI,UAAU,CAAA;QAE9C,IAAI;YACF,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,CAAA;YACpC,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,QAAQ,EAAE,CAAA;YAEvC,MAAM,MAAM,GAAa,EAAE,CAAA;YAC3B,MAAM,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;gBAC1C,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAa,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAA;gBACxD,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,CAAC,CAAA;gBACzB,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC,CAAA;YAC5B,CAAC,CAAC,CAAA;YAEF,CAAC,CAAC,IAAI,CAAC,kCAAkC,CAAC,CAAA;SAC3C;QAAC,OAAO,KAAK,EAAE;YACd,MAAM,GAAG,GAAG,KAAc,CAAA;YAC1B,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,8BAA8B,GAAG,CAAC,OAAO,EAAE,CAAC,CAAA;SACnF;QAED,CAAC,CAAC,GAAG,EAAE,CAAA;IACT,CAAC,CAAC,CAAA;IAEF,MAAM,CAAC,CAAC,IAAI,CAAC,+BAA+B,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE;QACxD,IAAI,eAAe,GAAG,KAAK,CAAA;QAE3B,uDAAuD;QACvD,MAAM,MAAM,GAAG,YAAY,CAAC,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,EAAE;YAC7C,IAAI,GAAG,CAAC,MAAM,KAAK,MAAM,EAAE;gBACzB,GAAG,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,gBAAgB,EAAE,KAAK,EAAE,CAAC,CAAA;gBAC/C,GAAG,CAAC,GAAG,EAAE,CAAA;gBACT,OAAM;aACP;YAED,eAAe,GAAG,IAAI,CAAA;YACtB,8BAA8B;YAC9B,iCAAiC;YACjC,MAAM,UAAU,CAAC,MAAM,CAAC,oBAAoB,GAAG,GAAG,CAAC,CAAA;YACnD,GAAG,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,gBAAgB,EAAE,IAAI,EAAE,CAAC,CAAA;YAC9C,GAAG,CAAC,GAAG,CAAC,UAAU,CAAC,CAAA;QACrB,CAAC,CAAC,CAAA;QAEF,MAAM,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE;YAClC,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,WAAW,EAAE,OAAO,CAAC,CAAA;QACxC,CAAC,CAAC,CAAA;QAEF,MAAM,IAAI,GAAI,MAAM,CAAC,OAAO,EAAkB,CAAC,IAAI,CAAA;QACnD,CAAC,CAAC,QAAQ,CAAC,GAAG,EAAE,GAAG,MAAM,CAAC,KAAK,EAAE,CAAA,CAAC,CAAC,CAAC,CAAA;QAEpC,MAAM,GAAG,GAAG,oBAAoB,IAAI,kBAAkB,CAAA;QAEtD,IAAI;YACF,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,CAAA;YACpC,MAAM,OAAO,CAAC,QAAQ,EAAE,CAAA;YACxB,CAAC,CAAC,IAAI,CAAC,kCAAkC,CAAC,CAAA;SAC3C;QAAC,OAAO,KAAK,EAAE;YACd,MAAM,GAAG,GAAG,KAAc,CAAA;YAC1B,CAAC,CAAC,EAAE,CAAC,eAAe,EAAE,8BAA8B,CAAC,CAAA;YACrD,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,8BAA8B,GAAG,CAAC,OAAO,EAAE,CAAC,CAAA;SACnF;QAED,CAAC,CAAC,GAAG,EAAE,CAAA;IACT,CAAC,CAAC,CAAA;AACJ,CAAC,CAAC,CAAA"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@juzi/file-box",
3
- "version": "1.7.20",
3
+ "version": "1.8.1",
4
4
  "description": "Pack a File into Box for easy move/transfer between servers no matter of where it is.(local path, remote url, or cloud storage)",
5
5
  "type": "module",
6
6
  "exports": {
@@ -87,7 +87,7 @@
87
87
  ],
88
88
  "publishConfig": {
89
89
  "access": "public",
90
- "tag": "next"
90
+ "tag": "latest"
91
91
  },
92
92
  "git": {
93
93
  "scripts": {
package/src/config.ts CHANGED
@@ -1,15 +1,9 @@
1
1
  /// <reference path="./typings.d.ts" />
2
2
  export { VERSION } from './version.js'
3
3
 
4
- export const HTTP_REQUEST_TIMEOUT = Number(process.env['FILEBOX_HTTP_REQUEST_TIMEOUT'])
5
- || 10 * 1000
6
-
7
- export const HTTP_RESPONSE_TIMEOUT = Number(process.env['FILEBOX_HTTP_RESPONSE_TIMEOUT'] ?? process.env['FILEBOX_HTTP_TIMEOUT'])
8
- || 60 * 1000
9
-
10
- export const NO_SLICE_DOWN = process.env['FILEBOX_NO_SLICE_DOWN'] === 'true'
11
-
12
- export const HTTP_CHUNK_SIZE = Number(process.env['FILEBOX_HTTP_CHUNK_SIZE'])
13
- || 1024 * 512
14
-
15
- export const READY_RETRY = Number(process.env['FILE_BOX_READY_RETRY']) || 3
4
+ // 导出可变配置对象,支持测试时动态修改
5
+ export const CONFIG = {
6
+ HTTP_REQUEST_TIMEOUT: Number(process.env['FILEBOX_HTTP_REQUEST_TIMEOUT']) || 10 * 1000,
7
+ HTTP_RESPONSE_TIMEOUT: Number(process.env['FILEBOX_HTTP_RESPONSE_TIMEOUT'] ?? process.env['FILEBOX_HTTP_TIMEOUT']) || 60 * 1000,
8
+ READY_RETRY: Number(process.env['FILEBOX_READY_RETRY'] ?? process.env['FILE_BOX_READY_RETRY']) || 3,
9
+ }
package/src/file-box.ts CHANGED
@@ -25,7 +25,7 @@ import {
25
25
  } from 'clone-class'
26
26
 
27
27
  import {
28
- READY_RETRY,
28
+ CONFIG,
29
29
  VERSION,
30
30
  } from './config.js'
31
31
  import {
@@ -637,7 +637,7 @@ class FileBox implements Pipeable, FileBoxInterface {
637
637
  break
638
638
  } catch (e) {
639
639
  tryCount++
640
- if (tryCount >= READY_RETRY) {
640
+ if (tryCount >= CONFIG.READY_RETRY) {
641
641
  throw e
642
642
  }
643
643
  }
package/src/misc.spec.ts CHANGED
@@ -5,6 +5,7 @@ import { createServer } from 'http'
5
5
  import type { AddressInfo } from 'net'
6
6
  import { test } from 'tstest'
7
7
 
8
+ import { CONFIG } from './config.js'
8
9
  import {
9
10
  dataUrlToBase64,
10
11
  httpHeaderToFileName,
@@ -13,6 +14,10 @@ import {
13
14
  streamToBuffer,
14
15
  } from './misc.js'
15
16
 
17
+ // 设置短超时用于测试
18
+ CONFIG.HTTP_REQUEST_TIMEOUT = 1000
19
+ CONFIG.HTTP_RESPONSE_TIMEOUT = 1000
20
+
16
21
  test('dataUrl to base64', async t => {
17
22
  const base64 = [
18
23
  'R0lGODlhEAAQAMQAAORHHOVSKudfOulrSOp3WOyDZu6QdvCchPGolfO0o/XBs/fNwfjZ0frl',
@@ -85,8 +90,25 @@ test('httpHeaderToFileName', async t => {
85
90
 
86
91
  test('httpStream', async t => {
87
92
  const server = createServer((req, res) => {
88
- res.writeHead(200, { 'Content-Type': 'application/json' })
89
- res.end(JSON.stringify({ headers: req.headers }))
93
+ const content = JSON.stringify({ headers: req.headers })
94
+
95
+ // Handle HEAD requests
96
+ if (req.method === 'HEAD') {
97
+ res.writeHead(200, {
98
+ 'Content-Length': String(content.length),
99
+ 'Content-Type': 'application/json',
100
+ })
101
+ res.end()
102
+ return
103
+ }
104
+
105
+ // This server doesn't support Range, always return 200 with full content
106
+ // (ignoring any Range header)
107
+ res.writeHead(200, {
108
+ 'Content-Length': String(content.length),
109
+ 'Content-Type': 'application/json',
110
+ })
111
+ res.end(content)
90
112
  })
91
113
 
92
114
  const host = await new Promise<string>((resolve) => {
@@ -128,14 +150,14 @@ test('httpStream in chunks', async (t) => {
128
150
 
129
151
  const range = req.headers.range
130
152
  if (range) {
131
- const m = String(range).match(/bytes=(\d+)-(\d+)/)
153
+ const m = String(range).match(/bytes=(\d+)-(\d*)/)
132
154
  if (!m) {
133
155
  res.writeHead(416)
134
156
  res.end()
135
157
  return
136
158
  }
137
159
  const start = Number(m[1])
138
- const end = Number(m[2])
160
+ const end = m[2] ? Number(m[2]) : FILE_SIZE - 1
139
161
  const chunk = content.subarray(start, end + 1)
140
162
  res.writeHead(206, {
141
163
  'Accept-Ranges': 'bytes',
@@ -161,14 +183,7 @@ test('httpStream in chunks', async (t) => {
161
183
  })
162
184
  t.teardown(() => { server.close() })
163
185
 
164
- const originalChunkSize = process.env['FILEBOX_HTTP_CHUNK_SIZE']
165
- process.env['FILEBOX_HTTP_CHUNK_SIZE'] = String(256 * 1024)
166
- try {
167
- const res = await httpStream(`${host}/file`)
168
- const buffer = await streamToBuffer(res)
169
- t.equal(buffer.length, FILE_SIZE, 'should get data in chunks right')
170
- } finally {
171
- if (originalChunkSize) process.env['FILEBOX_HTTP_CHUNK_SIZE'] = originalChunkSize
172
- else delete process.env['FILEBOX_HTTP_CHUNK_SIZE']
173
- }
186
+ const res = await httpStream(`${host}/file`)
187
+ const buffer = await streamToBuffer(res)
188
+ t.equal(buffer.length, FILE_SIZE, 'should get data in chunks right')
174
189
  })