web-dc-api 0.1.5 → 0.1.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.
Files changed (148) hide show
  1. package/dist/cjs/index.js +1 -1
  2. package/dist/dc.min.js +1 -1
  3. package/dist/esm/index.js +1 -1
  4. package/dist/index.d.ts +934 -878
  5. package/package.json +4 -8
  6. package/dist/cjs/helia-core-B1Xqha7a.js +0 -1
  7. package/dist/cjs/helia-core-D8Uv1KjQ.js +0 -1
  8. package/dist/cjs/polkadot-api-7PhQf3ws.js +0 -1
  9. package/dist/cjs/polkadot-api-CtrJVWuZ.js +0 -1
  10. package/dist/esm/chunks/helia-core-BxMqyK2Y.js +0 -1
  11. package/dist/esm/chunks/helia-core-DMXRpcO-.js +0 -1
  12. package/dist/esm/chunks/polkadot-api-5Y9Bw8VT.js +0 -1
  13. package/dist/esm/chunks/polkadot-api-D69Ioun_.js +0 -1
  14. package/lib/common/blowfish/block.ts +0 -259
  15. package/lib/common/blowfish/cipher.ts +0 -144
  16. package/lib/common/blowfish/const.ts +0 -195
  17. package/lib/common/chain.ts +0 -469
  18. package/lib/common/commonclient.ts +0 -202
  19. package/lib/common/constants.ts +0 -55
  20. package/lib/common/dc-key/ed25519.ts +0 -343
  21. package/lib/common/dc-key/keyManager.ts +0 -424
  22. package/lib/common/dcapi.ts +0 -98
  23. package/lib/common/dcutil.ts +0 -627
  24. package/lib/common/define.ts +0 -70
  25. package/lib/common/error.ts +0 -67
  26. package/lib/common/grpc-dc.ts +0 -104
  27. package/lib/common/module-system.ts +0 -184
  28. package/lib/common/service-worker.ts +0 -234
  29. package/lib/common/types/types.ts +0 -344
  30. package/lib/dc.ts +0 -701
  31. package/lib/implements/account/client.ts +0 -185
  32. package/lib/implements/account/manager.ts +0 -683
  33. package/lib/implements/aiproxy/client.ts +0 -357
  34. package/lib/implements/aiproxy/manager.ts +0 -670
  35. package/lib/implements/cache/client.ts +0 -105
  36. package/lib/implements/cache/manager.ts +0 -127
  37. package/lib/implements/comment/client.ts +0 -982
  38. package/lib/implements/comment/manager.ts +0 -1151
  39. package/lib/implements/dc/client.ts +0 -51
  40. package/lib/implements/dc/manager.ts +0 -33
  41. package/lib/implements/file/client.ts +0 -253
  42. package/lib/implements/file/file-cache-manager.ts +0 -142
  43. package/lib/implements/file/manager.ts +0 -1240
  44. package/lib/implements/file/seekableFileStream.ts +0 -344
  45. package/lib/implements/file/streamwriter.ts +0 -322
  46. package/lib/implements/keyvalue/client.ts +0 -376
  47. package/lib/implements/keyvalue/manager.ts +0 -759
  48. package/lib/implements/message/client.ts +0 -250
  49. package/lib/implements/message/manager.ts +0 -215
  50. package/lib/implements/threaddb/cbor/coding.ts +0 -62
  51. package/lib/implements/threaddb/cbor/event.ts +0 -336
  52. package/lib/implements/threaddb/cbor/node.ts +0 -542
  53. package/lib/implements/threaddb/cbor/record.ts +0 -398
  54. package/lib/implements/threaddb/common/AsyncMutex.ts +0 -24
  55. package/lib/implements/threaddb/common/addrinfo.ts +0 -135
  56. package/lib/implements/threaddb/common/dispatcher.ts +0 -81
  57. package/lib/implements/threaddb/common/idbstore-adapter.ts +0 -260
  58. package/lib/implements/threaddb/common/json-patcher.ts +0 -204
  59. package/lib/implements/threaddb/common/key.ts +0 -290
  60. package/lib/implements/threaddb/common/level-adapter.ts +0 -235
  61. package/lib/implements/threaddb/common/lineReader.ts +0 -79
  62. package/lib/implements/threaddb/common/logstore.ts +0 -215
  63. package/lib/implements/threaddb/common/transformed-datastore.ts +0 -308
  64. package/lib/implements/threaddb/core/app.ts +0 -206
  65. package/lib/implements/threaddb/core/core.ts +0 -230
  66. package/lib/implements/threaddb/core/db.ts +0 -249
  67. package/lib/implements/threaddb/core/event.ts +0 -54
  68. package/lib/implements/threaddb/core/head.ts +0 -89
  69. package/lib/implements/threaddb/core/identity.ts +0 -171
  70. package/lib/implements/threaddb/core/logstore.ts +0 -137
  71. package/lib/implements/threaddb/core/options.ts +0 -14
  72. package/lib/implements/threaddb/core/record.ts +0 -54
  73. package/lib/implements/threaddb/db/collection.ts +0 -1910
  74. package/lib/implements/threaddb/db/db.ts +0 -698
  75. package/lib/implements/threaddb/db/json2Query.ts +0 -192
  76. package/lib/implements/threaddb/db/query.ts +0 -524
  77. package/lib/implements/threaddb/dbclient.ts +0 -543
  78. package/lib/implements/threaddb/dbmanager.ts +0 -1906
  79. package/lib/implements/threaddb/lsstoreds/addr_book.ts +0 -549
  80. package/lib/implements/threaddb/lsstoreds/cache.ts +0 -36
  81. package/lib/implements/threaddb/lsstoreds/cyclic_batch.ts +0 -87
  82. package/lib/implements/threaddb/lsstoreds/global.ts +0 -151
  83. package/lib/implements/threaddb/lsstoreds/headbook.ts +0 -373
  84. package/lib/implements/threaddb/lsstoreds/keybook.ts +0 -297
  85. package/lib/implements/threaddb/lsstoreds/logstore.ts +0 -29
  86. package/lib/implements/threaddb/lsstoreds/metadata.ts +0 -223
  87. package/lib/implements/threaddb/net/define.ts +0 -149
  88. package/lib/implements/threaddb/net/grpcClient.ts +0 -589
  89. package/lib/implements/threaddb/net/grpcserver.ts +0 -146
  90. package/lib/implements/threaddb/net/net.ts +0 -2047
  91. package/lib/implements/threaddb/pb/lstore.proto +0 -38
  92. package/lib/implements/threaddb/pb/lstore.ts +0 -393
  93. package/lib/implements/threaddb/pb/lstore_pb.d.ts +0 -433
  94. package/lib/implements/threaddb/pb/lstore_pb.js +0 -1085
  95. package/lib/implements/threaddb/pb/net.proto +0 -194
  96. package/lib/implements/threaddb/pb/net_pb.d.ts +0 -2349
  97. package/lib/implements/threaddb/pb/net_pb.js +0 -5525
  98. package/lib/implements/threaddb/pb/proto-custom-types.ts +0 -212
  99. package/lib/implements/util/client.ts +0 -72
  100. package/lib/implements/util/manager.ts +0 -146
  101. package/lib/implements/wallet/manager.ts +0 -671
  102. package/lib/index.ts +0 -57
  103. package/lib/interfaces/DCContext.ts +0 -51
  104. package/lib/interfaces/aiproxy-interface.ts +0 -145
  105. package/lib/interfaces/auth-interface.ts +0 -118
  106. package/lib/interfaces/cache-interface.ts +0 -22
  107. package/lib/interfaces/client-interface.ts +0 -11
  108. package/lib/interfaces/comment-interface.ts +0 -167
  109. package/lib/interfaces/components/news-component.ts +0 -0
  110. package/lib/interfaces/database-interface.ts +0 -169
  111. package/lib/interfaces/file-interface.ts +0 -120
  112. package/lib/interfaces/index.ts +0 -10
  113. package/lib/interfaces/keyvalue-interface.ts +0 -156
  114. package/lib/interfaces/message-interface.ts +0 -22
  115. package/lib/interfaces/util-interface.ts +0 -31
  116. package/lib/modules/aiproxy-module.ts +0 -246
  117. package/lib/modules/auth-module.ts +0 -753
  118. package/lib/modules/cache-module.ts +0 -99
  119. package/lib/modules/client-module.ts +0 -71
  120. package/lib/modules/comment-module.ts +0 -429
  121. package/lib/modules/components/news-components.ts +0 -390
  122. package/lib/modules/database-module.ts +0 -598
  123. package/lib/modules/file-module.ts +0 -291
  124. package/lib/modules/index.ts +0 -13
  125. package/lib/modules/keyvalue-module.ts +0 -379
  126. package/lib/modules/message-module.ts +0 -107
  127. package/lib/modules/util-module.ts +0 -148
  128. package/lib/polyfills/process-env-browser.ts +0 -1
  129. package/lib/proto/datasource.ts +0 -93
  130. package/lib/proto/dcnet.proto +0 -1601
  131. package/lib/proto/dcnet_proto.d.ts +0 -22857
  132. package/lib/proto/dcnet_proto.js +0 -55204
  133. package/lib/proto/dcnet_proto_sparse.js +0 -55166
  134. package/lib/proto/oidfetch.proto +0 -25
  135. package/lib/proto/oidfetch_proto.d.ts +0 -585
  136. package/lib/proto/oidfetch_proto.js +0 -1247
  137. package/lib/serverless/babel-browser.ts +0 -39
  138. package/lib/serverless/base_entity.ts +0 -78
  139. package/lib/serverless/base_repository.ts +0 -414
  140. package/lib/serverless/browser_schema_extractor.ts +0 -283
  141. package/lib/serverless/decorator_factory.ts +0 -322
  142. package/lib/util/BrowserLineReader.ts +0 -73
  143. package/lib/util/base64.ts +0 -105
  144. package/lib/util/bcrypt.ts +0 -206
  145. package/lib/util/curve25519Encryption.ts +0 -418
  146. package/lib/util/dccrypt.ts +0 -73
  147. package/lib/util/logger.ts +0 -104
  148. package/lib/util/utils.ts +0 -289
@@ -1,344 +0,0 @@
1
- import { UnixFS } from "@helia/unixfs";
2
- import { CID } from "multiformats/cid";
3
- import toBuffer from "it-to-buffer";
4
- import { decryptContent } from "../../util/dccrypt";
5
-
6
- /**
7
- * 可定位文件流类
8
- * 支持随机访问和流式读取加密和未加密的IPFS文件
9
- */
10
- export class SeekableFileStream {
11
- private position: number = 0;
12
- private fs: UnixFS;
13
- private cid: CID;
14
- private decryptKey: string;
15
- private fileInfo: { size: number; hasHeader: boolean; headerSize: number };
16
- private encryptChunkSize: number;
17
-
18
- constructor(options: {
19
- fileInfo: { size: number; hasHeader: boolean; headerSize: number };
20
- fs: UnixFS;
21
- cid: CID;
22
- decryptKey: string;
23
- encryptChunkSize: number;
24
- }) {
25
- this.fileInfo = options.fileInfo;
26
- this.fs = options.fs;
27
- this.cid = options.cid;
28
- this.decryptKey = options.decryptKey;
29
- this.encryptChunkSize = options.encryptChunkSize;
30
- }
31
-
32
- /**
33
- * 获取文件大小
34
- */
35
- getSize(): number {
36
- return this.fileInfo.size;
37
- }
38
-
39
- /**
40
- * 定位到文件指定位置
41
- * @param position 目标位置
42
- */
43
- seek(position: number): void {
44
- if (position < 0) {
45
- position = 0;
46
- } else if (position > this.fileInfo.size) {
47
- position = this.fileInfo.size;
48
- }
49
- this.position = position;
50
- }
51
-
52
- /**
53
- * 获取当前位置
54
- */
55
- getPosition(): number {
56
- return this.position;
57
- }
58
-
59
- /**
60
- * 读取指定长度的数据
61
- * @param length 要读取的字节数
62
- */
63
- async read(length: number): Promise<Uint8Array> {
64
- // 使用局部变量存储当前位置,避免并发问题
65
- const startPosition = this.position;
66
-
67
- if (startPosition >= this.fileInfo.size) {
68
- // 已到达文件末尾
69
- return new Uint8Array(0);
70
- }
71
-
72
- // 计算实际读取长度(不超过文件末尾)
73
- const remainingBytes = this.fileInfo.size - startPosition;
74
- const actualLength = Math.min(length, remainingBytes);
75
-
76
- let result: Uint8Array;
77
- console.log("**************read actualLength:", actualLength);
78
- // 根据是否加密选择不同读取策略
79
- if (this.decryptKey) {
80
- result = await this.readEncrypted(startPosition, actualLength);
81
- } else {
82
- result = await this.readPlain(startPosition, actualLength);
83
- }
84
- console.log("**************read result length:", result.length);
85
- // 仅在成功读取后更新位置
86
- if(this.position == startPosition) { //表示没有seek过
87
- this.position = startPosition + result.length;
88
- console.log("**************read this.position:", this.position);
89
- }
90
- return result;
91
-
92
- }
93
-
94
- /**
95
- * 读取未加密的数据
96
- * @param startPosition 开始位置
97
- * @param length 读取长度
98
- */
99
- private async readPlain(startPosition: number, length: number): Promise<Uint8Array> {
100
- try {
101
- // 计算实际文件中的偏移量(考虑文件头)
102
- const actualOffset = startPosition + this.fileInfo.headerSize;
103
-
104
- // 直接读取指定长度
105
- const data = await toBuffer(this.fs.cat(this.cid, {
106
- offset: actualOffset,
107
- length: length
108
- }));
109
-
110
- return data;
111
- } catch (err) {
112
- console.error("Error reading plain data:", err);
113
- return new Uint8Array(0);
114
- }
115
- }
116
- /**
117
- * 读取并解密加密数据,无音视频帧保护
118
- * @param startPosition 开始位置
119
- * @param length 读取长度
120
- */
121
- private async readEncrypted(startPosition: number, length: number): Promise<Uint8Array> {
122
- // 计算块相关参数
123
- const blockSize = 3 << 20; // 3MB 原始块大小
124
- const blockIndex = Math.floor(startPosition / blockSize);
125
- const offsetInBlock = startPosition % blockSize ;
126
-
127
- // 计算需要读取的块数
128
- const endPosition = startPosition + length ;
129
- const endBlockIndex = Math.floor(endPosition / blockSize);
130
- const neededBlocks = endBlockIndex - blockIndex + 1;
131
-
132
- // 分配适当大小的内存
133
- const totalSize = Math.min(neededBlocks * blockSize, offsetInBlock + length);
134
- let decryptedData = new Uint8Array(totalSize);
135
-
136
- // 计算文件中的起始位置
137
- let readStartPosition = this.fileInfo.headerSize + blockIndex * this.encryptChunkSize;
138
-
139
-
140
- const readTotalSize = neededBlocks * this.encryptChunkSize;
141
-
142
- try {
143
- // 读取加密块
144
- const encryptedBlocks = await toBuffer(this.fs.cat(this.cid, {
145
- offset: readStartPosition,
146
- length: readTotalSize
147
- }));
148
-
149
- // 确保获取到了数据
150
- if (encryptedBlocks.length === 0) {
151
- console.warn("无法读取加密数据");
152
- return new Uint8Array(0);
153
- }
154
-
155
- // 解密所有块并合并
156
- let currentOffset = 0;
157
- for (let i = 0; i < neededBlocks; i++) {
158
- const start = i * this.encryptChunkSize;
159
- if (start >= encryptedBlocks.byteLength){
160
- console.warn("*******加密数据块超出范围");
161
- break;
162
- }
163
-
164
- const end = Math.min((i + 1) * this.encryptChunkSize, encryptedBlocks.length);
165
- const encryptedBlock = encryptedBlocks.slice(start, end);
166
-
167
- if (encryptedBlock.byteLength === 0){
168
- console.warn("*******加密数据块为空");
169
- continue;
170
- }
171
-
172
- // 解密块
173
- const decryptedBlock = await decryptContent(encryptedBlock, this.decryptKey);
174
-
175
- // 合并到结果中
176
- const bytesToCopy = Math.min(decryptedBlock.length, decryptedData.length - currentOffset);
177
- decryptedData.set(decryptedBlock.subarray(0, bytesToCopy), currentOffset);
178
- currentOffset += bytesToCopy;
179
- }
180
-
181
- // 从解密数据中提取请求的范围
182
- let result = new Uint8Array(length);
183
- result.set(decryptedData.subarray(offsetInBlock, offsetInBlock + length), 0);
184
- return result;
185
- } catch (err) {
186
- console.error("Error reading encrypted data:", err);
187
- return new Uint8Array(0);
188
- }
189
- }
190
-
191
- /**
192
- * 创建标准的可读流
193
- * 使用独立缓冲区,不影响随机访问读取
194
- */
195
- createReadableStream(options?: { start?: number, end?: number }): ReadableStream<Uint8Array> {
196
- // 为流创建独立的状态变量,不使用实例变量
197
- let streamPosition = options?.start !== undefined ? options.start : this.position;
198
- let streamBuffer = new Uint8Array(0);
199
- let streamBufferPosition = streamPosition;
200
-
201
- // 确保位置有效
202
- if (streamPosition < 0) {
203
- streamPosition = 0;
204
- } else if (streamPosition > this.fileInfo.size) {
205
- streamPosition = this.fileInfo.size;
206
- }
207
-
208
- const end = options?.end ?? this.fileInfo.size;
209
- const chunkSize = 64 * 1024; // 64KB 客户端块
210
- const bufferSize = 256 * 1024; // 256KB 预读缓冲区
211
-
212
- /**
213
- * 流式读取专用的预读函数
214
- * 使用独立缓冲区
215
- */
216
- const prefetchStreamData = async (size: number): Promise<void> => {
217
- const maxSize = Math.min(size, this.fileInfo.size - streamPosition);
218
-
219
- if (maxSize <= 0) return;
220
-
221
- try {
222
- // 读取数据到独立的流缓冲区
223
- let prefetchedData: Uint8Array;
224
- if (this.decryptKey) {
225
- prefetchedData = await this.readEncrypted(streamPosition, maxSize);
226
- } else {
227
- prefetchedData = await this.readPlain(streamPosition, maxSize);
228
- }
229
-
230
- // 更新流缓冲区和位置
231
- streamBuffer = prefetchedData;
232
- streamBufferPosition = streamPosition;
233
- } catch (err) {
234
- console.error("Error prefetching stream data:", err);
235
- streamBuffer = new Uint8Array(0);
236
- }
237
- };
238
-
239
- /**
240
- * 流式读取专用的读取函数
241
- * 使用独立缓冲区和位置
242
- */
243
- const streamRead = async (length: number): Promise<Uint8Array> => {
244
- if (streamPosition >= this.fileInfo.size) {
245
- return new Uint8Array(0);
246
- }
247
-
248
- const remainingBytes = this.fileInfo.size - streamPosition;
249
- const actualLength = Math.min(length, remainingBytes);
250
-
251
- let result: Uint8Array;
252
-
253
- // 检查流缓冲区中是否有足够数据
254
- if (streamPosition >= streamBufferPosition &&
255
- streamPosition + actualLength <= streamBufferPosition + streamBuffer.length) {
256
-
257
- // 从流缓冲区获取数据
258
- const bufferOffset = streamPosition - streamBufferPosition;
259
- result = streamBuffer.slice(bufferOffset, bufferOffset + actualLength);
260
- } else {
261
- // 缓冲区中没有所需数据,直接读取
262
- if (this.decryptKey) {
263
- result = await this.readEncrypted(streamPosition, actualLength);
264
- } else {
265
- result = await this.readPlain(streamPosition, actualLength);
266
- }
267
- }
268
-
269
- // 更新流位置
270
- streamPosition += result.length;
271
- return result;
272
- };
273
-
274
- return new ReadableStream({
275
- start: async (controller) => {
276
- // 初始预读
277
- try {
278
- await prefetchStreamData(bufferSize);
279
- } catch (err) {
280
- console.warn("Initial stream prefetch failed:", err);
281
- }
282
- },
283
-
284
- pull: async (controller) => {
285
- try {
286
- if (streamPosition >= end) {
287
- controller.close();
288
- return;
289
- }
290
-
291
- const bytesToRead = Math.min(chunkSize, end - streamPosition);
292
-
293
- // 检查流缓冲区中是否有足够数据
294
- if (streamPosition >= streamBufferPosition &&
295
- streamPosition + bytesToRead <= streamBufferPosition + streamBuffer.length) {
296
-
297
- // 从流缓冲区获取数据
298
- const bufferOffset = streamPosition - streamBufferPosition;
299
- const chunk = streamBuffer.slice(bufferOffset, bufferOffset + bytesToRead);
300
- streamPosition += chunk.length;
301
- controller.enqueue(chunk);
302
-
303
- // 如果接近缓冲区末尾,预读取更多数据
304
- const prefetchThreshold = chunkSize * 2;
305
- if (streamPosition + prefetchThreshold > streamBufferPosition + streamBuffer.length &&
306
- streamPosition < end) {
307
- prefetchStreamData(bufferSize)
308
- .catch(err => console.warn("Stream prefetch failed:", err));
309
- }
310
- } else {
311
- // 缓冲区中没有所需数据,直接读取并同时更新缓冲区
312
- await prefetchStreamData(Math.max(bufferSize, bytesToRead));
313
-
314
- // 从新缓冲区获取数据
315
- const bufferOffset = streamPosition - streamBufferPosition;
316
- if (bufferOffset >= 0 && bufferOffset < streamBuffer.length) {
317
- const chunk = streamBuffer.slice(bufferOffset,
318
- Math.min(bufferOffset + bytesToRead, streamBuffer.length));
319
- streamPosition += chunk.length;
320
- controller.enqueue(chunk);
321
- } else {
322
- // 缓冲区没有预期的数据,直接读取
323
- const chunk = await streamRead(bytesToRead);
324
- if (chunk.length > 0) {
325
- controller.enqueue(chunk);
326
- } else {
327
- controller.close();
328
- }
329
- }
330
- }
331
- } catch (err) {
332
- console.error("Stream read error:", err);
333
- controller.error(err);
334
- }
335
- },
336
-
337
- cancel: () => {
338
- // 清理资源
339
- streamBuffer = new Uint8Array(0);
340
- console.log("ReadableStream cancelled");
341
- }
342
- });
343
- }
344
- }
@@ -1,322 +0,0 @@
1
- import { pipe } from 'it-pipe'
2
- import { pushable, Pushable } from 'it-pushable'
3
-
4
- interface StreamWriterOptions {
5
- /** 分块大小(默认1MB) */
6
- chunkSize?: number
7
- /** 背压缓冲区最大值(默认5MB) */
8
- bufferSize?: number
9
- /** 失败重试次数(默认3次) */
10
- retries?: number
11
- }
12
-
13
- interface BackpressureEventDetail {
14
- currentSize: number
15
- averageSize: number
16
- threshold: number
17
- waitingTime: number
18
- }
19
-
20
- interface EnhancedPushable<T> extends Pushable<T> {
21
- // 使用类型合并增加自定义方法
22
- _originalPush: Pushable<T>['push']
23
- _originalNext: Pushable<T>['next']
24
- _queueSize: number
25
- }
26
-
27
-
28
- const MaxChunkSize = 4*1024 -5
29
- export class StreamWriter {
30
- private p: EnhancedPushable<Uint8Array>
31
- //private p = pushable({ objectMode: false })
32
-
33
- private bytesWritten = 0
34
- private abortController = new AbortController()
35
-
36
- // 背压控制相关属性
37
- private backpressureHistory: number[] = []
38
- private isBackpressure = false
39
- private writeQueue: (() => Promise<void>)[] = []
40
- private isProcessingQueue = false
41
-
42
-
43
- constructor(
44
- private sink: any,
45
- private options: StreamWriterOptions = {}
46
- ) {
47
-
48
- if (options){
49
- this.options = {
50
- chunkSize: options.chunkSize ?? MaxChunkSize,
51
- bufferSize: options.bufferSize ?? 5 * 1024 * 1024,
52
- retries: options.retries ?? 3
53
- }
54
- }else{
55
- this.options = {
56
- chunkSize: MaxChunkSize,
57
- bufferSize: 5 * 1024 * 1024,
58
- retries: 3
59
- }
60
- }
61
-
62
- if (this.options.chunkSize && this.options.chunkSize > MaxChunkSize) {
63
- this.options.chunkSize = MaxChunkSize
64
- }
65
- const basePushable = pushable<Uint8Array>({ objectMode: false }) as EnhancedPushable<Uint8Array>
66
-
67
- // 保留原始方法引用
68
- basePushable._originalPush = basePushable.push.bind(basePushable)
69
- basePushable._originalNext = basePushable.next.bind(basePushable)
70
- basePushable._queueSize = 0
71
- // 重写 next 方法
72
- Object.defineProperty(basePushable, 'next', {
73
- value: async () => {
74
- const result = await basePushable._originalNext();
75
- if (!result.done && result.value) {
76
- basePushable._queueSize -= result.value.byteLength;
77
- }
78
- return result;
79
- },
80
- writable: false,
81
- configurable: false
82
- });
83
- // 安全重写 push 方法
84
- Object.defineProperty(basePushable, 'push', {
85
- value: (chunk: Uint8Array) => {
86
- basePushable._queueSize += chunk.byteLength
87
- return basePushable._originalPush(chunk)
88
- },
89
- writable: false,
90
- configurable: false
91
- })
92
- this.p = basePushable
93
- this.startPipeline()
94
- }
95
-
96
-
97
- get queueSize():number {
98
- return this.p._queueSize
99
- }
100
-
101
- // 在 next() 操作时更新队列大小
102
- private async safeNext() {
103
- const result = await this.p.next()
104
- if (!result.done && result.value) {
105
- this.p._queueSize -= result.value.byteLength
106
- }
107
- return result
108
- }
109
-
110
- private handleError(err: Error) {
111
- this.dispatchEvent(new CustomEvent('error', { detail: err }))
112
- this.abort(err.message)
113
- }
114
-
115
-
116
- private startPipeline() {
117
- pipe(
118
- this.p,
119
- this.createTransform(),
120
- this.sink
121
- ).catch((err: Error) => this.handleError(err)) // 正确绑定this
122
- }
123
-
124
- private createTransform() {
125
- return async function* (source: AsyncIterable<Uint8Array>) {
126
- for await (const chunk of source) {
127
- yield chunk
128
- }
129
- }
130
- }
131
-
132
- async write(data: ArrayBuffer | Blob | string): Promise<void> {
133
- if (this.abortController.signal.aborted) return
134
-
135
- return new Promise((resolve, reject) => {
136
- const task = async () => {
137
- try {
138
- const buffer = await this.convertToBuffer(data)
139
- await this.writeChunks(buffer)
140
- resolve()
141
- } catch (err) {
142
- reject(err)
143
- }
144
- }
145
-
146
- this.writeQueue.push(task)
147
- this.processQueue()
148
- })
149
- }
150
-
151
- private async convertToBuffer(data: ArrayBuffer | Blob | string): Promise<ArrayBuffer> {
152
- if (data instanceof Blob) return data.arrayBuffer()
153
- if (typeof data === 'string') return new TextEncoder().encode(data).buffer
154
- return data
155
- }
156
-
157
- private async writeChunks(buffer: ArrayBuffer) {
158
- for (let offset = 0; offset < buffer.byteLength; offset += this.options.chunkSize!) {
159
- const end = Math.min(offset + this.options.chunkSize!, buffer.byteLength)
160
- const chunk = new Uint8Array( end - offset)
161
- chunk.set(new Uint8Array(buffer.slice(offset, end)))
162
-
163
- await this.retryableWrite(chunk)
164
- this.updateProgress(chunk.byteLength)
165
- }
166
- }
167
-
168
- private async retryableWrite(chunk: Uint8Array, attempt = 0): Promise<void> {
169
- try {
170
- await this.monitorBackpressure()
171
-
172
- await new Promise<void>((resolve, reject) => {
173
- try {
174
- this.p.push(chunk)
175
- }catch(err){
176
- reject(err)
177
- }
178
- resolve()
179
- })
180
- } catch (err) {
181
- if (attempt < this.options.retries!) {
182
- const delay = this.calculateRetryDelay(attempt)
183
- await new Promise(r => setTimeout(r, delay))
184
- return this.retryableWrite(chunk, attempt + 1)
185
- }
186
- throw err
187
- }
188
- }
189
-
190
- private async monitorBackpressure(): Promise<void> {
191
- const checkInterval = 50
192
- const historySize = 10
193
-
194
- while (true) {
195
- const currentSize = this.queueSize // 空值合并运算符
196
- if (currentSize>0){
197
- this.backpressureHistory.push(currentSize)
198
- }
199
- if (this.backpressureHistory.length > historySize) {
200
- this.backpressureHistory.shift()
201
- }
202
-
203
- const avg = this.backpressureHistory.reduce((a, b) => a + b, 0) / historySize
204
- const dynamicThreshold = Math.min(
205
- this.options.bufferSize! * 0.8,
206
- avg * 1.5
207
- )
208
-
209
- if (currentSize < dynamicThreshold || currentSize === 0 ) {
210
- if (this.isBackpressure) {
211
- this.isBackpressure = false
212
- this.dispatchEvent(new CustomEvent('backpressure', {
213
- detail: {
214
- currentSize,
215
- averageSize: avg,
216
- threshold: dynamicThreshold,
217
- waitingTime: 0
218
- }
219
- }))
220
- }
221
- return
222
- }
223
-
224
- if (!this.isBackpressure) {
225
- this.isBackpressure = true
226
- this.dispatchEvent(new CustomEvent('backpressure', {
227
- detail: {
228
- currentSize,
229
- averageSize: avg,
230
- threshold: dynamicThreshold,
231
- waitingTime: 0
232
- }
233
- }))
234
- }
235
-
236
- const waitTime = Math.min(
237
- 10,
238
- checkInterval * Math.pow(2, currentSize / dynamicThreshold)
239
- )
240
- await new Promise(r => setTimeout(r, waitTime))
241
-
242
- if (this.abortController.signal.aborted) break
243
- }
244
- }
245
-
246
-
247
- private calculateRetryDelay(attempt: number): number {
248
- const baseDelay = 10
249
- const maxDelay = 100
250
- return Math.min(
251
- baseDelay * Math.pow(2, attempt) + Math.random() * 100,
252
- maxDelay
253
- )
254
- }
255
-
256
- private async processQueue() {
257
- if (this.isProcessingQueue || this.abortController.signal.aborted) return
258
- this.isProcessingQueue = true
259
-
260
- while (this.writeQueue.length > 0) {
261
- if (this.abortController.signal.aborted) break
262
- await this.monitorBackpressure()
263
- const task = this.writeQueue.shift()!
264
- try {
265
- await task()
266
- } catch (err) {
267
- continue // 继续处理下一个任务
268
- }
269
-
270
- }
271
-
272
- this.isProcessingQueue = false
273
- }
274
-
275
- private updateProgress(bytes: number) {
276
- this.bytesWritten += bytes
277
- this.dispatchEvent(new CustomEvent('progress', {
278
- detail: { loaded: this.bytesWritten }
279
- }))
280
- }
281
-
282
- async end(): Promise<void> {
283
- this.p.end()
284
- await this.sink.return?.()
285
- this.cleanup()
286
- }
287
-
288
- abort(reason = 'User aborted') {
289
- this.abortController.abort(reason)
290
- this.cleanup()
291
- this.dispatchEvent(new CustomEvent('abort', { detail: reason }))
292
- }
293
-
294
- private cleanup() {
295
- this.p.end()
296
- this.abortController.abort()
297
- this.writeQueue = []
298
- }
299
-
300
- // 事件系统
301
- private listeners = new Map<string, Function[]>()
302
-
303
- addEventListener(type: string, callback: (event: CustomEvent) => void) {
304
- const handlers = this.listeners.get(type) || []
305
- handlers.push(callback)
306
- this.listeners.set(type, handlers)
307
- }
308
-
309
- // 修复后的代码片段
310
- private dispatchEvent(event: CustomEvent) {
311
- const handlers = this.listeners.get(event.type) || []
312
- handlers.forEach(handler => handler(event))
313
- }
314
-
315
- // 明确指定事件类型
316
- private dispatchBackpressureEvent(detail: BackpressureEventDetail) {
317
- this.dispatchEvent(new CustomEvent<BackpressureEventDetail>('backpressure', {
318
- detail
319
- }))
320
- }
321
-
322
- }