pure-md5 0.2.0 → 0.2.2
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 +38 -24
- package/dist/adapters/ie11.cjs +1 -2
- package/dist/adapters/ie11.js +1 -2
- package/dist/adapters/node.cjs +1 -2
- package/dist/adapters/node.js +1 -2
- package/dist/adapters/webcrypto.cjs +1 -2
- package/dist/adapters/webcrypto.js +1 -2
- package/dist/index.cjs +1 -2
- package/dist/index.d.ts +149 -5
- package/dist/index.js +3 -2
- package/dist/md5.cjs +1 -0
- package/dist/md5.d.ts +20 -0
- package/dist/md5.js +1 -0
- package/dist/stream/md5-stream.cjs +1 -2
- package/dist/stream/md5-stream.js +1 -2
- package/dist/stream/whatwg-stream.cjs +1 -2
- package/dist/stream/whatwg-stream.js +1 -2
- package/dist/utils/detect.cjs +1 -2
- package/dist/utils/detect.js +3 -2
- package/package.json +10 -15
- package/pure-md5-0.2.1.tgz +0 -0
- package/test-tree-shake.mjs +12 -0
- package/.aliases +0 -19
- package/.bash_profile +0 -12
- package/.bash_prompt +0 -56
- package/.changeset/README.md +0 -32
- package/.changeset/config.json +0 -16
- package/.continue/mcpServers/new-mcp-server.yaml +0 -10
- package/.continue/rules +0 -29
- package/.github/ISSUE_TEMPLATE/bug_report.md +0 -35
- package/.github/ISSUE_TEMPLATE/documentation.md +0 -20
- package/.github/ISSUE_TEMPLATE/feature_request.md +0 -20
- package/.github/PULL_REQUEST_TEMPLATE.md +0 -35
- package/.github/workflows/npm-publish.yml +0 -33
- package/.github/workflows/release.yml +0 -42
- package/CHANGELOG.md +0 -9
- package/CONTRIBUTING.md +0 -203
- package/MIGRATION_GUIDE_STREAMS.md +0 -374
- package/STREAM_API.md +0 -582
- package/STREAM_BENCHMARKS.md +0 -232
- package/STREAM_EXAMPLES.md +0 -669
- package/STREAM_OPTIMIZATION_REPORT.md +0 -136
- package/STREAM_TROUBLESHOOTING.md +0 -537
- package/WEB_CRYPTO_TESTS_SUMMARY.md +0 -140
- package/WHATWG_STREAMS.md +0 -191
- package/__tests__/adapters/node-crypto.test.ts +0 -167
- package/__tests__/adapters/web-crypto-node.test.ts +0 -73
- package/__tests__/adapters/web-crypto.test.ts +0 -195
- package/__tests__/add32.test.ts +0 -33
- package/__tests__/fallback.test.ts +0 -345
- package/__tests__/hex.test.ts +0 -38
- package/__tests__/hex_chr.test.ts +0 -20
- package/__tests__/index.test.ts +0 -87
- package/__tests__/integration/fixtures/test-file.txt +0 -1
- package/__tests__/integration/md5-stream-file.test.ts +0 -293
- package/__tests__/integration/node-crypto-file.test.ts +0 -86
- package/__tests__/integration/web-crypto.test.ts +0 -38
- package/__tests__/md51.test.ts +0 -73
- package/__tests__/md5block.test.ts +0 -61
- package/__tests__/md5cycle.test.ts +0 -48
- package/__tests__/round-functions.test.ts +0 -87
- package/__tests__/stream/fs-utils.test.ts +0 -209
- package/__tests__/stream/md5-stream-edge-cases.test.ts +0 -461
- package/__tests__/stream/md5-stream.test.ts +0 -418
- package/__tests__/stream/whatwg-stream.test.ts +0 -355
- package/__tests__/stream/whatwg-stream.test.ts.bak2 +0 -335
- package/benchmarks/md5-stream.bench.ts +0 -212
- package/benchmarks/whatwg-stream.bench.ts +0 -180
- package/dist/adapters/ie11.cjs.map +0 -1
- package/dist/adapters/ie11.js.map +0 -1
- package/dist/adapters/node.cjs.map +0 -1
- package/dist/adapters/node.js.map +0 -1
- package/dist/adapters/webcrypto.cjs.map +0 -1
- package/dist/adapters/webcrypto.js.map +0 -1
- package/dist/chunk-2YXXFGBV.js +0 -2
- package/dist/chunk-2YXXFGBV.js.map +0 -1
- package/dist/chunk-4KSCMS4Q.js +0 -2
- package/dist/chunk-4KSCMS4Q.js.map +0 -1
- package/dist/chunk-6P2QV5SR.js +0 -4
- package/dist/chunk-6P2QV5SR.js.map +0 -1
- package/dist/chunk-G5WHEAIQ.js +0 -2
- package/dist/chunk-G5WHEAIQ.js.map +0 -1
- package/dist/chunk-H2K353LR.js +0 -2
- package/dist/chunk-H2K353LR.js.map +0 -1
- package/dist/chunk-JKVD5LHZ.js +0 -2
- package/dist/chunk-JKVD5LHZ.js.map +0 -1
- package/dist/chunk-NWQ4N5RX.js +0 -2
- package/dist/chunk-NWQ4N5RX.js.map +0 -1
- package/dist/chunk-PHZ7FTYF.js +0 -2
- package/dist/chunk-PHZ7FTYF.js.map +0 -1
- package/dist/chunk-PNZTVQA7.js +0 -2
- package/dist/chunk-PNZTVQA7.js.map +0 -1
- package/dist/chunk-R4JB5MBR.js +0 -2
- package/dist/chunk-R4JB5MBR.js.map +0 -1
- package/dist/chunk-VFOAY6XI.js +0 -2
- package/dist/chunk-VFOAY6XI.js.map +0 -1
- package/dist/chunk-XB5BQIEX.js +0 -2
- package/dist/chunk-XB5BQIEX.js.map +0 -1
- package/dist/core/index.cjs +0 -2
- package/dist/core/index.cjs.map +0 -1
- package/dist/core/index.d.cts +0 -19
- package/dist/core/index.d.ts +0 -19
- package/dist/core/index.js +0 -2
- package/dist/core/index.js.map +0 -1
- package/dist/index.cjs.map +0 -1
- package/dist/index.d.cts +0 -84
- package/dist/index.js.map +0 -1
- package/dist/stream/adapter.cjs +0 -2
- package/dist/stream/adapter.cjs.map +0 -1
- package/dist/stream/adapter.d.cts +0 -63
- package/dist/stream/adapter.d.ts +0 -63
- package/dist/stream/adapter.js +0 -2
- package/dist/stream/adapter.js.map +0 -1
- package/dist/stream/fs-utils.cjs +0 -2
- package/dist/stream/fs-utils.cjs.map +0 -1
- package/dist/stream/fs-utils.d.cts +0 -137
- package/dist/stream/fs-utils.d.ts +0 -137
- package/dist/stream/fs-utils.js +0 -2
- package/dist/stream/fs-utils.js.map +0 -1
- package/dist/stream/index.cjs +0 -2
- package/dist/stream/index.cjs.map +0 -1
- package/dist/stream/index.d.cts +0 -4
- package/dist/stream/index.d.ts +0 -4
- package/dist/stream/index.js +0 -2
- package/dist/stream/index.js.map +0 -1
- package/dist/stream/light/index.cjs +0 -2
- package/dist/stream/light/index.cjs.map +0 -1
- package/dist/stream/light/index.d.cts +0 -4
- package/dist/stream/light/index.d.ts +0 -4
- package/dist/stream/light/index.js +0 -2
- package/dist/stream/light/index.js.map +0 -1
- package/dist/stream/md5-stream.cjs.map +0 -1
- package/dist/stream/md5-stream.js.map +0 -1
- package/dist/stream/whatwg-stream.cjs.map +0 -1
- package/dist/stream/whatwg-stream.js.map +0 -1
- package/dist/types-edGoGJ5V.d.cts +0 -42
- package/dist/types-edGoGJ5V.d.ts +0 -42
- package/dist/utils/detect.cjs.map +0 -1
- package/dist/utils/detect.js.map +0 -1
- package/planning/03-optimization-size-tree-shaking/01-es-modules-tree-shaking.md +0 -152
- package/planning/03-optimization-size-tree-shaking/02-consolidate-modules.md +0 -65
- package/planning/03-optimization-size-tree-shaking/03-remove-duplicate-add32.md +0 -93
- package/planning/03-optimization-size-tree-shaking/04-remove-runtime-check.md +0 -102
- package/planning/03-optimization-size-tree-shaking/05-optimize-loops-performance.md +0 -107
- package/planning/03-optimization-size-tree-shaking/06-tsup-formats-configuration.md +0 -227
- package/planning/03-optimization-size-tree-shaking/07-multiple-build-formats.md +0 -228
- package/planning/03-optimization-size-tree-shaking/08-benchmarks-metrics.md +0 -34
- package/planning/03-optimization-size-tree-shaking/MIGRATION_GUIDE.md +0 -260
- package/planning/03-optimization-size-tree-shaking/README.md +0 -173
- package/planning/03-optimization-size-tree-shaking/SUMMARY.md +0 -168
- package/planning/04-adapter-backend/03-backend-web-crypto.md +0 -149
- package/planning/04-adapter-backend/04-backend-node-crypto.md +0 -181
- package/planning/04-adapter-backend/05-backend-pure-js.md +0 -174
- package/planning/04-adapter-backend/06-backend-ie11.md +0 -158
- package/planning/04-adapter-backend/07-detection-environment.md +0 -232
- package/planning/04-adapter-backend/08-detection-backend.md +0 -210
- package/planning/04-adapter-backend/09-adapter-unified.md +0 -255
- package/planning/04-adapter-backend/10-fallback-mechanism.md +0 -333
- package/planning/04-adapter-backend/11-tests-backend-web-crypto.md +0 -191
- package/planning/04-adapter-backend/12-tests-backend-node-crypto.md +0 -222
- package/planning/04-adapter-backend/README.md +0 -45
- package/planning/05-documentation-publishing/01-README-optimization.md +0 -105
- package/planning/05-documentation-publishing/02-VitePress-site-evaluation.md +0 -136
- package/planning/05-documentation-publishing/03-Changeset-setup.md +0 -192
- package/planning/05-documentation-publishing/04-GitHub-templates.md +0 -252
- package/planning/05-documentation-publishing/README.md +0 -22
- package/planning/05-documentation-publishing/STATUS.md +0 -222
- package/planning/prd.md +0 -405
- package/planning/streams/01-create-md5stream-class.md +0 -69
- package/planning/streams/02-create-factory-api.md +0 -65
- package/planning/streams/03-fs-integration.md +0 -37
- package/planning/streams/04-whatwg-streams-support.md +0 -37
- package/planning/streams/05-audit-optimization.md +0 -121
- package/planning/streams/06-comprehensive-tests-docs.md +0 -137
- package/planning/streams/07-architecture-integration.md +0 -38
- package/planning/streams/README.md +0 -98
- package/tsup.config.ts +0 -24
package/planning/prd.md
DELETED
|
@@ -1,405 +0,0 @@
|
|
|
1
|
-
## 1. Stream Support (Streams) for Node.js
|
|
2
|
-
|
|
3
|
-
Stream processing allows hashing files of any size (videos, databases, logs) without loading the entire file into memory. This is critically important for server applications.
|
|
4
|
-
|
|
5
|
-
### How it works
|
|
6
|
-
|
|
7
|
-
In Node.js, streams read files in parts (chunks). Our task is to update the hash for each chunk and finalize it at the end.
|
|
8
|
-
|
|
9
|
-
### Implementation
|
|
10
|
-
|
|
11
|
-
```javascript
|
|
12
|
-
const { createHash } = require('node:crypto');
|
|
13
|
-
const { Readable } = require('node:stream');
|
|
14
|
-
|
|
15
|
-
class MD5Stream extends Readable {
|
|
16
|
-
constructor(options = {}) {
|
|
17
|
-
super(options);
|
|
18
|
-
this.hash = createHash('md5');
|
|
19
|
-
this.bytesProcessed = 0;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
// This method is called automatically when reading the stream
|
|
23
|
-
_transform(chunk, encoding, callback) {
|
|
24
|
-
this.hash.update(chunk);
|
|
25
|
-
this.bytesProcessed += chunk.length;
|
|
26
|
-
this.push(chunk); // Pass the chunk further down the stream
|
|
27
|
-
callback();
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
// Called at the end of the stream
|
|
31
|
-
_flush(callback) {
|
|
32
|
-
const digest = this.hash.digest('hex');
|
|
33
|
-
// Add metadata to the end of the stream or emit an event
|
|
34
|
-
this.emit('md5', { digest, bytes: this.bytesProcessed });
|
|
35
|
-
callback();
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
// Convenient API for users
|
|
40
|
-
function createMD5Stream() {
|
|
41
|
-
return new MD5Stream();
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
// Usage example:
|
|
45
|
-
const fs = require('node:fs');
|
|
46
|
-
|
|
47
|
-
const readStream = fs.createReadStream('large-file.iso');
|
|
48
|
-
const hasher = createMD5Stream();
|
|
49
|
-
|
|
50
|
-
readStream.pipe(hasher).on('md5', (result) => {
|
|
51
|
-
console.log(`MD5: ${result.digest}`);
|
|
52
|
-
console.log(`Size: ${result.bytes} bytes`);
|
|
53
|
-
});
|
|
54
|
-
```
|
|
55
|
-
|
|
56
|
-
### What this gives `pure-md5`
|
|
57
|
-
|
|
58
|
-
- **Competitive advantage**: None of the competitors (`md5`, `crypto-js`) offer built-in stream processing.
|
|
59
|
-
- **Memory efficiency**: Can hash 100 GB files on a server with 512 MB RAM.
|
|
60
|
-
- **Versatility**: Suitable for integration with any Node.js streams (HTTP requests, file system, network streams).
|
|
61
|
-
|
|
62
|
-
### Additionally: browser stream support
|
|
63
|
-
|
|
64
|
-
For browsers, you can add support for `ReadableStream` (WHATWG Streams):
|
|
65
|
-
|
|
66
|
-
```javascript
|
|
67
|
-
// browser-stream.js
|
|
68
|
-
async function hashFileThroughStream(file) {
|
|
69
|
-
const stream = file.stream();
|
|
70
|
-
const reader = stream.getReader();
|
|
71
|
-
const hash = await initializeMD5(); // your MD5 implementation
|
|
72
|
-
|
|
73
|
-
while (true) {
|
|
74
|
-
const { done, value } = await reader.read();
|
|
75
|
-
if (done) break;
|
|
76
|
-
hash.update(value);
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
return hash.digest('hex');
|
|
80
|
-
}
|
|
81
|
-
```
|
|
82
|
-
|
|
83
|
-
## 2. Web Crypto API Detection and Fallback Mechanisms
|
|
84
|
-
|
|
85
|
-
Web Crypto API is a cryptographic interface built into browsers and Node.js. It works dozens of times faster than pure JavaScript implementation and uses hardware acceleration.
|
|
86
|
-
|
|
87
|
-
### Smart Environment Detection
|
|
88
|
-
|
|
89
|
-
We need to determine which runtime environment and what level of support is available:
|
|
90
|
-
|
|
91
|
-
```javascript
|
|
92
|
-
// crypto-detect.js
|
|
93
|
-
export const CryptoBackend = {
|
|
94
|
-
WEB_CRYPTO: 'webcrypto', // Modern browsers, Node.js (with flags)
|
|
95
|
-
NODE_CRYPTO: 'nodecrypto', // Native Node.js crypto
|
|
96
|
-
IE11_MS_CRYPTO: 'ie11', // Internet Explorer 11 (window.msCrypto)
|
|
97
|
-
FALLBACK_JS: 'purejs' // Pure JS implementation (your current one)
|
|
98
|
-
};
|
|
99
|
-
|
|
100
|
-
export async function detectCryptoBackend() {
|
|
101
|
-
// 1. Check Node.js environment
|
|
102
|
-
if (typeof globalThis.crypto?.subtle?.digest === 'function') {
|
|
103
|
-
// Node.js 15+ with WebCrypto enabled
|
|
104
|
-
return CryptoBackend.WEB_CRYPTO;
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
if (typeof require === 'function') {
|
|
108
|
-
try {
|
|
109
|
-
const nodeCrypto = require('node:crypto');
|
|
110
|
-
if (nodeCrypto?.createHash) {
|
|
111
|
-
return CryptoBackend.NODE_CRYPTO;
|
|
112
|
-
}
|
|
113
|
-
} catch {
|
|
114
|
-
// node:crypto is not available
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
// 2. Check browser environment
|
|
119
|
-
if (typeof window !== 'undefined') {
|
|
120
|
-
// Modern browsers
|
|
121
|
-
if (window.crypto?.subtle?.digest) {
|
|
122
|
-
return CryptoBackend.WEB_CRYPTO;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
// Internet Explorer 11 (uses msCrypto)
|
|
126
|
-
if (window.msCrypto?.subtle?.digest) {
|
|
127
|
-
return CryptoBackend.IE11_MS_CRYPTO;
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
// 3. Nothing found — use pure JS
|
|
132
|
-
return CryptoBackend.FALLBACK_JS;
|
|
133
|
-
}
|
|
134
|
-
```
|
|
135
|
-
|
|
136
|
-
### Adapter for Different Backends
|
|
137
|
-
|
|
138
|
-
We create a unified interface that hides implementation details:
|
|
139
|
-
|
|
140
|
-
```javascript
|
|
141
|
-
// md5-adapter.js
|
|
142
|
-
import { CryptoBackend, detectCryptoBackend } from './crypto-detect.js';
|
|
143
|
-
import { md5 as pureJSMD5 } from './pure-md5.js';
|
|
144
|
-
|
|
145
|
-
class MD5Adapter {
|
|
146
|
-
constructor() {
|
|
147
|
-
this.backend = null;
|
|
148
|
-
this.initialized = false;
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
async initialize() {
|
|
152
|
-
if (this.initialized) return;
|
|
153
|
-
this.backend = await detectCryptoBackend();
|
|
154
|
-
this.initialized = true;
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
async hash(data) {
|
|
158
|
-
await this.initialize();
|
|
159
|
-
|
|
160
|
-
switch (this.backend) {
|
|
161
|
-
case CryptoBackend.WEB_CRYPTO:
|
|
162
|
-
return this.hashWithWebCrypto(data);
|
|
163
|
-
case CryptoBackend.NODE_CRYPTO:
|
|
164
|
-
return this.hashWithNodeCrypto(data);
|
|
165
|
-
case CryptoBackend.IE11_MS_CRYPTO:
|
|
166
|
-
return this.hashWithMsCrypto(data);
|
|
167
|
-
default:
|
|
168
|
-
return pureJSMD5(data);
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
async hashWithWebCrypto(data) {
|
|
173
|
-
const encoder = new TextEncoder();
|
|
174
|
-
const buffer = encoder.encode(data);
|
|
175
|
-
const hashBuffer = await crypto.subtle.digest('MD5', buffer);
|
|
176
|
-
return this.bufferToHex(hashBuffer);
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
async hashWithNodeCrypto(data) {
|
|
180
|
-
const crypto = require('node:crypto');
|
|
181
|
-
return crypto.createHash('md5').update(data).digest('hex');
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
// For IE11
|
|
185
|
-
async hashWithMsCrypto(data) {
|
|
186
|
-
const encoder = new TextEncoder();
|
|
187
|
-
const buffer = encoder.encode(data);
|
|
188
|
-
const hashBuffer = await window.msCrypto.subtle.digest('MD5', buffer);
|
|
189
|
-
return this.bufferToHex(hashBuffer);
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
bufferToHex(buffer) {
|
|
193
|
-
return Array.from(new Uint8Array(buffer))
|
|
194
|
-
.map((b) => b.toString(16).padStart(2, '0'))
|
|
195
|
-
.join('');
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
export const md5 = new MD5Adapter();
|
|
200
|
-
```
|
|
201
|
-
|
|
202
|
-
### Graceful Degradation
|
|
203
|
-
|
|
204
|
-
It's important not just to fail with an error, but to try alternative paths:
|
|
205
|
-
|
|
206
|
-
```javascript
|
|
207
|
-
async function robustHash(input, options = {}) {
|
|
208
|
-
const errors = [];
|
|
209
|
-
|
|
210
|
-
// Try to use WebCrypto
|
|
211
|
-
if (!options.forcePureJS) {
|
|
212
|
-
try {
|
|
213
|
-
return await hashWithWebCrypto(input);
|
|
214
|
-
} catch (e) {
|
|
215
|
-
errors.push({ backend: 'webcrypto', error: e.message });
|
|
216
|
-
// Log for monitoring but continue
|
|
217
|
-
console.warn('WebCrypto failed, falling back:', e.message);
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
// Try Node.js crypto
|
|
222
|
-
try {
|
|
223
|
-
return await hashWithNodeCrypto(input);
|
|
224
|
-
} catch (e) {
|
|
225
|
-
errors.push({ backend: 'nodecrypto', error: e.message });
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
// Final fallback — pure JS implementation
|
|
229
|
-
try {
|
|
230
|
-
const result = pureJSMD5(input);
|
|
231
|
-
|
|
232
|
-
// If there were errors but we managed to work — inform
|
|
233
|
-
if (errors.length > 0 && options.reportFallback) {
|
|
234
|
-
console.info('MD5 used fallback. Previous errors:', errors);
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
return result;
|
|
238
|
-
} catch (e) {
|
|
239
|
-
// Complete failure
|
|
240
|
-
throw new Error(
|
|
241
|
-
`MD5 hash failed after all attempts: ${errors.map((e) => e.error).join(', ')}`
|
|
242
|
-
);
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
```
|
|
246
|
-
|
|
247
|
-
### Fallback Usage Monitoring
|
|
248
|
-
|
|
249
|
-
For production systems, it's important to track how often fallback mechanisms are triggered:
|
|
250
|
-
|
|
251
|
-
```javascript
|
|
252
|
-
const metrics = {
|
|
253
|
-
webcrypto: { success: 0, fail: 0 },
|
|
254
|
-
nodecrypto: { success: 0, fail: 0 },
|
|
255
|
-
purejs: { success: 0, fail: 0 }
|
|
256
|
-
};
|
|
257
|
-
|
|
258
|
-
// Wrapper with metrics
|
|
259
|
-
async function monitoredHash(input) {
|
|
260
|
-
for (const backend of ['webcrypto', 'nodecrypto', 'purejs']) {
|
|
261
|
-
try {
|
|
262
|
-
const result = await hashWithBackend(backend, input);
|
|
263
|
-
metrics[backend].success++;
|
|
264
|
-
return result;
|
|
265
|
-
} catch (e) {
|
|
266
|
-
metrics[backend].fail++;
|
|
267
|
-
// continue to the next backend
|
|
268
|
-
}
|
|
269
|
-
}
|
|
270
|
-
throw new Error('All backends failed');
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
// Periodic report
|
|
274
|
-
setInterval(() => {
|
|
275
|
-
console.log('MD5 Backend Metrics:', metrics);
|
|
276
|
-
}, 60000); // Every minute
|
|
277
|
-
```
|
|
278
|
-
|
|
279
|
-
## 3. Size Optimization and Tree-Shaking Configuration
|
|
280
|
-
|
|
281
|
-
Tree-shaking allows bundlers (webpack, Rollup, Vite) to remove unused code from the final bundle. Here's how to make `pure-md5` fully "shakable".
|
|
282
|
-
|
|
283
|
-
### ESM Module Structure
|
|
284
|
-
|
|
285
|
-
We switch to the modern ES module format:
|
|
286
|
-
|
|
287
|
-
```javascript
|
|
288
|
-
// package.json
|
|
289
|
-
{
|
|
290
|
-
"name": "pure-md5",
|
|
291
|
-
"version": "2.0.0",
|
|
292
|
-
"type": "module", // <-- Entire package as ESM
|
|
293
|
-
"sideEffects": false, // <-- Critically important for tree-shaking!
|
|
294
|
-
"exports": {
|
|
295
|
-
".": {
|
|
296
|
-
"import": "./dist/index.js",
|
|
297
|
-
"require": "./dist/index.cjs",
|
|
298
|
-
"types": "./dist/index.d.ts"
|
|
299
|
-
},
|
|
300
|
-
"./stream": {
|
|
301
|
-
"import": "./dist/stream.js",
|
|
302
|
-
"require": "./dist/stream.cjs"
|
|
303
|
-
},
|
|
304
|
-
"./light": {
|
|
305
|
-
"import": "./dist/light.js" // Only basic function, without adapters
|
|
306
|
-
},
|
|
307
|
-
"./package.json": "./package.json"
|
|
308
|
-
},
|
|
309
|
-
"module": "./dist/index.js",
|
|
310
|
-
"main": "./dist/index.cjs",
|
|
311
|
-
"types": "./dist/index.d.ts"
|
|
312
|
-
}
|
|
313
|
-
```
|
|
314
|
-
|
|
315
|
-
### Modular Architecture
|
|
316
|
-
|
|
317
|
-
We split the code into separate files so that only what's needed can be imported:
|
|
318
|
-
|
|
319
|
-
```javascript
|
|
320
|
-
// src/core/md5.js - pure algorithm implementation (no dependencies)
|
|
321
|
-
export function md5Core(input) {
|
|
322
|
-
// Only the algorithm, without environment detection
|
|
323
|
-
// ~2KB gzipped
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
// src/adapters/webcrypto.js
|
|
327
|
-
export async function md5WebCrypto(input) {
|
|
328
|
-
// Uses Web Crypto API
|
|
329
|
-
// ~0.5KB gzipped
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
// src/adapters/node.js
|
|
333
|
-
export function md5Node(input) {
|
|
334
|
-
// Uses node:crypto
|
|
335
|
-
// ~0.5KB gzipped
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
// src/index.js - smart version with auto-detection
|
|
339
|
-
import { md5Core } from './core/md5.js';
|
|
340
|
-
import { detectBackend } from './utils/detect.js';
|
|
341
|
-
|
|
342
|
-
export async function md5(input) {
|
|
343
|
-
const backend = await detectBackend();
|
|
344
|
-
return backend(input);
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
// src/light.js - only pure JS implementation, minimal code
|
|
348
|
-
export { md5Core as md5 } from './core/md5.js';
|
|
349
|
-
```
|
|
350
|
-
|
|
351
|
-
### Optimization via "pure" Annotations
|
|
352
|
-
|
|
353
|
-
We help the bundler understand that functions are pure and can be safely removed:
|
|
354
|
-
|
|
355
|
-
```javascript
|
|
356
|
-
// Add comments in the code for Terser/webpack
|
|
357
|
-
const fastMD5 = /*#__PURE__*/ createMD5Function();
|
|
358
|
-
|
|
359
|
-
// For complex computations that can be removed if the result is not used
|
|
360
|
-
const precomputedTable = /*#__PURE__*/ (() => {
|
|
361
|
-
// Constant table for MD5
|
|
362
|
-
return [0x67452301, 0xefcdab89 /* ... */];
|
|
363
|
-
})();
|
|
364
|
-
```
|
|
365
|
-
|
|
366
|
-
### Minimization via Conditional Exports
|
|
367
|
-
|
|
368
|
-
We create "entry points" with different functionality:
|
|
369
|
-
|
|
370
|
-
```javascript
|
|
371
|
-
// Example usage in a user's project
|
|
372
|
-
// 1. Maximum compatibility (auto-detection)
|
|
373
|
-
import { md5 } from 'pure-md5';
|
|
374
|
-
await md5('hello');
|
|
375
|
-
|
|
376
|
-
// 2. Only stream processing (for Node.js)
|
|
377
|
-
import { createMD5Stream } from 'pure-md5/stream';
|
|
378
|
-
fs.createReadStream('big.iso').pipe(createMD5Stream());
|
|
379
|
-
|
|
380
|
-
// 3. Ultra-light version (for microcontrollers/small scripts)
|
|
381
|
-
import { md5 } from 'pure-md5/light'; // ~2KB!
|
|
382
|
-
console.log(md5('hello'));
|
|
383
|
-
|
|
384
|
-
// 4. Only WebCrypto (for modern browsers)
|
|
385
|
-
import { md5 } from 'pure-md5/webcrypto'; // ~0.5KB!
|
|
386
|
-
```
|
|
387
|
-
|
|
388
|
-
### Size Comparison After Optimization
|
|
389
|
-
|
|
390
|
-
| Version | Size (gzipped) | What's Included |
|
|
391
|
-
| -------------------- | -------------- | ----------------------------- |
|
|
392
|
-
| `pure-md5/light` | **~2 KB** | Only pure JS algorithm |
|
|
393
|
-
| `pure-md5/webcrypto` | **~0.5 KB** | Only Web Crypto API wrapper |
|
|
394
|
-
| `pure-md5` (full) | **~4 KB** | Auto-detection + all adapters |
|
|
395
|
-
| **Competitors** | | |
|
|
396
|
-
| `md5` | ~15 KB | Only JS implementation |
|
|
397
|
-
| `crypto-js/md5` | ~25 KB | MD5 + additional code |
|
|
398
|
-
|
|
399
|
-
## Summary: What This Optimization Provides
|
|
400
|
-
|
|
401
|
-
1. **Streams** — a unique feature that competitors don't have. Attracts server developers.
|
|
402
|
-
2. **WebCrypto + fallback** — security and speed on modern platforms + guaranteed operation everywhere.
|
|
403
|
-
3. **Tree-shaking** — ability to use `pure-md5` even in the smallest projects without fear of bloating the bundle.
|
|
404
|
-
|
|
405
|
-
The package becomes not just "another MD5 library", but **a tool with clear positioning**: modern, lightweight, smart, and performant.
|
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
# Task 1: Creating the MD5Stream Base Class
|
|
2
|
-
|
|
3
|
-
## Goal
|
|
4
|
-
Create a base `MD5Stream` class extending `stream.Transform` for streaming MD5 hash computation.
|
|
5
|
-
|
|
6
|
-
## Implementation Requirements
|
|
7
|
-
1. **SRP**: The class should only be responsible for computing MD5 hash in streaming mode
|
|
8
|
-
2. **Performance**: Minimal overhead when processing each chunk
|
|
9
|
-
3. **Compatibility**: Works with any Node.js streams (Readable, Writable, Transform)
|
|
10
|
-
|
|
11
|
-
## Implementation Details
|
|
12
|
-
- Extends `stream.Transform`
|
|
13
|
-
- Initializes MD5 hash in constructor
|
|
14
|
-
- Updates hash in `_transform` method
|
|
15
|
-
- Finalizes hash in `_flush` method
|
|
16
|
-
- Emits 'md5' event with result (digest and bytesProcessed)
|
|
17
|
-
|
|
18
|
-
## Acceptance Criteria
|
|
19
|
-
- [x] Created `MD5Stream` class in `src/stream/md5-stream.ts`
|
|
20
|
-
- [x] Class correctly extends `stream.Transform`
|
|
21
|
-
- [x] Implemented `_transform` method for hash updates
|
|
22
|
-
- [x] Implemented `_flush` method for hash finalization
|
|
23
|
-
- [x] Added `'md5'` event with result (digest and bytesProcessed)
|
|
24
|
-
- [x] Basic tests written for class functionality
|
|
25
|
-
|
|
26
|
-
## Implementation Status
|
|
27
|
-
- [x] Examined existing MD5 implementation structure
|
|
28
|
-
- [x] Created stream directory structure
|
|
29
|
-
- [x] Created MD5Stream class in src/stream/md5-stream.ts
|
|
30
|
-
- [x] Exported MD5Stream from index.ts
|
|
31
|
-
- [x] Added stream entry to tsup.config.ts
|
|
32
|
-
- [x] Created tests for MD5Stream
|
|
33
|
-
- [x] Verified implementation with tests
|
|
34
|
-
- [x] Documentation updated
|
|
35
|
-
|
|
36
|
-
## Implementation Summary
|
|
37
|
-
- **Location**: `src/stream/md5-stream.ts`
|
|
38
|
-
- **Exports**: `MD5Stream` and `createMD5Stream` (factory function)
|
|
39
|
-
- **Features**:
|
|
40
|
-
- Efficient chunk processing with 64-byte block optimization
|
|
41
|
-
- Automatic padding and length handling
|
|
42
|
-
- Configurable add32 function for testing
|
|
43
|
-
- State management with `reset()` method
|
|
44
|
-
- State inspection via `getCurrentState()` and `getBytesProcessed()`
|
|
45
|
-
|
|
46
|
-
## Test Results
|
|
47
|
-
All 19 MD5Stream tests pass:
|
|
48
|
-
- Empty string handling
|
|
49
|
-
- Simple and longer strings
|
|
50
|
-
- Chunked data processing
|
|
51
|
-
- 64-byte block alignment
|
|
52
|
-
- Special characters
|
|
53
|
-
- Binary buffer input
|
|
54
|
-
- Large file simulation
|
|
55
|
-
- Factory function
|
|
56
|
-
- Custom add32 function
|
|
57
|
-
- Sequential processing
|
|
58
|
-
- Single byte chunks
|
|
59
|
-
- Hex digest validation
|
|
60
|
-
- State reset functionality
|
|
61
|
-
- Current state inspection
|
|
62
|
-
- Empty chunks handling
|
|
63
|
-
|
|
64
|
-
## Notes for Agent
|
|
65
|
-
- Uses native MD5 implementation from `src/core/index.ts`
|
|
66
|
-
- Minimal performance overhead
|
|
67
|
-
- Compatible with all Node.js stream types
|
|
68
|
-
- Stream is marked as completed in the planning document
|
|
69
|
-
|
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
# Задача 2: Создание фабричной функции и удобного API
|
|
2
|
-
|
|
3
|
-
## Цель
|
|
4
|
-
Создать удобный API для работы с MD5 потоками через фабричные функции и упрощенные методы.
|
|
5
|
-
|
|
6
|
-
## Требования к реализации
|
|
7
|
-
1. **SRP**: Функции должны предоставлять простой интерфейс без внутренней логики
|
|
8
|
-
2. **Удобство использования**: API должен быть интуитивно понятен
|
|
9
|
-
3. **Гибкость**: Поддержка различных сценариев использования
|
|
10
|
-
|
|
11
|
-
## Детали реализации
|
|
12
|
-
- Фабричная функция `createMD5Stream()` для создания экземпляров
|
|
13
|
-
- Метод `pipeThroughMD5()` для удобной интеграции в цепочки pipe
|
|
14
|
-
- Статический метод `MD5Stream.fromStream()` для создания из существующего потока
|
|
15
|
-
- Экспорт всех необходимых компонентов
|
|
16
|
-
|
|
17
|
-
## Критерии выполнения
|
|
18
|
-
- [x] Создана фабричная функция `createMD5Stream()` в `src/stream/index.js`
|
|
19
|
-
- [x] Реализован метод `pipeThroughMD5()` для обертки потоков
|
|
20
|
-
- [x] Добавлен статический метод `MD5Stream.fromStream()`
|
|
21
|
-
- [x] Экспортированы все публичные API в основном экспорте
|
|
22
|
-
- [x] Написаны примеры использования в документации
|
|
23
|
-
- [x] Добавлены тесты для фабричных функций
|
|
24
|
-
|
|
25
|
-
## Прогресс выполнения
|
|
26
|
-
- [x] Начало работы
|
|
27
|
-
- [x] Фабричные функции созданы
|
|
28
|
-
- [x] API документирован
|
|
29
|
-
- [x] Тесты написаны
|
|
30
|
-
- [x] Код проверен на соответствие SRP
|
|
31
|
-
- [x] Задача завершена
|
|
32
|
-
|
|
33
|
-
## Реализовано
|
|
34
|
-
|
|
35
|
-
### Фабричные функции
|
|
36
|
-
- ✅ `createMD5Stream()` - создание экземпляра MD5Stream
|
|
37
|
-
- ✅ `fromStream()` - создание stream из существующего readable stream
|
|
38
|
-
- ✅ `MD5Stream.fromStream()` - статический метод класса
|
|
39
|
-
|
|
40
|
-
### Методы и утилиты
|
|
41
|
-
- ✅ `pipeThroughMD5()` - удобная обертка для pipe
|
|
42
|
-
- ✅ `hashFile()` - хэширование файла по пути
|
|
43
|
-
- ✅ `hashFileDigest()` - хэширование с возвратом только digest
|
|
44
|
-
- ✅ `hashFileStream()` - хэширование потока
|
|
45
|
-
- ✅ `hashFileSync()` - синхронное хэширование
|
|
46
|
-
- ✅ `verifyFile()` - проверка целостности файла
|
|
47
|
-
- ✅ `createProgressTracker()` - отслеживание прогресса
|
|
48
|
-
|
|
49
|
-
### Документация
|
|
50
|
-
- ✅ Примеры использования в README.md
|
|
51
|
-
- ✅ Комментарии JSDoc в коде
|
|
52
|
-
- ✅ Примеры для всех функций
|
|
53
|
-
|
|
54
|
-
### Тесты
|
|
55
|
-
- ✅ Тесты `createMD5Stream()`
|
|
56
|
-
- ✅ Тесты `pipeThroughMD5()`
|
|
57
|
-
- ✅ Тесты `fromStream()`
|
|
58
|
-
- ✅ Тесты `MD5Stream.fromStream()`
|
|
59
|
-
- ✅ Тесты файловых утилит
|
|
60
|
-
|
|
61
|
-
## Примечания для агента
|
|
62
|
-
- API должен быть согласован с существующим интерфейсом библиотеки
|
|
63
|
-
- Функции должны быть чистыми и не иметь побочных эффектов
|
|
64
|
-
- Учесть различные сценарии использования (файлы, HTTP, пользовательские потоки)
|
|
65
|
-
- Отметить прогресс в чеклисте выше
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
# Задача 3: Интеграция с файловой системой Node.js
|
|
2
|
-
|
|
3
|
-
## Цель
|
|
4
|
-
Создать удобные утилиты для хэширования файлов через потоки файловой системы.
|
|
5
|
-
|
|
6
|
-
## Требования к реализации
|
|
7
|
-
1. **SRP**: Утилиты должны отвечать только за работу с файловой системой
|
|
8
|
-
2. **Эффективность**: Минимальное использование памяти при работе с большими файлами
|
|
9
|
-
3. **Обработка ошибок**: Корректная обработка ошибок файловой системы
|
|
10
|
-
|
|
11
|
-
## Детали реализации
|
|
12
|
-
- Функция `hashFile(filePath)` для хэширования файла по пути
|
|
13
|
-
- Функция `hashFileStream(readStream)` для хэширования существующего потока
|
|
14
|
-
- Поддержка прогресса хэширования (опционально)
|
|
15
|
-
- Обработка ошибок чтения файлов
|
|
16
|
-
|
|
17
|
-
## Критерии выполнения
|
|
18
|
-
- [ ] Создана функция `hashFile()` в `src/stream/fs-utils.js`
|
|
19
|
-
- [ ] Создана функция `hashFileStream()`
|
|
20
|
-
- [ ] Реализована обработка ошибок файловой системы
|
|
21
|
-
- [ ] Добавлена опциональная поддержка прогресса
|
|
22
|
-
- [ ] Написаны тесты с реальными файлами
|
|
23
|
-
- [ ] Добавлены примеры использования
|
|
24
|
-
|
|
25
|
-
## Прогресс выполнения
|
|
26
|
-
- [ ] Начало работы
|
|
27
|
-
- [ ] Функции созданы
|
|
28
|
-
- [ ] Обработка ошибок реализована
|
|
29
|
-
- [ ] Тесты написаны
|
|
30
|
-
- [ ] Код проверен на соответствие SRP
|
|
31
|
-
- [ ] Задача завершена
|
|
32
|
-
|
|
33
|
-
## Примечания для агента
|
|
34
|
-
- Использовать `fs.createReadStream` для эффективного чтения
|
|
35
|
-
- Учесть различные кодировки и типы файлов
|
|
36
|
-
- Обеспечить корректное освобождение ресурсов
|
|
37
|
-
- Отметить прогресс в чеклисте выше
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
# Задача 4: Поддержка WHATWG Streams для браузеров
|
|
2
|
-
|
|
3
|
-
## Цель
|
|
4
|
-
Добавить поддержку браузерных потоков (WHATWG Streams) для использования в веб-приложениях.
|
|
5
|
-
|
|
6
|
-
## Требования к реализации
|
|
7
|
-
1. **SRP**: Отдельная реализация для браузерных потоков
|
|
8
|
-
2. **Совместимость**: Работа с современными браузерами
|
|
9
|
-
3. **Производительность**: Эффективная работа в браузерной среде
|
|
10
|
-
|
|
11
|
-
## Детали реализации
|
|
12
|
-
- Класс `MD5ReadableStream` для работы с WHATWG Streams
|
|
13
|
-
- Функция `hashReadableStream()` для хэширования существующих потоков
|
|
14
|
-
- Поддержка `ReadableStream`, `File`, `Blob` объектов
|
|
15
|
-
- Адаптер для конвертации между Node.js и WHATWG потоками
|
|
16
|
-
|
|
17
|
-
## Критерии выполнения
|
|
18
|
-
- [ ] Создан класс `MD5ReadableStream` в `src/stream/whatwg-stream.js`
|
|
19
|
-
- [ ] Реализована функция `hashReadableStream()`
|
|
20
|
-
- [ ] Добавлена поддержка `File` и `Blob` объектов
|
|
21
|
-
- [ ] Создан адаптер для конвертации потоков
|
|
22
|
-
- [ ] Написаны тесты для браузерной среды
|
|
23
|
-
- [ ] Добавлена документация по использованию
|
|
24
|
-
|
|
25
|
-
## Прогресс выполнения
|
|
26
|
-
- [ ] Начало работы
|
|
27
|
-
- [ ] WHATWG Streams поддержка реализована
|
|
28
|
-
- [ ] Адаптеры созданы
|
|
29
|
-
- [ ] Тесты написаны
|
|
30
|
-
- [ ] Код проверен на соответствие SRP
|
|
31
|
-
- [ ] Задача завершена
|
|
32
|
-
|
|
33
|
-
## Примечания для агента
|
|
34
|
-
- Учесть различия между Node.js и WHATWG Streams API
|
|
35
|
-
- Обеспечить кросс-браузерную совместимость
|
|
36
|
-
- Минимизировать размер кода для браузерной сборки
|
|
37
|
-
- Отметить прогресс в чеклисте выше
|