@allstak/angular 0.1.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/CHANGELOG.md ADDED
@@ -0,0 +1,52 @@
1
+ # Changelog
2
+
3
+ All notable changes to `@allstak/angular` are documented here.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [0.1.0] — 2026-05-29
9
+
10
+ Initial release of the official AllStak SDK for Angular.
11
+
12
+ ### Added
13
+
14
+ - `init(config)` — thin bootstrap over `@allstak/js` that stamps the wrapper
15
+ identity (`sdkName: 'allstak-angular'`, `sdkVersion`) so backend ingest can
16
+ distinguish Angular traffic. The SDK version is generated at build time from
17
+ `package.json`, never hand-written.
18
+ - `createErrorHandler(options?)` / `AllStakErrorHandler` — an Angular
19
+ `ErrorHandler` that forwards uncaught errors to `AllStak.captureException`.
20
+ Unwraps the common Angular/zone.js envelopes (`ngOriginalError`, promise
21
+ `rejection`, `HttpErrorResponse.error`, `ErrorEvent.error`), coerces
22
+ non-`Error` throws, and supports a custom `extractor` plus a `logErrors`
23
+ toggle.
24
+ - `AllStakHttpInterceptor` (class) and `allStakHttpInterceptor` (functional) —
25
+ an `HttpInterceptor` that records every outbound request as an `outbound`
26
+ HTTP item and opens a `http.client` span around it, finishing `error` on
27
+ `4xx`/`5xx` responses or transport failures. Never mutates the request.
28
+ - `TraceService` — injectable, Router-aware navigation instrumentation that
29
+ opens a `navigation` span per route change (`ok` on `NavigationEnd`, `error`
30
+ on `NavigationCancel` / `NavigationError`) and records a navigation
31
+ breadcrumb. `@angular/router` is an optional peer, so the service degrades to
32
+ a no-op when no `Router` is present.
33
+ - `TraceDirective` (`[trace]`) and `AllStakTraceModule` — component
34
+ render-timing directive that opens a `ui.angular.init` span between
35
+ `ngOnInit` and `ngAfterViewInit`.
36
+ - `TraceClassDecorator` / `TraceClass` and `TraceMethodDecorator` /
37
+ `TraceMethod` — class- and method-level lifecycle span decorators with a
38
+ required `name` so spans survive minification.
39
+ - Provider functions for standalone apps: `provideAllStak(config)`,
40
+ `provideAllStakErrorHandler(options?)`, and
41
+ `provideAllStakRouterInstrumentation()` (which force-instantiates
42
+ `TraceService` via `APP_INITIALIZER`).
43
+ - `AllStakModule.forRoot(config)` for module-based apps — initialises the SDK,
44
+ overrides the global `ErrorHandler`, force-instantiates `TraceService`, and
45
+ exposes the `[trace]` directive.
46
+ - Full re-export of the `@allstak/js` public surface so consumers can pull the
47
+ entire observability API from a single namespaced import.
48
+ - Built with ng-packagr (Angular Package Format): FESM2022 bundles, `esm2022`
49
+ output, and partial-Ivy type declarations across the `@angular` 14–18 peer
50
+ range.
51
+
52
+ [0.1.0]: https://github.com/AllStak/allstak-angular/releases/tag/v0.1.0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 AllStak
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,186 @@
1
+ # @allstak/angular
2
+
3
+ [![npm](https://img.shields.io/npm/v/@allstak/angular.svg)](https://www.npmjs.com/package/@allstak/angular)
4
+ [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
5
+
6
+ Official AllStak SDK for **Angular**. Captures uncaught exceptions, structured logs, navigation spans, outbound HTTP requests, and component render timing — with first-class support for both standalone and NgModule-based apps.
7
+
8
+ ## Install
9
+
10
+ ```bash
11
+ npm install @allstak/angular
12
+ # or
13
+ pnpm add @allstak/angular
14
+ # or
15
+ yarn add @allstak/angular
16
+ ```
17
+
18
+ `@angular/core`, `@angular/common`, and `rxjs` are peer dependencies; `@angular/router` is an optional peer (only needed for navigation instrumentation).
19
+
20
+ ## Setup
21
+
22
+ Initialise the SDK once in `main.ts`, **before** you bootstrap the app, so the global error handler and router instrumentation see a live client:
23
+
24
+ ```ts
25
+ import { init } from '@allstak/angular';
26
+ import { bootstrapApplication } from '@angular/platform-browser';
27
+ import { AppComponent } from './app/app.component';
28
+ import { appConfig } from './app/app.config';
29
+
30
+ init({
31
+ apiKey: 'your-ingest-key',
32
+ environment: 'production',
33
+ });
34
+
35
+ bootstrapApplication(AppComponent, appConfig);
36
+ ```
37
+
38
+ ## Standalone apps (provider functions)
39
+
40
+ Wire the DI pieces in `app.config.ts`:
41
+
42
+ ```ts
43
+ import { ApplicationConfig } from '@angular/core';
44
+ import { provideRouter } from '@angular/router';
45
+ import { provideHttpClient, withInterceptors } from '@angular/common/http';
46
+ import {
47
+ provideAllStak,
48
+ provideAllStakErrorHandler,
49
+ provideAllStakRouterInstrumentation,
50
+ allStakHttpInterceptor,
51
+ } from '@allstak/angular';
52
+ import { routes } from './app.routes';
53
+
54
+ export const appConfig: ApplicationConfig = {
55
+ providers: [
56
+ provideRouter(routes),
57
+
58
+ // Bootstrap the SDK from DI (alternative to calling init() in main.ts).
59
+ provideAllStak({ apiKey: 'your-ingest-key', environment: 'production' }),
60
+
61
+ // Override Angular's global ErrorHandler — every uncaught error becomes an Issue.
62
+ provideAllStakErrorHandler(),
63
+
64
+ // Open a navigation span per route change.
65
+ provideAllStakRouterInstrumentation(),
66
+
67
+ // Record every outbound HTTP request + open a http.client span.
68
+ provideHttpClient(withInterceptors([allStakHttpInterceptor])),
69
+ ],
70
+ };
71
+ ```
72
+
73
+ ## NgModule apps
74
+
75
+ ```ts
76
+ import { NgModule, ErrorHandler } from '@angular/core';
77
+ import { BrowserModule } from '@angular/platform-browser';
78
+ import { HTTP_INTERCEPTORS, provideHttpClient, withInterceptorsFromDi } from '@angular/common/http';
79
+ import { AllStakModule, AllStakHttpInterceptor } from '@allstak/angular';
80
+ import { AppComponent } from './app.component';
81
+
82
+ @NgModule({
83
+ declarations: [AppComponent],
84
+ imports: [
85
+ BrowserModule,
86
+
87
+ // forRoot() initialises the SDK, overrides ErrorHandler, force-instantiates
88
+ // the router TraceService, and exposes the [trace] directive.
89
+ AllStakModule.forRoot({ apiKey: 'your-ingest-key', environment: 'production' }),
90
+ ],
91
+ providers: [
92
+ provideHttpClient(withInterceptorsFromDi()),
93
+ { provide: HTTP_INTERCEPTORS, useClass: AllStakHttpInterceptor, multi: true },
94
+ ],
95
+ bootstrap: [AppComponent],
96
+ })
97
+ export class AppModule {}
98
+ ```
99
+
100
+ ## Error handling
101
+
102
+ `createErrorHandler()` returns an Angular `ErrorHandler` that forwards to `AllStak.captureException`. It unwraps the common Angular/zone.js error envelopes (`ngOriginalError`, promise `rejection`, `HttpErrorResponse.error`, `ErrorEvent.error`) before capture.
103
+
104
+ ```ts
105
+ import { ErrorHandler } from '@angular/core';
106
+ import { createErrorHandler } from '@allstak/angular';
107
+
108
+ providers: [
109
+ {
110
+ provide: ErrorHandler,
111
+ useValue: createErrorHandler({
112
+ logErrors: true, // also console.error the original (default)
113
+ extractor: (error, defaultExtractor) =>
114
+ (error as { cause?: unknown }).cause ?? defaultExtractor(error),
115
+ }),
116
+ },
117
+ ];
118
+ ```
119
+
120
+ ## Router instrumentation
121
+
122
+ `TraceService` subscribes to Angular `Router` events and opens a `navigation` span per route change (finishing `ok` on `NavigationEnd`, `error` on `NavigationCancel` / `NavigationError`), plus a navigation breadcrumb. It must be force-instantiated — `provideAllStakRouterInstrumentation()` (standalone) or `AllStakModule.forRoot()` (NgModule) does that for you.
123
+
124
+ ## Component render timing
125
+
126
+ The `[trace]` directive opens a `ui.angular.init` span between a component's `ngOnInit` and `ngAfterViewInit`. Import `TraceDirective` directly (standalone) or `AllStakTraceModule` (NgModule):
127
+
128
+ ```ts
129
+ // standalone component
130
+ import { Component } from '@angular/core';
131
+ import { TraceDirective } from '@allstak/angular';
132
+
133
+ @Component({
134
+ standalone: true,
135
+ imports: [TraceDirective],
136
+ template: `<section trace="dashboard">…</section>`,
137
+ })
138
+ export class DashboardComponent {}
139
+ ```
140
+
141
+ You can also instrument classes/methods with the decorators (the `name` is required so spans survive minification):
142
+
143
+ ```ts
144
+ import { TraceClassDecorator, TraceMethodDecorator } from '@allstak/angular';
145
+
146
+ @TraceClassDecorator({ name: 'CheckoutComponent' })
147
+ @Component({ /* … */ })
148
+ export class CheckoutComponent {
149
+ @TraceMethodDecorator({ name: 'CheckoutComponent.ngOnInit' })
150
+ ngOnInit() {}
151
+ }
152
+ ```
153
+
154
+ ## HTTP instrumentation
155
+
156
+ The interceptor records every outbound request as an `outbound` HTTP item and opens a `http.client` span around it (finished `error` on `4xx`/`5xx` or transport failure). Use the functional `allStakHttpInterceptor` with `withInterceptors([...])` or the class `AllStakHttpInterceptor` with `HTTP_INTERCEPTORS`. It never mutates the request.
157
+
158
+ ## Configuration
159
+
160
+ `init(config)`, `provideAllStak(config)`, and `AllStakModule.forRoot(config)` all accept every option that `@allstak/js`'s `AllStak.init` accepts. Common options:
161
+
162
+ | Option | Purpose |
163
+ |---|---|
164
+ | `apiKey` | Ingest key (required) |
165
+ | `host` | Override the ingest host |
166
+ | `environment` | e.g. `'production'` / `'development'` |
167
+ | `release` | Release identifier for issue grouping / release health |
168
+ | `tracesSampleRate` | Span sampling rate (0–1) |
169
+ | `enableWebVitals` | Capture web vitals |
170
+ | `enableAutoSessionTracking` | Release-health sessions |
171
+ | `enableOfflineQueue` | Buffer events while offline |
172
+ | `beforeSend` | Mutate/drop events before they are sent |
173
+
174
+ The full `@allstak/js` API is re-exported, so you can pull the entire observability surface from one import:
175
+
176
+ ```ts
177
+ import * as AllStakAngular from '@allstak/angular';
178
+
179
+ AllStakAngular.AllStak.captureMessage('checkout started', 'info');
180
+ const span = AllStakAngular.AllStak.startSpan('checkout.flow', { description: 'review → pay' });
181
+ span.finish('ok');
182
+ ```
183
+
184
+ ## License
185
+
186
+ MIT
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Generated bundle index. Do not edit.
3
+ */
4
+ export * from './public-api';
5
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYWxsc3Rhay1hbmd1bGFyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vYWxsc3Rhay1hbmd1bGFyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOztHQUVHO0FBRUgsY0FBYyxjQUFjLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqIEdlbmVyYXRlZCBidW5kbGUgaW5kZXguIERvIG5vdCBlZGl0LlxuICovXG5cbmV4cG9ydCAqIGZyb20gJy4vcHVibGljLWFwaSc7XG4iXX0=
@@ -0,0 +1,40 @@
1
+ /**
2
+ * AllStak for Angular.
3
+ *
4
+ * Public surface:
5
+ * - `init` — bootstrap (delegates to @allstak/js with Angular tagging)
6
+ * - `createErrorHandler` / `AllStakErrorHandler` — Angular `ErrorHandler` forwarding to captureException
7
+ * - `AllStakHttpInterceptor` / `allStakHttpInterceptor` — records outbound requests + opens spans
8
+ * - `TraceService` — Router-aware navigation span instrumentation
9
+ * - `TraceDirective` / `AllStakTraceModule` — `[trace]` component render-timing directive
10
+ * - `TraceClassDecorator` / `TraceMethodDecorator` — lifecycle-span decorators
11
+ * - `provideAllStak` / `provideAllStakErrorHandler` / `provideAllStakRouterInstrumentation`
12
+ * — standalone provider functions
13
+ * - `AllStakModule.forRoot(config)` — NgModule registration for module-based apps
14
+ * - re-exports — every top-level @allstak/js export
15
+ */
16
+ // Bootstrap.
17
+ export { init } from './src/init';
18
+ // Error handling.
19
+ export { createErrorHandler, AllStakErrorHandler, } from './src/error-handler';
20
+ // HTTP instrumentation.
21
+ export { AllStakHttpInterceptor, allStakHttpInterceptor, } from './src/http-interceptor';
22
+ // Router / navigation instrumentation.
23
+ export { TraceService } from './src/trace-service';
24
+ // Component render-timing directive + module.
25
+ export { TraceDirective } from './src/trace-directive';
26
+ // Lifecycle-span decorators.
27
+ export { TraceClass, TraceClassDecorator, TraceMethod, TraceMethodDecorator, } from './src/trace-decorators';
28
+ // Standalone provider functions.
29
+ export { provideAllStak, provideAllStakErrorHandler, provideAllStakRouterInstrumentation, } from './src/providers';
30
+ // NgModule registration for module-based apps.
31
+ export { AllStakModule, AllStakTraceModule, ALLSTAK_CONFIG, appInitializerFactory, } from './src/module';
32
+ // Wrapper identity.
33
+ export { SDK_NAME, SDK_VERSION } from './src/version';
34
+ // ---------------------------------------------------------------------------
35
+ // Mirror the @allstak/js public surface so consumers can pull the entire
36
+ // observability API from a single namespaced import:
37
+ // import * as AllStakAngular from '@allstak/angular';
38
+ // ---------------------------------------------------------------------------
39
+ export { AllStak, Scope, Session, SessionTracker, Span, WebVitalsModule, isWebVitalsSupported, defineIntegration, dedupeIntegration, consoleIntegration, httpClientIntegration, databaseIntegration, eventFiltersIntegration, inboundFiltersIntegration, } from '@allstak/js';
40
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicHVibGljLWFwaS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3B1YmxpYy1hcGkudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7Ozs7Ozs7Ozs7Ozs7O0dBY0c7QUFFSCxhQUFhO0FBQ2IsT0FBTyxFQUFFLElBQUksRUFBRSxNQUFNLFlBQVksQ0FBQztBQUdsQyxrQkFBa0I7QUFDbEIsT0FBTyxFQUNMLGtCQUFrQixFQUNsQixtQkFBbUIsR0FDcEIsTUFBTSxxQkFBcUIsQ0FBQztBQUc3Qix3QkFBd0I7QUFDeEIsT0FBTyxFQUNMLHNCQUFzQixFQUN0QixzQkFBc0IsR0FDdkIsTUFBTSx3QkFBd0IsQ0FBQztBQUVoQyx1Q0FBdUM7QUFDdkMsT0FBTyxFQUFFLFlBQVksRUFBRSxNQUFNLHFCQUFxQixDQUFDO0FBRW5ELDhDQUE4QztBQUM5QyxPQUFPLEVBQUUsY0FBYyxFQUFFLE1BQU0sdUJBQXVCLENBQUM7QUFFdkQsNkJBQTZCO0FBQzdCLE9BQU8sRUFDTCxVQUFVLEVBQ1YsbUJBQW1CLEVBQ25CLFdBQVcsRUFDWCxvQkFBb0IsR0FDckIsTUFBTSx3QkFBd0IsQ0FBQztBQUdoQyxpQ0FBaUM7QUFDakMsT0FBTyxFQUNMLGNBQWMsRUFDZCwwQkFBMEIsRUFDMUIsbUNBQW1DLEdBQ3BDLE1BQU0saUJBQWlCLENBQUM7QUFFekIsK0NBQStDO0FBQy9DLE9BQU8sRUFDTCxhQUFhLEVBQ2Isa0JBQWtCLEVBQ2xCLGNBQWMsRUFDZCxxQkFBcUIsR0FDdEIsTUFBTSxjQUFjLENBQUM7QUFFdEIsb0JBQW9CO0FBQ3BCLE9BQU8sRUFBRSxRQUFRLEVBQUUsV0FBVyxFQUFFLE1BQU0sZUFBZSxDQUFDO0FBRXRELDhFQUE4RTtBQUM5RSx5RUFBeUU7QUFDekUscURBQXFEO0FBQ3JELHdEQUF3RDtBQUN4RCw4RUFBOEU7QUFDOUUsT0FBTyxFQUNMLE9BQU8sRUFDUCxLQUFLLEVBQ0wsT0FBTyxFQUNQLGNBQWMsRUFDZCxJQUFJLEVBQ0osZUFBZSxFQUNmLG9CQUFvQixFQUNwQixpQkFBaUIsRUFDakIsaUJBQWlCLEVBQ2pCLGtCQUFrQixFQUNsQixxQkFBcUIsRUFDckIsbUJBQW1CLEVBQ25CLHVCQUF1QixFQUN2Qix5QkFBeUIsR0FDMUIsTUFBTSxhQUFhLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqIEFsbFN0YWsgZm9yIEFuZ3VsYXIuXG4gKlxuICogUHVibGljIHN1cmZhY2U6XG4gKiAgIC0gYGluaXRgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg4oCUIGJvb3RzdHJhcCAoZGVsZWdhdGVzIHRvIEBhbGxzdGFrL2pzIHdpdGggQW5ndWxhciB0YWdnaW5nKVxuICogICAtIGBjcmVhdGVFcnJvckhhbmRsZXJgIC8gYEFsbFN0YWtFcnJvckhhbmRsZXJgIOKAlCBBbmd1bGFyIGBFcnJvckhhbmRsZXJgIGZvcndhcmRpbmcgdG8gY2FwdHVyZUV4Y2VwdGlvblxuICogICAtIGBBbGxTdGFrSHR0cEludGVyY2VwdG9yYCAvIGBhbGxTdGFrSHR0cEludGVyY2VwdG9yYCDigJQgcmVjb3JkcyBvdXRib3VuZCByZXF1ZXN0cyArIG9wZW5zIHNwYW5zXG4gKiAgIC0gYFRyYWNlU2VydmljZWAgICAgICAgICAgICAgICAgICAgICAg4oCUIFJvdXRlci1hd2FyZSBuYXZpZ2F0aW9uIHNwYW4gaW5zdHJ1bWVudGF0aW9uXG4gKiAgIC0gYFRyYWNlRGlyZWN0aXZlYCAvIGBBbGxTdGFrVHJhY2VNb2R1bGVgIOKAlCBgW3RyYWNlXWAgY29tcG9uZW50IHJlbmRlci10aW1pbmcgZGlyZWN0aXZlXG4gKiAgIC0gYFRyYWNlQ2xhc3NEZWNvcmF0b3JgIC8gYFRyYWNlTWV0aG9kRGVjb3JhdG9yYCDigJQgbGlmZWN5Y2xlLXNwYW4gZGVjb3JhdG9yc1xuICogICAtIGBwcm92aWRlQWxsU3Rha2AgLyBgcHJvdmlkZUFsbFN0YWtFcnJvckhhbmRsZXJgIC8gYHByb3ZpZGVBbGxTdGFrUm91dGVySW5zdHJ1bWVudGF0aW9uYFxuICogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICDigJQgc3RhbmRhbG9uZSBwcm92aWRlciBmdW5jdGlvbnNcbiAqICAgLSBgQWxsU3Rha01vZHVsZS5mb3JSb290KGNvbmZpZylgICAgICDigJQgTmdNb2R1bGUgcmVnaXN0cmF0aW9uIGZvciBtb2R1bGUtYmFzZWQgYXBwc1xuICogICAtIHJlLWV4cG9ydHMgICAgICAgICAgICAgICAgICAgICAgICAgIOKAlCBldmVyeSB0b3AtbGV2ZWwgQGFsbHN0YWsvanMgZXhwb3J0XG4gKi9cblxuLy8gQm9vdHN0cmFwLlxuZXhwb3J0IHsgaW5pdCB9IGZyb20gJy4vc3JjL2luaXQnO1xuZXhwb3J0IHR5cGUgeyBBbGxTdGFrQ2xpZW50SW5zdGFuY2UgfSBmcm9tICcuL3NyYy9pbml0JztcblxuLy8gRXJyb3IgaGFuZGxpbmcuXG5leHBvcnQge1xuICBjcmVhdGVFcnJvckhhbmRsZXIsXG4gIEFsbFN0YWtFcnJvckhhbmRsZXIsXG59IGZyb20gJy4vc3JjL2Vycm9yLWhhbmRsZXInO1xuZXhwb3J0IHR5cGUgeyBFcnJvckhhbmRsZXJPcHRpb25zIH0gZnJvbSAnLi9zcmMvZXJyb3ItaGFuZGxlcic7XG5cbi8vIEhUVFAgaW5zdHJ1bWVudGF0aW9uLlxuZXhwb3J0IHtcbiAgQWxsU3Rha0h0dHBJbnRlcmNlcHRvcixcbiAgYWxsU3Rha0h0dHBJbnRlcmNlcHRvcixcbn0gZnJvbSAnLi9zcmMvaHR0cC1pbnRlcmNlcHRvcic7XG5cbi8vIFJvdXRlciAvIG5hdmlnYXRpb24gaW5zdHJ1bWVudGF0aW9uLlxuZXhwb3J0IHsgVHJhY2VTZXJ2aWNlIH0gZnJvbSAnLi9zcmMvdHJhY2Utc2VydmljZSc7XG5cbi8vIENvbXBvbmVudCByZW5kZXItdGltaW5nIGRpcmVjdGl2ZSArIG1vZHVsZS5cbmV4cG9ydCB7IFRyYWNlRGlyZWN0aXZlIH0gZnJvbSAnLi9zcmMvdHJhY2UtZGlyZWN0aXZlJztcblxuLy8gTGlmZWN5Y2xlLXNwYW4gZGVjb3JhdG9ycy5cbmV4cG9ydCB7XG4gIFRyYWNlQ2xhc3MsXG4gIFRyYWNlQ2xhc3NEZWNvcmF0b3IsXG4gIFRyYWNlTWV0aG9kLFxuICBUcmFjZU1ldGhvZERlY29yYXRvcixcbn0gZnJvbSAnLi9zcmMvdHJhY2UtZGVjb3JhdG9ycyc7XG5leHBvcnQgdHlwZSB7IFRyYWNlRGVjb3JhdG9yT3B0aW9ucyB9IGZyb20gJy4vc3JjL3RyYWNlLWRlY29yYXRvcnMnO1xuXG4vLyBTdGFuZGFsb25lIHByb3ZpZGVyIGZ1bmN0aW9ucy5cbmV4cG9ydCB7XG4gIHByb3ZpZGVBbGxTdGFrLFxuICBwcm92aWRlQWxsU3Rha0Vycm9ySGFuZGxlcixcbiAgcHJvdmlkZUFsbFN0YWtSb3V0ZXJJbnN0cnVtZW50YXRpb24sXG59IGZyb20gJy4vc3JjL3Byb3ZpZGVycyc7XG5cbi8vIE5nTW9kdWxlIHJlZ2lzdHJhdGlvbiBmb3IgbW9kdWxlLWJhc2VkIGFwcHMuXG5leHBvcnQge1xuICBBbGxTdGFrTW9kdWxlLFxuICBBbGxTdGFrVHJhY2VNb2R1bGUsXG4gIEFMTFNUQUtfQ09ORklHLFxuICBhcHBJbml0aWFsaXplckZhY3RvcnksXG59IGZyb20gJy4vc3JjL21vZHVsZSc7XG5cbi8vIFdyYXBwZXIgaWRlbnRpdHkuXG5leHBvcnQgeyBTREtfTkFNRSwgU0RLX1ZFUlNJT04gfSBmcm9tICcuL3NyYy92ZXJzaW9uJztcblxuLy8gLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG4vLyBNaXJyb3IgdGhlIEBhbGxzdGFrL2pzIHB1YmxpYyBzdXJmYWNlIHNvIGNvbnN1bWVycyBjYW4gcHVsbCB0aGUgZW50aXJlXG4vLyBvYnNlcnZhYmlsaXR5IEFQSSBmcm9tIGEgc2luZ2xlIG5hbWVzcGFjZWQgaW1wb3J0OlxuLy8gICBpbXBvcnQgKiBhcyBBbGxTdGFrQW5ndWxhciBmcm9tICdAYWxsc3Rhay9hbmd1bGFyJztcbi8vIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuZXhwb3J0IHtcbiAgQWxsU3RhayxcbiAgU2NvcGUsXG4gIFNlc3Npb24sXG4gIFNlc3Npb25UcmFja2VyLFxuICBTcGFuLFxuICBXZWJWaXRhbHNNb2R1bGUsXG4gIGlzV2ViVml0YWxzU3VwcG9ydGVkLFxuICBkZWZpbmVJbnRlZ3JhdGlvbixcbiAgZGVkdXBlSW50ZWdyYXRpb24sXG4gIGNvbnNvbGVJbnRlZ3JhdGlvbixcbiAgaHR0cENsaWVudEludGVncmF0aW9uLFxuICBkYXRhYmFzZUludGVncmF0aW9uLFxuICBldmVudEZpbHRlcnNJbnRlZ3JhdGlvbixcbiAgaW5ib3VuZEZpbHRlcnNJbnRlZ3JhdGlvbixcbn0gZnJvbSAnQGFsbHN0YWsvanMnO1xuXG5leHBvcnQgdHlwZSB7XG4gIEFsbFN0YWtDb25maWcsXG4gIEFsbFN0YWtJbnRlZ3JhdGlvbixcbiAgQnJlYWRjcnVtYixcbiAgRXJyb3JFdmVudCxcbiAgRXJyb3JFdmVudFByb2Nlc3NvcixcbiAgTG9nRXZlbnQsXG4gIExvZ0xldmVsLFxuICBIdHRwUmVxdWVzdEl0ZW0sXG4gIEhlYXJ0YmVhdE9wdGlvbnMsXG4gIFNwYW5EYXRhLFxuICBTcGFuT3B0aW9ucyxcbiAgU3BhblByb2Nlc3NvcixcbiAgVHJhY2VzU2FtcGxlcixcbiAgU2FtcGxpbmdDb250ZXh0LFxuICBXZWJWaXRhbHNDb250ZXh0LFxuICBEYlF1ZXJ5SXRlbSxcbiAgU2Vzc2lvblN0YXR1cyxcbn0gZnJvbSAnQGFsbHN0YWsvanMnO1xuIl19
@@ -0,0 +1,112 @@
1
+ import { Injectable } from '@angular/core';
2
+ import { AllStak } from '@allstak/js';
3
+ import * as i0 from "@angular/core";
4
+ const DEFAULT_OPTIONS = {
5
+ logErrors: true,
6
+ };
7
+ /**
8
+ * Unwrap the common Angular/zone.js error envelopes so we capture the real
9
+ * underlying error rather than the wrapper:
10
+ *
11
+ * - Angular wraps errors thrown during change detection as
12
+ * `{ ngOriginalError }` / `{ originalError }`.
13
+ * - `HttpErrorResponse` carries the server payload on `.error`.
14
+ * - DOM `ErrorEvent` carries the real error on `.error`.
15
+ * - Rejected promises may surface as `{ rejection }`.
16
+ */
17
+ function defaultExtractor(error) {
18
+ if (error && typeof error === 'object') {
19
+ const candidate = error;
20
+ // PromiseRejectionEvent-style wrapping from zone.js.
21
+ if ('rejection' in candidate && candidate['rejection'] != null) {
22
+ return defaultExtractor(candidate['rejection']);
23
+ }
24
+ // Angular wraps the thrown value during change detection.
25
+ if ('ngOriginalError' in candidate && candidate['ngOriginalError'] != null) {
26
+ return defaultExtractor(candidate['ngOriginalError']);
27
+ }
28
+ if ('originalError' in candidate && candidate['originalError'] != null) {
29
+ return defaultExtractor(candidate['originalError']);
30
+ }
31
+ // DOM ErrorEvent / HttpErrorResponse expose the real error on `.error`.
32
+ if ('error' in candidate && candidate['error'] instanceof Error) {
33
+ return candidate['error'];
34
+ }
35
+ }
36
+ return error;
37
+ }
38
+ /** Coerce any extracted value into an `Error` so `captureException` is happy. */
39
+ function toError(value) {
40
+ if (value instanceof Error)
41
+ return value;
42
+ if (typeof value === 'string')
43
+ return new Error(value);
44
+ try {
45
+ return new Error(JSON.stringify(value));
46
+ }
47
+ catch {
48
+ return new Error(String(value));
49
+ }
50
+ }
51
+ /**
52
+ * AllStak's implementation of Angular's `ErrorHandler`. Captures every error
53
+ * routed through Angular's global error handling — uncaught render/lifecycle
54
+ * errors, change-detection errors, and (when zone.js is present) unhandled
55
+ * promise rejections.
56
+ *
57
+ * Wired manually as `{ provide: ErrorHandler, useValue: createErrorHandler() }`
58
+ * or via {@link provideAllStakErrorHandler}.
59
+ */
60
+ export class AllStakErrorHandler {
61
+ options;
62
+ constructor(options = {}) {
63
+ this.options = { ...DEFAULT_OPTIONS, ...options };
64
+ }
65
+ handleError(error) {
66
+ const extracted = this.options.extractor
67
+ ? this.options.extractor(error, defaultExtractor)
68
+ : defaultExtractor(error);
69
+ const err = toError(extracted);
70
+ try {
71
+ AllStak.captureException(err, { framework: 'angular' });
72
+ }
73
+ catch {
74
+ // Never let our handler break the host's error path.
75
+ }
76
+ if (this.options.logErrors !== false) {
77
+ // Preserve Angular's default of surfacing the original (pre-unwrap)
78
+ // value so stack traces and zone context stay intact in the console.
79
+ // eslint-disable-next-line no-console
80
+ console.error(error);
81
+ }
82
+ }
83
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: AllStakErrorHandler, deps: "invalid", target: i0.ɵɵFactoryTarget.Injectable });
84
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: AllStakErrorHandler });
85
+ }
86
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: AllStakErrorHandler, decorators: [{
87
+ type: Injectable
88
+ }], ctorParameters: () => [{ type: undefined }] });
89
+ /**
90
+ * Factory returning an Angular `ErrorHandler` that forwards to
91
+ * `AllStak.captureException`.
92
+ *
93
+ * Standalone (`app.config.ts`):
94
+ *
95
+ * import { ErrorHandler } from '@angular/core';
96
+ * import { createErrorHandler } from '@allstak/angular';
97
+ *
98
+ * export const appConfig = {
99
+ * providers: [{ provide: ErrorHandler, useValue: createErrorHandler() }],
100
+ * };
101
+ *
102
+ * NgModule (`AppModule`):
103
+ *
104
+ * @NgModule({
105
+ * providers: [{ provide: ErrorHandler, useValue: createErrorHandler() }],
106
+ * })
107
+ * export class AppModule {}
108
+ */
109
+ export function createErrorHandler(options) {
110
+ return new AllStakErrorHandler(options);
111
+ }
112
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZXJyb3ItaGFuZGxlci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy9lcnJvci1oYW5kbGVyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBZ0IsVUFBVSxFQUFFLE1BQU0sZUFBZSxDQUFDO0FBQ3pELE9BQU8sRUFBRSxPQUFPLEVBQUUsTUFBTSxhQUFhLENBQUM7O0FBK0J0QyxNQUFNLGVBQWUsR0FBcUQ7SUFDeEUsU0FBUyxFQUFFLElBQUk7Q0FDaEIsQ0FBQztBQUVGOzs7Ozs7Ozs7R0FTRztBQUNILFNBQVMsZ0JBQWdCLENBQUMsS0FBYztJQUN0QyxJQUFJLEtBQUssSUFBSSxPQUFPLEtBQUssS0FBSyxRQUFRLEVBQUUsQ0FBQztRQUN2QyxNQUFNLFNBQVMsR0FBRyxLQUFnQyxDQUFDO1FBQ25ELHFEQUFxRDtRQUNyRCxJQUFJLFdBQVcsSUFBSSxTQUFTLElBQUksU0FBUyxDQUFDLFdBQVcsQ0FBQyxJQUFJLElBQUksRUFBRSxDQUFDO1lBQy9ELE9BQU8sZ0JBQWdCLENBQUMsU0FBUyxDQUFDLFdBQVcsQ0FBQyxDQUFDLENBQUM7UUFDbEQsQ0FBQztRQUNELDBEQUEwRDtRQUMxRCxJQUFJLGlCQUFpQixJQUFJLFNBQVMsSUFBSSxTQUFTLENBQUMsaUJBQWlCLENBQUMsSUFBSSxJQUFJLEVBQUUsQ0FBQztZQUMzRSxPQUFPLGdCQUFnQixDQUFDLFNBQVMsQ0FBQyxpQkFBaUIsQ0FBQyxDQUFDLENBQUM7UUFDeEQsQ0FBQztRQUNELElBQUksZUFBZSxJQUFJLFNBQVMsSUFBSSxTQUFTLENBQUMsZUFBZSxDQUFDLElBQUksSUFBSSxFQUFFLENBQUM7WUFDdkUsT0FBTyxnQkFBZ0IsQ0FBQyxTQUFTLENBQUMsZUFBZSxDQUFDLENBQUMsQ0FBQztRQUN0RCxDQUFDO1FBQ0Qsd0VBQXdFO1FBQ3hFLElBQUksT0FBTyxJQUFJLFNBQVMsSUFBSSxTQUFTLENBQUMsT0FBTyxDQUFDLFlBQVksS0FBSyxFQUFFLENBQUM7WUFDaEUsT0FBTyxTQUFTLENBQUMsT0FBTyxDQUFDLENBQUM7UUFDNUIsQ0FBQztJQUNILENBQUM7SUFDRCxPQUFPLEtBQUssQ0FBQztBQUNmLENBQUM7QUFFRCxpRkFBaUY7QUFDakYsU0FBUyxPQUFPLENBQUMsS0FBYztJQUM3QixJQUFJLEtBQUssWUFBWSxLQUFLO1FBQUUsT0FBTyxLQUFLLENBQUM7SUFDekMsSUFBSSxPQUFPLEtBQUssS0FBSyxRQUFRO1FBQUUsT0FBTyxJQUFJLEtBQUssQ0FBQyxLQUFLLENBQUMsQ0FBQztJQUN2RCxJQUFJLENBQUM7UUFDSCxPQUFPLElBQUksS0FBSyxDQUFDLElBQUksQ0FBQyxTQUFTLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQztJQUMxQyxDQUFDO0lBQUMsTUFBTSxDQUFDO1FBQ1AsT0FBTyxJQUFJLEtBQUssQ0FBQyxNQUFNLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQztJQUNsQyxDQUFDO0FBQ0gsQ0FBQztBQUVEOzs7Ozs7OztHQVFHO0FBRUgsTUFBTSxPQUFPLG1CQUFtQjtJQUNiLE9BQU8sQ0FBc0I7SUFFOUMsWUFBWSxVQUErQixFQUFFO1FBQzNDLElBQUksQ0FBQyxPQUFPLEdBQUcsRUFBRSxHQUFHLGVBQWUsRUFBRSxHQUFHLE9BQU8sRUFBRSxDQUFDO0lBQ3BELENBQUM7SUFFRCxXQUFXLENBQUMsS0FBYztRQUN4QixNQUFNLFNBQVMsR0FBRyxJQUFJLENBQUMsT0FBTyxDQUFDLFNBQVM7WUFDdEMsQ0FBQyxDQUFDLElBQUksQ0FBQyxPQUFPLENBQUMsU0FBUyxDQUFDLEtBQUssRUFBRSxnQkFBZ0IsQ0FBQztZQUNqRCxDQUFDLENBQUMsZ0JBQWdCLENBQUMsS0FBSyxDQUFDLENBQUM7UUFFNUIsTUFBTSxHQUFHLEdBQUcsT0FBTyxDQUFDLFNBQVMsQ0FBQyxDQUFDO1FBRS9CLElBQUksQ0FBQztZQUNILE9BQU8sQ0FBQyxnQkFBZ0IsQ0FBQyxHQUFHLEVBQUUsRUFBRSxTQUFTLEVBQUUsU0FBUyxFQUFFLENBQUMsQ0FBQztRQUMxRCxDQUFDO1FBQUMsTUFBTSxDQUFDO1lBQ1AscURBQXFEO1FBQ3ZELENBQUM7UUFFRCxJQUFJLElBQUksQ0FBQyxPQUFPLENBQUMsU0FBUyxLQUFLLEtBQUssRUFBRSxDQUFDO1lBQ3JDLG9FQUFvRTtZQUNwRSxxRUFBcUU7WUFDckUsc0NBQXNDO1lBQ3RDLE9BQU8sQ0FBQyxLQUFLLENBQUMsS0FBSyxDQUFDLENBQUM7UUFDdkIsQ0FBQztJQUNILENBQUM7d0dBMUJVLG1CQUFtQjs0R0FBbkIsbUJBQW1COzs0RkFBbkIsbUJBQW1CO2tCQUQvQixVQUFVOztBQThCWDs7Ozs7Ozs7Ozs7Ozs7Ozs7OztHQW1CRztBQUNILE1BQU0sVUFBVSxrQkFBa0IsQ0FBQyxPQUE2QjtJQUM5RCxPQUFPLElBQUksbUJBQW1CLENBQUMsT0FBTyxDQUFDLENBQUM7QUFDMUMsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IEVycm9ySGFuZGxlciwgSW5qZWN0YWJsZSB9IGZyb20gJ0Bhbmd1bGFyL2NvcmUnO1xuaW1wb3J0IHsgQWxsU3RhayB9IGZyb20gJ0BhbGxzdGFrL2pzJztcblxuLyoqXG4gKiBPcHRpb25zIGFjY2VwdGVkIGJ5IHtAbGluayBjcmVhdGVFcnJvckhhbmRsZXJ9IC8ge0BsaW5rIEFsbFN0YWtFcnJvckhhbmRsZXJ9LlxuICpcbiAqIE1pcnJvcnMgdGhlIHNoYXBlIGhvc3QgYXBwcyBleHBlY3QgZnJvbSBhIGZyYW1ld29yayBlcnJvci1oYW5kbGVyIGZhY3Rvcnk6XG4gKiBhIGxvZ2dpbmcgdG9nZ2xlIGFuZCBhbiBgZXh0cmFjdG9yYCBlc2NhcGUgaGF0Y2ggc28gdGhlIGhvc3QgY2FuIGN1c3RvbWlzZVxuICogaG93IGEgdGhyb3duIHZhbHVlIGlzIHVud3JhcHBlZCBiZWZvcmUgd2UgaGFuZCBpdCB0byBgY2FwdHVyZUV4Y2VwdGlvbmAuXG4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgRXJyb3JIYW5kbGVyT3B0aW9ucyB7XG4gIC8qKlxuICAgKiBBbHNvIGBjb25zb2xlLmVycm9yYCB0aGUgb3JpZ2luYWwgZXJyb3IgYWZ0ZXIgY2FwdHVyaW5nIGl0LiBEZWZhdWx0OlxuICAgKiBgdHJ1ZWAg4oCUIHByZXNlcnZpbmcgQW5ndWxhcidzIGRlZmF1bHQgYmVoYXZpb3VyIG9mIHN1cmZhY2luZyBlcnJvcnMgdG9cbiAgICogdGhlIGNvbnNvbGUgc28gbG9jYWwgZGV2ZWxvcG1lbnQgaXMgbm90IHNpbGVuY2VkLlxuICAgKi9cbiAgbG9nRXJyb3JzPzogYm9vbGVhbjtcblxuICAvKipcbiAgICogQ3VzdG9taXNlIGhvdyB0aGUgdGhyb3duIHZhbHVlIGlzIHJlZHVjZWQgdG8gdGhlIGBFcnJvcmAgd2UgY2FwdHVyZS5cbiAgICogUmVjZWl2ZXMgdGhlIHJhdyB2YWx1ZSBBbmd1bGFyIGhhbmRlZCB0byBgaGFuZGxlRXJyb3JgIHBsdXMgdGhlIFNESydzXG4gICAqIGBkZWZhdWx0RXh0cmFjdG9yYCwgc28gYSBob3N0IGNhbiBmYWxsIGJhY2sgdG8gZGVmYXVsdCBiZWhhdmlvdXIgZm9yXG4gICAqIGNhc2VzIGl0IGRvZXMgbm90IGNhcmUgYWJvdXQuXG4gICAqXG4gICAqICAgY3JlYXRlRXJyb3JIYW5kbGVyKHtcbiAgICogICAgIGV4dHJhY3RvcjogKGVycm9yLCBkZWZhdWx0RXh0cmFjdG9yKSA9PlxuICAgKiAgICAgICBlcnJvcj8uY3VzdG9tQ2F1c2UgPz8gZGVmYXVsdEV4dHJhY3RvcihlcnJvciksXG4gICAqICAgfSk7XG4gICAqL1xuICBleHRyYWN0b3I/OiAoZXJyb3I6IHVua25vd24sIGRlZmF1bHRFeHRyYWN0b3I6IChlcnJvcjogdW5rbm93bikgPT4gdW5rbm93bikgPT4gdW5rbm93bjtcbn1cblxuY29uc3QgREVGQVVMVF9PUFRJT05TOiBSZXF1aXJlZDxQaWNrPEVycm9ySGFuZGxlck9wdGlvbnMsICdsb2dFcnJvcnMnPj4gPSB7XG4gIGxvZ0Vycm9yczogdHJ1ZSxcbn07XG5cbi8qKlxuICogVW53cmFwIHRoZSBjb21tb24gQW5ndWxhci96b25lLmpzIGVycm9yIGVudmVsb3BlcyBzbyB3ZSBjYXB0dXJlIHRoZSByZWFsXG4gKiB1bmRlcmx5aW5nIGVycm9yIHJhdGhlciB0aGFuIHRoZSB3cmFwcGVyOlxuICpcbiAqICAgLSBBbmd1bGFyIHdyYXBzIGVycm9ycyB0aHJvd24gZHVyaW5nIGNoYW5nZSBkZXRlY3Rpb24gYXNcbiAqICAgICBgeyBuZ09yaWdpbmFsRXJyb3IgfWAgLyBgeyBvcmlnaW5hbEVycm9yIH1gLlxuICogICAtIGBIdHRwRXJyb3JSZXNwb25zZWAgY2FycmllcyB0aGUgc2VydmVyIHBheWxvYWQgb24gYC5lcnJvcmAuXG4gKiAgIC0gRE9NIGBFcnJvckV2ZW50YCBjYXJyaWVzIHRoZSByZWFsIGVycm9yIG9uIGAuZXJyb3JgLlxuICogICAtIFJlamVjdGVkIHByb21pc2VzIG1heSBzdXJmYWNlIGFzIGB7IHJlamVjdGlvbiB9YC5cbiAqL1xuZnVuY3Rpb24gZGVmYXVsdEV4dHJhY3RvcihlcnJvcjogdW5rbm93bik6IHVua25vd24ge1xuICBpZiAoZXJyb3IgJiYgdHlwZW9mIGVycm9yID09PSAnb2JqZWN0Jykge1xuICAgIGNvbnN0IGNhbmRpZGF0ZSA9IGVycm9yIGFzIFJlY29yZDxzdHJpbmcsIHVua25vd24+O1xuICAgIC8vIFByb21pc2VSZWplY3Rpb25FdmVudC1zdHlsZSB3cmFwcGluZyBmcm9tIHpvbmUuanMuXG4gICAgaWYgKCdyZWplY3Rpb24nIGluIGNhbmRpZGF0ZSAmJiBjYW5kaWRhdGVbJ3JlamVjdGlvbiddICE9IG51bGwpIHtcbiAgICAgIHJldHVybiBkZWZhdWx0RXh0cmFjdG9yKGNhbmRpZGF0ZVsncmVqZWN0aW9uJ10pO1xuICAgIH1cbiAgICAvLyBBbmd1bGFyIHdyYXBzIHRoZSB0aHJvd24gdmFsdWUgZHVyaW5nIGNoYW5nZSBkZXRlY3Rpb24uXG4gICAgaWYgKCduZ09yaWdpbmFsRXJyb3InIGluIGNhbmRpZGF0ZSAmJiBjYW5kaWRhdGVbJ25nT3JpZ2luYWxFcnJvciddICE9IG51bGwpIHtcbiAgICAgIHJldHVybiBkZWZhdWx0RXh0cmFjdG9yKGNhbmRpZGF0ZVsnbmdPcmlnaW5hbEVycm9yJ10pO1xuICAgIH1cbiAgICBpZiAoJ29yaWdpbmFsRXJyb3InIGluIGNhbmRpZGF0ZSAmJiBjYW5kaWRhdGVbJ29yaWdpbmFsRXJyb3InXSAhPSBudWxsKSB7XG4gICAgICByZXR1cm4gZGVmYXVsdEV4dHJhY3RvcihjYW5kaWRhdGVbJ29yaWdpbmFsRXJyb3InXSk7XG4gICAgfVxuICAgIC8vIERPTSBFcnJvckV2ZW50IC8gSHR0cEVycm9yUmVzcG9uc2UgZXhwb3NlIHRoZSByZWFsIGVycm9yIG9uIGAuZXJyb3JgLlxuICAgIGlmICgnZXJyb3InIGluIGNhbmRpZGF0ZSAmJiBjYW5kaWRhdGVbJ2Vycm9yJ10gaW5zdGFuY2VvZiBFcnJvcikge1xuICAgICAgcmV0dXJuIGNhbmRpZGF0ZVsnZXJyb3InXTtcbiAgICB9XG4gIH1cbiAgcmV0dXJuIGVycm9yO1xufVxuXG4vKiogQ29lcmNlIGFueSBleHRyYWN0ZWQgdmFsdWUgaW50byBhbiBgRXJyb3JgIHNvIGBjYXB0dXJlRXhjZXB0aW9uYCBpcyBoYXBweS4gKi9cbmZ1bmN0aW9uIHRvRXJyb3IodmFsdWU6IHVua25vd24pOiBFcnJvciB7XG4gIGlmICh2YWx1ZSBpbnN0YW5jZW9mIEVycm9yKSByZXR1cm4gdmFsdWU7XG4gIGlmICh0eXBlb2YgdmFsdWUgPT09ICdzdHJpbmcnKSByZXR1cm4gbmV3IEVycm9yKHZhbHVlKTtcbiAgdHJ5IHtcbiAgICByZXR1cm4gbmV3IEVycm9yKEpTT04uc3RyaW5naWZ5KHZhbHVlKSk7XG4gIH0gY2F0Y2gge1xuICAgIHJldHVybiBuZXcgRXJyb3IoU3RyaW5nKHZhbHVlKSk7XG4gIH1cbn1cblxuLyoqXG4gKiBBbGxTdGFrJ3MgaW1wbGVtZW50YXRpb24gb2YgQW5ndWxhcidzIGBFcnJvckhhbmRsZXJgLiBDYXB0dXJlcyBldmVyeSBlcnJvclxuICogcm91dGVkIHRocm91Z2ggQW5ndWxhcidzIGdsb2JhbCBlcnJvciBoYW5kbGluZyDigJQgdW5jYXVnaHQgcmVuZGVyL2xpZmVjeWNsZVxuICogZXJyb3JzLCBjaGFuZ2UtZGV0ZWN0aW9uIGVycm9ycywgYW5kICh3aGVuIHpvbmUuanMgaXMgcHJlc2VudCkgdW5oYW5kbGVkXG4gKiBwcm9taXNlIHJlamVjdGlvbnMuXG4gKlxuICogV2lyZWQgbWFudWFsbHkgYXMgYHsgcHJvdmlkZTogRXJyb3JIYW5kbGVyLCB1c2VWYWx1ZTogY3JlYXRlRXJyb3JIYW5kbGVyKCkgfWBcbiAqIG9yIHZpYSB7QGxpbmsgcHJvdmlkZUFsbFN0YWtFcnJvckhhbmRsZXJ9LlxuICovXG5ASW5qZWN0YWJsZSgpXG5leHBvcnQgY2xhc3MgQWxsU3Rha0Vycm9ySGFuZGxlciBpbXBsZW1lbnRzIEVycm9ySGFuZGxlciB7XG4gIHByaXZhdGUgcmVhZG9ubHkgb3B0aW9uczogRXJyb3JIYW5kbGVyT3B0aW9ucztcblxuICBjb25zdHJ1Y3RvcihvcHRpb25zOiBFcnJvckhhbmRsZXJPcHRpb25zID0ge30pIHtcbiAgICB0aGlzLm9wdGlvbnMgPSB7IC4uLkRFRkFVTFRfT1BUSU9OUywgLi4ub3B0aW9ucyB9O1xuICB9XG5cbiAgaGFuZGxlRXJyb3IoZXJyb3I6IHVua25vd24pOiB2b2lkIHtcbiAgICBjb25zdCBleHRyYWN0ZWQgPSB0aGlzLm9wdGlvbnMuZXh0cmFjdG9yXG4gICAgICA/IHRoaXMub3B0aW9ucy5leHRyYWN0b3IoZXJyb3IsIGRlZmF1bHRFeHRyYWN0b3IpXG4gICAgICA6IGRlZmF1bHRFeHRyYWN0b3IoZXJyb3IpO1xuXG4gICAgY29uc3QgZXJyID0gdG9FcnJvcihleHRyYWN0ZWQpO1xuXG4gICAgdHJ5IHtcbiAgICAgIEFsbFN0YWsuY2FwdHVyZUV4Y2VwdGlvbihlcnIsIHsgZnJhbWV3b3JrOiAnYW5ndWxhcicgfSk7XG4gICAgfSBjYXRjaCB7XG4gICAgICAvLyBOZXZlciBsZXQgb3VyIGhhbmRsZXIgYnJlYWsgdGhlIGhvc3QncyBlcnJvciBwYXRoLlxuICAgIH1cblxuICAgIGlmICh0aGlzLm9wdGlvbnMubG9nRXJyb3JzICE9PSBmYWxzZSkge1xuICAgICAgLy8gUHJlc2VydmUgQW5ndWxhcidzIGRlZmF1bHQgb2Ygc3VyZmFjaW5nIHRoZSBvcmlnaW5hbCAocHJlLXVud3JhcClcbiAgICAgIC8vIHZhbHVlIHNvIHN0YWNrIHRyYWNlcyBhbmQgem9uZSBjb250ZXh0IHN0YXkgaW50YWN0IGluIHRoZSBjb25zb2xlLlxuICAgICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIG5vLWNvbnNvbGVcbiAgICAgIGNvbnNvbGUuZXJyb3IoZXJyb3IpO1xuICAgIH1cbiAgfVxufVxuXG4vKipcbiAqIEZhY3RvcnkgcmV0dXJuaW5nIGFuIEFuZ3VsYXIgYEVycm9ySGFuZGxlcmAgdGhhdCBmb3J3YXJkcyB0b1xuICogYEFsbFN0YWsuY2FwdHVyZUV4Y2VwdGlvbmAuXG4gKlxuICogU3RhbmRhbG9uZSAoYGFwcC5jb25maWcudHNgKTpcbiAqXG4gKiAgIGltcG9ydCB7IEVycm9ySGFuZGxlciB9IGZyb20gJ0Bhbmd1bGFyL2NvcmUnO1xuICogICBpbXBvcnQgeyBjcmVhdGVFcnJvckhhbmRsZXIgfSBmcm9tICdAYWxsc3Rhay9hbmd1bGFyJztcbiAqXG4gKiAgIGV4cG9ydCBjb25zdCBhcHBDb25maWcgPSB7XG4gKiAgICAgcHJvdmlkZXJzOiBbeyBwcm92aWRlOiBFcnJvckhhbmRsZXIsIHVzZVZhbHVlOiBjcmVhdGVFcnJvckhhbmRsZXIoKSB9XSxcbiAqICAgfTtcbiAqXG4gKiBOZ01vZHVsZSAoYEFwcE1vZHVsZWApOlxuICpcbiAqICAgQE5nTW9kdWxlKHtcbiAqICAgICBwcm92aWRlcnM6IFt7IHByb3ZpZGU6IEVycm9ySGFuZGxlciwgdXNlVmFsdWU6IGNyZWF0ZUVycm9ySGFuZGxlcigpIH1dLFxuICogICB9KVxuICogICBleHBvcnQgY2xhc3MgQXBwTW9kdWxlIHt9XG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBjcmVhdGVFcnJvckhhbmRsZXIob3B0aW9ucz86IEVycm9ySGFuZGxlck9wdGlvbnMpOiBFcnJvckhhbmRsZXIge1xuICByZXR1cm4gbmV3IEFsbFN0YWtFcnJvckhhbmRsZXIob3B0aW9ucyk7XG59XG4iXX0=
@@ -0,0 +1,158 @@
1
+ import { Injectable } from '@angular/core';
2
+ import { HttpResponse, HttpErrorResponse } from '@angular/common/http';
3
+ import { Observable } from 'rxjs';
4
+ import { AllStak } from '@allstak/js';
5
+ import * as i0 from "@angular/core";
6
+ const KNOWN_METHODS = new Set([
7
+ 'GET',
8
+ 'POST',
9
+ 'PUT',
10
+ 'DELETE',
11
+ 'PATCH',
12
+ 'HEAD',
13
+ 'OPTIONS',
14
+ ]);
15
+ /** Normalise an Angular request method into the core method union. */
16
+ function normalizeMethod(method) {
17
+ const upper = method.toUpperCase();
18
+ return (KNOWN_METHODS.has(upper) ? upper : 'GET');
19
+ }
20
+ /** Split an outbound URL into `host` + `path`, tolerating relative URLs. */
21
+ function splitUrl(url) {
22
+ try {
23
+ const base = typeof window !== 'undefined' && window.location
24
+ ? window.location.origin
25
+ : 'http://localhost';
26
+ const parsed = new URL(url, base);
27
+ return { host: parsed.host, path: parsed.pathname + parsed.search };
28
+ }
29
+ catch {
30
+ return { host: '', path: url };
31
+ }
32
+ }
33
+ /**
34
+ * Open a span + record an outbound request for a single `HttpRequest`, and
35
+ * return the lifecycle hooks the interceptor (class or functional) uses to
36
+ * finish it. Shared so the class-based and functional interceptors stay
37
+ * behaviourally identical.
38
+ */
39
+ function instrumentRequest(req) {
40
+ const startedAt = Date.now();
41
+ const method = normalizeMethod(req.method);
42
+ const { host, path } = splitUrl(req.urlWithParams);
43
+ let span = null;
44
+ try {
45
+ span = AllStak.startSpan('http.client', {
46
+ op: 'http.client',
47
+ description: `${req.method} ${req.urlWithParams}`,
48
+ attributes: {
49
+ 'allstak.origin': 'auto.http.angular',
50
+ 'http.method': req.method,
51
+ 'http.url': req.urlWithParams,
52
+ },
53
+ });
54
+ }
55
+ catch {
56
+ // Never block the request on observability errors.
57
+ }
58
+ const finish = (statusCode, status) => {
59
+ const durationMs = Date.now() - startedAt;
60
+ try {
61
+ span?.setMeasurement('http.duration_ms', durationMs);
62
+ span?.finish(status);
63
+ }
64
+ catch {
65
+ // ignore — span may already be finished
66
+ }
67
+ try {
68
+ const item = {
69
+ direction: 'outbound',
70
+ method,
71
+ host,
72
+ path,
73
+ statusCode,
74
+ durationMs,
75
+ };
76
+ AllStak.captureRequest(item);
77
+ }
78
+ catch {
79
+ // ignore — request recording is best-effort
80
+ }
81
+ };
82
+ return {
83
+ onResponse(statusCode) {
84
+ finish(statusCode, statusCode >= 400 ? 'error' : 'ok');
85
+ },
86
+ onError(error) {
87
+ const statusCode = error instanceof HttpErrorResponse ? error.status : 0;
88
+ finish(statusCode, 'error');
89
+ },
90
+ };
91
+ }
92
+ /**
93
+ * Class-based `HttpInterceptor` that records every outbound HTTP request and
94
+ * opens a `http.client` span around it. Register in the DI-token style used by
95
+ * NgModule apps and standalone apps that opt into `withInterceptorsFromDi()`:
96
+ *
97
+ * import { HTTP_INTERCEPTORS } from '@angular/common/http';
98
+ * import { AllStakHttpInterceptor } from '@allstak/angular';
99
+ *
100
+ * providers: [
101
+ * { provide: HTTP_INTERCEPTORS, useClass: AllStakHttpInterceptor, multi: true },
102
+ * ]
103
+ *
104
+ * The interceptor never mutates the request and never swallows errors — it
105
+ * only observes the stream.
106
+ */
107
+ export class AllStakHttpInterceptor {
108
+ intercept(req, next) {
109
+ const hooks = instrumentRequest(req);
110
+ return new Observable((subscriber) => {
111
+ const sub = next.handle(req).subscribe({
112
+ next: (event) => {
113
+ if (event instanceof HttpResponse) {
114
+ hooks.onResponse(event.status);
115
+ }
116
+ subscriber.next(event);
117
+ },
118
+ error: (error) => {
119
+ hooks.onError(error);
120
+ subscriber.error(error);
121
+ },
122
+ complete: () => subscriber.complete(),
123
+ });
124
+ return () => sub.unsubscribe();
125
+ });
126
+ }
127
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: AllStakHttpInterceptor, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
128
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: AllStakHttpInterceptor });
129
+ }
130
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: AllStakHttpInterceptor, decorators: [{
131
+ type: Injectable
132
+ }] });
133
+ /**
134
+ * Functional `HttpInterceptor` for standalone apps using
135
+ * `provideHttpClient(withInterceptors([allStakHttpInterceptor]))`.
136
+ *
137
+ * Behaviourally identical to {@link AllStakHttpInterceptor}.
138
+ */
139
+ export function allStakHttpInterceptor(req, next) {
140
+ const hooks = instrumentRequest(req);
141
+ return new Observable((subscriber) => {
142
+ const sub = next(req).subscribe({
143
+ next: (event) => {
144
+ if (event instanceof HttpResponse) {
145
+ hooks.onResponse(event.status);
146
+ }
147
+ subscriber.next(event);
148
+ },
149
+ error: (error) => {
150
+ hooks.onError(error);
151
+ subscriber.error(error);
152
+ },
153
+ complete: () => subscriber.complete(),
154
+ });
155
+ return () => sub.unsubscribe();
156
+ });
157
+ }
158
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaHR0cC1pbnRlcmNlcHRvci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy9odHRwLWludGVyY2VwdG9yLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxVQUFVLEVBQUUsTUFBTSxlQUFlLENBQUM7QUFRM0MsT0FBTyxFQUFFLFlBQVksRUFBRSxpQkFBaUIsRUFBRSxNQUFNLHNCQUFzQixDQUFDO0FBQ3ZFLE9BQU8sRUFBRSxVQUFVLEVBQUUsTUFBTSxNQUFNLENBQUM7QUFDbEMsT0FBTyxFQUFFLE9BQU8sRUFBRSxNQUFNLGFBQWEsQ0FBQzs7QUFNdEMsTUFBTSxhQUFhLEdBQXdCLElBQUksR0FBRyxDQUFDO0lBQ2pELEtBQUs7SUFDTCxNQUFNO0lBQ04sS0FBSztJQUNMLFFBQVE7SUFDUixPQUFPO0lBQ1AsTUFBTTtJQUNOLFNBQVM7Q0FDVixDQUFDLENBQUM7QUFFSCxzRUFBc0U7QUFDdEUsU0FBUyxlQUFlLENBQUMsTUFBYztJQUNyQyxNQUFNLEtBQUssR0FBRyxNQUFNLENBQUMsV0FBVyxFQUFFLENBQUM7SUFDbkMsT0FBTyxDQUFDLGFBQWEsQ0FBQyxHQUFHLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsS0FBSyxDQUFlLENBQUM7QUFDbEUsQ0FBQztBQUVELDRFQUE0RTtBQUM1RSxTQUFTLFFBQVEsQ0FBQyxHQUFXO0lBQzNCLElBQUksQ0FBQztRQUNILE1BQU0sSUFBSSxHQUNSLE9BQU8sTUFBTSxLQUFLLFdBQVcsSUFBSSxNQUFNLENBQUMsUUFBUTtZQUM5QyxDQUFDLENBQUMsTUFBTSxDQUFDLFFBQVEsQ0FBQyxNQUFNO1lBQ3hCLENBQUMsQ0FBQyxrQkFBa0IsQ0FBQztRQUN6QixNQUFNLE1BQU0sR0FBRyxJQUFJLEdBQUcsQ0FBQyxHQUFHLEVBQUUsSUFBSSxDQUFDLENBQUM7UUFDbEMsT0FBTyxFQUFFLElBQUksRUFBRSxNQUFNLENBQUMs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QyxDQUFDO1lBQ3pCLENBQUM7WUFDRCxLQUFLLEVBQUUsQ0FBQyxLQUFLLEVBQUUsRUFBRTtnQkFDZixLQUFLLENBQUMsT0FBTyxDQUFDLEtBQUssQ0FBQyxDQUFDO2dCQUNyQixVQUFVLENBQUMsS0FBSyxDQUFDLEtBQUssQ0FBQyxDQUFDO1lBQzFCLENBQUM7WUFDRCxRQUFRLEVBQUUsR0FBRyxFQUFFLENBQUMsVUFBVSxDQUFDLFFBQVEsRUFBRTtTQUN0QyxDQUFDLENBQUM7UUFDSCxPQUFPLEdBQUcsRUFBRSxDQUFDLEdBQUcsQ0FBQyxXQUFXLEVBQUUsQ0FBQztJQUNqQyxDQUFDLENBQUMsQ0FBQztBQUNMLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBJbmplY3RhYmxlIH0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XG5pbXBvcnQgdHlwZSB7XG4gIEh0dHBFdmVudCxcbiAgSHR0cEhhbmRsZXIsXG4gIEh0dHBJbnRlcmNlcHRvcixcbiAgSHR0cEhhbmRsZXJGbixcbiAgSHR0cFJlcXVlc3QsXG59IGZyb20gJ0Bhbmd1bGFyL2NvbW1vbi9odHRwJztcbmltcG9ydCB7IEh0dHBSZXNwb25zZSwgSHR0cEVycm9yUmVzcG9uc2UgfSBmcm9tICdAYW5ndWxhci9jb21tb24vaHR0cCc7XG5pbXBvcnQgeyBPYnNlcnZhYmxlIH0gZnJvbSAncnhqcyc7XG5pbXBvcnQgeyBBbGxTdGFrIH0gZnJvbSAnQGFsbHN0YWsvanMnO1xuaW1wb3J0IHR5cGUgeyBIdHRwUmVxdWVzdEl0ZW0sIFNwYW4gfSBmcm9tICdAYWxsc3Rhay9qcyc7XG5cbi8qKiBIVFRQIHZlcmJzIHRoZSBjb3JlIGBIdHRwUmVxdWVzdEl0ZW1gIGFjY2VwdHMuICovXG50eXBlIEh0dHBNZXRob2QgPSBIdHRwUmVxdWVzdEl0ZW1bJ21ldGhvZCddO1xuXG5jb25zdCBLTk9XTl9NRVRIT0RTOiBSZWFkb25seVNldDxzdHJpbmc+ID0gbmV3IFNldChbXG4gICdHRVQnLFxuICAnUE9TVCcsXG4gICdQVVQnLFxuICAnREVMRVRFJyxcbiAgJ1BBVENIJyxcbiAgJ0hFQUQnLFxuICAnT1BUSU9OUycsXG5dKTtcblxuLyoqIE5vcm1hbGlzZSBhbiBBbmd1bGFyIHJlcXVlc3QgbWV0aG9kIGludG8gdGhlIGNvcmUgbWV0aG9kIHVuaW9uLiAqL1xuZnVuY3Rpb24gbm9ybWFsaXplTWV0aG9kKG1ldGhvZDogc3RyaW5nKTogSHR0cE1ldGhvZCB7XG4gIGNvbnN0IHVwcGVyID0gbWV0aG9kLnRvVXBwZXJDYXNlKCk7XG4gIHJldHVybiAoS05PV05fTUVUSE9EUy5oYXModXBwZXIpID8gdXBwZXIgOiAnR0VUJykgYXMgSHR0cE1ldGhvZDtcbn1cblxuLyoqIFNwbGl0IGFuIG91dGJvdW5kIFVSTCBpbnRvIGBob3N0YCArIGBwYXRoYCwgdG9sZXJhdGluZyByZWxhdGl2ZSBVUkxzLiAqL1xuZnVuY3Rpb24gc3BsaXRVcmwodXJsOiBzdHJpbmcpOiB7IGhvc3Q6IHN0cmluZzsgcGF0aDogc3RyaW5nIH0ge1xuICB0cnkge1xuICAgIGNvbnN0IGJhc2UgPVxuICAgICAgdHlwZW9mIHdpbmRvdyAhPT0gJ3VuZGVmaW5lZCcgJiYgd2luZG93LmxvY2F0aW9uXG4gICAgICAgID8gd2luZG93LmxvY2F0aW9uLm9yaWdpblxuICAgICAgICA6ICdodHRwOi8vbG9jYWxob3N0JztcbiAgICBjb25zdCBwYXJzZWQgPSBuZXcgVVJMKHVybCwgYmFzZSk7XG4gICAgcmV0dXJuIHsgaG9zdDogcGFyc2VkLmhvc3QsIHBhdGg6IHBhcnNlZC5wYXRobmFtZSArIHBhcnNlZC5zZWFyY2ggfTtcbiAgfSBjYXRjaCB7XG4gICAgcmV0dXJuIHsgaG9zdDogJycsIHBhdGg6IHVybCB9O1xuICB9XG59XG5cbi8qKlxuICogT3BlbiBhIHNwYW4gKyByZWNvcmQgYW4gb3V0Ym91bmQgcmVxdWVzdCBmb3IgYSBzaW5nbGUgYEh0dHBSZXF1ZXN0YCwgYW5kXG4gKiByZXR1cm4gdGhlIGxpZmVjeWNsZSBob29rcyB0aGUgaW50ZXJjZXB0b3IgKGNsYXNzIG9yIGZ1bmN0aW9uYWwpIHVzZXMgdG9cbiAqIGZpbmlzaCBpdC4gU2hhcmVkIHNvIHRoZSBjbGFzcy1iYXNlZCBhbmQgZnVuY3Rpb25hbCBpbnRlcmNlcHRvcnMgc3RheVxuICogYmVoYXZpb3VyYWxseSBpZGVudGljYWwuXG4gKi9cbmZ1bmN0aW9uIGluc3RydW1lbnRSZXF1ZXN0KHJlcTogSHR0cFJlcXVlc3Q8dW5rbm93bj4pOiB7XG4gIG9uUmVzcG9uc2U6IChzdGF0dXM6IG51bWJlcikgPT4gdm9pZDtcbiAgb25FcnJvcjogKGVycm9yOiB1bmtub3duKSA9PiB2b2lkO1xufSB7XG4gIGNvbnN0IHN0YXJ0ZWRBdCA9IERhdGUubm93KCk7XG4gIGNvbnN0IG1ldGhvZCA9IG5vcm1hbGl6ZU1ldGhvZChyZXEubWV0aG9kKTtcbiAgY29uc3QgeyBob3N0LCBwYXRoIH0gPSBzcGxpdFVybChyZXEudXJsV2l0aFBhcmFtcyk7XG4gIGxldCBzcGFuOiBTcGFuIHwgbnVsbCA9IG51bGw7XG5cbiAgdHJ5IHtcbiAgICBzcGFuID0gQWxsU3Rhay5zdGFydFNwYW4oJ2h0dHAuY2xpZW50Jywge1xuICAgICAgb3A6ICdodHRwLmNsaWVudCcsXG4gICAgICBkZXNjcmlwdGlvbjogYCR7cmVxLm1ldGhvZH0gJHtyZXEudXJsV2l0aFBhcmFtc31gLFxuICAgICAgYXR0cmlidXRlczoge1xuICAgICAgICAnYWxsc3Rhay5vcmlnaW4nOiAnYXV0by5odHRwLmFuZ3VsYXInLFxuICAgICAgICAnaHR0cC5tZXRob2QnOiByZXEubWV0aG9kLFxuICAgICAgICAnaHR0cC51cmwnOiByZXEudXJsV2l0aFBhcmFtcyxcbiAgICAgIH0sXG4gICAgfSk7XG4gIH0gY2F0Y2gge1xuICAgIC8vIE5ldmVyIGJsb2NrIHRoZSByZXF1ZXN0IG9uIG9ic2VydmFiaWxpdHkgZXJyb3JzLlxuICB9XG5cbiAgY29uc3QgZmluaXNoID0gKHN0YXR1c0NvZGU6IG51bWJlciwgc3RhdHVzOiAnb2snIHwgJ2Vycm9yJykgPT4ge1xuICAgIGNvbnN0IGR1cmF0aW9uTXMgPSBEYXRlLm5vdygpIC0gc3RhcnRlZEF0O1xuICAgIHRyeSB7XG4gICAgICBzcGFuPy5zZXRNZWFzdXJlbWVudCgnaHR0cC5kdXJhdGlvbl9tcycsIGR1cmF0aW9uTXMpO1xuICAgICAgc3Bhbj8uZmluaXNoKHN0YXR1cyk7XG4gICAgfSBjYXRjaCB7XG4gICAgICAvLyBpZ25vcmUg4oCUIHNwYW4gbWF5IGFscmVhZHkgYmUgZmluaXNoZWRcbiAgICB9XG4gICAgdHJ5IHtcbiAgICAgIGNvbnN0IGl0ZW06IEh0dHBSZXF1ZXN0SXRlbSA9IHtcbiAgICAgICAgZGlyZWN0aW9uOiAnb3V0Ym91bmQnLFxuICAgICAgICBtZXRob2QsXG4gICAgICAgIGhvc3QsXG4gICAgICAgIHBhdGgsXG4gICAgICAgIHN0YXR1c0NvZGUsXG4gICAgICAgIGR1cmF0aW9uTXMsXG4gICAgICB9O1xuICAgICAgQWxsU3Rhay5jYXB0dXJlUmVxdWVzdChpdGVtKTtcbiAgICB9IGNhdGNoIHtcbiAgICAgIC8vIGlnbm9yZSDigJQgcmVxdWVzdCByZWNvcmRpbmcgaXMgYmVzdC1lZmZvcnRcbiAgICB9XG4gIH07XG5cbiAgcmV0dXJuIHtcbiAgICBvblJlc3BvbnNlKHN0YXR1c0NvZGU6IG51bWJlcikge1xuICAgICAgZmluaXNoKHN0YXR1c0NvZGUsIHN0YXR1c0NvZGUgPj0gNDAwID8gJ2Vycm9yJyA6ICdvaycpO1xuICAgIH0sXG4gICAgb25FcnJvcihlcnJvcjogdW5rbm93bikge1xuICAgICAgY29uc3Qgc3RhdHVzQ29kZSA9IGVycm9yIGluc3RhbmNlb2YgSHR0cEVycm9yUmVzcG9uc2UgPyBlcnJvci5zdGF0dXMgOiAwO1xuICAgICAgZmluaXNoKHN0YXR1c0NvZGUsICdlcnJvcicpO1xuICAgIH0sXG4gIH07XG59XG5cbi8qKlxuICogQ2xhc3MtYmFzZWQgYEh0dHBJbnRlcmNlcHRvcmAgdGhhdCByZWNvcmRzIGV2ZXJ5IG91dGJvdW5kIEhUVFAgcmVxdWVzdCBhbmRcbiAqIG9wZW5zIGEgYGh0dHAuY2xpZW50YCBzcGFuIGFyb3VuZCBpdC4gUmVnaXN0ZXIgaW4gdGhlIERJLXRva2VuIHN0eWxlIHVzZWQgYnlcbiAqIE5nTW9kdWxlIGFwcHMgYW5kIHN0YW5kYWxvbmUgYXBwcyB0aGF0IG9wdCBpbnRvIGB3aXRoSW50ZXJjZXB0b3JzRnJvbURpKClgOlxuICpcbiAqICAgaW1wb3J0IHsgSFRUUF9JTlRFUkNFUFRPUlMgfSBmcm9tICdAYW5ndWxhci9jb21tb24vaHR0cCc7XG4gKiAgIGltcG9ydCB7IEFsbFN0YWtIdHRwSW50ZXJjZXB0b3IgfSBmcm9tICdAYWxsc3Rhay9hbmd1bGFyJztcbiAqXG4gKiAgIHByb3ZpZGVyczogW1xuICogICAgIHsgcHJvdmlkZTogSFRUUF9JTlRFUkNFUFRPUlMsIHVzZUNsYXNzOiBBbGxTdGFrSHR0cEludGVyY2VwdG9yLCBtdWx0aTogdHJ1ZSB9LFxuICogICBdXG4gKlxuICogVGhlIGludGVyY2VwdG9yIG5ldmVyIG11dGF0ZXMgdGhlIHJlcXVlc3QgYW5kIG5ldmVyIHN3YWxsb3dzIGVycm9ycyDigJQgaXRcbiAqIG9ubHkgb2JzZXJ2ZXMgdGhlIHN0cmVhbS5cbiAqL1xuQEluamVjdGFibGUoKVxuZXhwb3J0IGNsYXNzIEFsbFN0YWtIdHRwSW50ZXJjZXB0b3IgaW1wbGVtZW50cyBIdHRwSW50ZXJjZXB0b3Ige1xuICBpbnRlcmNlcHQoXG4gICAgcmVxOiBIdHRwUmVxdWVzdDx1bmtub3duPixcbiAgICBuZXh0OiBIdHRwSGFuZGxlcixcbiAgKTogT2JzZXJ2YWJsZTxIdHRwRXZlbnQ8dW5rbm93bj4+IHtcbiAgICBjb25zdCBob29rcyA9IGluc3RydW1lbnRSZXF1ZXN0KHJlcSk7XG5cbiAgICByZXR1cm4gbmV3IE9ic2VydmFibGU8SHR0cEV2ZW50PHVua25vd24+Pigoc3Vic2NyaWJlcikgPT4ge1xuICAgICAgY29uc3Qgc3ViID0gbmV4dC5oYW5kbGUocmVxKS5zdWJzY3JpYmUoe1xuICAgICAgICBuZXh0OiAoZXZlbnQpID0+IHtcbiAgICAgICAgICBpZiAoZXZlbnQgaW5zdGFuY2VvZiBIdHRwUmVzcG9uc2UpIHtcbiAgICAgICAgICAgIGhvb2tzLm9uUmVzcG9uc2UoZXZlbnQuc3RhdHVzKTtcbiAgICAgICAgICB9XG4gICAgICAgICAgc3Vic2NyaWJlci5uZXh0KGV2ZW50KTtcbiAgICAgICAgfSxcbiAgICAgICAgZXJyb3I6IChlcnJvcikgPT4ge1xuICAgICAgICAgIGhvb2tzLm9uRXJyb3IoZXJyb3IpO1xuICAgICAgICAgIHN1YnNjcmliZXIuZXJyb3IoZXJyb3IpO1xuICAgICAgICB9LFxuICAgICAgICBjb21wbGV0ZTogKCkgPT4gc3Vic2NyaWJlci5jb21wbGV0ZSgpLFxuICAgICAgfSk7XG4gICAgICByZXR1cm4gKCkgPT4gc3ViLnVuc3Vic2NyaWJlKCk7XG4gICAgfSk7XG4gIH1cbn1cblxuLyoqXG4gKiBGdW5jdGlvbmFsIGBIdHRwSW50ZXJjZXB0b3JgIGZvciBzdGFuZGFsb25lIGFwcHMgdXNpbmdcbiAqIGBwcm92aWRlSHR0cENsaWVudCh3aXRoSW50ZXJjZXB0b3JzKFthbGxTdGFrSHR0cEludGVyY2VwdG9yXSkpYC5cbiAqXG4gKiBCZWhhdmlvdXJhbGx5IGlkZW50aWNhbCB0byB7QGxpbmsgQWxsU3Rha0h0dHBJbnRlcmNlcHRvcn0uXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBhbGxTdGFrSHR0cEludGVyY2VwdG9yKFxuICByZXE6IEh0dHBSZXF1ZXN0PHVua25vd24+LFxuICBuZXh0OiBIdHRwSGFuZGxlckZuLFxuKTogT2JzZXJ2YWJsZTxIdHRwRXZlbnQ8dW5rbm93bj4+IHtcbiAgY29uc3QgaG9va3MgPSBpbnN0cnVtZW50UmVxdWVzdChyZXEpO1xuXG4gIHJldHVybiBuZXcgT2JzZXJ2YWJsZTxIdHRwRXZlbnQ8dW5rbm93bj4+KChzdWJzY3JpYmVyKSA9PiB7XG4gICAgY29uc3Qgc3ViID0gbmV4dChyZXEpLnN1YnNjcmliZSh7XG4gICAgICBuZXh0OiAoZXZlbnQpID0+IHtcbiAgICAgICAgaWYgKGV2ZW50IGluc3RhbmNlb2YgSHR0cFJlc3BvbnNlKSB7XG4gICAgICAgICAgaG9va3Mub25SZXNwb25zZShldmVudC5zdGF0dXMpO1xuICAgICAgICB9XG4gICAgICAgIHN1YnNjcmliZXIubmV4dChldmVudCk7XG4gICAgICB9LFxuICAgICAgZXJyb3I6IChlcnJvcikgPT4ge1xuICAgICAgICBob29rcy5vbkVycm9yKGVycm9yKTtcbiAgICAgICAgc3Vic2NyaWJlci5lcnJvcihlcnJvcik7XG4gICAgICB9LFxuICAgICAgY29tcGxldGU6ICgpID0+IHN1YnNjcmliZXIuY29tcGxldGUoKSxcbiAgICB9KTtcbiAgICByZXR1cm4gKCkgPT4gc3ViLnVuc3Vic2NyaWJlKCk7XG4gIH0pO1xufVxuIl19