@angular-helpers/worker-http 0.2.0
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 +399 -0
- package/fesm2022/angular-helpers-worker-http-backend.mjs +3 -0
- package/fesm2022/angular-helpers-worker-http-crypto.mjs +146 -0
- package/fesm2022/angular-helpers-worker-http-interceptors.mjs +453 -0
- package/fesm2022/angular-helpers-worker-http-serializer.mjs +180 -0
- package/fesm2022/angular-helpers-worker-http-transport.mjs +116 -0
- package/fesm2022/angular-helpers-worker-http.mjs +20 -0
- package/package.json +81 -0
- package/types/angular-helpers-worker-http-backend.d.ts +42 -0
- package/types/angular-helpers-worker-http-crypto.d.ts +127 -0
- package/types/angular-helpers-worker-http-interceptors.d.ts +244 -0
- package/types/angular-helpers-worker-http-serializer.d.ts +89 -0
- package/types/angular-helpers-worker-http-transport.d.ts +115 -0
- package/types/angular-helpers-worker-http.d.ts +16 -0
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { Observable } from 'rxjs';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Creates a typed, Observable-based transport for communicating with a web worker.
|
|
5
|
+
*
|
|
6
|
+
* Features:
|
|
7
|
+
* - Request/response correlation via `requestId`
|
|
8
|
+
* - Automatic cancellation on Observable unsubscribe
|
|
9
|
+
* - Optional worker pool with round-robin dispatch
|
|
10
|
+
* - Lazy worker creation (default)
|
|
11
|
+
* - Transferable auto-detection for ArrayBuffer payloads
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```typescript
|
|
15
|
+
* const transport = createWorkerTransport<MyRequest, MyResponse>({
|
|
16
|
+
* workerFactory: () => new Worker(new URL('./my.worker.ts', import.meta.url), { type: 'module' }),
|
|
17
|
+
* maxInstances: 2,
|
|
18
|
+
* });
|
|
19
|
+
*
|
|
20
|
+
* transport.execute(request).subscribe({
|
|
21
|
+
* next: (response) => console.log(response),
|
|
22
|
+
* error: (err) => console.error(err),
|
|
23
|
+
* });
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
function createWorkerTransport(config) {
|
|
27
|
+
const workers = [];
|
|
28
|
+
let roundRobinIndex = 0;
|
|
29
|
+
let terminated = false;
|
|
30
|
+
const maxInstances = Math.min(config.maxInstances ?? 1, typeof navigator !== 'undefined' ? (navigator.hardwareConcurrency ?? 4) : 1);
|
|
31
|
+
function createWorker() {
|
|
32
|
+
if (config.workerFactory) {
|
|
33
|
+
return config.workerFactory();
|
|
34
|
+
}
|
|
35
|
+
if (config.workerUrl) {
|
|
36
|
+
const url = typeof config.workerUrl === 'string'
|
|
37
|
+
? new URL(config.workerUrl, document.baseURI)
|
|
38
|
+
: config.workerUrl;
|
|
39
|
+
return new Worker(url, { type: 'module' });
|
|
40
|
+
}
|
|
41
|
+
throw new Error('Either workerFactory or workerUrl must be provided');
|
|
42
|
+
}
|
|
43
|
+
function getOrCreateWorker() {
|
|
44
|
+
if (workers.length < maxInstances) {
|
|
45
|
+
const worker = createWorker();
|
|
46
|
+
workers.push(worker);
|
|
47
|
+
return worker;
|
|
48
|
+
}
|
|
49
|
+
const worker = workers[roundRobinIndex % workers.length];
|
|
50
|
+
roundRobinIndex++;
|
|
51
|
+
return worker;
|
|
52
|
+
}
|
|
53
|
+
function execute(request) {
|
|
54
|
+
if (terminated) {
|
|
55
|
+
return new Observable((subscriber) => {
|
|
56
|
+
subscriber.error(new Error('WorkerTransport has been terminated'));
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
const requestId = crypto.randomUUID();
|
|
60
|
+
return new Observable((subscriber) => {
|
|
61
|
+
const worker = getOrCreateWorker();
|
|
62
|
+
const messageHandler = (event) => {
|
|
63
|
+
const data = event.data;
|
|
64
|
+
if (data.requestId !== requestId)
|
|
65
|
+
return;
|
|
66
|
+
worker.removeEventListener('message', messageHandler);
|
|
67
|
+
worker.removeEventListener('error', errorHandler);
|
|
68
|
+
if (data.type === 'error') {
|
|
69
|
+
const err = data.error;
|
|
70
|
+
subscriber.error(new Error(err.message));
|
|
71
|
+
}
|
|
72
|
+
else {
|
|
73
|
+
subscriber.next(data.result);
|
|
74
|
+
subscriber.complete();
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
const errorHandler = (event) => {
|
|
78
|
+
worker.removeEventListener('message', messageHandler);
|
|
79
|
+
worker.removeEventListener('error', errorHandler);
|
|
80
|
+
subscriber.error(new Error(event.message ?? 'Worker error'));
|
|
81
|
+
};
|
|
82
|
+
worker.addEventListener('message', messageHandler);
|
|
83
|
+
worker.addEventListener('error', errorHandler);
|
|
84
|
+
worker.postMessage({ type: 'request', requestId, payload: request });
|
|
85
|
+
// Teardown: send cancel message on unsubscribe
|
|
86
|
+
return () => {
|
|
87
|
+
worker.removeEventListener('message', messageHandler);
|
|
88
|
+
worker.removeEventListener('error', errorHandler);
|
|
89
|
+
worker.postMessage({ type: 'cancel', requestId });
|
|
90
|
+
};
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
function terminate() {
|
|
94
|
+
terminated = true;
|
|
95
|
+
for (const worker of workers) {
|
|
96
|
+
worker.terminate();
|
|
97
|
+
}
|
|
98
|
+
workers.length = 0;
|
|
99
|
+
}
|
|
100
|
+
return {
|
|
101
|
+
execute,
|
|
102
|
+
terminate,
|
|
103
|
+
get isActive() {
|
|
104
|
+
return !terminated && workers.length > 0;
|
|
105
|
+
},
|
|
106
|
+
get activeInstances() {
|
|
107
|
+
return workers.length;
|
|
108
|
+
},
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Generated bundle index. Do not edit.
|
|
114
|
+
*/
|
|
115
|
+
|
|
116
|
+
export { createWorkerTransport };
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @angular-helpers/worker-http
|
|
3
|
+
*
|
|
4
|
+
* Angular HTTP over Web Workers — off-main-thread HTTP pipelines
|
|
5
|
+
* with configurable interceptors, WebCrypto security, and pluggable serialization.
|
|
6
|
+
*
|
|
7
|
+
* Sub-entry points:
|
|
8
|
+
* - @angular-helpers/worker-http/transport (P1: typed RPC bridge)
|
|
9
|
+
* - @angular-helpers/worker-http/serializer (P2: TOON, seroval, auto-detect)
|
|
10
|
+
* - @angular-helpers/worker-http/backend (P3: Angular HttpBackend replacement)
|
|
11
|
+
* - @angular-helpers/worker-http/interceptors (P4: pure-fn interceptors for workers)
|
|
12
|
+
* - @angular-helpers/worker-http/crypto (P5: WebCrypto primitives)
|
|
13
|
+
*/
|
|
14
|
+
const WORKER_HTTP_VERSION = '0.0.1';
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Generated bundle index. Do not edit.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
export { WORKER_HTTP_VERSION };
|
package/package.json
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@angular-helpers/worker-http",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Angular HTTP over Web Workers — off-main-thread HTTP pipelines with configurable interceptors, WebCrypto security, and pluggable serialization",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"angular",
|
|
7
|
+
"web-worker",
|
|
8
|
+
"http",
|
|
9
|
+
"off-main-thread",
|
|
10
|
+
"interceptors",
|
|
11
|
+
"webcrypto",
|
|
12
|
+
"hmac",
|
|
13
|
+
"serialization",
|
|
14
|
+
"toon",
|
|
15
|
+
"performance"
|
|
16
|
+
],
|
|
17
|
+
"repository": {
|
|
18
|
+
"type": "git",
|
|
19
|
+
"url": "https://github.com/Gaspar1992/angular-helpers"
|
|
20
|
+
},
|
|
21
|
+
"bugs": {
|
|
22
|
+
"url": "https://github.com/Gaspar1992/angular-helpers/issues"
|
|
23
|
+
},
|
|
24
|
+
"author": "",
|
|
25
|
+
"license": "MIT",
|
|
26
|
+
"publishConfig": {
|
|
27
|
+
"access": "public"
|
|
28
|
+
},
|
|
29
|
+
"peerDependencies": {
|
|
30
|
+
"@angular/common": "^21.0.0",
|
|
31
|
+
"@angular/core": "^21.0.0",
|
|
32
|
+
"rxjs": "^7.0.0",
|
|
33
|
+
"seroval": "^1.0.0"
|
|
34
|
+
},
|
|
35
|
+
"peerDependenciesMeta": {
|
|
36
|
+
"@angular/common": {
|
|
37
|
+
"optional": true
|
|
38
|
+
},
|
|
39
|
+
"@angular/core": {
|
|
40
|
+
"optional": true
|
|
41
|
+
},
|
|
42
|
+
"seroval": {
|
|
43
|
+
"optional": true
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
"module": "fesm2022/angular-helpers-worker-http.mjs",
|
|
47
|
+
"typings": "types/angular-helpers-worker-http.d.ts",
|
|
48
|
+
"exports": {
|
|
49
|
+
"./package.json": {
|
|
50
|
+
"default": "./package.json"
|
|
51
|
+
},
|
|
52
|
+
".": {
|
|
53
|
+
"types": "./types/angular-helpers-worker-http.d.ts",
|
|
54
|
+
"default": "./fesm2022/angular-helpers-worker-http.mjs"
|
|
55
|
+
},
|
|
56
|
+
"./backend": {
|
|
57
|
+
"types": "./types/angular-helpers-worker-http-backend.d.ts",
|
|
58
|
+
"default": "./fesm2022/angular-helpers-worker-http-backend.mjs"
|
|
59
|
+
},
|
|
60
|
+
"./crypto": {
|
|
61
|
+
"types": "./types/angular-helpers-worker-http-crypto.d.ts",
|
|
62
|
+
"default": "./fesm2022/angular-helpers-worker-http-crypto.mjs"
|
|
63
|
+
},
|
|
64
|
+
"./interceptors": {
|
|
65
|
+
"types": "./types/angular-helpers-worker-http-interceptors.d.ts",
|
|
66
|
+
"default": "./fesm2022/angular-helpers-worker-http-interceptors.mjs"
|
|
67
|
+
},
|
|
68
|
+
"./serializer": {
|
|
69
|
+
"types": "./types/angular-helpers-worker-http-serializer.d.ts",
|
|
70
|
+
"default": "./fesm2022/angular-helpers-worker-http-serializer.mjs"
|
|
71
|
+
},
|
|
72
|
+
"./transport": {
|
|
73
|
+
"types": "./types/angular-helpers-worker-http-transport.d.ts",
|
|
74
|
+
"default": "./fesm2022/angular-helpers-worker-http-transport.mjs"
|
|
75
|
+
}
|
|
76
|
+
},
|
|
77
|
+
"sideEffects": false,
|
|
78
|
+
"dependencies": {
|
|
79
|
+
"tslib": "^2.3.0"
|
|
80
|
+
}
|
|
81
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { Provider } from '@angular/core';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Discriminated union for worker HTTP feature kinds.
|
|
5
|
+
* Mirrors Angular's HttpFeatureKind pattern.
|
|
6
|
+
*/
|
|
7
|
+
type WorkerHttpFeatureKind = 'WorkerConfigs' | 'WorkerRoutes' | 'WorkerFallback' | 'WorkerSerialization';
|
|
8
|
+
/**
|
|
9
|
+
* Feature object — mirrors Angular's HttpFeature<K> shape.
|
|
10
|
+
*/
|
|
11
|
+
interface WorkerHttpFeature<K extends WorkerHttpFeatureKind> {
|
|
12
|
+
readonly kind: K;
|
|
13
|
+
readonly providers: Provider[];
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Single worker definition.
|
|
17
|
+
*/
|
|
18
|
+
interface WorkerConfig {
|
|
19
|
+
/** Unique identifier used for routing / explicit selection */
|
|
20
|
+
id: string;
|
|
21
|
+
/** URL of the worker script (passed to `new Worker(url)`) */
|
|
22
|
+
workerUrl: URL;
|
|
23
|
+
/** Maximum worker instances in the pool (default: 1) */
|
|
24
|
+
maxInstances?: number;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* URL-pattern to worker auto-routing rule.
|
|
28
|
+
*/
|
|
29
|
+
interface WorkerRoute {
|
|
30
|
+
/** URL pattern to match (RegExp or string prefix) */
|
|
31
|
+
pattern: RegExp | string;
|
|
32
|
+
/** Must match a WorkerConfig.id */
|
|
33
|
+
worker: string;
|
|
34
|
+
/** Higher priority = evaluated first (default: 0) */
|
|
35
|
+
priority?: number;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Fallback strategy when workers are unavailable (SSR, old browsers).
|
|
39
|
+
*/
|
|
40
|
+
type WorkerFallbackStrategy = 'main-thread' | 'error';
|
|
41
|
+
|
|
42
|
+
export type { WorkerConfig, WorkerFallbackStrategy, WorkerHttpFeature, WorkerHttpFeatureKind, WorkerRoute };
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Supported HMAC hash algorithms.
|
|
3
|
+
*/
|
|
4
|
+
type HmacAlgorithm = 'SHA-256' | 'SHA-384' | 'SHA-512';
|
|
5
|
+
/**
|
|
6
|
+
* Supported AES algorithms.
|
|
7
|
+
*/
|
|
8
|
+
type AesAlgorithm = 'AES-GCM' | 'AES-CBC' | 'AES-CTR';
|
|
9
|
+
/**
|
|
10
|
+
* Supported hash algorithms for content integrity.
|
|
11
|
+
*/
|
|
12
|
+
type HashAlgorithm = 'SHA-256' | 'SHA-384' | 'SHA-512';
|
|
13
|
+
/**
|
|
14
|
+
* Configuration for HMAC signing operations.
|
|
15
|
+
*/
|
|
16
|
+
interface HmacSignerConfig {
|
|
17
|
+
/** The raw key material (e.g., a secret string encoded as ArrayBuffer) */
|
|
18
|
+
keyMaterial: ArrayBuffer | Uint8Array;
|
|
19
|
+
/** Hash algorithm to use (default: 'SHA-256') */
|
|
20
|
+
algorithm?: HmacAlgorithm;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Configuration for AES encryption operations.
|
|
24
|
+
*/
|
|
25
|
+
interface AesEncryptorConfig {
|
|
26
|
+
/** The raw key material */
|
|
27
|
+
keyMaterial: ArrayBuffer | Uint8Array;
|
|
28
|
+
/** AES algorithm to use (default: 'AES-GCM') */
|
|
29
|
+
algorithm?: AesAlgorithm;
|
|
30
|
+
/** Key length in bits (default: 256) */
|
|
31
|
+
keyLength?: 128 | 192 | 256;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Result of an AES encryption operation.
|
|
35
|
+
*/
|
|
36
|
+
interface EncryptedPayload {
|
|
37
|
+
/** The encrypted data */
|
|
38
|
+
ciphertext: ArrayBuffer;
|
|
39
|
+
/** The initialization vector used */
|
|
40
|
+
iv: Uint8Array;
|
|
41
|
+
/** The algorithm used */
|
|
42
|
+
algorithm: AesAlgorithm;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Interface for HMAC signing and verification.
|
|
46
|
+
*/
|
|
47
|
+
interface HmacSigner {
|
|
48
|
+
/** Sign data and return the signature as ArrayBuffer */
|
|
49
|
+
sign(data: string | ArrayBuffer | Uint8Array): Promise<ArrayBuffer>;
|
|
50
|
+
/** Sign data and return the signature as hex string */
|
|
51
|
+
signHex(data: string | ArrayBuffer | Uint8Array): Promise<string>;
|
|
52
|
+
/** Verify a signature against data */
|
|
53
|
+
verify(data: string | ArrayBuffer | Uint8Array, signature: ArrayBuffer): Promise<boolean>;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Interface for AES encryption and decryption.
|
|
57
|
+
*/
|
|
58
|
+
interface AesEncryptor {
|
|
59
|
+
/** Encrypt data */
|
|
60
|
+
encrypt(data: string | ArrayBuffer | Uint8Array): Promise<EncryptedPayload>;
|
|
61
|
+
/** Decrypt data */
|
|
62
|
+
decrypt(payload: EncryptedPayload): Promise<ArrayBuffer>;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Interface for content hashing.
|
|
66
|
+
*/
|
|
67
|
+
interface ContentHasher {
|
|
68
|
+
/** Hash data and return as ArrayBuffer */
|
|
69
|
+
hash(data: string | ArrayBuffer | Uint8Array): Promise<ArrayBuffer>;
|
|
70
|
+
/** Hash data and return as hex string */
|
|
71
|
+
hashHex(data: string | ArrayBuffer | Uint8Array): Promise<string>;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Creates an HMAC signer using the Web Crypto API.
|
|
76
|
+
*
|
|
77
|
+
* Works in both main thread and web workers.
|
|
78
|
+
* Requires a secure context (HTTPS).
|
|
79
|
+
*
|
|
80
|
+
* @example
|
|
81
|
+
* ```typescript
|
|
82
|
+
* const signer = await createHmacSigner({
|
|
83
|
+
* keyMaterial: new TextEncoder().encode('my-secret-key'),
|
|
84
|
+
* algorithm: 'SHA-256',
|
|
85
|
+
* });
|
|
86
|
+
*
|
|
87
|
+
* const signature = await signer.signHex('request-payload');
|
|
88
|
+
* const isValid = await signer.verify('request-payload', signatureBuffer);
|
|
89
|
+
* ```
|
|
90
|
+
*/
|
|
91
|
+
declare function createHmacSigner(config: HmacSignerConfig): Promise<HmacSigner>;
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Creates an AES encryptor using the Web Crypto API.
|
|
95
|
+
*
|
|
96
|
+
* Works in both main thread and web workers.
|
|
97
|
+
* Requires a secure context (HTTPS).
|
|
98
|
+
*
|
|
99
|
+
* @example
|
|
100
|
+
* ```typescript
|
|
101
|
+
* const encryptor = await createAesEncryptor({
|
|
102
|
+
* keyMaterial: new TextEncoder().encode('32-byte-secret-key-for-aes-256!'),
|
|
103
|
+
* algorithm: 'AES-GCM',
|
|
104
|
+
* });
|
|
105
|
+
*
|
|
106
|
+
* const encrypted = await encryptor.encrypt('sensitive data');
|
|
107
|
+
* const decrypted = await encryptor.decrypt(encrypted);
|
|
108
|
+
* ```
|
|
109
|
+
*/
|
|
110
|
+
declare function createAesEncryptor(config: AesEncryptorConfig): Promise<AesEncryptor>;
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Creates a content hasher using the Web Crypto API.
|
|
114
|
+
*
|
|
115
|
+
* Works in both main thread and web workers.
|
|
116
|
+
* Useful for content integrity verification (e.g., response body hashing).
|
|
117
|
+
*
|
|
118
|
+
* @example
|
|
119
|
+
* ```typescript
|
|
120
|
+
* const hasher = createContentHasher('SHA-256');
|
|
121
|
+
* const hex = await hasher.hashHex('response-body-content');
|
|
122
|
+
* ```
|
|
123
|
+
*/
|
|
124
|
+
declare function createContentHasher(algorithm?: HashAlgorithm): ContentHasher;
|
|
125
|
+
|
|
126
|
+
export { createAesEncryptor, createContentHasher, createHmacSigner };
|
|
127
|
+
export type { AesAlgorithm, AesEncryptor, AesEncryptorConfig, ContentHasher, EncryptedPayload, HashAlgorithm, HmacAlgorithm, HmacSigner, HmacSignerConfig };
|
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Serializable HTTP request — POJO version of Angular's HttpRequest.
|
|
3
|
+
* Structured-clone safe: no classes, no functions, no prototype chains.
|
|
4
|
+
*/
|
|
5
|
+
interface SerializableRequest {
|
|
6
|
+
method: string;
|
|
7
|
+
url: string;
|
|
8
|
+
headers: Record<string, string[]>;
|
|
9
|
+
params: Record<string, string[]>;
|
|
10
|
+
body: unknown;
|
|
11
|
+
responseType: 'json' | 'text' | 'blob' | 'arraybuffer';
|
|
12
|
+
withCredentials: boolean;
|
|
13
|
+
context: Record<string, unknown>;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Serializable HTTP response — POJO version of Angular's HttpResponse.
|
|
17
|
+
*/
|
|
18
|
+
interface SerializableResponse {
|
|
19
|
+
status: number;
|
|
20
|
+
statusText: string;
|
|
21
|
+
headers: Record<string, string[]>;
|
|
22
|
+
body: unknown;
|
|
23
|
+
url: string;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Pure-function interceptor that runs inside a web worker.
|
|
27
|
+
*
|
|
28
|
+
* Constraints:
|
|
29
|
+
* - No `inject()` — Angular DI does not exist in the worker
|
|
30
|
+
* - No DOM access — workers have no `document` or `window`
|
|
31
|
+
* - No closures over external mutable state
|
|
32
|
+
* - Must be serialization-safe (bundled at build time, not transferred at runtime)
|
|
33
|
+
*/
|
|
34
|
+
type WorkerInterceptorFn = (req: SerializableRequest, next: (req: SerializableRequest) => Promise<SerializableResponse>) => Promise<SerializableResponse>;
|
|
35
|
+
/**
|
|
36
|
+
* Configuration for the retry interceptor.
|
|
37
|
+
*/
|
|
38
|
+
interface RetryConfig {
|
|
39
|
+
/** Maximum number of retries (default: 3) */
|
|
40
|
+
maxRetries?: number;
|
|
41
|
+
/** Initial delay in ms (default: 1000) */
|
|
42
|
+
initialDelay?: number;
|
|
43
|
+
/** Backoff multiplier (default: 2) */
|
|
44
|
+
backoffMultiplier?: number;
|
|
45
|
+
/** HTTP status codes that should trigger a retry (default: [408, 429, 500, 502, 503, 504]) */
|
|
46
|
+
retryStatusCodes?: number[];
|
|
47
|
+
/** Whether to retry on network errors (fetch throws) (default: true) */
|
|
48
|
+
retryOnNetworkError?: boolean;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Configuration for the cache interceptor.
|
|
52
|
+
*/
|
|
53
|
+
interface CacheConfig {
|
|
54
|
+
/** TTL in ms (default: 60000 = 1 min) */
|
|
55
|
+
ttl?: number;
|
|
56
|
+
/** Maximum number of cached entries (default: 100) */
|
|
57
|
+
maxEntries?: number;
|
|
58
|
+
/** HTTP methods to cache (default: ['GET']) */
|
|
59
|
+
methods?: string[];
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Configuration for the HMAC signing interceptor.
|
|
63
|
+
*/
|
|
64
|
+
interface HmacInterceptorConfig {
|
|
65
|
+
/** Raw key material for HMAC signing */
|
|
66
|
+
keyMaterial: ArrayBuffer | Uint8Array;
|
|
67
|
+
/** Hash algorithm (default: 'SHA-256') */
|
|
68
|
+
algorithm?: 'SHA-256' | 'SHA-384' | 'SHA-512';
|
|
69
|
+
/** Header name for the signature (default: 'X-HMAC-Signature') */
|
|
70
|
+
headerName?: string;
|
|
71
|
+
/** Function to build the signing payload from the request (default: `${method}:${url}:${body}`) */
|
|
72
|
+
payloadBuilder?: (req: SerializableRequest) => string;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Configuration for the rate limit interceptor.
|
|
76
|
+
*/
|
|
77
|
+
interface RateLimitConfig {
|
|
78
|
+
/** Maximum requests per window (default: 100) */
|
|
79
|
+
maxRequests?: number;
|
|
80
|
+
/** Window size in ms (default: 60000 = 1 min) */
|
|
81
|
+
windowMs?: number;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Configuration for the logging interceptor.
|
|
85
|
+
*/
|
|
86
|
+
interface LoggingConfig {
|
|
87
|
+
/** Custom logger function (default: console.log) */
|
|
88
|
+
logger?: (message: string, data?: unknown) => void;
|
|
89
|
+
/** Whether to include request/response headers in logs (default: false) */
|
|
90
|
+
includeHeaders?: boolean;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Configuration for the content integrity interceptor.
|
|
94
|
+
*/
|
|
95
|
+
interface ContentIntegrityConfig {
|
|
96
|
+
/** Hash algorithm to use (default: 'SHA-256') */
|
|
97
|
+
algorithm?: 'SHA-256' | 'SHA-384' | 'SHA-512';
|
|
98
|
+
/** Response header containing the expected hash (default: 'X-Content-Hash') */
|
|
99
|
+
headerName?: string;
|
|
100
|
+
/** Throw if the integrity header is absent (default: false) */
|
|
101
|
+
requireHash?: boolean;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Creates and registers a worker-side HTTP pipeline.
|
|
106
|
+
*
|
|
107
|
+
* Call this inside a worker file to set up the interceptor chain.
|
|
108
|
+
* The pipeline listens for incoming requests via `postMessage`,
|
|
109
|
+
* runs them through the interceptor chain, executes `fetch()`,
|
|
110
|
+
* and sends the response back.
|
|
111
|
+
*
|
|
112
|
+
* @example
|
|
113
|
+
* ```typescript
|
|
114
|
+
* // workers/secure.worker.ts
|
|
115
|
+
* import { createWorkerPipeline } from '@angular-helpers/worker-http/interceptors';
|
|
116
|
+
* import { hmacSigningInterceptor } from './my-interceptors';
|
|
117
|
+
*
|
|
118
|
+
* createWorkerPipeline([hmacSigningInterceptor]);
|
|
119
|
+
* ```
|
|
120
|
+
*/
|
|
121
|
+
declare function createWorkerPipeline(interceptors: WorkerInterceptorFn[]): void;
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Creates a retry interceptor with exponential backoff.
|
|
125
|
+
*
|
|
126
|
+
* Retries requests that fail with specific HTTP status codes.
|
|
127
|
+
* Respects the `Retry-After` response header when present.
|
|
128
|
+
*
|
|
129
|
+
* @example
|
|
130
|
+
* ```typescript
|
|
131
|
+
* createWorkerPipeline([
|
|
132
|
+
* retryInterceptor({ maxRetries: 3, initialDelay: 500 }),
|
|
133
|
+
* ]);
|
|
134
|
+
* ```
|
|
135
|
+
*/
|
|
136
|
+
declare function retryInterceptor(config?: RetryConfig): WorkerInterceptorFn;
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Creates a cache interceptor that stores responses in worker memory.
|
|
140
|
+
*
|
|
141
|
+
* Caches GET responses by default (configurable). Evicts oldest entry
|
|
142
|
+
* when `maxEntries` is reached (insertion-order eviction).
|
|
143
|
+
* Each factory call creates an independent cache instance.
|
|
144
|
+
*
|
|
145
|
+
* @example
|
|
146
|
+
* ```typescript
|
|
147
|
+
* createWorkerPipeline([
|
|
148
|
+
* cacheInterceptor({ ttl: 30000, maxEntries: 50 }),
|
|
149
|
+
* ]);
|
|
150
|
+
* ```
|
|
151
|
+
*/
|
|
152
|
+
declare function cacheInterceptor(config?: CacheConfig): WorkerInterceptorFn;
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Creates an HMAC signing interceptor that adds a signature header to outgoing requests.
|
|
156
|
+
*
|
|
157
|
+
* Uses WebCrypto `SubtleCrypto` (native in web workers). The `CryptoKey` is
|
|
158
|
+
* imported lazily on the first request and reused for all subsequent requests
|
|
159
|
+
* in the same factory instance.
|
|
160
|
+
*
|
|
161
|
+
* @example
|
|
162
|
+
* ```typescript
|
|
163
|
+
* createWorkerPipeline([
|
|
164
|
+
* hmacSigningInterceptor({
|
|
165
|
+
* keyMaterial: new TextEncoder().encode('my-secret-key'),
|
|
166
|
+
* algorithm: 'SHA-256',
|
|
167
|
+
* headerName: 'X-HMAC-Signature',
|
|
168
|
+
* }),
|
|
169
|
+
* ]);
|
|
170
|
+
* ```
|
|
171
|
+
*/
|
|
172
|
+
declare function hmacSigningInterceptor(config: HmacInterceptorConfig): WorkerInterceptorFn;
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Creates a logging interceptor that logs request and response details.
|
|
176
|
+
*
|
|
177
|
+
* Uses `console.log` by default. A custom logger can be provided via config.
|
|
178
|
+
* Logger exceptions are swallowed — a logging failure never interrupts the pipeline.
|
|
179
|
+
*
|
|
180
|
+
* @example
|
|
181
|
+
* ```typescript
|
|
182
|
+
* createWorkerPipeline([
|
|
183
|
+
* loggingInterceptor({ includeHeaders: true }),
|
|
184
|
+
* ]);
|
|
185
|
+
* ```
|
|
186
|
+
*/
|
|
187
|
+
declare function loggingInterceptor(config?: LoggingConfig): WorkerInterceptorFn;
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Creates a client-side rate limiting interceptor using a sliding window algorithm.
|
|
191
|
+
*
|
|
192
|
+
* Tracks request timestamps within the configured window. When the limit is
|
|
193
|
+
* exceeded, throws an error with status 429. State is per-factory-instance
|
|
194
|
+
* (resets when the worker is terminated).
|
|
195
|
+
*
|
|
196
|
+
* @example
|
|
197
|
+
* ```typescript
|
|
198
|
+
* createWorkerPipeline([
|
|
199
|
+
* rateLimitInterceptor({ maxRequests: 10, windowMs: 5000 }),
|
|
200
|
+
* ]);
|
|
201
|
+
* ```
|
|
202
|
+
*/
|
|
203
|
+
declare function rateLimitInterceptor(config?: RateLimitConfig): WorkerInterceptorFn;
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Creates a content integrity interceptor that verifies response body integrity
|
|
207
|
+
* against a hash provided in a response header.
|
|
208
|
+
*
|
|
209
|
+
* Uses WebCrypto `SubtleCrypto` (native in web workers).
|
|
210
|
+
*
|
|
211
|
+
* @example
|
|
212
|
+
* ```typescript
|
|
213
|
+
* createWorkerPipeline([
|
|
214
|
+
* contentIntegrityInterceptor({
|
|
215
|
+
* algorithm: 'SHA-256',
|
|
216
|
+
* headerName: 'X-Content-Hash',
|
|
217
|
+
* requireHash: true,
|
|
218
|
+
* }),
|
|
219
|
+
* ]);
|
|
220
|
+
* ```
|
|
221
|
+
*/
|
|
222
|
+
declare function contentIntegrityInterceptor(config?: ContentIntegrityConfig): WorkerInterceptorFn;
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Composes multiple worker interceptors into a single `WorkerInterceptorFn`.
|
|
226
|
+
*
|
|
227
|
+
* Interceptors are executed left-to-right, matching Angular's interceptor chain convention.
|
|
228
|
+
* Each interceptor calls `next()` to pass control to the next one.
|
|
229
|
+
*
|
|
230
|
+
* @example
|
|
231
|
+
* ```typescript
|
|
232
|
+
* const pipeline = composeInterceptors(
|
|
233
|
+
* loggingInterceptor(),
|
|
234
|
+
* retryInterceptor({ maxRetries: 2 }),
|
|
235
|
+
* hmacSigningInterceptor({ keyMaterial: key }),
|
|
236
|
+
* );
|
|
237
|
+
*
|
|
238
|
+
* createWorkerPipeline([pipeline]);
|
|
239
|
+
* ```
|
|
240
|
+
*/
|
|
241
|
+
declare function composeInterceptors(...fns: WorkerInterceptorFn[]): WorkerInterceptorFn;
|
|
242
|
+
|
|
243
|
+
export { cacheInterceptor, composeInterceptors, contentIntegrityInterceptor, createWorkerPipeline, hmacSigningInterceptor, loggingInterceptor, rateLimitInterceptor, retryInterceptor };
|
|
244
|
+
export type { CacheConfig, ContentIntegrityConfig, HmacInterceptorConfig, LoggingConfig, RateLimitConfig, RetryConfig, SerializableRequest, SerializableResponse, WorkerInterceptorFn };
|