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
|
@@ -1,255 +0,0 @@
|
|
|
1
|
-
# Task 09: Creating Unified Adapter with Unified Interface
|
|
2
|
-
|
|
3
|
-
## Goal
|
|
4
|
-
|
|
5
|
-
Создать unified adapter, который объединяет все backend'ы и предоставляет единый API для пользователей.
|
|
6
|
-
|
|
7
|
-
## Implementation
|
|
8
|
-
|
|
9
|
-
### 1. Создать файл src/adapters/unified.ts
|
|
10
|
-
|
|
11
|
-
```typescript
|
|
12
|
-
import { PureJSBackend } from './pure-js.js';
|
|
13
|
-
import { WebCryptoBackend } from './web-crypto.js';
|
|
14
|
-
import { NodeCryptoBackend } from './node-crypto.js';
|
|
15
|
-
import { IE11Backend } from './ie11.js';
|
|
16
|
-
import { detector } from '../detection/backend-detector.js';
|
|
17
|
-
|
|
18
|
-
export interface MD5AdapterOptions {
|
|
19
|
-
backend?: string;
|
|
20
|
-
fallback?: boolean;
|
|
21
|
-
reportFallback?: boolean;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
export class MD5Adapter {
|
|
25
|
-
private backend: any | null = null;
|
|
26
|
-
private backendName: string | null = null;
|
|
27
|
-
private options: MD5AdapterOptions;
|
|
28
|
-
|
|
29
|
-
constructor(options: MD5AdapterOptions = {}) {
|
|
30
|
-
this.options = {
|
|
31
|
-
fallback: true,
|
|
32
|
-
reportFallback: false,
|
|
33
|
-
...options
|
|
34
|
-
};
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* Hash string data with automatic backend selection or specified backend
|
|
39
|
-
*/
|
|
40
|
-
async hash(data: string, options?: { backend?: string }): Promise<string> {
|
|
41
|
-
const backend = await this.getBackend(options?.backend);
|
|
42
|
-
return backend.hash(data);
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* Hash binary data (ArrayBuffer, Uint8Array)
|
|
47
|
-
*/
|
|
48
|
-
async hashBinary(data: ArrayBuffer | Uint8Array, options?: { backend?: string }): Promise<string> {
|
|
49
|
-
const backend = await this.getBackend(options?.backend);
|
|
50
|
-
return backend.hashBinary(data);
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
/**
|
|
54
|
-
* Update hash with additional data (streaming)
|
|
55
|
-
*/
|
|
56
|
-
async update(data: string | ArrayBuffer | Uint8Array, options?: { backend?: string }): Promise<void> {
|
|
57
|
-
const backend = await this.getBackend(options?.backend);
|
|
58
|
-
backend.update(data);
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
/**
|
|
62
|
-
* Get final hash digest (for streaming)
|
|
63
|
-
*/
|
|
64
|
-
async digest(encoding: 'hex' | 'buffer' = 'hex', options?: { backend?: string }): Promise<string | Uint8Array> {
|
|
65
|
-
const backend = await this.getBackend(options?.backend);
|
|
66
|
-
return backend.digest(encoding);
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
/**
|
|
70
|
-
* Reset the hash state
|
|
71
|
-
*/
|
|
72
|
-
async reset(options?: { backend?: string }): Promise<void> {
|
|
73
|
-
const backend = await this.getBackend(options?.backend);
|
|
74
|
-
backend.reset();
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
/**
|
|
78
|
-
* Get backend name
|
|
79
|
-
*/
|
|
80
|
-
async getBackendName(): Promise<string> {
|
|
81
|
-
if (!this.backendName) {
|
|
82
|
-
const result = await detector.detect();
|
|
83
|
-
this.backendName = result.backend;
|
|
84
|
-
}
|
|
85
|
-
return this.backendName;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
/**
|
|
89
|
-
* Check if specific backend is available
|
|
90
|
-
*/
|
|
91
|
-
static async isAvailable(backend: string): Promise<boolean> {
|
|
92
|
-
const { checkBackendAvailability } = await import('./backend-availability.js');
|
|
93
|
-
const result = await checkBackendAvailability(backend);
|
|
94
|
-
return result.available;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
/**
|
|
98
|
-
* Get list of available backends
|
|
99
|
-
*/
|
|
100
|
-
static async getAvailableBackends(): Promise<string[]> {
|
|
101
|
-
const { getAllAvailableBackends } = await import('./backend-availability.js');
|
|
102
|
-
return getAllAvailableBackends();
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
/**
|
|
106
|
-
* Force specific backend for all operations
|
|
107
|
-
*/
|
|
108
|
-
async useBackend(backendName: string): Promise<void> {
|
|
109
|
-
this.backend = await detector.createBackendByName(backendName);
|
|
110
|
-
this.backendName = backendName;
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
/**
|
|
114
|
-
* Reset to automatic backend selection
|
|
115
|
-
*/
|
|
116
|
-
async resetBackend(): Promise<void> {
|
|
117
|
-
this.backend = null;
|
|
118
|
-
this.backendName = null;
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
/**
|
|
122
|
-
* Get backend instance (lazy initialization)
|
|
123
|
-
*/
|
|
124
|
-
private async getBackend(overrideBackend?: string): Promise<any> {
|
|
125
|
-
if (overrideBackend) {
|
|
126
|
-
return await detector.createBackendByName(overrideBackend);
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
if (this.backend) {
|
|
130
|
-
return this.backend;
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
this.backend = await detector.createBackend();
|
|
134
|
-
this.backendName = await detector.getBackendName();
|
|
135
|
-
return this.backend;
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
export const md5 = new MD5Adapter();
|
|
140
|
-
```
|
|
141
|
-
|
|
142
|
-
### 2. Integration с основным API
|
|
143
|
-
|
|
144
|
-
В `src/index.ts` обновить экспорт:
|
|
145
|
-
|
|
146
|
-
```typescript
|
|
147
|
-
export { md5 } from './adapters/unified.js';
|
|
148
|
-
|
|
149
|
-
// Also keep original export
|
|
150
|
-
export { default as pureMD5 } from './md51.js';
|
|
151
|
-
|
|
152
|
-
// Export backend-related utilities
|
|
153
|
-
export { detector } from './detection/backend-detector.js';
|
|
154
|
-
export { BackendDetector } from './detection/backend-detector.js';
|
|
155
|
-
```
|
|
156
|
-
|
|
157
|
-
### 3. Упрощенный API для простых случаев
|
|
158
|
-
|
|
159
|
-
Создать файл `src/index-simple.ts`:
|
|
160
|
-
|
|
161
|
-
```typescript
|
|
162
|
-
import { md5 } from './adapters/unified.js';
|
|
163
|
-
|
|
164
|
-
/**
|
|
165
|
-
* Simple MD5 hashing function
|
|
166
|
-
* @param data - String to hash
|
|
167
|
-
* @returns MD5 hash as hex string
|
|
168
|
-
*/
|
|
169
|
-
export async function hash(data: string): Promise<string> {
|
|
170
|
-
return md5.hash(data);
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
/**
|
|
174
|
-
* Hash with specific backend
|
|
175
|
-
*/
|
|
176
|
-
export async function hashWithBackend(data: string, backend: string): Promise<string> {
|
|
177
|
-
return md5.hash(data, { backend });
|
|
178
|
-
}
|
|
179
|
-
```
|
|
180
|
-
|
|
181
|
-
### 4. Tests
|
|
182
|
-
|
|
183
|
-
Создать файл `__tests__/adapters/unified.test.ts`:
|
|
184
|
-
|
|
185
|
-
```typescript
|
|
186
|
-
describe('MD5Adapter', () => {
|
|
187
|
-
let adapter: MD5Adapter;
|
|
188
|
-
|
|
189
|
-
beforeAll(() => {
|
|
190
|
-
adapter = new MD5Adapter();
|
|
191
|
-
});
|
|
192
|
-
|
|
193
|
-
it('should hash string with automatic backend', async () => {
|
|
194
|
-
const result = await adapter.hash('hello');
|
|
195
|
-
expect(result).toBe('5d41402abc4b2a76b9719d911017c592');
|
|
196
|
-
});
|
|
197
|
-
|
|
198
|
-
it('should hash with specific backend', async () => {
|
|
199
|
-
const result = await adapter.hash('hello', { backend: 'purejs' });
|
|
200
|
-
expect(result).toBe('5d41402abc4b2a76b9719d911017c592');
|
|
201
|
-
});
|
|
202
|
-
|
|
203
|
-
it('should hash binary data', async () => {
|
|
204
|
-
const data = new Uint8Array([104, 101, 108, 108, 111]); // "hello"
|
|
205
|
-
const result = await adapter.hashBinary(data);
|
|
206
|
-
expect(result).toBe('5d41402abc4b2a76b9719d911017c592');
|
|
207
|
-
});
|
|
208
|
-
|
|
209
|
-
it('should support streaming', async () => {
|
|
210
|
-
await adapter.update('he');
|
|
211
|
-
await adapter.update('l');
|
|
212
|
-
await adapter.update('lo');
|
|
213
|
-
const result = await adapter.digest('hex');
|
|
214
|
-
expect(result).toBe('5d41402abc4b2a76b9719d911017c592');
|
|
215
|
-
});
|
|
216
|
-
|
|
217
|
-
it('should reset state', async () => {
|
|
218
|
-
await adapter.update('hello');
|
|
219
|
-
await adapter.reset();
|
|
220
|
-
await adapter.update('world');
|
|
221
|
-
const result = await adapter.digest('hex');
|
|
222
|
-
expect(result).toBe('5d41402abc4b2a76b9719d911017c592');
|
|
223
|
-
});
|
|
224
|
-
|
|
225
|
-
it('should get backend name', async () => {
|
|
226
|
-
const name = await adapter.getBackendName();
|
|
227
|
-
expect(typeof name).toBe('string');
|
|
228
|
-
});
|
|
229
|
-
|
|
230
|
-
it('should force specific backend', async () => {
|
|
231
|
-
await adapter.useBackend('purejs');
|
|
232
|
-
const backendName = await adapter.getBackendName();
|
|
233
|
-
expect(backendName).toBe('purejs');
|
|
234
|
-
});
|
|
235
|
-
|
|
236
|
-
it('should get available backends', async () => {
|
|
237
|
-
const backends = await MD5Adapter.getAvailableBackends();
|
|
238
|
-
expect(Array.isArray(backends)).toBe(true);
|
|
239
|
-
expect(backends.length).toBeGreaterThan(0);
|
|
240
|
-
});
|
|
241
|
-
});
|
|
242
|
-
```
|
|
243
|
-
|
|
244
|
-
### 5. Documentation
|
|
245
|
-
|
|
246
|
-
Добавить JSDoc комментарии для всех публичных методов и классов.
|
|
247
|
-
|
|
248
|
-
## Ожидаемый результат
|
|
249
|
-
|
|
250
|
-
- ✅ Unified adapter реализован
|
|
251
|
-
- ✅ Единый API для всех backend'ов
|
|
252
|
-
- ✅ Support автоматического и принудительного выбора backend
|
|
253
|
-
- ✅ Tests проходят успешно
|
|
254
|
-
- ✅ Интегрирован в основной API
|
|
255
|
-
- ✅ Documentation создана
|
|
@@ -1,333 +0,0 @@
|
|
|
1
|
-
# Task 10: Fallback Mechanisms Implementation with Priority Order
|
|
2
|
-
|
|
3
|
-
## Goal
|
|
4
|
-
|
|
5
|
-
Implement fallback механизм, который автоматически пробует альтернативные backend'ы при ошибке основного.
|
|
6
|
-
|
|
7
|
-
## Implementation
|
|
8
|
-
|
|
9
|
-
### 1. Создать файл src/adapters/fallback.ts
|
|
10
|
-
|
|
11
|
-
```typescript
|
|
12
|
-
import { PureJSBackend } from './pure-js.js';
|
|
13
|
-
import { WebCryptoBackend } from './web-crypto.js';
|
|
14
|
-
import { NodeCryptoBackend } from './node-crypto.js';
|
|
15
|
-
import { IE11Backend } from './ie11.js';
|
|
16
|
-
import { BackendDetector } from '../detection/backend-detector.js';
|
|
17
|
-
|
|
18
|
-
export interface FallbackResult<T> {
|
|
19
|
-
success: boolean;
|
|
20
|
-
backend: string;
|
|
21
|
-
data: T;
|
|
22
|
-
errors?: { backend: string; error: Error }[];
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
export class FallbackManager {
|
|
26
|
-
private detector: BackendDetector;
|
|
27
|
-
private fallbackOrder: string[];
|
|
28
|
-
|
|
29
|
-
constructor(fallbackOrder: string[] = ['nodecrypto', 'webcrypto', 'ie11', 'purejs']) {
|
|
30
|
-
this.detector = new BackendDetector();
|
|
31
|
-
this.fallbackOrder = fallbackOrder;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* Execute operation with fallback mechanism
|
|
36
|
-
*/
|
|
37
|
-
async execute<T>(operation: (backend: any) => Promise<T>): Promise<FallbackResult<T>> {
|
|
38
|
-
const errors: { backend: string; error: Error }[] = [];
|
|
39
|
-
|
|
40
|
-
for (const backendName of this.fallbackOrder) {
|
|
41
|
-
try {
|
|
42
|
-
const backend = await this.detector.createBackendByName(backendName);
|
|
43
|
-
const result = await operation(backend);
|
|
44
|
-
return {
|
|
45
|
-
success: true,
|
|
46
|
-
backend: backendName,
|
|
47
|
-
data: result
|
|
48
|
-
};
|
|
49
|
-
} catch (error) {
|
|
50
|
-
errors.push({ backend: backendName, error: error as Error });
|
|
51
|
-
|
|
52
|
-
// Don't fallback if last backend failed
|
|
53
|
-
if (backendName === this.fallbackOrder[this.fallbackOrder.length - 1]) {
|
|
54
|
-
return {
|
|
55
|
-
success: false,
|
|
56
|
-
backend: backendName,
|
|
57
|
-
data: null as unknown as T,
|
|
58
|
-
errors
|
|
59
|
-
};
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
return {
|
|
65
|
-
success: false,
|
|
66
|
-
backend: '',
|
|
67
|
-
data: null as unknown as T,
|
|
68
|
-
errors
|
|
69
|
-
};
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
/**
|
|
73
|
-
* Hash with fallback mechanism
|
|
74
|
-
*/
|
|
75
|
-
async hash(data: string): Promise<FallbackResult<string>> {
|
|
76
|
-
return this.execute(async (backend) => backend.hash(data));
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
/**
|
|
80
|
-
* Hash binary data with fallback
|
|
81
|
-
*/
|
|
82
|
-
async hashBinary(data: ArrayBuffer | Uint8Array): Promise<FallbackResult<string>> {
|
|
83
|
-
return this.execute(async (backend) => backend.hashBinary(data));
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
/**
|
|
87
|
-
* Get the best available backend
|
|
88
|
-
*/
|
|
89
|
-
async getBestBackend(): Promise<string> {
|
|
90
|
-
const available = await this.getAvailableBackends();
|
|
91
|
-
if (available.length === 0) {
|
|
92
|
-
return 'purejs'; // Fallback
|
|
93
|
-
}
|
|
94
|
-
return available[0]; // First is best
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
/**
|
|
98
|
-
* Get available backends in priority order
|
|
99
|
-
*/
|
|
100
|
-
async getAvailableBackends(): Promise<string[]> {
|
|
101
|
-
const available: string[] = [];
|
|
102
|
-
|
|
103
|
-
for (const backendName of this.fallbackOrder) {
|
|
104
|
-
try {
|
|
105
|
-
const backend = await this.detector.createBackendByName(backendName);
|
|
106
|
-
available.push(backendName);
|
|
107
|
-
} catch {
|
|
108
|
-
// Backend not available
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
return available;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
/**
|
|
116
|
-
* Get backend metrics for monitoring
|
|
117
|
-
*/
|
|
118
|
-
getMetrics(): Record<string, { success: number; fail: number }> {
|
|
119
|
-
// Implementation for tracking usage
|
|
120
|
-
return {};
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
export const fallbackManager = new FallbackManager();
|
|
125
|
-
```
|
|
126
|
-
|
|
127
|
-
### 2. Integration с unified adapter
|
|
128
|
-
|
|
129
|
-
Обновить `src/adapters/unified.ts`:
|
|
130
|
-
|
|
131
|
-
```typescript
|
|
132
|
-
import { FallbackManager, fallbackManager } from './fallback.js';
|
|
133
|
-
|
|
134
|
-
export class MD5Adapter {
|
|
135
|
-
private fallbackManager: FallbackManager;
|
|
136
|
-
|
|
137
|
-
constructor(options: MD5AdapterOptions = {}) {
|
|
138
|
-
this.fallbackManager = options.fallback !== false
|
|
139
|
-
? new FallbackManager()
|
|
140
|
-
: null;
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
async hash(data: string, options?: { backend?: string }): Promise<string> {
|
|
144
|
-
if (options?.backend) {
|
|
145
|
-
const backend = await this.getBackend(options.backend);
|
|
146
|
-
return backend.hash(data);
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
// Use fallback if enabled
|
|
150
|
-
if (this.fallbackManager) {
|
|
151
|
-
const result = await this.fallbackManager.hash(data);
|
|
152
|
-
if (result.success) {
|
|
153
|
-
return result.data;
|
|
154
|
-
}
|
|
155
|
-
throw new Error(`MD5 hash failed: ${result.errors.map(e => e.error.message).join(', ')}`);
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
const backend = await this.getBackend();
|
|
159
|
-
return backend.hash(data);
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
// ... остальные методы с fallback
|
|
163
|
-
}
|
|
164
|
-
```
|
|
165
|
-
|
|
166
|
-
### 3. Робастная функция хэширования
|
|
167
|
-
|
|
168
|
-
Создать файл `src/adapters/robust.ts`:
|
|
169
|
-
|
|
170
|
-
```typescript
|
|
171
|
-
import { FallbackManager } from './fallback.js';
|
|
172
|
-
|
|
173
|
-
export async function robustHash(
|
|
174
|
-
data: string,
|
|
175
|
-
options: {
|
|
176
|
-
fallback?: boolean;
|
|
177
|
-
reportFallback?: boolean;
|
|
178
|
-
forceBackend?: string;
|
|
179
|
-
} = {}
|
|
180
|
-
): Promise<string> {
|
|
181
|
-
const fallbackManager = new FallbackManager();
|
|
182
|
-
|
|
183
|
-
if (options.forceBackend) {
|
|
184
|
-
// Use specific backend without fallback
|
|
185
|
-
const backend = await fallbackManager.detector.createBackendByName(options.forceBackend);
|
|
186
|
-
return backend.hash(data);
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
if (options.fallback === false) {
|
|
190
|
-
// Use default backend without fallback
|
|
191
|
-
const backend = await fallbackManager.detector.createBackend();
|
|
192
|
-
return backend.hash(data);
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
// Use fallback
|
|
196
|
-
const result = await fallbackManager.hash(data);
|
|
197
|
-
|
|
198
|
-
if (result.success) {
|
|
199
|
-
if (options.reportFallback && result.backend !== 'nodecrypto' && result.backend !== 'webcrypto') {
|
|
200
|
-
console.info(`MD5 used fallback backend: ${result.backend}`);
|
|
201
|
-
}
|
|
202
|
-
return result.data;
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
// All backends failed
|
|
206
|
-
const errorMessage = result.errors
|
|
207
|
-
? result.errors.map(e => `${e.backend}: ${e.error.message}`).join(', ')
|
|
208
|
-
: 'All backends failed';
|
|
209
|
-
|
|
210
|
-
throw new Error(`MD5 hash failed after all attempts: ${errorMessage}`);
|
|
211
|
-
}
|
|
212
|
-
```
|
|
213
|
-
|
|
214
|
-
### 4. Мониторинг fallback механизмов
|
|
215
|
-
|
|
216
|
-
Создать файл `src/adapters/metrics.ts`:
|
|
217
|
-
|
|
218
|
-
```typescript
|
|
219
|
-
interface BackendMetrics {
|
|
220
|
-
success: number;
|
|
221
|
-
fail: number;
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
export class MetricsCollector {
|
|
225
|
-
private metrics: Record<string, BackendMetrics> = {
|
|
226
|
-
nodecrypto: { success: 0, fail: 0 },
|
|
227
|
-
webcrypto: { success: 0, fail: 0 },
|
|
228
|
-
ie11: { success: 0, fail: 0 },
|
|
229
|
-
purejs: { success: 0, fail: 0 }
|
|
230
|
-
};
|
|
231
|
-
|
|
232
|
-
recordSuccess(backend: string): void {
|
|
233
|
-
if (this.metrics[backend]) {
|
|
234
|
-
this.metrics[backend].success++;
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
recordFail(backend: string): void {
|
|
239
|
-
if (this.metrics[backend]) {
|
|
240
|
-
this.metrics[backend].fail++;
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
getMetrics(): Record<string, BackendMetrics> {
|
|
245
|
-
return this.metrics;
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
getSummary(): string {
|
|
249
|
-
const total = Object.values(this.metrics).reduce(
|
|
250
|
-
(sum, m) => sum + m.success + m.fail,
|
|
251
|
-
0
|
|
252
|
-
);
|
|
253
|
-
|
|
254
|
-
return `Total operations: ${total}\n` +
|
|
255
|
-
Object.entries(this.metrics)
|
|
256
|
-
.map(([backend, m]) =>
|
|
257
|
-
`${backend}: ${m.success} success, ${m.fail} fail`
|
|
258
|
-
)
|
|
259
|
-
.join('\n');
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
reset(): void {
|
|
263
|
-
Object.keys(this.metrics).forEach(key => {
|
|
264
|
-
this.metrics[key] = { success: 0, fail: 0 };
|
|
265
|
-
});
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
export const metrics = new MetricsCollector();
|
|
270
|
-
```
|
|
271
|
-
|
|
272
|
-
### 5. Tests
|
|
273
|
-
|
|
274
|
-
Создать файл `__tests__/adapters/fallback.test.ts`:
|
|
275
|
-
|
|
276
|
-
```typescript
|
|
277
|
-
describe('FallbackManager', () => {
|
|
278
|
-
let manager: FallbackManager;
|
|
279
|
-
|
|
280
|
-
beforeAll(() => {
|
|
281
|
-
manager = new FallbackManager();
|
|
282
|
-
});
|
|
283
|
-
|
|
284
|
-
it('should hash with fallback', async () => {
|
|
285
|
-
const result = await manager.hash('hello');
|
|
286
|
-
expect(result.success).toBe(true);
|
|
287
|
-
expect(result.data).toBe('5d41402abc4b2a76b9719d911017c592');
|
|
288
|
-
});
|
|
289
|
-
|
|
290
|
-
it('should get best backend', async () => {
|
|
291
|
-
const best = await manager.getBestBackend();
|
|
292
|
-
expect(typeof best).toBe('string');
|
|
293
|
-
});
|
|
294
|
-
|
|
295
|
-
it('should get available backends', async () => {
|
|
296
|
-
const available = await manager.getAvailableBackends();
|
|
297
|
-
expect(Array.isArray(available)).toBe(true);
|
|
298
|
-
expect(available.length).toBeGreaterThan(0);
|
|
299
|
-
});
|
|
300
|
-
|
|
301
|
-
it('should execute operation with fallback', async () => {
|
|
302
|
-
const result = await manager.execute(async (backend) => backend.hash('test'));
|
|
303
|
-
expect(result.success).toBe(true);
|
|
304
|
-
});
|
|
305
|
-
|
|
306
|
-
describe('Robust hash', () => {
|
|
307
|
-
it('should hash with fallback', async () => {
|
|
308
|
-
const result = await robustHash('hello');
|
|
309
|
-
expect(result).toBe('5d41402abc4b2a76b9719d911017c592');
|
|
310
|
-
});
|
|
311
|
-
|
|
312
|
-
it('should hash with force backend', async () => {
|
|
313
|
-
const result = await robustHash('hello', { forceBackend: 'purejs' });
|
|
314
|
-
expect(result).toBe('5d41402abc4b2a76b9719d911017c592');
|
|
315
|
-
});
|
|
316
|
-
|
|
317
|
-
it('should throw on all backends failure', async () => {
|
|
318
|
-
// This test requires mocking all backends to fail
|
|
319
|
-
// In practice, this should never happen
|
|
320
|
-
await expect(robustHash('test')).resolves.toBeDefined();
|
|
321
|
-
});
|
|
322
|
-
});
|
|
323
|
-
});
|
|
324
|
-
```
|
|
325
|
-
|
|
326
|
-
## Ожидаемый результат
|
|
327
|
-
|
|
328
|
-
- ✅ Fallback mechanism реализован
|
|
329
|
-
- ✅ Автоматический retry с другими backend'ами
|
|
330
|
-
- ✅ Мониторинг и метрики
|
|
331
|
-
- ✅ Робастная функция robustHash
|
|
332
|
-
- ✅ Tests проходят успешно
|
|
333
|
-
- ✅ Documentation создана
|