@lib-acr/angular 0.0.0-stage → 0.0.1
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/LICENSE +21 -0
- package/README.md +90 -2
- package/dist/cjs/error-handler.js +22 -0
- package/dist/cjs/error-handler.js.map +1 -0
- package/dist/cjs/index.js +34 -0
- package/dist/cjs/index.js.map +1 -0
- package/dist/cjs/interceptor.js +69 -0
- package/dist/cjs/interceptor.js.map +1 -0
- package/dist/cjs/providers.js +51 -0
- package/dist/cjs/providers.js.map +1 -0
- package/dist/cjs/router.js +48 -0
- package/dist/cjs/router.js.map +1 -0
- package/dist/cjs/sdk-info.js +6 -0
- package/dist/cjs/sdk-info.js.map +1 -0
- package/dist/cjs/service.js +96 -0
- package/dist/cjs/service.js.map +1 -0
- package/dist/cjs/tokens.js +7 -0
- package/dist/cjs/tokens.js.map +1 -0
- package/dist/esm/error-handler.js +18 -0
- package/dist/esm/error-handler.js.map +1 -0
- package/dist/esm/index.js +8 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/interceptor.js +65 -0
- package/dist/esm/interceptor.js.map +1 -0
- package/dist/esm/package.json +3 -0
- package/dist/esm/providers.js +46 -0
- package/dist/esm/providers.js.map +1 -0
- package/dist/esm/router.js +42 -0
- package/dist/esm/router.js.map +1 -0
- package/dist/esm/sdk-info.js +3 -0
- package/dist/esm/sdk-info.js.map +1 -0
- package/dist/esm/service.js +92 -0
- package/dist/esm/service.js.map +1 -0
- package/dist/esm/tokens.js +4 -0
- package/dist/esm/tokens.js.map +1 -0
- package/dist/types/error-handler.d.ts +11 -0
- package/dist/types/index.d.ts +8 -0
- package/dist/types/interceptor.d.ts +9 -0
- package/dist/types/providers.d.ts +23 -0
- package/dist/types/router.d.ts +23 -0
- package/dist/types/sdk-info.d.ts +4 -0
- package/dist/types/service.d.ts +56 -0
- package/dist/types/tokens.d.ts +9 -0
- package/package.json +66 -4
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Application Control Room
|
|
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
CHANGED
|
@@ -1,3 +1,91 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @lib-acr/angular
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Angular SDK: provideACR() (one client, an ErrorHandler that reports uncaught errors, start and stop with the app), ACRService, an HttpClient interceptor that times requests, and router page tracking from `@lib-acr/angular/router`.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @lib-acr/angular
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Quick start
|
|
12
|
+
|
|
13
|
+
```ts
|
|
14
|
+
// app.config.ts
|
|
15
|
+
import { provideHttpClient, withInterceptors } from '@angular/common/http';
|
|
16
|
+
import { provideRouter } from '@angular/router';
|
|
17
|
+
import { provideACR, acrHttpInterceptor } from '@lib-acr/angular';
|
|
18
|
+
import { provideACRRouterTracking } from '@lib-acr/angular/router';
|
|
19
|
+
|
|
20
|
+
export const appConfig = {
|
|
21
|
+
providers: [
|
|
22
|
+
provideACR({ apiKey: environment.acrApiKey, endpoint: environment.acrEndpoint, appVersion: '1.0.0' }),
|
|
23
|
+
provideRouter(routes),
|
|
24
|
+
provideACRRouterTracking(),
|
|
25
|
+
provideHttpClient(withInterceptors([acrHttpInterceptor])),
|
|
26
|
+
],
|
|
27
|
+
};
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Notes
|
|
31
|
+
|
|
32
|
+
Keep the publishable key in your environment files. Do not put secret keys in browser bundles. If you also use the fetch backend, set `trackFetch: false` so requests are not counted twice.
|
|
33
|
+
|
|
34
|
+
- `ACRService` offers `trackEvent`, `log`, `captureError`, `identify`, `trackPageView`, `flush`, `optOut`, `optIn` and
|
|
35
|
+
`reset()` (on logout: clears the user id, new anonymous id, new session).
|
|
36
|
+
- All `@lib-acr/browser` options apply, including `minLogLevel` (default `warn`) and `activePingMinutes` (default 5,
|
|
37
|
+
`0` turns the live-visitor ping off).
|
|
38
|
+
- HTTP failures reported by `acrHttpInterceptor` are not reported again by the ErrorHandler.
|
|
39
|
+
- The entry point does not re-export `AcrClient`, `HttpTransport` or `resolveConfig` from `@lib-acr/core`.
|
|
40
|
+
|
|
41
|
+
## Remote control
|
|
42
|
+
|
|
43
|
+
`ui` in `provideACR()` controls the built-in maintenance screen, announcement banner and update prompt (default: all
|
|
44
|
+
on). Set parts to `false` and use the signals to render your own:
|
|
45
|
+
|
|
46
|
+
```ts
|
|
47
|
+
export class Shell {
|
|
48
|
+
readonly controls = injectACRControls(); // Signal<ControlState>: maintenance, announcement, release, flags, revision
|
|
49
|
+
private readonly acr = inject(ACRService);
|
|
50
|
+
dismiss(id: string) { this.acr.dismissAnnouncement(id); }
|
|
51
|
+
}
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Block routes while maintenance applies to the current user (from `@lib-acr/angular/router`):
|
|
55
|
+
|
|
56
|
+
```ts
|
|
57
|
+
{ path: 'checkout', component: CheckoutPage, canActivate: [acrMaintenanceGuard] }
|
|
58
|
+
{ path: 'checkout', component: CheckoutPage, canActivate: [acrMaintenanceGuardWith({ redirectTo: '/maintenance' })] }
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
The guard uses the last snapshot, so navigations that happen before the first snapshot are allowed.
|
|
62
|
+
|
|
63
|
+
## Feature flags
|
|
64
|
+
|
|
65
|
+
```ts
|
|
66
|
+
export class CheckoutPage {
|
|
67
|
+
readonly newCheckout = injectFlag('new-checkout', false); // or inject(ACRService).flagSignal(...)
|
|
68
|
+
}
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
```html
|
|
72
|
+
@if (newCheckout()) { <acr-new-checkout /> } @else { <acr-old-checkout /> }
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
`ACRService` also has `flag(key, fallback)` for a one-off read, `flag$(key, fallback)` as an observable and `flags()`.
|
|
76
|
+
There is no `*acrFlag` structural directive. This package is compiled with plain `tsc` and has no Angular compiler
|
|
77
|
+
step, so a decorated directive would need the JIT compiler at runtime and would break AOT builds. Use the built-in
|
|
78
|
+
`@if` with the flag signal instead.
|
|
79
|
+
|
|
80
|
+
## Wire protocol
|
|
81
|
+
|
|
82
|
+
Every SDK sends the same protocol: `POST {endpoint}/ingest/{events|logs|errors|heartbeat}` with the `X-ACR-Api-Key`
|
|
83
|
+
header (and `X-ACR-Secret-Key` for server keys). The credential determines the app and environment. The body is
|
|
84
|
+
`{ schemaVersion: 1, sdk, items[1..500] }`.
|
|
85
|
+
Each item has a client `id`, which lets the server drop duplicates when a batch is retried.
|
|
86
|
+
|
|
87
|
+
Retries use exponential backoff with jitter for network errors, 429 and 5xx responses. Other 4xx responses are not retried.
|
|
88
|
+
Telemetry is buffered in a bounded in-memory queue. When it is full, the oldest items are dropped and counted in `stats()`.
|
|
89
|
+
SDK code never throws into the host application.
|
|
90
|
+
|
|
91
|
+
Metrics (`increment`, `gauge`, `histogram`) and traces are not yet available.
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ACRErrorHandler = void 0;
|
|
4
|
+
const browser_1 = require("@lib-acr/browser");
|
|
5
|
+
/**
|
|
6
|
+
* Angular ErrorHandler that reports uncaught errors to ACR and keeps logging them to the console. Register it with
|
|
7
|
+
* provideACR(); it replaces the default handler. HTTP failures already reported by acrHttpInterceptor are not sent
|
|
8
|
+
* again when the app leaves them unhandled.
|
|
9
|
+
*/
|
|
10
|
+
class ACRErrorHandler {
|
|
11
|
+
constructor(client) {
|
|
12
|
+
this.client = client;
|
|
13
|
+
}
|
|
14
|
+
handleError(error) {
|
|
15
|
+
if (!(0, browser_1.wasCaptured)(error)) {
|
|
16
|
+
this.client.captureError(error, { handled: false, source: 'frontend', attributes: { kind: 'angular' } });
|
|
17
|
+
}
|
|
18
|
+
console.error(error);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
exports.ACRErrorHandler = ACRErrorHandler;
|
|
22
|
+
//# sourceMappingURL=error-handler.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"error-handler.js","sourceRoot":"","sources":["../../src/error-handler.ts"],"names":[],"mappings":";;;AAAA,8CAAgE;AAEhE;;;;GAIG;AACH,MAAa,eAAe;IAC1B,YAA6B,MAAkB;QAAlB,WAAM,GAAN,MAAM,CAAY;IAAG,CAAC;IAEnD,WAAW,CAAC,KAAc;QACxB,IAAI,CAAC,IAAA,qBAAW,EAAC,KAAK,CAAC,EAAE,CAAC;YACxB,IAAI,CAAC,MAAM,CAAC,YAAY,CAAC,KAAK,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,UAAU,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,EAAE,CAAC,CAAC;QAC3G,CAAC;QACD,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IACvB,CAAC;CACF;AATD,0CASC"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
exports.SDK_INFO = exports.ACR_CONFIG = exports.ACR_CLIENT = exports.acrHttpInterceptor = exports.ACRErrorHandler = exports.ACRService = exports.injectACRControls = exports.injectFlag = exports.provideACR = void 0;
|
|
18
|
+
var providers_js_1 = require("./providers.js");
|
|
19
|
+
Object.defineProperty(exports, "provideACR", { enumerable: true, get: function () { return providers_js_1.provideACR; } });
|
|
20
|
+
Object.defineProperty(exports, "injectFlag", { enumerable: true, get: function () { return providers_js_1.injectFlag; } });
|
|
21
|
+
Object.defineProperty(exports, "injectACRControls", { enumerable: true, get: function () { return providers_js_1.injectACRControls; } });
|
|
22
|
+
var service_js_1 = require("./service.js");
|
|
23
|
+
Object.defineProperty(exports, "ACRService", { enumerable: true, get: function () { return service_js_1.ACRService; } });
|
|
24
|
+
var error_handler_js_1 = require("./error-handler.js");
|
|
25
|
+
Object.defineProperty(exports, "ACRErrorHandler", { enumerable: true, get: function () { return error_handler_js_1.ACRErrorHandler; } });
|
|
26
|
+
var interceptor_js_1 = require("./interceptor.js");
|
|
27
|
+
Object.defineProperty(exports, "acrHttpInterceptor", { enumerable: true, get: function () { return interceptor_js_1.acrHttpInterceptor; } });
|
|
28
|
+
var tokens_js_1 = require("./tokens.js");
|
|
29
|
+
Object.defineProperty(exports, "ACR_CLIENT", { enumerable: true, get: function () { return tokens_js_1.ACR_CLIENT; } });
|
|
30
|
+
Object.defineProperty(exports, "ACR_CONFIG", { enumerable: true, get: function () { return tokens_js_1.ACR_CONFIG; } });
|
|
31
|
+
__exportStar(require("@lib-acr/browser"), exports);
|
|
32
|
+
var sdk_info_js_1 = require("./sdk-info.js");
|
|
33
|
+
Object.defineProperty(exports, "SDK_INFO", { enumerable: true, get: function () { return sdk_info_js_1.SDK_INFO; } });
|
|
34
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;AAAA,+CAA2E;AAAlE,0GAAA,UAAU,OAAA;AAAE,0GAAA,UAAU,OAAA;AAAE,iHAAA,iBAAiB,OAAA;AAClD,2CAA0C;AAAjC,wGAAA,UAAU,OAAA;AACnB,uDAAqD;AAA5C,mHAAA,eAAe,OAAA;AACxB,mDAAsD;AAA7C,oHAAA,kBAAkB,OAAA;AAC3B,yCAAqD;AAA5C,uGAAA,UAAU,OAAA;AAAE,uGAAA,UAAU,OAAA;AAE/B,mDAAiC;AACjC,6CAAyC;AAAhC,uGAAA,QAAQ,OAAA"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.acrHttpInterceptor = void 0;
|
|
4
|
+
const http_1 = require("@angular/common/http");
|
|
5
|
+
const core_1 = require("@angular/core");
|
|
6
|
+
const browser_1 = require("@lib-acr/browser");
|
|
7
|
+
const rxjs_1 = require("rxjs");
|
|
8
|
+
const tokens_js_1 = require("./tokens.js");
|
|
9
|
+
/**
|
|
10
|
+
* Times every HttpClient request: one `api_request` event with host, normalized path, status and duration. Server
|
|
11
|
+
* (5xx) and network failures are also reported as errors. The ACR endpoint itself is never recorded.
|
|
12
|
+
*
|
|
13
|
+
* Register it with `provideHttpClient(withInterceptors([acrHttpInterceptor]))`. If your app also uses the fetch
|
|
14
|
+
* backend, set `trackFetch: false` in provideACR() so requests are not counted twice.
|
|
15
|
+
*/
|
|
16
|
+
const acrHttpInterceptor = (req, next) => {
|
|
17
|
+
const client = (0, core_1.inject)(tokens_js_1.ACR_CLIENT);
|
|
18
|
+
const config = (0, core_1.inject)(tokens_js_1.ACR_CONFIG);
|
|
19
|
+
const endpoint = config.endpoint.replace(/\/+$/, '');
|
|
20
|
+
if (config.trackHttp === false || req.url.startsWith(endpoint))
|
|
21
|
+
return next(req);
|
|
22
|
+
const described = describe(req.url);
|
|
23
|
+
const started = performance.now();
|
|
24
|
+
const elapsed = () => Math.round((performance.now() - started) * 100) / 100;
|
|
25
|
+
return next(req).pipe((0, rxjs_1.tap)({
|
|
26
|
+
next: (event) => {
|
|
27
|
+
if (event instanceof http_1.HttpResponse) {
|
|
28
|
+
client.trackEvent('api_request', {
|
|
29
|
+
method: req.method,
|
|
30
|
+
host: described.host,
|
|
31
|
+
path: described.path,
|
|
32
|
+
status: event.status,
|
|
33
|
+
ok: true,
|
|
34
|
+
durationMs: elapsed(),
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
error: (err) => {
|
|
39
|
+
const status = err instanceof http_1.HttpErrorResponse ? err.status : 0;
|
|
40
|
+
client.trackEvent('api_request', {
|
|
41
|
+
method: req.method,
|
|
42
|
+
host: described.host,
|
|
43
|
+
path: described.path,
|
|
44
|
+
status,
|
|
45
|
+
ok: false,
|
|
46
|
+
durationMs: elapsed(),
|
|
47
|
+
});
|
|
48
|
+
if (status === 0 || status >= 500) {
|
|
49
|
+
client.captureError(err, {
|
|
50
|
+
handled: false,
|
|
51
|
+
source: 'frontend',
|
|
52
|
+
attributes: { kind: status === 0 ? 'network' : 'server', method: req.method, host: described.host, path: described.path, status },
|
|
53
|
+
});
|
|
54
|
+
(0, browser_1.markCaptured)(err);
|
|
55
|
+
}
|
|
56
|
+
},
|
|
57
|
+
}));
|
|
58
|
+
};
|
|
59
|
+
exports.acrHttpInterceptor = acrHttpInterceptor;
|
|
60
|
+
function describe(url) {
|
|
61
|
+
try {
|
|
62
|
+
const parsed = new URL(url, globalThis.location?.href ?? 'http://localhost/');
|
|
63
|
+
return { host: parsed.host, path: (0, browser_1.normalizePath)(parsed.pathname) };
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
return { host: 'unknown', path: '/' };
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
//# sourceMappingURL=interceptor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"interceptor.js","sourceRoot":"","sources":["../../src/interceptor.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.provideACR = provideACR;
|
|
4
|
+
exports.injectFlag = injectFlag;
|
|
5
|
+
exports.injectACRControls = injectACRControls;
|
|
6
|
+
const core_1 = require("@angular/core");
|
|
7
|
+
const browser_1 = require("@lib-acr/browser");
|
|
8
|
+
const error_handler_js_1 = require("./error-handler.js");
|
|
9
|
+
const service_js_1 = require("./service.js");
|
|
10
|
+
const tokens_js_1 = require("./tokens.js");
|
|
11
|
+
const sdk_info_js_1 = require("./sdk-info.js");
|
|
12
|
+
/**
|
|
13
|
+
* Registers ACR for the application: a single BrowserACR, ACRService, an ErrorHandler that reports uncaught errors,
|
|
14
|
+
* and automatic start and stop tied to the application's lifetime. Add provideACRRouterTracking() from
|
|
15
|
+
* `@lib-acr/angular/router` to record page views.
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* export const appConfig = { providers: [provideACR({ apiKey: environment.acrApiKey, endpoint: environment.acrEndpoint })] };
|
|
19
|
+
*/
|
|
20
|
+
function provideACR(options) {
|
|
21
|
+
return (0, core_1.makeEnvironmentProviders)([
|
|
22
|
+
{ provide: tokens_js_1.ACR_CONFIG, useValue: options },
|
|
23
|
+
{ provide: tokens_js_1.ACR_CLIENT, useFactory: () => new browser_1.BrowserACR((0, core_1.inject)(tokens_js_1.ACR_CONFIG), sdk_info_js_1.SDK_INFO) },
|
|
24
|
+
{ provide: service_js_1.ACRService, useFactory: () => new service_js_1.ACRService((0, core_1.inject)(tokens_js_1.ACR_CLIENT), (0, core_1.inject)(core_1.DestroyRef)) },
|
|
25
|
+
{ provide: core_1.ErrorHandler, useFactory: () => new error_handler_js_1.ACRErrorHandler((0, core_1.inject)(tokens_js_1.ACR_CLIENT)) },
|
|
26
|
+
(0, core_1.provideEnvironmentInitializer)(() => {
|
|
27
|
+
const client = (0, core_1.inject)(tokens_js_1.ACR_CLIENT);
|
|
28
|
+
client.start();
|
|
29
|
+
(0, core_1.inject)(core_1.DestroyRef).onDestroy(() => {
|
|
30
|
+
client.stop();
|
|
31
|
+
void client.flush();
|
|
32
|
+
});
|
|
33
|
+
}),
|
|
34
|
+
]);
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* A feature flag as a signal. Call it in an injection context (a component field initialiser or constructor). Angular
|
|
38
|
+
* templates use it with built-in control flow, which replaces a `*acrFlag` directive:
|
|
39
|
+
*
|
|
40
|
+
* @example
|
|
41
|
+
* readonly newCheckout = injectFlag('new-checkout', false);
|
|
42
|
+
* // @if (newCheckout()) { <new-checkout /> } @else { <old-checkout /> }
|
|
43
|
+
*/
|
|
44
|
+
function injectFlag(key, fallback) {
|
|
45
|
+
return (0, core_1.inject)(service_js_1.ACRService).flagSignal(key, fallback);
|
|
46
|
+
}
|
|
47
|
+
/** The remote-control state as a signal. Call it in an injection context. */
|
|
48
|
+
function injectACRControls() {
|
|
49
|
+
return (0, core_1.inject)(service_js_1.ACRService).controls;
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=providers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"providers.js","sourceRoot":"","sources":["../../src/providers.ts"],"names":[],"mappings":";;AAuBA,gCAeC;AAUD,gCAEC;AAGD,8CAEC;AAvDD,wCAQuB;AACvB,8CAAiE;AACjE,yDAAqD;AACrD,6CAA0C;AAC1C,2CAA6E;AAC7E,+CAAyC;AAEzC;;;;;;;GAOG;AACH,SAAgB,UAAU,CAAC,OAA0B;IACnD,OAAO,IAAA,+BAAwB,EAAC;QAC9B,EAAE,OAAO,EAAE,sBAAU,EAAE,QAAQ,EAAE,OAAO,EAAE;QAC1C,EAAE,OAAO,EAAE,sBAAU,EAAE,UAAU,EAAE,GAAG,EAAE,CAAC,IAAI,oBAAU,CAAC,IAAA,aAAM,EAAC,sBAAU,CAAC,EAAE,sBAAQ,CAAC,EAAE;QACvF,EAAE,OAAO,EAAE,uBAAU,EAAE,UAAU,EAAE,GAAG,EAAE,CAAC,IAAI,uBAAU,CAAC,IAAA,aAAM,EAAC,sBAAU,CAAC,EAAE,IAAA,aAAM,EAAC,iBAAU,CAAC,CAAC,EAAE;QACjG,EAAE,OAAO,EAAE,mBAAY,EAAE,UAAU,EAAE,GAAG,EAAE,CAAC,IAAI,kCAAe,CAAC,IAAA,aAAM,EAAC,sBAAU,CAAC,CAAC,EAAE;QACpF,IAAA,oCAA6B,EAAC,GAAG,EAAE;YACjC,MAAM,MAAM,GAAG,IAAA,aAAM,EAAC,sBAAU,CAAC,CAAC;YAClC,MAAM,CAAC,KAAK,EAAE,CAAC;YACf,IAAA,aAAM,EAAC,iBAAU,CAAC,CAAC,SAAS,CAAC,GAAG,EAAE;gBAChC,MAAM,CAAC,IAAI,EAAE,CAAC;gBACd,KAAK,MAAM,CAAC,KAAK,EAAE,CAAC;YACtB,CAAC,CAAC,CAAC;QACL,CAAC,CAAC;KACH,CAAC,CAAC;AACL,CAAC;AAED;;;;;;;GAOG;AACH,SAAgB,UAAU,CAAI,GAAW,EAAE,QAAW;IACpD,OAAO,IAAA,aAAM,EAAC,uBAAU,CAAC,CAAC,UAAU,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;AACtD,CAAC;AAED,6EAA6E;AAC7E,SAAgB,iBAAiB;IAC/B,OAAO,IAAA,aAAM,EAAC,uBAAU,CAAC,CAAC,QAAQ,CAAC;AACrC,CAAC"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.acrMaintenanceGuard = void 0;
|
|
4
|
+
exports.provideACRRouterTracking = provideACRRouterTracking;
|
|
5
|
+
exports.acrMaintenanceGuardWith = acrMaintenanceGuardWith;
|
|
6
|
+
const core_1 = require("@angular/core");
|
|
7
|
+
const router_1 = require("@angular/router");
|
|
8
|
+
const rxjs_1 = require("rxjs");
|
|
9
|
+
const tokens_js_1 = require("./tokens.js");
|
|
10
|
+
/**
|
|
11
|
+
* Records a page view for every completed navigation. Exported from `@lib-acr/angular/router` so apps without
|
|
12
|
+
* @angular/router never load it. Add it next to provideACR() and provideRouter().
|
|
13
|
+
*/
|
|
14
|
+
function provideACRRouterTracking() {
|
|
15
|
+
return (0, core_1.makeEnvironmentProviders)([
|
|
16
|
+
(0, core_1.provideEnvironmentInitializer)(() => {
|
|
17
|
+
const client = (0, core_1.inject)(tokens_js_1.ACR_CLIENT);
|
|
18
|
+
const router = (0, core_1.inject)(router_1.Router);
|
|
19
|
+
const sub = router.events
|
|
20
|
+
.pipe((0, rxjs_1.filter)((event) => event instanceof router_1.NavigationEnd))
|
|
21
|
+
.subscribe((event) => client.trackPageView(event.urlAfterRedirects));
|
|
22
|
+
(0, core_1.inject)(core_1.DestroyRef).onDestroy(() => sub.unsubscribe());
|
|
23
|
+
}),
|
|
24
|
+
]);
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Route guard that blocks navigation while maintenance applies to the current visitor (users in `allowUserIds` pass).
|
|
28
|
+
* The decision uses the last snapshot, so navigations before the first snapshot pass; the built-in maintenance screen
|
|
29
|
+
* covers the page as soon as it arrives.
|
|
30
|
+
*
|
|
31
|
+
* @example
|
|
32
|
+
* { path: 'checkout', component: CheckoutPage, canActivate: [acrMaintenanceGuard] }
|
|
33
|
+
* { path: 'checkout', component: CheckoutPage, canActivate: [acrMaintenanceGuardWith({ redirectTo: '/maintenance' })] }
|
|
34
|
+
*/
|
|
35
|
+
const acrMaintenanceGuard = () => !(0, core_1.inject)(tokens_js_1.ACR_CLIENT).getControlState().maintenance.active;
|
|
36
|
+
exports.acrMaintenanceGuard = acrMaintenanceGuard;
|
|
37
|
+
/** acrMaintenanceGuard with a redirect instead of `false`. */
|
|
38
|
+
function acrMaintenanceGuardWith(options = {}) {
|
|
39
|
+
return () => {
|
|
40
|
+
if (!(0, core_1.inject)(tokens_js_1.ACR_CLIENT).getControlState().maintenance.active)
|
|
41
|
+
return true;
|
|
42
|
+
const target = options.redirectTo;
|
|
43
|
+
if (target === undefined)
|
|
44
|
+
return false;
|
|
45
|
+
return typeof target === 'string' ? (0, core_1.inject)(router_1.Router).parseUrl(target) : target;
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=router.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"router.js","sourceRoot":"","sources":["../../src/router.ts"],"names":[],"mappings":";;;AASA,4DAWC;AAmBD,0DAOC;AA9CD,wCAAkI;AAClI,4CAAqF;AACrF,+BAA8B;AAC9B,2CAAyC;AAEzC;;;GAGG;AACH,SAAgB,wBAAwB;IACtC,OAAO,IAAA,+BAAwB,EAAC;QAC9B,IAAA,oCAA6B,EAAC,GAAG,EAAE;YACjC,MAAM,MAAM,GAAG,IAAA,aAAM,EAAC,sBAAU,CAAC,CAAC;YAClC,MAAM,MAAM,GAAG,IAAA,aAAM,EAAC,eAAM,CAAC,CAAC;YAC9B,MAAM,GAAG,GAAG,MAAM,CAAC,MAAM;iBACtB,IAAI,CAAC,IAAA,aAAM,EAAC,CAAC,KAAK,EAA0B,EAAE,CAAC,KAAK,YAAY,sBAAa,CAAC,CAAC;iBAC/E,SAAS,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,MAAM,CAAC,aAAa,CAAC,KAAK,CAAC,iBAAiB,CAAC,CAAC,CAAC;YACvE,IAAA,aAAM,EAAC,iBAAU,CAAC,CAAC,SAAS,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC,CAAC;QACxD,CAAC,CAAC;KACH,CAAC,CAAC;AACL,CAAC;AAOD;;;;;;;;GAQG;AACI,MAAM,mBAAmB,GAAkB,GAAG,EAAE,CAAC,CAAC,IAAA,aAAM,EAAC,sBAAU,CAAC,CAAC,eAAe,EAAE,CAAC,WAAW,CAAC,MAAM,CAAC;AAApG,QAAA,mBAAmB,uBAAiF;AAEjH,8DAA8D;AAC9D,SAAgB,uBAAuB,CAAC,UAAsC,EAAE;IAC9E,OAAO,GAAG,EAAE;QACV,IAAI,CAAC,IAAA,aAAM,EAAC,sBAAU,CAAC,CAAC,eAAe,EAAE,CAAC,WAAW,CAAC,MAAM;YAAE,OAAO,IAAI,CAAC;QAC1E,MAAM,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC;QAClC,IAAI,MAAM,KAAK,SAAS;YAAE,OAAO,KAAK,CAAC;QACvC,OAAO,OAAO,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAA,aAAM,EAAC,eAAM,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC;IAC/E,CAAC,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SDK_INFO = void 0;
|
|
4
|
+
// Generated by scripts/sync-sdk-info.mjs from package.json. Do not edit by hand.
|
|
5
|
+
exports.SDK_INFO = { name: '@lib-acr/angular', version: '0.0.1' };
|
|
6
|
+
//# sourceMappingURL=sdk-info.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sdk-info.js","sourceRoot":"","sources":["../../src/sdk-info.ts"],"names":[],"mappings":";;;AAAA,iFAAiF;AACpE,QAAA,QAAQ,GAAG,EAAE,IAAI,EAAE,kBAAkB,EAAE,OAAO,EAAE,OAAO,EAAW,CAAC"}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ACRService = void 0;
|
|
4
|
+
const core_1 = require("@angular/core");
|
|
5
|
+
const rxjs_1 = require("rxjs");
|
|
6
|
+
/**
|
|
7
|
+
* Injectable facade over the client. Inject it with `inject(ACRService)` or as a constructor parameter. It is a plain
|
|
8
|
+
* class, so the library ships without decorators and works with AOT builds.
|
|
9
|
+
*/
|
|
10
|
+
class ACRService {
|
|
11
|
+
constructor(client, destroyRef) {
|
|
12
|
+
this.client = client;
|
|
13
|
+
this.state = (0, core_1.signal)(client.getControlState());
|
|
14
|
+
this.controls = this.state.asReadonly();
|
|
15
|
+
const off = client.onControlChange((state) => this.state.set(state));
|
|
16
|
+
destroyRef?.onDestroy(off);
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Current value of a feature flag for this visitor. Returns `fallback` before the first snapshot and when the flag
|
|
20
|
+
* is missing, disabled or served with another type. Use flagSignal() in templates so the view updates.
|
|
21
|
+
*/
|
|
22
|
+
flag(key, fallback) {
|
|
23
|
+
return this.client.flag(key, fallback);
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* A flag as a signal, for templates and computed():
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* readonly newCheckout = inject(ACRService).flagSignal('new-checkout', false);
|
|
30
|
+
* // template: @if (newCheckout()) { <new-checkout /> } @else { <old-checkout /> }
|
|
31
|
+
*/
|
|
32
|
+
flagSignal(key, fallback) {
|
|
33
|
+
return (0, core_1.computed)(() => {
|
|
34
|
+
this.state();
|
|
35
|
+
return this.client.flag(key, fallback);
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
/** A flag as an observable that emits the current value, then every change. */
|
|
39
|
+
flag$(key, fallback) {
|
|
40
|
+
return new rxjs_1.Observable((subscriber) => {
|
|
41
|
+
subscriber.next(this.client.flag(key, fallback));
|
|
42
|
+
return this.client.onControlChange(() => subscriber.next(this.client.flag(key, fallback)));
|
|
43
|
+
}).pipe((0, rxjs_1.distinctUntilChanged)());
|
|
44
|
+
}
|
|
45
|
+
/** Every flag evaluated for this visitor. */
|
|
46
|
+
flags() {
|
|
47
|
+
return this.client.flags();
|
|
48
|
+
}
|
|
49
|
+
/** Hides the announcement for this visitor (remembered per id). */
|
|
50
|
+
dismissAnnouncement(id) {
|
|
51
|
+
this.client.dismissAnnouncement(id);
|
|
52
|
+
}
|
|
53
|
+
/** Fetches the control snapshot now. */
|
|
54
|
+
refreshControl() {
|
|
55
|
+
return this.client.refreshControl();
|
|
56
|
+
}
|
|
57
|
+
trackEvent(name, attributes) {
|
|
58
|
+
this.client.trackEvent(name, attributes);
|
|
59
|
+
}
|
|
60
|
+
log(level, message, attributes) {
|
|
61
|
+
this.client.log(level, message, attributes);
|
|
62
|
+
}
|
|
63
|
+
captureError(error, options) {
|
|
64
|
+
this.client.captureError(error, options);
|
|
65
|
+
}
|
|
66
|
+
identify(userId) {
|
|
67
|
+
this.client.identify(userId);
|
|
68
|
+
}
|
|
69
|
+
trackPageView(path) {
|
|
70
|
+
this.client.trackPageView(path);
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Forgets the current visitor, for example on logout: clears the user id, replaces the anonymous id and starts a new
|
|
74
|
+
* session.
|
|
75
|
+
*/
|
|
76
|
+
reset() {
|
|
77
|
+
this.client.reset();
|
|
78
|
+
}
|
|
79
|
+
/** Stops collection, discards unsent telemetry and remembers the choice on this browser. */
|
|
80
|
+
optOut() {
|
|
81
|
+
this.client.optOut();
|
|
82
|
+
}
|
|
83
|
+
/** Resumes collection after optOut(). */
|
|
84
|
+
optIn() {
|
|
85
|
+
this.client.optIn();
|
|
86
|
+
}
|
|
87
|
+
flush() {
|
|
88
|
+
return this.client.flush();
|
|
89
|
+
}
|
|
90
|
+
/** The underlying client, for web vitals and other browser-level APIs. */
|
|
91
|
+
get raw() {
|
|
92
|
+
return this.client;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
exports.ACRService = ACRService;
|
|
96
|
+
//# sourceMappingURL=service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"service.js","sourceRoot":"","sources":["../../src/service.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ACR_CLIENT = exports.ACR_CONFIG = void 0;
|
|
4
|
+
const core_1 = require("@angular/core");
|
|
5
|
+
exports.ACR_CONFIG = new core_1.InjectionToken('ACR_CONFIG');
|
|
6
|
+
exports.ACR_CLIENT = new core_1.InjectionToken('ACR_CLIENT');
|
|
7
|
+
//# sourceMappingURL=tokens.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tokens.js","sourceRoot":"","sources":["../../src/tokens.ts"],"names":[],"mappings":";;;AAAA,wCAA+C;AASlC,QAAA,UAAU,GAAG,IAAI,qBAAc,CAAoB,YAAY,CAAC,CAAC;AACjE,QAAA,UAAU,GAAG,IAAI,qBAAc,CAAa,YAAY,CAAC,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { wasCaptured } from '@lib-acr/browser';
|
|
2
|
+
/**
|
|
3
|
+
* Angular ErrorHandler that reports uncaught errors to ACR and keeps logging them to the console. Register it with
|
|
4
|
+
* provideACR(); it replaces the default handler. HTTP failures already reported by acrHttpInterceptor are not sent
|
|
5
|
+
* again when the app leaves them unhandled.
|
|
6
|
+
*/
|
|
7
|
+
export class ACRErrorHandler {
|
|
8
|
+
constructor(client) {
|
|
9
|
+
this.client = client;
|
|
10
|
+
}
|
|
11
|
+
handleError(error) {
|
|
12
|
+
if (!wasCaptured(error)) {
|
|
13
|
+
this.client.captureError(error, { handled: false, source: 'frontend', attributes: { kind: 'angular' } });
|
|
14
|
+
}
|
|
15
|
+
console.error(error);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=error-handler.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"error-handler.js","sourceRoot":"","sources":["../../src/error-handler.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAmB,MAAM,kBAAkB,CAAC;AAEhE;;;;GAIG;AACH,MAAM,OAAO,eAAe;IAC1B,YAA6B,MAAkB;QAAlB,WAAM,GAAN,MAAM,CAAY;IAAG,CAAC;IAEnD,WAAW,CAAC,KAAc;QACxB,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,EAAE,CAAC;YACxB,IAAI,CAAC,MAAM,CAAC,YAAY,CAAC,KAAK,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,UAAU,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,EAAE,CAAC,CAAC;QAC3G,CAAC;QACD,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IACvB,CAAC;CACF"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export { provideACR, injectFlag, injectACRControls } from './providers.js';
|
|
2
|
+
export { ACRService } from './service.js';
|
|
3
|
+
export { ACRErrorHandler } from './error-handler.js';
|
|
4
|
+
export { acrHttpInterceptor } from './interceptor.js';
|
|
5
|
+
export { ACR_CLIENT, ACR_CONFIG } from './tokens.js';
|
|
6
|
+
export * from '@lib-acr/browser';
|
|
7
|
+
export { SDK_INFO } from './sdk-info.js';
|
|
8
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAC3E,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AACrD,OAAO,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AACtD,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAErD,cAAc,kBAAkB,CAAC;AACjC,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { HttpErrorResponse, HttpResponse } from '@angular/common/http';
|
|
2
|
+
import { inject } from '@angular/core';
|
|
3
|
+
import { markCaptured, normalizePath } from '@lib-acr/browser';
|
|
4
|
+
import { tap } from 'rxjs';
|
|
5
|
+
import { ACR_CLIENT, ACR_CONFIG } from './tokens.js';
|
|
6
|
+
/**
|
|
7
|
+
* Times every HttpClient request: one `api_request` event with host, normalized path, status and duration. Server
|
|
8
|
+
* (5xx) and network failures are also reported as errors. The ACR endpoint itself is never recorded.
|
|
9
|
+
*
|
|
10
|
+
* Register it with `provideHttpClient(withInterceptors([acrHttpInterceptor]))`. If your app also uses the fetch
|
|
11
|
+
* backend, set `trackFetch: false` in provideACR() so requests are not counted twice.
|
|
12
|
+
*/
|
|
13
|
+
export const acrHttpInterceptor = (req, next) => {
|
|
14
|
+
const client = inject(ACR_CLIENT);
|
|
15
|
+
const config = inject(ACR_CONFIG);
|
|
16
|
+
const endpoint = config.endpoint.replace(/\/+$/, '');
|
|
17
|
+
if (config.trackHttp === false || req.url.startsWith(endpoint))
|
|
18
|
+
return next(req);
|
|
19
|
+
const described = describe(req.url);
|
|
20
|
+
const started = performance.now();
|
|
21
|
+
const elapsed = () => Math.round((performance.now() - started) * 100) / 100;
|
|
22
|
+
return next(req).pipe(tap({
|
|
23
|
+
next: (event) => {
|
|
24
|
+
if (event instanceof HttpResponse) {
|
|
25
|
+
client.trackEvent('api_request', {
|
|
26
|
+
method: req.method,
|
|
27
|
+
host: described.host,
|
|
28
|
+
path: described.path,
|
|
29
|
+
status: event.status,
|
|
30
|
+
ok: true,
|
|
31
|
+
durationMs: elapsed(),
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
error: (err) => {
|
|
36
|
+
const status = err instanceof HttpErrorResponse ? err.status : 0;
|
|
37
|
+
client.trackEvent('api_request', {
|
|
38
|
+
method: req.method,
|
|
39
|
+
host: described.host,
|
|
40
|
+
path: described.path,
|
|
41
|
+
status,
|
|
42
|
+
ok: false,
|
|
43
|
+
durationMs: elapsed(),
|
|
44
|
+
});
|
|
45
|
+
if (status === 0 || status >= 500) {
|
|
46
|
+
client.captureError(err, {
|
|
47
|
+
handled: false,
|
|
48
|
+
source: 'frontend',
|
|
49
|
+
attributes: { kind: status === 0 ? 'network' : 'server', method: req.method, host: described.host, path: described.path, status },
|
|
50
|
+
});
|
|
51
|
+
markCaptured(err);
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
}));
|
|
55
|
+
};
|
|
56
|
+
function describe(url) {
|
|
57
|
+
try {
|
|
58
|
+
const parsed = new URL(url, globalThis.location?.href ?? 'http://localhost/');
|
|
59
|
+
return { host: parsed.host, path: normalizePath(parsed.pathname) };
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
return { host: 'unknown', path: '/' };
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
//# sourceMappingURL=interceptor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"interceptor.js","sourceRoot":"","sources":["../../src/interceptor.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { DestroyRef, ErrorHandler, inject, makeEnvironmentProviders, provideEnvironmentInitializer, } from '@angular/core';
|
|
2
|
+
import { BrowserACR } from '@lib-acr/browser';
|
|
3
|
+
import { ACRErrorHandler } from './error-handler.js';
|
|
4
|
+
import { ACRService } from './service.js';
|
|
5
|
+
import { ACR_CLIENT, ACR_CONFIG } from './tokens.js';
|
|
6
|
+
import { SDK_INFO } from './sdk-info.js';
|
|
7
|
+
/**
|
|
8
|
+
* Registers ACR for the application: a single BrowserACR, ACRService, an ErrorHandler that reports uncaught errors,
|
|
9
|
+
* and automatic start and stop tied to the application's lifetime. Add provideACRRouterTracking() from
|
|
10
|
+
* `@lib-acr/angular/router` to record page views.
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* export const appConfig = { providers: [provideACR({ apiKey: environment.acrApiKey, endpoint: environment.acrEndpoint })] };
|
|
14
|
+
*/
|
|
15
|
+
export function provideACR(options) {
|
|
16
|
+
return makeEnvironmentProviders([
|
|
17
|
+
{ provide: ACR_CONFIG, useValue: options },
|
|
18
|
+
{ provide: ACR_CLIENT, useFactory: () => new BrowserACR(inject(ACR_CONFIG), SDK_INFO) },
|
|
19
|
+
{ provide: ACRService, useFactory: () => new ACRService(inject(ACR_CLIENT), inject(DestroyRef)) },
|
|
20
|
+
{ provide: ErrorHandler, useFactory: () => new ACRErrorHandler(inject(ACR_CLIENT)) },
|
|
21
|
+
provideEnvironmentInitializer(() => {
|
|
22
|
+
const client = inject(ACR_CLIENT);
|
|
23
|
+
client.start();
|
|
24
|
+
inject(DestroyRef).onDestroy(() => {
|
|
25
|
+
client.stop();
|
|
26
|
+
void client.flush();
|
|
27
|
+
});
|
|
28
|
+
}),
|
|
29
|
+
]);
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* A feature flag as a signal. Call it in an injection context (a component field initialiser or constructor). Angular
|
|
33
|
+
* templates use it with built-in control flow, which replaces a `*acrFlag` directive:
|
|
34
|
+
*
|
|
35
|
+
* @example
|
|
36
|
+
* readonly newCheckout = injectFlag('new-checkout', false);
|
|
37
|
+
* // @if (newCheckout()) { <new-checkout /> } @else { <old-checkout /> }
|
|
38
|
+
*/
|
|
39
|
+
export function injectFlag(key, fallback) {
|
|
40
|
+
return inject(ACRService).flagSignal(key, fallback);
|
|
41
|
+
}
|
|
42
|
+
/** The remote-control state as a signal. Call it in an injection context. */
|
|
43
|
+
export function injectACRControls() {
|
|
44
|
+
return inject(ACRService).controls;
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=providers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"providers.js","sourceRoot":"","sources":["../../src/providers.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,UAAU,EAEV,YAAY,EACZ,MAAM,EACN,wBAAwB,EACxB,6BAA6B,GAE9B,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,UAAU,EAAqB,MAAM,kBAAkB,CAAC;AACjE,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AACrD,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAE,UAAU,EAAE,UAAU,EAA0B,MAAM,aAAa,CAAC;AAC7E,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAEzC;;;;;;;GAOG;AACH,MAAM,UAAU,UAAU,CAAC,OAA0B;IACnD,OAAO,wBAAwB,CAAC;QAC9B,EAAE,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,OAAO,EAAE;QAC1C,EAAE,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,GAAG,EAAE,CAAC,IAAI,UAAU,CAAC,MAAM,CAAC,UAAU,CAAC,EAAE,QAAQ,CAAC,EAAE;QACvF,EAAE,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,GAAG,EAAE,CAAC,IAAI,UAAU,CAAC,MAAM,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC,CAAC,EAAE;QACjG,EAAE,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,GAAG,EAAE,CAAC,IAAI,eAAe,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,EAAE;QACpF,6BAA6B,CAAC,GAAG,EAAE;YACjC,MAAM,MAAM,GAAG,MAAM,CAAC,UAAU,CAAC,CAAC;YAClC,MAAM,CAAC,KAAK,EAAE,CAAC;YACf,MAAM,CAAC,UAAU,CAAC,CAAC,SAAS,CAAC,GAAG,EAAE;gBAChC,MAAM,CAAC,IAAI,EAAE,CAAC;gBACd,KAAK,MAAM,CAAC,KAAK,EAAE,CAAC;YACtB,CAAC,CAAC,CAAC;QACL,CAAC,CAAC;KACH,CAAC,CAAC;AACL,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,UAAU,CAAI,GAAW,EAAE,QAAW;IACpD,OAAO,MAAM,CAAC,UAAU,CAAC,CAAC,UAAU,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;AACtD,CAAC;AAED,6EAA6E;AAC7E,MAAM,UAAU,iBAAiB;IAC/B,OAAO,MAAM,CAAC,UAAU,CAAC,CAAC,QAAQ,CAAC;AACrC,CAAC"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { DestroyRef, inject, makeEnvironmentProviders, provideEnvironmentInitializer } from '@angular/core';
|
|
2
|
+
import { NavigationEnd, Router } from '@angular/router';
|
|
3
|
+
import { filter } from 'rxjs';
|
|
4
|
+
import { ACR_CLIENT } from './tokens.js';
|
|
5
|
+
/**
|
|
6
|
+
* Records a page view for every completed navigation. Exported from `@lib-acr/angular/router` so apps without
|
|
7
|
+
* @angular/router never load it. Add it next to provideACR() and provideRouter().
|
|
8
|
+
*/
|
|
9
|
+
export function provideACRRouterTracking() {
|
|
10
|
+
return makeEnvironmentProviders([
|
|
11
|
+
provideEnvironmentInitializer(() => {
|
|
12
|
+
const client = inject(ACR_CLIENT);
|
|
13
|
+
const router = inject(Router);
|
|
14
|
+
const sub = router.events
|
|
15
|
+
.pipe(filter((event) => event instanceof NavigationEnd))
|
|
16
|
+
.subscribe((event) => client.trackPageView(event.urlAfterRedirects));
|
|
17
|
+
inject(DestroyRef).onDestroy(() => sub.unsubscribe());
|
|
18
|
+
}),
|
|
19
|
+
]);
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Route guard that blocks navigation while maintenance applies to the current visitor (users in `allowUserIds` pass).
|
|
23
|
+
* The decision uses the last snapshot, so navigations before the first snapshot pass; the built-in maintenance screen
|
|
24
|
+
* covers the page as soon as it arrives.
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* { path: 'checkout', component: CheckoutPage, canActivate: [acrMaintenanceGuard] }
|
|
28
|
+
* { path: 'checkout', component: CheckoutPage, canActivate: [acrMaintenanceGuardWith({ redirectTo: '/maintenance' })] }
|
|
29
|
+
*/
|
|
30
|
+
export const acrMaintenanceGuard = () => !inject(ACR_CLIENT).getControlState().maintenance.active;
|
|
31
|
+
/** acrMaintenanceGuard with a redirect instead of `false`. */
|
|
32
|
+
export function acrMaintenanceGuardWith(options = {}) {
|
|
33
|
+
return () => {
|
|
34
|
+
if (!inject(ACR_CLIENT).getControlState().maintenance.active)
|
|
35
|
+
return true;
|
|
36
|
+
const target = options.redirectTo;
|
|
37
|
+
if (target === undefined)
|
|
38
|
+
return false;
|
|
39
|
+
return typeof target === 'string' ? inject(Router).parseUrl(target) : target;
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=router.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"router.js","sourceRoot":"","sources":["../../src/router.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAwB,MAAM,EAAE,wBAAwB,EAAE,6BAA6B,EAAE,MAAM,eAAe,CAAC;AAClI,OAAO,EAAE,aAAa,EAAE,MAAM,EAA+B,MAAM,iBAAiB,CAAC;AACrF,OAAO,EAAE,MAAM,EAAE,MAAM,MAAM,CAAC;AAC9B,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzC;;;GAGG;AACH,MAAM,UAAU,wBAAwB;IACtC,OAAO,wBAAwB,CAAC;QAC9B,6BAA6B,CAAC,GAAG,EAAE;YACjC,MAAM,MAAM,GAAG,MAAM,CAAC,UAAU,CAAC,CAAC;YAClC,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC;YAC9B,MAAM,GAAG,GAAG,MAAM,CAAC,MAAM;iBACtB,IAAI,CAAC,MAAM,CAAC,CAAC,KAAK,EAA0B,EAAE,CAAC,KAAK,YAAY,aAAa,CAAC,CAAC;iBAC/E,SAAS,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,MAAM,CAAC,aAAa,CAAC,KAAK,CAAC,iBAAiB,CAAC,CAAC,CAAC;YACvE,MAAM,CAAC,UAAU,CAAC,CAAC,SAAS,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC,CAAC;QACxD,CAAC,CAAC;KACH,CAAC,CAAC;AACL,CAAC;AAOD;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAkB,GAAG,EAAE,CAAC,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,eAAe,EAAE,CAAC,WAAW,CAAC,MAAM,CAAC;AAEjH,8DAA8D;AAC9D,MAAM,UAAU,uBAAuB,CAAC,UAAsC,EAAE;IAC9E,OAAO,GAAG,EAAE;QACV,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,eAAe,EAAE,CAAC,WAAW,CAAC,MAAM;YAAE,OAAO,IAAI,CAAC;QAC1E,MAAM,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC;QAClC,IAAI,MAAM,KAAK,SAAS;YAAE,OAAO,KAAK,CAAC;QACvC,OAAO,OAAO,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC;IAC/E,CAAC,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sdk-info.js","sourceRoot":"","sources":["../../src/sdk-info.ts"],"names":[],"mappings":"AAAA,iFAAiF;AACjF,MAAM,CAAC,MAAM,QAAQ,GAAG,EAAE,IAAI,EAAE,kBAAkB,EAAE,OAAO,EAAE,OAAO,EAAW,CAAC"}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { computed, signal } from '@angular/core';
|
|
2
|
+
import { Observable, distinctUntilChanged } from 'rxjs';
|
|
3
|
+
/**
|
|
4
|
+
* Injectable facade over the client. Inject it with `inject(ACRService)` or as a constructor parameter. It is a plain
|
|
5
|
+
* class, so the library ships without decorators and works with AOT builds.
|
|
6
|
+
*/
|
|
7
|
+
export class ACRService {
|
|
8
|
+
constructor(client, destroyRef) {
|
|
9
|
+
this.client = client;
|
|
10
|
+
this.state = signal(client.getControlState());
|
|
11
|
+
this.controls = this.state.asReadonly();
|
|
12
|
+
const off = client.onControlChange((state) => this.state.set(state));
|
|
13
|
+
destroyRef?.onDestroy(off);
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Current value of a feature flag for this visitor. Returns `fallback` before the first snapshot and when the flag
|
|
17
|
+
* is missing, disabled or served with another type. Use flagSignal() in templates so the view updates.
|
|
18
|
+
*/
|
|
19
|
+
flag(key, fallback) {
|
|
20
|
+
return this.client.flag(key, fallback);
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* A flag as a signal, for templates and computed():
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* readonly newCheckout = inject(ACRService).flagSignal('new-checkout', false);
|
|
27
|
+
* // template: @if (newCheckout()) { <new-checkout /> } @else { <old-checkout /> }
|
|
28
|
+
*/
|
|
29
|
+
flagSignal(key, fallback) {
|
|
30
|
+
return computed(() => {
|
|
31
|
+
this.state();
|
|
32
|
+
return this.client.flag(key, fallback);
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
/** A flag as an observable that emits the current value, then every change. */
|
|
36
|
+
flag$(key, fallback) {
|
|
37
|
+
return new Observable((subscriber) => {
|
|
38
|
+
subscriber.next(this.client.flag(key, fallback));
|
|
39
|
+
return this.client.onControlChange(() => subscriber.next(this.client.flag(key, fallback)));
|
|
40
|
+
}).pipe(distinctUntilChanged());
|
|
41
|
+
}
|
|
42
|
+
/** Every flag evaluated for this visitor. */
|
|
43
|
+
flags() {
|
|
44
|
+
return this.client.flags();
|
|
45
|
+
}
|
|
46
|
+
/** Hides the announcement for this visitor (remembered per id). */
|
|
47
|
+
dismissAnnouncement(id) {
|
|
48
|
+
this.client.dismissAnnouncement(id);
|
|
49
|
+
}
|
|
50
|
+
/** Fetches the control snapshot now. */
|
|
51
|
+
refreshControl() {
|
|
52
|
+
return this.client.refreshControl();
|
|
53
|
+
}
|
|
54
|
+
trackEvent(name, attributes) {
|
|
55
|
+
this.client.trackEvent(name, attributes);
|
|
56
|
+
}
|
|
57
|
+
log(level, message, attributes) {
|
|
58
|
+
this.client.log(level, message, attributes);
|
|
59
|
+
}
|
|
60
|
+
captureError(error, options) {
|
|
61
|
+
this.client.captureError(error, options);
|
|
62
|
+
}
|
|
63
|
+
identify(userId) {
|
|
64
|
+
this.client.identify(userId);
|
|
65
|
+
}
|
|
66
|
+
trackPageView(path) {
|
|
67
|
+
this.client.trackPageView(path);
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Forgets the current visitor, for example on logout: clears the user id, replaces the anonymous id and starts a new
|
|
71
|
+
* session.
|
|
72
|
+
*/
|
|
73
|
+
reset() {
|
|
74
|
+
this.client.reset();
|
|
75
|
+
}
|
|
76
|
+
/** Stops collection, discards unsent telemetry and remembers the choice on this browser. */
|
|
77
|
+
optOut() {
|
|
78
|
+
this.client.optOut();
|
|
79
|
+
}
|
|
80
|
+
/** Resumes collection after optOut(). */
|
|
81
|
+
optIn() {
|
|
82
|
+
this.client.optIn();
|
|
83
|
+
}
|
|
84
|
+
flush() {
|
|
85
|
+
return this.client.flush();
|
|
86
|
+
}
|
|
87
|
+
/** The underlying client, for web vitals and other browser-level APIs. */
|
|
88
|
+
get raw() {
|
|
89
|
+
return this.client;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
//# sourceMappingURL=service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"service.js","sourceRoot":"","sources":["../../src/service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAqD,MAAM,eAAe,CAAC;AACpG,OAAO,EAAE,UAAU,EAAE,oBAAoB,EAAE,MAAM,MAAM,CAAC;AAIxD;;;GAGG;AACH,MAAM,OAAO,UAAU;IASrB,YACmB,MAAkB,EACnC,UAAuB;QADN,WAAM,GAAN,MAAM,CAAY;QAGnC,IAAI,CAAC,KAAK,GAAG,MAAM,CAAC,MAAM,CAAC,eAAe,EAAE,CAAC,CAAC;QAC9C,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,UAAU,EAAE,CAAC;QACxC,MAAM,GAAG,GAAG,MAAM,CAAC,eAAe,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC;QACrE,UAAU,EAAE,SAAS,CAAC,GAAG,CAAC,CAAC;IAC7B,CAAC;IAED;;;OAGG;IACH,IAAI,CAAI,GAAW,EAAE,QAAW;QAC9B,OAAO,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;IACzC,CAAC;IAED;;;;;;OAMG;IACH,UAAU,CAAI,GAAW,EAAE,QAAW;QACpC,OAAO,QAAQ,CAAC,GAAG,EAAE;YACnB,IAAI,CAAC,KAAK,EAAE,CAAC;YACb,OAAO,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;QACzC,CAAC,CAAC,CAAC;IACL,CAAC;IAED,+EAA+E;IAC/E,KAAK,CAAI,GAAW,EAAE,QAAW;QAC/B,OAAO,IAAI,UAAU,CAAI,CAAC,UAAU,EAAE,EAAE;YACtC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC,CAAC;YACjD,OAAO,IAAI,CAAC,MAAM,CAAC,eAAe,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC;QAC7F,CAAC,CAAC,CAAC,IAAI,CAAC,oBAAoB,EAAE,CAAC,CAAC;IAClC,CAAC;IAED,6CAA6C;IAC7C,KAAK;QACH,OAAO,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;IAC7B,CAAC;IAED,mEAAmE;IACnE,mBAAmB,CAAC,EAAU;QAC5B,IAAI,CAAC,MAAM,CAAC,mBAAmB,CAAC,EAAE,CAAC,CAAC;IACtC,CAAC;IAED,wCAAwC;IACxC,cAAc;QACZ,OAAO,IAAI,CAAC,MAAM,CAAC,cAAc,EAAE,CAAC;IACtC,CAAC;IAED,UAAU,CAAC,IAAY,EAAE,UAAuB;QAC9C,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC;IAC3C,CAAC;IAED,GAAG,CAAC,KAAe,EAAE,OAAe,EAAE,UAAuB;QAC3D,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,EAAE,OAAO,EAAE,UAAU,CAAC,CAAC;IAC9C,CAAC;IAED,YAAY,CAAC,KAAc,EAAE,OAAsB;QACjD,IAAI,CAAC,MAAM,CAAC,YAAY,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;IAC3C,CAAC;IAED,QAAQ,CAAC,MAAqB;QAC5B,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;IAC/B,CAAC;IAED,aAAa,CAAC,IAAa;QACzB,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC;IAClC,CAAC;IAED;;;OAGG;IACH,KAAK;QACH,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;IACtB,CAAC;IAED,4FAA4F;IAC5F,MAAM;QACJ,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC;IACvB,CAAC;IAED,yCAAyC;IACzC,KAAK;QACH,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;IACtB,CAAC;IAED,KAAK;QACH,OAAO,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;IAC7B,CAAC;IAED,0EAA0E;IAC1E,IAAI,GAAG;QACL,OAAO,IAAI,CAAC,MAAM,CAAC;IACrB,CAAC;CACF"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tokens.js","sourceRoot":"","sources":["../../src/tokens.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAS/C,MAAM,CAAC,MAAM,UAAU,GAAG,IAAI,cAAc,CAAoB,YAAY,CAAC,CAAC;AAC9E,MAAM,CAAC,MAAM,UAAU,GAAG,IAAI,cAAc,CAAa,YAAY,CAAC,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { type BrowserACR } from '@lib-acr/browser';
|
|
2
|
+
/**
|
|
3
|
+
* Angular ErrorHandler that reports uncaught errors to ACR and keeps logging them to the console. Register it with
|
|
4
|
+
* provideACR(); it replaces the default handler. HTTP failures already reported by acrHttpInterceptor are not sent
|
|
5
|
+
* again when the app leaves them unhandled.
|
|
6
|
+
*/
|
|
7
|
+
export declare class ACRErrorHandler {
|
|
8
|
+
private readonly client;
|
|
9
|
+
constructor(client: BrowserACR);
|
|
10
|
+
handleError(error: unknown): void;
|
|
11
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export { provideACR, injectFlag, injectACRControls } from './providers.js';
|
|
2
|
+
export { ACRService } from './service.js';
|
|
3
|
+
export { ACRErrorHandler } from './error-handler.js';
|
|
4
|
+
export { acrHttpInterceptor } from './interceptor.js';
|
|
5
|
+
export { ACR_CLIENT, ACR_CONFIG } from './tokens.js';
|
|
6
|
+
export type { AngularAcrOptions } from './tokens.js';
|
|
7
|
+
export * from '@lib-acr/browser';
|
|
8
|
+
export { SDK_INFO } from './sdk-info.js';
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { HttpInterceptorFn } from '@angular/common/http';
|
|
2
|
+
/**
|
|
3
|
+
* Times every HttpClient request: one `api_request` event with host, normalized path, status and duration. Server
|
|
4
|
+
* (5xx) and network failures are also reported as errors. The ACR endpoint itself is never recorded.
|
|
5
|
+
*
|
|
6
|
+
* Register it with `provideHttpClient(withInterceptors([acrHttpInterceptor]))`. If your app also uses the fetch
|
|
7
|
+
* backend, set `trackFetch: false` in provideACR() so requests are not counted twice.
|
|
8
|
+
*/
|
|
9
|
+
export declare const acrHttpInterceptor: HttpInterceptorFn;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { EnvironmentProviders, type Signal } from '@angular/core';
|
|
2
|
+
import { type ControlState } from '@lib-acr/browser';
|
|
3
|
+
import { type AngularAcrOptions } from './tokens.js';
|
|
4
|
+
/**
|
|
5
|
+
* Registers ACR for the application: a single BrowserACR, ACRService, an ErrorHandler that reports uncaught errors,
|
|
6
|
+
* and automatic start and stop tied to the application's lifetime. Add provideACRRouterTracking() from
|
|
7
|
+
* `@lib-acr/angular/router` to record page views.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* export const appConfig = { providers: [provideACR({ apiKey: environment.acrApiKey, endpoint: environment.acrEndpoint })] };
|
|
11
|
+
*/
|
|
12
|
+
export declare function provideACR(options: AngularAcrOptions): EnvironmentProviders;
|
|
13
|
+
/**
|
|
14
|
+
* A feature flag as a signal. Call it in an injection context (a component field initialiser or constructor). Angular
|
|
15
|
+
* templates use it with built-in control flow, which replaces a `*acrFlag` directive:
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* readonly newCheckout = injectFlag('new-checkout', false);
|
|
19
|
+
* // @if (newCheckout()) { <new-checkout /> } @else { <old-checkout /> }
|
|
20
|
+
*/
|
|
21
|
+
export declare function injectFlag<T>(key: string, fallback: T): Signal<T>;
|
|
22
|
+
/** The remote-control state as a signal. Call it in an injection context. */
|
|
23
|
+
export declare function injectACRControls(): Signal<ControlState>;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { EnvironmentProviders } from '@angular/core';
|
|
2
|
+
import { UrlTree, type CanActivateFn } from '@angular/router';
|
|
3
|
+
/**
|
|
4
|
+
* Records a page view for every completed navigation. Exported from `@lib-acr/angular/router` so apps without
|
|
5
|
+
* @angular/router never load it. Add it next to provideACR() and provideRouter().
|
|
6
|
+
*/
|
|
7
|
+
export declare function provideACRRouterTracking(): EnvironmentProviders;
|
|
8
|
+
export interface ACRMaintenanceGuardOptions {
|
|
9
|
+
/** Where to send visitors while maintenance applies to them, for example '/maintenance'. Without it the guard returns false. */
|
|
10
|
+
redirectTo?: string | UrlTree;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Route guard that blocks navigation while maintenance applies to the current visitor (users in `allowUserIds` pass).
|
|
14
|
+
* The decision uses the last snapshot, so navigations before the first snapshot pass; the built-in maintenance screen
|
|
15
|
+
* covers the page as soon as it arrives.
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* { path: 'checkout', component: CheckoutPage, canActivate: [acrMaintenanceGuard] }
|
|
19
|
+
* { path: 'checkout', component: CheckoutPage, canActivate: [acrMaintenanceGuardWith({ redirectTo: '/maintenance' })] }
|
|
20
|
+
*/
|
|
21
|
+
export declare const acrMaintenanceGuard: CanActivateFn;
|
|
22
|
+
/** acrMaintenanceGuard with a redirect instead of `false`. */
|
|
23
|
+
export declare function acrMaintenanceGuardWith(options?: ACRMaintenanceGuardOptions): CanActivateFn;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { type DestroyRef, type Signal } from '@angular/core';
|
|
2
|
+
import { Observable } from 'rxjs';
|
|
3
|
+
import type { Attributes, ControlState, ErrorOptions, LogLevel } from '@lib-acr/core';
|
|
4
|
+
import type { BrowserACR } from '@lib-acr/browser';
|
|
5
|
+
/**
|
|
6
|
+
* Injectable facade over the client. Inject it with `inject(ACRService)` or as a constructor parameter. It is a plain
|
|
7
|
+
* class, so the library ships without decorators and works with AOT builds.
|
|
8
|
+
*/
|
|
9
|
+
export declare class ACRService {
|
|
10
|
+
private readonly client;
|
|
11
|
+
private readonly state;
|
|
12
|
+
/**
|
|
13
|
+
* Remote-control state (maintenance, announcement, release, flags, revision) as a signal. Updated when the snapshot
|
|
14
|
+
* or the identified user changes.
|
|
15
|
+
*/
|
|
16
|
+
readonly controls: Signal<ControlState>;
|
|
17
|
+
constructor(client: BrowserACR, destroyRef?: DestroyRef);
|
|
18
|
+
/**
|
|
19
|
+
* Current value of a feature flag for this visitor. Returns `fallback` before the first snapshot and when the flag
|
|
20
|
+
* is missing, disabled or served with another type. Use flagSignal() in templates so the view updates.
|
|
21
|
+
*/
|
|
22
|
+
flag<T>(key: string, fallback: T): T;
|
|
23
|
+
/**
|
|
24
|
+
* A flag as a signal, for templates and computed():
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* readonly newCheckout = inject(ACRService).flagSignal('new-checkout', false);
|
|
28
|
+
* // template: @if (newCheckout()) { <new-checkout /> } @else { <old-checkout /> }
|
|
29
|
+
*/
|
|
30
|
+
flagSignal<T>(key: string, fallback: T): Signal<T>;
|
|
31
|
+
/** A flag as an observable that emits the current value, then every change. */
|
|
32
|
+
flag$<T>(key: string, fallback: T): Observable<T>;
|
|
33
|
+
/** Every flag evaluated for this visitor. */
|
|
34
|
+
flags(): Record<string, unknown>;
|
|
35
|
+
/** Hides the announcement for this visitor (remembered per id). */
|
|
36
|
+
dismissAnnouncement(id: string): void;
|
|
37
|
+
/** Fetches the control snapshot now. */
|
|
38
|
+
refreshControl(): Promise<void>;
|
|
39
|
+
trackEvent(name: string, attributes?: Attributes): void;
|
|
40
|
+
log(level: LogLevel, message: string, attributes?: Attributes): void;
|
|
41
|
+
captureError(error: unknown, options?: ErrorOptions): void;
|
|
42
|
+
identify(userId: string | null): void;
|
|
43
|
+
trackPageView(path?: string): void;
|
|
44
|
+
/**
|
|
45
|
+
* Forgets the current visitor, for example on logout: clears the user id, replaces the anonymous id and starts a new
|
|
46
|
+
* session.
|
|
47
|
+
*/
|
|
48
|
+
reset(): void;
|
|
49
|
+
/** Stops collection, discards unsent telemetry and remembers the choice on this browser. */
|
|
50
|
+
optOut(): void;
|
|
51
|
+
/** Resumes collection after optOut(). */
|
|
52
|
+
optIn(): void;
|
|
53
|
+
flush(): Promise<void>;
|
|
54
|
+
/** The underlying client, for web vitals and other browser-level APIs. */
|
|
55
|
+
get raw(): BrowserACR;
|
|
56
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { InjectionToken } from '@angular/core';
|
|
2
|
+
import type { BrowserACR, BrowserAcrOptions } from '@lib-acr/browser';
|
|
3
|
+
/** Options given to provideACR(). Injected by the other providers. */
|
|
4
|
+
export interface AngularAcrOptions extends BrowserAcrOptions {
|
|
5
|
+
/** Time HttpClient requests through acrHttpInterceptor. Default true. */
|
|
6
|
+
trackHttp?: boolean;
|
|
7
|
+
}
|
|
8
|
+
export declare const ACR_CONFIG: InjectionToken<AngularAcrOptions>;
|
|
9
|
+
export declare const ACR_CLIENT: InjectionToken<BrowserACR>;
|
package/package.json
CHANGED
|
@@ -1,6 +1,68 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lib-acr/angular",
|
|
3
|
-
"version": "0.0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "ACR SDK for Angular: provideACR(), an ErrorHandler, an HttpClient interceptor and router page tracking.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"main": "./dist/cjs/index.js",
|
|
7
|
+
"module": "./dist/esm/index.js",
|
|
8
|
+
"types": "./dist/types/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/types/index.d.ts",
|
|
12
|
+
"import": "./dist/esm/index.js",
|
|
13
|
+
"require": "./dist/cjs/index.js"
|
|
14
|
+
},
|
|
15
|
+
"./router": {
|
|
16
|
+
"types": "./dist/types/router.d.ts",
|
|
17
|
+
"import": "./dist/esm/router.js",
|
|
18
|
+
"require": "./dist/cjs/router.js"
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
"files": [
|
|
22
|
+
"dist",
|
|
23
|
+
"README.md",
|
|
24
|
+
"LICENSE"
|
|
25
|
+
],
|
|
26
|
+
"sideEffects": false,
|
|
27
|
+
"engines": {
|
|
28
|
+
"node": ">=18.17"
|
|
29
|
+
},
|
|
30
|
+
"dependencies": {
|
|
31
|
+
"@lib-acr/browser": "0.0.1",
|
|
32
|
+
"@lib-acr/core": "0.0.1"
|
|
33
|
+
},
|
|
34
|
+
"peerDependencies": {
|
|
35
|
+
"@angular/common": ">=19.0.0 <22",
|
|
36
|
+
"@angular/core": ">=19.0.0 <22",
|
|
37
|
+
"@angular/router": ">=19.0.0 <22",
|
|
38
|
+
"rxjs": "^7.8.0"
|
|
39
|
+
},
|
|
40
|
+
"peerDependenciesMeta": {
|
|
41
|
+
"@angular/router": {
|
|
42
|
+
"optional": true
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
"devDependencies": {
|
|
46
|
+
"@angular/common": "^20.3.0",
|
|
47
|
+
"@angular/core": "^20.3.0",
|
|
48
|
+
"@angular/platform-browser": "^20.3.0",
|
|
49
|
+
"@angular/router": "^20.3.0",
|
|
50
|
+
"rxjs": "^7.8.2",
|
|
51
|
+
"zone.js": "~0.15.0",
|
|
52
|
+
"@angular/compiler": "^20.3.0"
|
|
53
|
+
},
|
|
54
|
+
"scripts": {
|
|
55
|
+
"build": "node ../../scripts/build-package.mjs",
|
|
56
|
+
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
57
|
+
"test": "vitest run"
|
|
58
|
+
},
|
|
59
|
+
"repository": {
|
|
60
|
+
"type": "git",
|
|
61
|
+
"url": "git+https://github.com/application-control-room/application.git",
|
|
62
|
+
"directory": "packages/angular"
|
|
63
|
+
},
|
|
64
|
+
"homepage": "https://github.com/application-control-room/application/tree/main/packages/angular#readme",
|
|
65
|
+
"publishConfig": {
|
|
66
|
+
"access": "public"
|
|
67
|
+
}
|
|
68
|
+
}
|