@angular-helpers/worker-http 0.5.0 → 0.6.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
CHANGED
|
@@ -406,6 +406,79 @@ manual control.
|
|
|
406
406
|
|
|
407
407
|
---
|
|
408
408
|
|
|
409
|
+
## Telemetry
|
|
410
|
+
|
|
411
|
+
Main-thread extension point for APM / metrics. `withTelemetry(...)` registers
|
|
412
|
+
a subscriber that fires synchronously at three lifecycle points of every
|
|
413
|
+
request handled by `WorkerHttpBackend`:
|
|
414
|
+
|
|
415
|
+
- **`onRequest`** — after worker resolution, before dispatch
|
|
416
|
+
- **`onResponse`** — when a successful response is emitted
|
|
417
|
+
- **`onError`** — when the request fails (transport or non-2xx)
|
|
418
|
+
|
|
419
|
+
Events share a `requestId` so you can correlate the three emissions for one
|
|
420
|
+
request. `transport` is `'worker'` or `'fallback-fetch'` (SSR /
|
|
421
|
+
no-route). Errors in your subscriber are caught and logged — they **never**
|
|
422
|
+
affect the actual HTTP request.
|
|
423
|
+
|
|
424
|
+
Call `withTelemetry(...)` multiple times to attach independent subscribers
|
|
425
|
+
(e.g. one for Sentry, one for custom metrics). All subscribers receive every
|
|
426
|
+
event in registration order.
|
|
427
|
+
|
|
428
|
+
```ts
|
|
429
|
+
provideWorkerHttpClient(
|
|
430
|
+
withWorkerConfigs([{ id: 'api', workerUrl: new URL('./workers/api.worker', import.meta.url) }]),
|
|
431
|
+
withWorkerRoutes([{ pattern: /\/api\//, worker: 'api' }]),
|
|
432
|
+
|
|
433
|
+
// Latency histogram
|
|
434
|
+
withTelemetry({
|
|
435
|
+
onResponse: (e) =>
|
|
436
|
+
histogram.record(e.durationMs, {
|
|
437
|
+
workerId: e.workerId,
|
|
438
|
+
status: e.status,
|
|
439
|
+
transport: e.transport,
|
|
440
|
+
}),
|
|
441
|
+
onError: (e) =>
|
|
442
|
+
histogram.record(e.durationMs, {
|
|
443
|
+
workerId: e.workerId,
|
|
444
|
+
status: 'error',
|
|
445
|
+
}),
|
|
446
|
+
}),
|
|
447
|
+
|
|
448
|
+
// Sentry breadcrumbs
|
|
449
|
+
withTelemetry({
|
|
450
|
+
onRequest: (e) =>
|
|
451
|
+
Sentry.addBreadcrumb({
|
|
452
|
+
category: 'worker-http',
|
|
453
|
+
message: `${e.method} ${e.url}`,
|
|
454
|
+
data: { requestId: e.requestId, workerId: e.workerId },
|
|
455
|
+
}),
|
|
456
|
+
onError: (e) =>
|
|
457
|
+
Sentry.captureException(e.error, {
|
|
458
|
+
tags: { workerId: e.workerId ?? 'fallback' },
|
|
459
|
+
}),
|
|
460
|
+
}),
|
|
461
|
+
);
|
|
462
|
+
```
|
|
463
|
+
|
|
464
|
+
Event interface:
|
|
465
|
+
|
|
466
|
+
```ts
|
|
467
|
+
interface WorkerHttpTelemetryEventBase {
|
|
468
|
+
readonly requestId: string; // unique per request, correlates events
|
|
469
|
+
readonly method: string;
|
|
470
|
+
readonly url: string;
|
|
471
|
+
readonly urlWithParams: string;
|
|
472
|
+
readonly workerId: string | null; // null = fallback fetch
|
|
473
|
+
readonly transport: 'worker' | 'fallback-fetch';
|
|
474
|
+
readonly timestamp: number; // performance.now() at emission
|
|
475
|
+
}
|
|
476
|
+
// onResponse adds: kind: 'response', status, durationMs
|
|
477
|
+
// onError adds: kind: 'error', error, durationMs
|
|
478
|
+
```
|
|
479
|
+
|
|
480
|
+
---
|
|
481
|
+
|
|
409
482
|
## SSR + hydration
|
|
410
483
|
|
|
411
484
|
Worker HTTP integrates transparently with Angular SSR. The two problems SSR
|
|
@@ -2,7 +2,7 @@ import * as i0 from '@angular/core';
|
|
|
2
2
|
import { InjectionToken, inject, Injectable, makeEnvironmentProviders } from '@angular/core';
|
|
3
3
|
import { HttpContextToken, HttpHeaders, HttpResponse, HttpBackend, FetchBackend, HttpErrorResponse, HttpClient, HttpContext, provideHttpClient, withFetch } from '@angular/common/http';
|
|
4
4
|
import { throwError } from 'rxjs';
|
|
5
|
-
import { map, catchError } from 'rxjs/operators';
|
|
5
|
+
import { map, tap, catchError } from 'rxjs/operators';
|
|
6
6
|
import { createWorkerTransport } from '@angular-helpers/worker-http/transport';
|
|
7
7
|
|
|
8
8
|
/**
|
|
@@ -53,6 +53,15 @@ const WORKER_HTTP_SERIALIZER_TOKEN = new InjectionToken('WorkerHttpSerializer',
|
|
|
53
53
|
* Defaults to an empty map (no interceptors).
|
|
54
54
|
*/
|
|
55
55
|
const WORKER_HTTP_INTERCEPTORS_TOKEN = new InjectionToken('WorkerHttpInterceptors', { factory: () => ({}) });
|
|
56
|
+
/**
|
|
57
|
+
* Telemetry subscribers registered via `withTelemetry()`.
|
|
58
|
+
*
|
|
59
|
+
* Multi-provider token — each `withTelemetry(...)` call appends one
|
|
60
|
+
* subscriber. All subscribers are invoked on every emission; a throwing
|
|
61
|
+
* subscriber is isolated (the backend swallows the error so it never
|
|
62
|
+
* affects the HTTP request).
|
|
63
|
+
*/
|
|
64
|
+
const WORKER_HTTP_TELEMETRY_TOKEN = new InjectionToken('WorkerHttpTelemetry', { factory: () => [] });
|
|
56
65
|
|
|
57
66
|
/**
|
|
58
67
|
* Converts an Angular `HttpRequest` into a structured-clone-safe POJO
|
|
@@ -117,6 +126,16 @@ function matchWorkerRoute(url, routes) {
|
|
|
117
126
|
return null;
|
|
118
127
|
}
|
|
119
128
|
|
|
129
|
+
let telemetryRequestCounter = 0;
|
|
130
|
+
function nextTelemetryRequestId() {
|
|
131
|
+
telemetryRequestCounter = (telemetryRequestCounter + 1) >>> 0;
|
|
132
|
+
return `whttp-${telemetryRequestCounter.toString(36)}`;
|
|
133
|
+
}
|
|
134
|
+
function now() {
|
|
135
|
+
return typeof performance !== 'undefined' && typeof performance.now === 'function'
|
|
136
|
+
? performance.now()
|
|
137
|
+
: Date.now();
|
|
138
|
+
}
|
|
120
139
|
/**
|
|
121
140
|
* Angular `HttpBackend` replacement that routes HTTP requests to web workers.
|
|
122
141
|
*
|
|
@@ -136,14 +155,15 @@ class WorkerHttpBackend extends HttpBackend {
|
|
|
136
155
|
serializer = inject(WORKER_HTTP_SERIALIZER_TOKEN);
|
|
137
156
|
interceptorSpecs = inject(WORKER_HTTP_INTERCEPTORS_TOKEN);
|
|
138
157
|
fetchBackend = inject(FetchBackend, { optional: true });
|
|
158
|
+
telemetry = inject(WORKER_HTTP_TELEMETRY_TOKEN);
|
|
139
159
|
transports = new Map();
|
|
140
160
|
handle(req) {
|
|
141
161
|
if (typeof Worker === 'undefined') {
|
|
142
|
-
return this.handleFallback(req, 'Web Workers are not available in this environment (SSR)');
|
|
162
|
+
return this.handleFallback(req, null, 'Web Workers are not available in this environment (SSR)');
|
|
143
163
|
}
|
|
144
164
|
const workerId = req.context.get(WORKER_TARGET) ?? matchWorkerRoute(req.url, this.routes);
|
|
145
165
|
if (!workerId) {
|
|
146
|
-
return this.handleFallback(req, `No worker route matched for URL: ${req.url}`);
|
|
166
|
+
return this.handleFallback(req, null, `No worker route matched for URL: ${req.url}`);
|
|
147
167
|
}
|
|
148
168
|
const config = this.configs.find((c) => c.id === workerId);
|
|
149
169
|
if (!config) {
|
|
@@ -156,12 +176,22 @@ class WorkerHttpBackend extends HttpBackend {
|
|
|
156
176
|
? this.serializer.serialize(serializable.body).data
|
|
157
177
|
: serializable.body;
|
|
158
178
|
const payload = body !== serializable.body ? { ...serializable, body } : serializable;
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
179
|
+
const base = this.buildEventBase(req, workerId, 'worker');
|
|
180
|
+
this.emitRequest(base);
|
|
181
|
+
return transport.execute(payload).pipe(map((res) => toHttpResponse(res, req)), tap((event) => {
|
|
182
|
+
if (event instanceof HttpResponse) {
|
|
183
|
+
this.emitResponse(base, event.status);
|
|
184
|
+
}
|
|
185
|
+
}), catchError((err) => {
|
|
186
|
+
const httpError = new HttpErrorResponse({
|
|
187
|
+
error: err,
|
|
188
|
+
status: 0,
|
|
189
|
+
statusText: 'Worker Error',
|
|
190
|
+
url: req.urlWithParams,
|
|
191
|
+
});
|
|
192
|
+
this.emitError(base, httpError);
|
|
193
|
+
return throwError(() => httpError);
|
|
194
|
+
}));
|
|
165
195
|
}
|
|
166
196
|
ngOnDestroy() {
|
|
167
197
|
for (const transport of this.transports.values()) {
|
|
@@ -191,11 +221,73 @@ class WorkerHttpBackend extends HttpBackend {
|
|
|
191
221
|
return wildcard;
|
|
192
222
|
return [...wildcard, ...specific];
|
|
193
223
|
}
|
|
194
|
-
handleFallback(req, reason) {
|
|
224
|
+
handleFallback(req, workerId, reason) {
|
|
195
225
|
if (this.fallback === 'error' || !this.fetchBackend) {
|
|
196
226
|
return throwError(() => new Error(`[WorkerHttpBackend] ${reason}`));
|
|
197
227
|
}
|
|
198
|
-
|
|
228
|
+
const base = this.buildEventBase(req, workerId, 'fallback-fetch');
|
|
229
|
+
this.emitRequest(base);
|
|
230
|
+
return this.fetchBackend.handle(req).pipe(tap((event) => {
|
|
231
|
+
if (event instanceof HttpResponse) {
|
|
232
|
+
this.emitResponse(base, event.status);
|
|
233
|
+
}
|
|
234
|
+
}), catchError((err) => {
|
|
235
|
+
this.emitError(base, err);
|
|
236
|
+
return throwError(() => err);
|
|
237
|
+
}));
|
|
238
|
+
}
|
|
239
|
+
// --- Telemetry ---------------------------------------------------------
|
|
240
|
+
buildEventBase(req, workerId, transport) {
|
|
241
|
+
return {
|
|
242
|
+
requestId: nextTelemetryRequestId(),
|
|
243
|
+
method: req.method,
|
|
244
|
+
url: req.url,
|
|
245
|
+
urlWithParams: req.urlWithParams,
|
|
246
|
+
workerId,
|
|
247
|
+
transport,
|
|
248
|
+
timestamp: now(),
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
emitRequest(base) {
|
|
252
|
+
if (this.telemetry.length === 0)
|
|
253
|
+
return;
|
|
254
|
+
const event = { ...base, kind: 'request' };
|
|
255
|
+
this.dispatch((sub) => sub.onRequest?.(event));
|
|
256
|
+
}
|
|
257
|
+
emitResponse(base, status) {
|
|
258
|
+
if (this.telemetry.length === 0)
|
|
259
|
+
return;
|
|
260
|
+
const event = {
|
|
261
|
+
...base,
|
|
262
|
+
kind: 'response',
|
|
263
|
+
status,
|
|
264
|
+
durationMs: now() - base.timestamp,
|
|
265
|
+
timestamp: now(),
|
|
266
|
+
};
|
|
267
|
+
this.dispatch((sub) => sub.onResponse?.(event));
|
|
268
|
+
}
|
|
269
|
+
emitError(base, error) {
|
|
270
|
+
if (this.telemetry.length === 0)
|
|
271
|
+
return;
|
|
272
|
+
const event = {
|
|
273
|
+
...base,
|
|
274
|
+
kind: 'error',
|
|
275
|
+
error,
|
|
276
|
+
durationMs: now() - base.timestamp,
|
|
277
|
+
timestamp: now(),
|
|
278
|
+
};
|
|
279
|
+
this.dispatch((sub) => sub.onError?.(event));
|
|
280
|
+
}
|
|
281
|
+
dispatch(invoke) {
|
|
282
|
+
for (const subscriber of this.telemetry) {
|
|
283
|
+
try {
|
|
284
|
+
invoke(subscriber);
|
|
285
|
+
}
|
|
286
|
+
catch (telemetryError) {
|
|
287
|
+
// A throwing telemetry subscriber must never affect the HTTP request.
|
|
288
|
+
console.error('[WorkerHttpBackend] telemetry subscriber threw:', telemetryError);
|
|
289
|
+
}
|
|
290
|
+
}
|
|
199
291
|
}
|
|
200
292
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: WorkerHttpBackend, deps: null, target: i0.ɵɵFactoryTarget.Injectable });
|
|
201
293
|
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: WorkerHttpBackend });
|
|
@@ -423,9 +515,51 @@ function withWorkerInterceptors(specs) {
|
|
|
423
515
|
providers: [{ provide: WORKER_HTTP_INTERCEPTORS_TOKEN, useValue: map }],
|
|
424
516
|
};
|
|
425
517
|
}
|
|
518
|
+
/**
|
|
519
|
+
* Registers a telemetry subscriber for `WorkerHttpBackend`.
|
|
520
|
+
*
|
|
521
|
+
* Telemetry hooks are a main-thread extension point for APM integrations
|
|
522
|
+
* (Sentry, Datadog, OpenTelemetry, ad-hoc logging). They fire synchronously
|
|
523
|
+
* at three lifecycle points of every HTTP request handled by the backend:
|
|
524
|
+
*
|
|
525
|
+
* - `onRequest` — after worker resolution, before dispatch
|
|
526
|
+
* - `onResponse` — when a successful response is emitted
|
|
527
|
+
* - `onError` — when the request fails
|
|
528
|
+
*
|
|
529
|
+
* The feature is repeatable — call `withTelemetry(...)` multiple times to
|
|
530
|
+
* attach independent subscribers. All of them receive every event, in
|
|
531
|
+
* registration order. A throwing subscriber is isolated: the backend
|
|
532
|
+
* catches and logs the error so telemetry bugs never break the request.
|
|
533
|
+
*
|
|
534
|
+
* @example Basic counter
|
|
535
|
+
* ```typescript
|
|
536
|
+
* const counters = { requests: 0, errors: 0 };
|
|
537
|
+
* provideWorkerHttpClient(
|
|
538
|
+
* withWorkerConfigs([...]),
|
|
539
|
+
* withTelemetry({
|
|
540
|
+
* onRequest: () => counters.requests++,
|
|
541
|
+
* onError: () => counters.errors++,
|
|
542
|
+
* }),
|
|
543
|
+
* );
|
|
544
|
+
* ```
|
|
545
|
+
*
|
|
546
|
+
* @example Latency histogram
|
|
547
|
+
* ```typescript
|
|
548
|
+
* withTelemetry({
|
|
549
|
+
* onResponse: (e) => histogram.record(e.durationMs, { workerId: e.workerId }),
|
|
550
|
+
* onError: (e) => histogram.record(e.durationMs, { workerId: e.workerId, error: true }),
|
|
551
|
+
* });
|
|
552
|
+
* ```
|
|
553
|
+
*/
|
|
554
|
+
function withTelemetry(telemetry) {
|
|
555
|
+
return {
|
|
556
|
+
kind: 'Telemetry',
|
|
557
|
+
providers: [{ provide: WORKER_HTTP_TELEMETRY_TOKEN, useValue: telemetry, multi: true }],
|
|
558
|
+
};
|
|
559
|
+
}
|
|
426
560
|
|
|
427
561
|
/**
|
|
428
562
|
* Generated bundle index. Do not edit.
|
|
429
563
|
*/
|
|
430
564
|
|
|
431
|
-
export { WORKER_HTTP_INTERCEPTORS_TOKEN, WORKER_HTTP_SERIALIZER_TOKEN, WORKER_TARGET, WorkerHttpBackend, WorkerHttpClient, matchWorkerRoute, provideWorkerHttpClient, toHttpResponse, toSerializableRequest, withWorkerConfigs, withWorkerFallback, withWorkerInterceptors, withWorkerRoutes, withWorkerSerialization };
|
|
565
|
+
export { WORKER_HTTP_INTERCEPTORS_TOKEN, WORKER_HTTP_SERIALIZER_TOKEN, WORKER_TARGET, WorkerHttpBackend, WorkerHttpClient, matchWorkerRoute, provideWorkerHttpClient, toHttpResponse, toSerializableRequest, withTelemetry, withWorkerConfigs, withWorkerFallback, withWorkerInterceptors, withWorkerRoutes, withWorkerSerialization };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@angular-helpers/worker-http",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "Angular HTTP over Web Workers — off-main-thread HTTP pipelines with configurable interceptors, WebCrypto security, and pluggable serialization",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"angular",
|
|
@@ -9,7 +9,7 @@ import { Observable } from 'rxjs';
|
|
|
9
9
|
* Discriminated union for worker HTTP feature kinds.
|
|
10
10
|
* Mirrors Angular's HttpFeatureKind pattern.
|
|
11
11
|
*/
|
|
12
|
-
type WorkerHttpFeatureKind = 'WorkerConfigs' | 'WorkerRoutes' | 'WorkerFallback' | 'WorkerSerialization' | 'WorkerInterceptors';
|
|
12
|
+
type WorkerHttpFeatureKind = 'WorkerConfigs' | 'WorkerRoutes' | 'WorkerFallback' | 'WorkerSerialization' | 'WorkerInterceptors' | 'Telemetry';
|
|
13
13
|
/**
|
|
14
14
|
* Feature object — mirrors Angular's HttpFeature<K> shape.
|
|
15
15
|
*/
|
|
@@ -68,6 +68,82 @@ interface SerializableResponse {
|
|
|
68
68
|
url: string;
|
|
69
69
|
}
|
|
70
70
|
|
|
71
|
+
/**
|
|
72
|
+
* Telemetry hooks for `WorkerHttpBackend`.
|
|
73
|
+
*
|
|
74
|
+
* Extension point for APM integrations (Sentry, Datadog, OpenTelemetry,
|
|
75
|
+
* ad-hoc metrics). Fires on the main thread, synchronously, at three
|
|
76
|
+
* lifecycle points of every HTTP request that reaches
|
|
77
|
+
* `WorkerHttpBackend`:
|
|
78
|
+
*
|
|
79
|
+
* - `onRequest` — after worker resolution / fallback decision, BEFORE
|
|
80
|
+
* dispatch.
|
|
81
|
+
* - `onResponse` — when a successful response is returned from the worker
|
|
82
|
+
* (or the fallback `FetchBackend`).
|
|
83
|
+
* - `onError` — when the request fails in transport or surfaces as a
|
|
84
|
+
* non-2xx `HttpErrorResponse`.
|
|
85
|
+
*
|
|
86
|
+
* Subscribers are invoked inside a try/catch — a throwing or misbehaving
|
|
87
|
+
* subscriber is isolated from the HTTP request and from every other
|
|
88
|
+
* subscriber. Telemetry errors are logged via `console.error` and
|
|
89
|
+
* swallowed.
|
|
90
|
+
*
|
|
91
|
+
* Multiple subscribers are supported via a multi-provider DI token.
|
|
92
|
+
* Register with `withTelemetry(...)`; every call appends one subscriber.
|
|
93
|
+
*/
|
|
94
|
+
/**
|
|
95
|
+
* How a request was dispatched.
|
|
96
|
+
*
|
|
97
|
+
* - `'worker'` — dispatched to a worker from the pool.
|
|
98
|
+
* - `'fallback-fetch'` — dispatched to the main-thread `FetchBackend`
|
|
99
|
+
* (SSR context, no matching route, unknown worker with `'main-thread'`
|
|
100
|
+
* fallback strategy).
|
|
101
|
+
*/
|
|
102
|
+
type WorkerHttpTransportKind = 'worker' | 'fallback-fetch';
|
|
103
|
+
/**
|
|
104
|
+
* Base shape shared by every telemetry event.
|
|
105
|
+
*/
|
|
106
|
+
interface WorkerHttpTelemetryEventBase {
|
|
107
|
+
/** Stable id unique to this request within the process. Correlates all three events. */
|
|
108
|
+
readonly requestId: string;
|
|
109
|
+
/** HTTP method (`'GET'`, `'POST'`, ...). */
|
|
110
|
+
readonly method: string;
|
|
111
|
+
/** URL without query params, as Angular sees it. */
|
|
112
|
+
readonly url: string;
|
|
113
|
+
/** URL with query params baked in. */
|
|
114
|
+
readonly urlWithParams: string;
|
|
115
|
+
/** Worker id that served the request. `null` when routed to fallback fetch. */
|
|
116
|
+
readonly workerId: string | null;
|
|
117
|
+
/** How the request was actually dispatched. */
|
|
118
|
+
readonly transport: WorkerHttpTransportKind;
|
|
119
|
+
/** `performance.now()` value at emission time. */
|
|
120
|
+
readonly timestamp: number;
|
|
121
|
+
}
|
|
122
|
+
/** Fires before the request is dispatched. */
|
|
123
|
+
interface WorkerHttpRequestEvent extends WorkerHttpTelemetryEventBase {
|
|
124
|
+
readonly kind: 'request';
|
|
125
|
+
}
|
|
126
|
+
/** Fires when a successful response is returned. */
|
|
127
|
+
interface WorkerHttpResponseEvent extends WorkerHttpTelemetryEventBase {
|
|
128
|
+
readonly kind: 'response';
|
|
129
|
+
readonly status: number;
|
|
130
|
+
readonly durationMs: number;
|
|
131
|
+
}
|
|
132
|
+
/** Fires when the request fails. */
|
|
133
|
+
interface WorkerHttpErrorEvent extends WorkerHttpTelemetryEventBase {
|
|
134
|
+
readonly kind: 'error';
|
|
135
|
+
readonly error: unknown;
|
|
136
|
+
readonly durationMs: number;
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Telemetry subscriber — all callbacks are optional.
|
|
140
|
+
*/
|
|
141
|
+
interface WorkerHttpTelemetry {
|
|
142
|
+
onRequest?(event: WorkerHttpRequestEvent): void;
|
|
143
|
+
onResponse?(event: WorkerHttpResponseEvent): void;
|
|
144
|
+
onError?(event: WorkerHttpErrorEvent): void;
|
|
145
|
+
}
|
|
146
|
+
|
|
71
147
|
/**
|
|
72
148
|
* Per-worker interceptor specs map. Key is the worker id from
|
|
73
149
|
* `WorkerConfig.id`, plus the special `'*'` wildcard that applies to every
|
|
@@ -232,6 +308,43 @@ declare function withWorkerSerialization(serializer: WorkerSerializer): WorkerHt
|
|
|
232
308
|
* ```
|
|
233
309
|
*/
|
|
234
310
|
declare function withWorkerInterceptors(specs: readonly WorkerInterceptorSpec[] | WorkerInterceptorSpecsMap): WorkerHttpFeature<'WorkerInterceptors'>;
|
|
311
|
+
/**
|
|
312
|
+
* Registers a telemetry subscriber for `WorkerHttpBackend`.
|
|
313
|
+
*
|
|
314
|
+
* Telemetry hooks are a main-thread extension point for APM integrations
|
|
315
|
+
* (Sentry, Datadog, OpenTelemetry, ad-hoc logging). They fire synchronously
|
|
316
|
+
* at three lifecycle points of every HTTP request handled by the backend:
|
|
317
|
+
*
|
|
318
|
+
* - `onRequest` — after worker resolution, before dispatch
|
|
319
|
+
* - `onResponse` — when a successful response is emitted
|
|
320
|
+
* - `onError` — when the request fails
|
|
321
|
+
*
|
|
322
|
+
* The feature is repeatable — call `withTelemetry(...)` multiple times to
|
|
323
|
+
* attach independent subscribers. All of them receive every event, in
|
|
324
|
+
* registration order. A throwing subscriber is isolated: the backend
|
|
325
|
+
* catches and logs the error so telemetry bugs never break the request.
|
|
326
|
+
*
|
|
327
|
+
* @example Basic counter
|
|
328
|
+
* ```typescript
|
|
329
|
+
* const counters = { requests: 0, errors: 0 };
|
|
330
|
+
* provideWorkerHttpClient(
|
|
331
|
+
* withWorkerConfigs([...]),
|
|
332
|
+
* withTelemetry({
|
|
333
|
+
* onRequest: () => counters.requests++,
|
|
334
|
+
* onError: () => counters.errors++,
|
|
335
|
+
* }),
|
|
336
|
+
* );
|
|
337
|
+
* ```
|
|
338
|
+
*
|
|
339
|
+
* @example Latency histogram
|
|
340
|
+
* ```typescript
|
|
341
|
+
* withTelemetry({
|
|
342
|
+
* onResponse: (e) => histogram.record(e.durationMs, { workerId: e.workerId }),
|
|
343
|
+
* onError: (e) => histogram.record(e.durationMs, { workerId: e.workerId, error: true }),
|
|
344
|
+
* });
|
|
345
|
+
* ```
|
|
346
|
+
*/
|
|
347
|
+
declare function withTelemetry(telemetry: WorkerHttpTelemetry): WorkerHttpFeature<'Telemetry'>;
|
|
235
348
|
|
|
236
349
|
/**
|
|
237
350
|
* Angular `HttpBackend` replacement that routes HTTP requests to web workers.
|
|
@@ -252,12 +365,18 @@ declare class WorkerHttpBackend extends HttpBackend implements OnDestroy {
|
|
|
252
365
|
private readonly serializer;
|
|
253
366
|
private readonly interceptorSpecs;
|
|
254
367
|
private readonly fetchBackend;
|
|
368
|
+
private readonly telemetry;
|
|
255
369
|
private readonly transports;
|
|
256
370
|
handle(req: HttpRequest<unknown>): Observable<HttpEvent<unknown>>;
|
|
257
371
|
ngOnDestroy(): void;
|
|
258
372
|
private getOrCreateTransport;
|
|
259
373
|
private resolveSpecsFor;
|
|
260
374
|
private handleFallback;
|
|
375
|
+
private buildEventBase;
|
|
376
|
+
private emitRequest;
|
|
377
|
+
private emitResponse;
|
|
378
|
+
private emitError;
|
|
379
|
+
private dispatch;
|
|
261
380
|
static ɵfac: i0.ɵɵFactoryDeclaration<WorkerHttpBackend, never>;
|
|
262
381
|
static ɵprov: i0.ɵɵInjectableDeclaration<WorkerHttpBackend>;
|
|
263
382
|
}
|
|
@@ -341,5 +460,5 @@ declare function matchWorkerRoute(url: string, routes: Array<{
|
|
|
341
460
|
priority?: number;
|
|
342
461
|
}>): string | null;
|
|
343
462
|
|
|
344
|
-
export { WORKER_HTTP_INTERCEPTORS_TOKEN, WORKER_HTTP_SERIALIZER_TOKEN, WORKER_TARGET, WorkerHttpBackend, WorkerHttpClient, matchWorkerRoute, provideWorkerHttpClient, toHttpResponse, toSerializableRequest, withWorkerConfigs, withWorkerFallback, withWorkerInterceptors, withWorkerRoutes, withWorkerSerialization };
|
|
345
|
-
export type { SerializableRequest, SerializableResponse, WorkerConfig, WorkerFallbackStrategy, WorkerHttpFeature, WorkerHttpFeatureKind, WorkerInterceptorSpecsMap, WorkerRequestOptions, WorkerRoute };
|
|
463
|
+
export { WORKER_HTTP_INTERCEPTORS_TOKEN, WORKER_HTTP_SERIALIZER_TOKEN, WORKER_TARGET, WorkerHttpBackend, WorkerHttpClient, matchWorkerRoute, provideWorkerHttpClient, toHttpResponse, toSerializableRequest, withTelemetry, withWorkerConfigs, withWorkerFallback, withWorkerInterceptors, withWorkerRoutes, withWorkerSerialization };
|
|
464
|
+
export type { SerializableRequest, SerializableResponse, WorkerConfig, WorkerFallbackStrategy, WorkerHttpErrorEvent, WorkerHttpFeature, WorkerHttpFeatureKind, WorkerHttpRequestEvent, WorkerHttpResponseEvent, WorkerHttpTelemetry, WorkerHttpTelemetryEventBase, WorkerHttpTransportKind, WorkerInterceptorSpecsMap, WorkerRequestOptions, WorkerRoute };
|