@bloomscorp/ngx-bloomsight 1.5.1 → 2.0.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/LICENSE.md +21 -0
- package/README.md +276 -144
- package/fesm2022/bloomscorp-ngx-bloomsight.mjs +168 -0
- package/fesm2022/bloomscorp-ngx-bloomsight.mjs.map +1 -0
- package/package.json +30 -17
- package/types/bloomscorp-ngx-bloomsight.d.ts +171 -0
- package/esm2020/bloomscorp-ngx-bloomsight.mjs +0 -5
- package/esm2020/lib/ngx-bloomsight.component.mjs +0 -22
- package/esm2020/lib/ngx-bloomsight.module.mjs +0 -33
- package/esm2020/lib/ngx-bloomsight.service.mjs +0 -14
- package/esm2020/lib/service/bloomsight-event.service.mjs +0 -45
- package/esm2020/lib/service/bloomsight-mail.service.mjs +0 -25
- package/esm2020/public-api.mjs +0 -9
- package/fesm2015/bloomscorp-ngx-bloomsight.mjs +0 -138
- package/fesm2015/bloomscorp-ngx-bloomsight.mjs.map +0 -1
- package/fesm2020/bloomscorp-ngx-bloomsight.mjs +0 -134
- package/fesm2020/bloomscorp-ngx-bloomsight.mjs.map +0 -1
- package/index.d.ts +0 -5
- package/lib/ngx-bloomsight.component.d.ts +0 -8
- package/lib/ngx-bloomsight.module.d.ts +0 -10
- package/lib/ngx-bloomsight.service.d.ts +0 -6
- package/lib/service/bloomsight-event.service.d.ts +0 -17
- package/lib/service/bloomsight-mail.service.d.ts +0 -10
- package/public-api.d.ts +0 -5
package/LICENSE.md
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) [2024] [Bloomscorp]
|
|
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,144 +1,276 @@
|
|
|
1
|
-
# ngx-bloomsight
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
|
81
|
-
|
|
82
|
-
| `
|
|
83
|
-
| `
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
'
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
1
|
+
# @bloomscorp/ngx-bloomsight
|
|
2
|
+
|
|
3
|
+
Bloomsight analytics for Angular: events, page views, mail and failure
|
|
4
|
+
reporting, through one provider and one service.
|
|
5
|
+
|
|
6
|
+
It wraps [`@bloomscorp/bloomsight.js`](https://www.npmjs.com/package/@bloomscorp/bloomsight.js)
|
|
7
|
+
and works with both backends: the standalone Bloomsight server, and Bloomsight
|
|
8
|
+
inside blooms.ai.
|
|
9
|
+
|
|
10
|
+
[](https://github.com/bloomscorp/ngx-bloomsight/blob/main/LICENSE.md)
|
|
11
|
+
|
|
12
|
+
> **Upgrading from 1.x?** 2.0.0 replaces `NgxBloomsightModule.forRoot()` and the
|
|
13
|
+
> two services. See the [migration guide](https://github.com/bloomscorp/ngx-bloomsight/blob/main/MIGRATION.md).
|
|
14
|
+
|
|
15
|
+
## Requirements
|
|
16
|
+
|
|
17
|
+
| | Supported |
|
|
18
|
+
|---|---|
|
|
19
|
+
| Angular (`@angular/core`, `common`, `router`) | **19, 20, 21, 22**. CI compiles the published package with each of them |
|
|
20
|
+
| `@bloomscorp/bloomsight.js` | `>=0.9.2 <1.0.0` |
|
|
21
|
+
| RxJS | 7 or 8 |
|
|
22
|
+
|
|
23
|
+
## Install
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
npm install @bloomscorp/ngx-bloomsight @bloomscorp/bloomsight.js
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
`@bloomscorp/bloomsight.js` is a peer dependency: install it, but never import
|
|
30
|
+
it. Everything your app needs comes from `@bloomscorp/ngx-bloomsight` (see
|
|
31
|
+
[Types](#types)).
|
|
32
|
+
|
|
33
|
+
## Set up
|
|
34
|
+
|
|
35
|
+
Call `provideBloomsight()` once, in the application's root providers:
|
|
36
|
+
|
|
37
|
+
```ts
|
|
38
|
+
// app.config.ts
|
|
39
|
+
import {ApplicationConfig} from '@angular/core';
|
|
40
|
+
import {provideRouter} from '@angular/router';
|
|
41
|
+
import {provideBloomsight} from '@bloomscorp/ngx-bloomsight';
|
|
42
|
+
|
|
43
|
+
export const appConfig: ApplicationConfig = {
|
|
44
|
+
providers: [
|
|
45
|
+
provideRouter(routes),
|
|
46
|
+
provideBloomsight({
|
|
47
|
+
propertyToken: '65d72f0b5e990c6028790156',
|
|
48
|
+
isDevelopmentMode: false
|
|
49
|
+
})
|
|
50
|
+
]
|
|
51
|
+
};
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
An app that still bootstraps an `AppModule` puts the same call in that module's
|
|
55
|
+
`providers`:
|
|
56
|
+
|
|
57
|
+
```ts
|
|
58
|
+
@NgModule({
|
|
59
|
+
// …
|
|
60
|
+
providers: [provideBloomsight({propertyToken: '…', isDevelopmentMode: false})]
|
|
61
|
+
})
|
|
62
|
+
export class AppModule {}
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
That is all the setup. The SDK starts when the app starts, page views are
|
|
66
|
+
recorded from the router, and `BloomsightService` can be injected anywhere,
|
|
67
|
+
including lazy-loaded routes.
|
|
68
|
+
|
|
69
|
+
Call it **once**, in the root providers: not in a lazy route's `providers`, and
|
|
70
|
+
not in a component.
|
|
71
|
+
|
|
72
|
+
## Configuration
|
|
73
|
+
|
|
74
|
+
| Field | Type | Default | What it does |
|
|
75
|
+
|---|---|---|---|
|
|
76
|
+
| `propertyToken` | `string` | **required** | The property's token. Never regenerate it: the token is how events are attributed |
|
|
77
|
+
| `isDevelopmentMode` | `boolean` | **required** | Logs everything to the console, and skips checking the property against the page's host. Never ship `true` to production |
|
|
78
|
+
| `target` | `'bloomsight'` \| `'blooms-ai'` | `'bloomsight'` | Which backend receives events. See [Moving to blooms.ai](#moving-to-bloomsai) |
|
|
79
|
+
| `apiBase` | `string` | `https://z.bloomscorp.com` | blooms.ai only: the API origin, for pointing a project at staging first |
|
|
80
|
+
| `logOnly` | `boolean` | `false` | Runs everything and sends nothing: each event and mail is logged instead. For demos, tests and dry runs |
|
|
81
|
+
| `debug` | `boolean` | `false` | blooms.ai only: full logging without `isDevelopmentMode` |
|
|
82
|
+
| `onError` | `(error: BloomsightError) => void` | — | Called for every failure the SDK reports. See [Errors](#errors) |
|
|
83
|
+
| `stopAll` | `boolean` | `false` | Records nothing |
|
|
84
|
+
| `stopSimpleEvent` | `boolean` | `false` | Records no simple events |
|
|
85
|
+
| `stopDataEvent` | `boolean` | `false` | Records no data events |
|
|
86
|
+
| `stopPageViewEvent` | `boolean` | `false` | Records no page views |
|
|
87
|
+
|
|
88
|
+
The SDK reads its configuration once, when the app starts.
|
|
89
|
+
|
|
90
|
+
## Events
|
|
91
|
+
|
|
92
|
+
```ts
|
|
93
|
+
import {Component, inject} from '@angular/core';
|
|
94
|
+
import {BloomsightService} from '@bloomscorp/ngx-bloomsight';
|
|
95
|
+
|
|
96
|
+
@Component({/* … */})
|
|
97
|
+
export class PricingComponent {
|
|
98
|
+
private readonly bloomsight = inject(BloomsightService);
|
|
99
|
+
|
|
100
|
+
contactUs(): void {
|
|
101
|
+
this.bloomsight.track('65d735b122354c8ba6a489c2');
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
choosePlan(plan: string, seats: number): void {
|
|
105
|
+
this.bloomsight.trackData('66d735b122355c8ba6a456f8', {plan, seats});
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
| Method | Records |
|
|
111
|
+
|---|---|
|
|
112
|
+
| `track(eventToken)` | a simple event |
|
|
113
|
+
| `trackData(eventToken, data)` | a data event, with `data` as a JSON-serialisable object |
|
|
114
|
+
|
|
115
|
+
The event token comes from the Bloomsight dashboard, where the event is defined.
|
|
116
|
+
|
|
117
|
+
## Page views
|
|
118
|
+
|
|
119
|
+
Recorded automatically, with no code in your app:
|
|
120
|
+
|
|
121
|
+
- **Once per new destination.** A view is recorded each time a navigation lands
|
|
122
|
+
somewhere new, by the URL the user ends up on after redirects. Following a
|
|
123
|
+
link that redirects to the current page is not a new view
|
|
124
|
+
- **Standalone Bloomsight server only.** blooms.ai has no page-view endpoint, so
|
|
125
|
+
with `target: 'blooms-ai'` the router is not watched at all
|
|
126
|
+
- **After two seconds.** The SDK records a view only once the visitor has spent
|
|
127
|
+
two seconds on the page
|
|
128
|
+
|
|
129
|
+
`stopPageViewEvent: true` turns them off.
|
|
130
|
+
|
|
131
|
+
## Mail
|
|
132
|
+
|
|
133
|
+
```ts
|
|
134
|
+
import {BloomsightError, BloomsightService} from '@bloomscorp/ngx-bloomsight';
|
|
135
|
+
|
|
136
|
+
const data = new FormData();
|
|
137
|
+
data.append('name', form.name);
|
|
138
|
+
data.append('email', form.email);
|
|
139
|
+
|
|
140
|
+
this.bloomsight.sendMail({
|
|
141
|
+
engineId: '66d735b122355c8ba6a456f8',
|
|
142
|
+
templateId: '63d735h822355s6ba6a4556k',
|
|
143
|
+
templateOwnerId: '61d895h922355b6ba6a4587a',
|
|
144
|
+
data,
|
|
145
|
+
engine: 'GMAIL' // or 'CUSTOM_SMTP'; defaults to 'GMAIL'
|
|
146
|
+
}).subscribe({
|
|
147
|
+
next: () => this.sent.set(true),
|
|
148
|
+
error: (error: BloomsightError) => this.failure.set(error.message)
|
|
149
|
+
});
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
| Field | Required | |
|
|
153
|
+
|---|---|---|
|
|
154
|
+
| `engineId` | ✅ | The mail engine, from the dashboard |
|
|
155
|
+
| `templateId` | ✅ | The template to render |
|
|
156
|
+
| `templateOwnerId` | ✅ | The user who owns the template |
|
|
157
|
+
| `data` | ✅ | The template's values; each field arrives as `metadata[<name>]` |
|
|
158
|
+
| `engine` | | `'GMAIL'` or `'CUSTOM_SMTP'`. Defaults to `'GMAIL'` |
|
|
159
|
+
|
|
160
|
+
The result behaves like `HttpClient`:
|
|
161
|
+
|
|
162
|
+
- **Nothing is sent until you subscribe**, and each subscription sends once
|
|
163
|
+
- It emits once and completes when the server accepts the mail
|
|
164
|
+
- It errors with a `BloomsightError` (`code: 'MAIL_FAILED'`) when the server
|
|
165
|
+
rejects it or cannot be reached
|
|
166
|
+
- Under `logOnly` the mail is logged, not sent, and the result still emits and
|
|
167
|
+
completes, so a "sending…" state is always released
|
|
168
|
+
|
|
169
|
+
Mail always goes to the standalone Bloomsight server, whatever `target` is set
|
|
170
|
+
to.
|
|
171
|
+
|
|
172
|
+
## Errors
|
|
173
|
+
|
|
174
|
+
On blooms.ai, every failed request is reported, with the server's own code
|
|
175
|
+
where there is one (`ORIGIN_REJECTED`, `UNKNOWN_PROPERTY`, `NETWORK_FAILURE`,
|
|
176
|
+
…). There are two ways to receive them:
|
|
177
|
+
|
|
178
|
+
```ts
|
|
179
|
+
// 1. In the configuration. Called for every failure, including at startup.
|
|
180
|
+
provideBloomsight({
|
|
181
|
+
propertyToken: '…',
|
|
182
|
+
isDevelopmentMode: false,
|
|
183
|
+
target: 'blooms-ai',
|
|
184
|
+
onError: (error) => monitoring.report(error)
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
// 2. As a stream, anywhere.
|
|
188
|
+
inject(BloomsightService).errors$.subscribe((error) => console.warn(error.code, error.message));
|
|
189
|
+
|
|
190
|
+
// …or the latest one as a signal, for a template.
|
|
191
|
+
readonly lastError = toSignal(inject(BloomsightService).errors$);
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
- **`errors$` only sees what comes after you subscribe.** It emits the failures
|
|
195
|
+
that happen after that point, not earlier ones
|
|
196
|
+
- **A failed start doesn't stop your app.** If the SDK fails to start, the app
|
|
197
|
+
keeps running without it. The failure (`INIT_FAILED`) is reported to `onError`
|
|
198
|
+
and the console, on either target. It is not on `errors$`, because startup
|
|
199
|
+
happens before anything can subscribe
|
|
200
|
+
- **Mail failures stay with the mail.** They are the error of that `sendMail()`
|
|
201
|
+
call, not on `errors$`
|
|
202
|
+
- **The standalone server reports almost nothing.** There the SDK reports no
|
|
203
|
+
request failures; only `INIT_FAILED` reaches `onError`
|
|
204
|
+
|
|
205
|
+
## Moving to blooms.ai
|
|
206
|
+
|
|
207
|
+
Change the target. The property token and every call site stay the same:
|
|
208
|
+
|
|
209
|
+
```ts
|
|
210
|
+
provideBloomsight({
|
|
211
|
+
propertyToken: '65d72f0b5e990c6028790156', // unchanged
|
|
212
|
+
isDevelopmentMode: false,
|
|
213
|
+
target: 'blooms-ai',
|
|
214
|
+
apiBase: 'https://staging.example.com', // optional: try staging first
|
|
215
|
+
onError: (error) => monitoring.report(error)
|
|
216
|
+
});
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
On blooms.ai:
|
|
220
|
+
|
|
221
|
+
- events go to blooms.ai, and never to the standalone server
|
|
222
|
+
- page views are not collected
|
|
223
|
+
- mail is unchanged: it still goes to the standalone server
|
|
224
|
+
- failures are reported through `onError` and `errors$`
|
|
225
|
+
|
|
226
|
+
## Server-side rendering
|
|
227
|
+
|
|
228
|
+
Nothing runs on the server. The SDK starts in the browser, `track` and
|
|
229
|
+
`trackData` do nothing during server rendering, and `sendMail` completes without
|
|
230
|
+
sending.
|
|
231
|
+
|
|
232
|
+
## Types
|
|
233
|
+
|
|
234
|
+
Everything is exported from `@bloomscorp/ngx-bloomsight`:
|
|
235
|
+
|
|
236
|
+
| Export | |
|
|
237
|
+
|---|---|
|
|
238
|
+
| `provideBloomsight(config)` | Sets up the SDK |
|
|
239
|
+
| `BloomsightService` | `track`, `trackData`, `sendMail`, `errors$` |
|
|
240
|
+
| `BLOOMSIGHT_CONFIG` | Injection token holding the configuration the app started with |
|
|
241
|
+
| `BloomsightConfig` | The configuration |
|
|
242
|
+
| `BloomsightError` | `{code, message, status?, eventToken?}` |
|
|
243
|
+
| `BloomsightEmailEngine` | `'GMAIL' \| 'CUSTOM_SMTP'` |
|
|
244
|
+
| `BloomsightMailRequest` | What `sendMail` takes |
|
|
245
|
+
| `BloomsightEventData` | What `trackData` takes |
|
|
246
|
+
|
|
247
|
+
**Don't import `@bloomscorp/bloomsight.js` in your app.** If something you need
|
|
248
|
+
is not exported here, that's a gap in this library: please
|
|
249
|
+
[open an issue](https://github.com/bloomscorp/ngx-bloomsight/issues).
|
|
250
|
+
|
|
251
|
+
## Testing your app
|
|
252
|
+
|
|
253
|
+
Use `logOnly` in tests, so nothing is sent:
|
|
254
|
+
|
|
255
|
+
```ts
|
|
256
|
+
TestBed.configureTestingModule({
|
|
257
|
+
providers: [provideBloomsight({propertyToken: 'test', isDevelopmentMode: false, logOnly: true})]
|
|
258
|
+
});
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
In a jsdom test environment (Vitest or Jest), the SDK reads `window.matchMedia`
|
|
262
|
+
at startup, and jsdom doesn't have it. Your tests still pass, but the SDK
|
|
263
|
+
reports `INIT_FAILED` and stays off. To run it for real, stub `matchMedia` in
|
|
264
|
+
your test setup file:
|
|
265
|
+
|
|
266
|
+
```ts
|
|
267
|
+
window.matchMedia ??= (query: string) => ({
|
|
268
|
+
matches: false, media: query, onchange: null,
|
|
269
|
+
addListener: () => {}, removeListener: () => {},
|
|
270
|
+
addEventListener: () => {}, removeEventListener: () => {}, dispatchEvent: () => false
|
|
271
|
+
}) as MediaQueryList;
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
## License
|
|
275
|
+
|
|
276
|
+
[MIT](https://github.com/bloomscorp/ngx-bloomsight/blob/main/LICENSE.md)
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import * as i0 from "@angular/core";
|
|
2
|
+
import { DestroyRef, Injectable, InjectionToken, PLATFORM_ID, inject, makeEnvironmentProviders, provideAppInitializer } from "@angular/core";
|
|
3
|
+
import { isPlatformBrowser } from "@angular/common";
|
|
4
|
+
import { takeUntilDestroyed } from "@angular/core/rxjs-interop";
|
|
5
|
+
import { NavigationEnd, Router } from "@angular/router";
|
|
6
|
+
import { Observable, Subject, distinctUntilChanged, filter, map } from "rxjs";
|
|
7
|
+
import { init, pageViewObserver, resolveDataEvent, resolveSimpleEvent, sendEmail } from "@bloomscorp/bloomsight.js";
|
|
8
|
+
const BLOOMSIGHT_CONFIG = new InjectionToken("BLOOMSIGHT_CONFIG");
|
|
9
|
+
var BloomsightErrors = class BloomsightErrors {
|
|
10
|
+
constructor() {
|
|
11
|
+
this.failures = new Subject();
|
|
12
|
+
this.errors$ = this.failures.asObservable();
|
|
13
|
+
}
|
|
14
|
+
report(error) {
|
|
15
|
+
this.failures.next(error);
|
|
16
|
+
}
|
|
17
|
+
static {
|
|
18
|
+
this.ɵfac = i0.ɵɵngDeclareFactory({
|
|
19
|
+
minVersion: "12.0.0",
|
|
20
|
+
version: "22.2.2",
|
|
21
|
+
ngImport: i0,
|
|
22
|
+
type: BloomsightErrors,
|
|
23
|
+
deps: [],
|
|
24
|
+
target: i0.ɵɵFactoryTarget.Injectable
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
static {
|
|
28
|
+
this.ɵprov = i0.ɵɵngDeclareInjectable({
|
|
29
|
+
minVersion: "12.0.0",
|
|
30
|
+
version: "22.2.2",
|
|
31
|
+
ngImport: i0,
|
|
32
|
+
type: BloomsightErrors,
|
|
33
|
+
providedIn: "root"
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
i0.ɵɵngDeclareClassMetadata({
|
|
38
|
+
minVersion: "12.0.0",
|
|
39
|
+
version: "22.2.2",
|
|
40
|
+
ngImport: i0,
|
|
41
|
+
type: BloomsightErrors,
|
|
42
|
+
decorators: [{
|
|
43
|
+
type: Injectable,
|
|
44
|
+
args: [{ providedIn: "root" }]
|
|
45
|
+
}]
|
|
46
|
+
});
|
|
47
|
+
function startBloomsight() {
|
|
48
|
+
if (!isPlatformBrowser(inject(PLATFORM_ID))) return;
|
|
49
|
+
const config = inject(BLOOMSIGHT_CONFIG);
|
|
50
|
+
initialise(config, inject(BloomsightErrors));
|
|
51
|
+
observePageViews(config, inject(Router), inject(DestroyRef));
|
|
52
|
+
}
|
|
53
|
+
function initialise(config, errors) {
|
|
54
|
+
try {
|
|
55
|
+
init({
|
|
56
|
+
...config,
|
|
57
|
+
onError: (error) => {
|
|
58
|
+
errors.report(error);
|
|
59
|
+
config.onError?.(error);
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
} catch (cause) {
|
|
63
|
+
reportInitFailure(config, cause);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
function reportInitFailure(config, cause) {
|
|
67
|
+
const error = {
|
|
68
|
+
code: "INIT_FAILED",
|
|
69
|
+
message: `bloomsight.js failed to initialise: ${cause instanceof Error ? cause.message : String(cause)}`
|
|
70
|
+
};
|
|
71
|
+
console.error("ngx-bloomsight:", error.message);
|
|
72
|
+
try {
|
|
73
|
+
config.onError?.(error);
|
|
74
|
+
} catch {}
|
|
75
|
+
}
|
|
76
|
+
function observePageViews(config, router, destroyRef) {
|
|
77
|
+
if (config.target === "blooms-ai") {
|
|
78
|
+
if (config.debug || config.isDevelopmentMode) console.log("ngx-bloomsight: page views are not collected on blooms.ai, so the router is not observed");
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
router.events.pipe(filter((event) => event instanceof NavigationEnd), map((event) => event.urlAfterRedirects), distinctUntilChanged(), takeUntilDestroyed(destroyRef)).subscribe(() => pageViewObserver());
|
|
82
|
+
}
|
|
83
|
+
function provideBloomsight(config) {
|
|
84
|
+
return makeEnvironmentProviders([{
|
|
85
|
+
provide: BLOOMSIGHT_CONFIG,
|
|
86
|
+
useValue: config
|
|
87
|
+
}, provideAppInitializer(startBloomsight)]);
|
|
88
|
+
}
|
|
89
|
+
var BloomsightService = class BloomsightService {
|
|
90
|
+
constructor() {
|
|
91
|
+
this.config = requireConfig();
|
|
92
|
+
this.isBrowser = isPlatformBrowser(inject(PLATFORM_ID));
|
|
93
|
+
this.errors$ = inject(BloomsightErrors).errors$;
|
|
94
|
+
}
|
|
95
|
+
track(eventToken) {
|
|
96
|
+
if (this.isBrowser) resolveSimpleEvent(eventToken);
|
|
97
|
+
}
|
|
98
|
+
trackData(eventToken, data) {
|
|
99
|
+
if (this.isBrowser) resolveDataEvent(eventToken, data);
|
|
100
|
+
}
|
|
101
|
+
sendMail(request) {
|
|
102
|
+
return new Observable((subscriber) => {
|
|
103
|
+
if (!this.isBrowser) {
|
|
104
|
+
subscriber.complete();
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
const succeed = () => {
|
|
108
|
+
subscriber.next();
|
|
109
|
+
subscriber.complete();
|
|
110
|
+
};
|
|
111
|
+
const fail = (cause) => subscriber.error(mailFailure(cause));
|
|
112
|
+
try {
|
|
113
|
+
sendEmail(request.engine ?? "GMAIL", request.engineId, request.templateId, request.templateOwnerId, request.data, succeed, () => fail());
|
|
114
|
+
} catch (cause) {
|
|
115
|
+
fail(cause);
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
if (this.config.logOnly) succeed();
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
static {
|
|
122
|
+
this.ɵfac = i0.ɵɵngDeclareFactory({
|
|
123
|
+
minVersion: "12.0.0",
|
|
124
|
+
version: "22.2.2",
|
|
125
|
+
ngImport: i0,
|
|
126
|
+
type: BloomsightService,
|
|
127
|
+
deps: [],
|
|
128
|
+
target: i0.ɵɵFactoryTarget.Injectable
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
static {
|
|
132
|
+
this.ɵprov = i0.ɵɵngDeclareInjectable({
|
|
133
|
+
minVersion: "12.0.0",
|
|
134
|
+
version: "22.2.2",
|
|
135
|
+
ngImport: i0,
|
|
136
|
+
type: BloomsightService,
|
|
137
|
+
providedIn: "root"
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
};
|
|
141
|
+
i0.ɵɵngDeclareClassMetadata({
|
|
142
|
+
minVersion: "12.0.0",
|
|
143
|
+
version: "22.2.2",
|
|
144
|
+
ngImport: i0,
|
|
145
|
+
type: BloomsightService,
|
|
146
|
+
decorators: [{
|
|
147
|
+
type: Injectable,
|
|
148
|
+
args: [{ providedIn: "root" }]
|
|
149
|
+
}]
|
|
150
|
+
});
|
|
151
|
+
function requireConfig() {
|
|
152
|
+
const config = inject(BLOOMSIGHT_CONFIG, { optional: true });
|
|
153
|
+
if (!config) throw new Error("ngx-bloomsight: BloomsightService was injected, but the application never called provideBloomsight(). Add provideBloomsight({ propertyToken, isDevelopmentMode }) to the root providers.");
|
|
154
|
+
return config;
|
|
155
|
+
}
|
|
156
|
+
function mailFailure(cause) {
|
|
157
|
+
if (cause === void 0) return {
|
|
158
|
+
code: "MAIL_FAILED",
|
|
159
|
+
message: "The mail was not sent: the server rejected it or could not be reached"
|
|
160
|
+
};
|
|
161
|
+
return {
|
|
162
|
+
code: "MAIL_FAILED",
|
|
163
|
+
message: `The mail was not sent: ${cause instanceof Error ? cause.message : String(cause)}`
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
export { BLOOMSIGHT_CONFIG, BloomsightService, provideBloomsight };
|
|
167
|
+
|
|
168
|
+
//# sourceMappingURL=bloomscorp-ngx-bloomsight.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bloomscorp-ngx-bloomsight.mjs","names":[],"sources":["../../../../projects/bloomscorp/ngx-bloomsight/src/lib/bloomsight-config.token.ts","../../../../projects/bloomscorp/ngx-bloomsight/src/lib/internal/bloomsight-errors.ts","../../../../projects/bloomscorp/ngx-bloomsight/src/lib/internal/start-bloomsight.ts","../../../../projects/bloomscorp/ngx-bloomsight/src/lib/provide-bloomsight.ts","../../../../projects/bloomscorp/ngx-bloomsight/src/lib/bloomsight.service.ts"],"sourcesContent":["import {InjectionToken} from '@angular/core';\nimport {BloomsightConfig} from './type/bloomsight-config';\n\n/**\n * The configuration given to `provideBloomsight()`.\n *\n * A real `InjectionToken` (P3). 1.x used the string `'bloomsightConfig'`,\n * which carries no type — whatever was injected under it was trusted to be a\n * config — and which any other library could collide with.\n *\n * Inject it to read the configuration the app started with. Provide it through\n * `provideBloomsight()`, not directly: that is what also starts the SDK.\n *\n * The explicit annotation is deliberate. Inferred, the published declaration\n * comes out as `InjectionToken<import(\"@bloomscorp/bloomsight.js\").IConfig>` —\n * the compiler expands the alias — and the core's name leaks into this\n * package's public types (§2.3).\n */\nexport const BLOOMSIGHT_CONFIG: InjectionToken<BloomsightConfig> =\n\tnew InjectionToken<BloomsightConfig>('BLOOMSIGHT_CONFIG');\n","import {Injectable} from '@angular/core';\nimport {Observable, Subject} from 'rxjs';\nimport {BloomsightError} from '../type/bloomsight-error';\n\n/**\n * Collects the failures the core reports, for `BloomsightService.errors$`.\n *\n * Internal — not exported from the public API. The service hands out the\n * stream read-only, so only the SDK's own `onError` hook can push to it.\n *\n * Hot, like `Router.events`: a subscriber sees the failures that happen after\n * it subscribes. That is why `INIT_FAILED` is not emitted here — initialisation\n * runs while the app starts, before any component exists to subscribe. It goes\n * to the app's `onError` and the console instead.\n */\n@Injectable({providedIn: 'root'})\nexport class BloomsightErrors {\n\n\tprivate readonly failures = new Subject<BloomsightError>();\n\n\treadonly errors$: Observable<BloomsightError> = this.failures.asObservable();\n\n\treport(error: BloomsightError): void {\n\t\tthis.failures.next(error);\n\t}\n}\n","import {DestroyRef, inject, PLATFORM_ID} from '@angular/core';\nimport {isPlatformBrowser} from '@angular/common';\nimport {takeUntilDestroyed} from '@angular/core/rxjs-interop';\nimport {NavigationEnd, Router} from '@angular/router';\nimport {distinctUntilChanged, filter, map} from 'rxjs';\nimport {init, pageViewObserver} from '@bloomscorp/bloomsight.js';\nimport {BLOOMSIGHT_CONFIG} from '../bloomsight-config.token';\nimport {BloomsightConfig} from '../type/bloomsight-config';\nimport {BloomsightErrors} from './bloomsight-errors';\n\n/**\n * The app initializer `provideBloomsight()` registers: starts the SDK once,\n * when the application starts.\n *\n * In 1.x the services did this from their constructors, which is what B1, B7\n * and B8 all came from: the SDK started only if something happened to inject a\n * service, started twice if two did, and started again in a lazy-loaded module\n * that imported `forRoot()`. An initializer runs once per application, whether\n * or not anything injects anything.\n *\n * Nothing runs during server-side rendering (A3): there is no visitor to\n * measure, and the core's browser detection would only report blanks.\n */\nexport function startBloomsight(): void {\n\tif (!isPlatformBrowser(inject(PLATFORM_ID))) return;\n\n\tconst config = inject(BLOOMSIGHT_CONFIG);\n\tinitialise(config, inject(BloomsightErrors));\n\tobservePageViews(config, inject(Router), inject(DestroyRef));\n}\n\n/**\n * Start the core, and never let a failure escape.\n *\n * A throw here would abort the application's bootstrap, so a broken analytics\n * SDK would take the whole app down with it (RV4). The failure is reported\n * instead, and the app carries on without the SDK.\n *\n * The core is given a copy of the config with its own `onError`, which feeds\n * `errors$` and then calls the app's handler. The core copies the config it is\n * given, so the app's object is never touched either way.\n */\nfunction initialise(config: BloomsightConfig, errors: BloomsightErrors): void {\n\ttry {\n\t\tinit({\n\t\t\t...config,\n\t\t\tonError: (error) => {\n\t\t\t\terrors.report(error);\n\t\t\t\tconfig.onError?.(error);\n\t\t\t}\n\t\t});\n\t} catch (cause) {\n\t\treportInitFailure(config, cause);\n\t}\n}\n\nfunction reportInitFailure(config: BloomsightConfig, cause: unknown): void {\n\tconst error = {\n\t\tcode: 'INIT_FAILED',\n\t\tmessage: `bloomsight.js failed to initialise: ${cause instanceof Error ? cause.message : String(cause)}`\n\t};\n\tconsole.error('ngx-bloomsight:', error.message);\n\ttry {\n\t\tconfig.onError?.(error);\n\t} catch {\n\t\t// A throwing handler must not turn a reported failure back into a crash.\n\t}\n}\n\n/**\n * Record a page view each time a navigation lands somewhere new.\n *\n * - `urlAfterRedirects`, not `url` (B3): a redirect or guard means the two\n * differ, and only the first is where the user actually is.\n * - `distinctUntilChanged`: two requested URLs resolving to the same page are\n * one page view, as is a same-URL reload.\n * - `takeUntilDestroyed` (B4): 1.x never unsubscribed. Here the subscription\n * ends with the application's injector.\n *\n * Not observed at all on blooms.ai, which has no page-view endpoint: every call\n * would be a no-op in the core.\n */\nfunction observePageViews(config: BloomsightConfig, router: Router, destroyRef: DestroyRef): void {\n\tif (config.target === 'blooms-ai') {\n\t\tif (config.debug || config.isDevelopmentMode) {\n\t\t\tconsole.log('ngx-bloomsight: page views are not collected on blooms.ai, so the router is not observed');\n\t\t}\n\t\treturn;\n\t}\n\n\trouter.events.pipe(\n\t\tfilter((event): event is NavigationEnd => event instanceof NavigationEnd),\n\t\tmap((event) => event.urlAfterRedirects),\n\t\tdistinctUntilChanged(),\n\t\ttakeUntilDestroyed(destroyRef)\n\t).subscribe(() => pageViewObserver());\n}\n","import {EnvironmentProviders, makeEnvironmentProviders, provideAppInitializer} from '@angular/core';\nimport {BLOOMSIGHT_CONFIG} from './bloomsight-config.token';\nimport {startBloomsight} from './internal/start-bloomsight';\nimport {BloomsightConfig} from './type/bloomsight-config';\n\n/**\n * Set up Bloomsight for an application. Call it once, in the root providers:\n *\n * ```ts\n * bootstrapApplication(AppComponent, {\n * providers: [\n * provideRouter(routes),\n * provideBloomsight({ propertyToken: '…', isDevelopmentMode: false })\n * ]\n * });\n * ```\n *\n * It provides the configuration and starts the SDK when the application starts.\n * Page views are then recorded from the router automatically, and\n * `BloomsightService` can be injected anywhere — including lazy-loaded routes,\n * and components that only send mail.\n *\n * Returns `EnvironmentProviders`, which Angular accepts only in an environment\n * injector — the application or a route — never in a component's `providers`.\n * That replaces 1.x's `NgxBloomsightModule.forRoot()`, whose services existed\n * only where the module was imported with `forRoot()` (B6), and twice if a\n * lazy module imported it again (B7).\n */\nexport function provideBloomsight(config: BloomsightConfig): EnvironmentProviders {\n\treturn makeEnvironmentProviders([\n\t\t{provide: BLOOMSIGHT_CONFIG, useValue: config},\n\t\tprovideAppInitializer(startBloomsight)\n\t]);\n}\n","import {inject, Injectable, PLATFORM_ID} from '@angular/core';\nimport {isPlatformBrowser} from '@angular/common';\nimport {Observable} from 'rxjs';\nimport {resolveDataEvent, resolveSimpleEvent, sendEmail} from '@bloomscorp/bloomsight.js';\nimport {BLOOMSIGHT_CONFIG} from './bloomsight-config.token';\nimport {BloomsightErrors} from './internal/bloomsight-errors';\nimport {BloomsightConfig} from './type/bloomsight-config';\nimport {BloomsightError} from './type/bloomsight-error';\nimport {BloomsightEventData} from './type/bloomsight-event-data';\nimport {BloomsightMailRequest} from './type/bloomsight-mail-request';\n\n/**\n * Everything an app does with Bloomsight: events, mail, and failures.\n *\n * One root service, replacing 1.x's `BloomsightEventService` and\n * `BloomsightMailService`. It does not start the SDK — `provideBloomsight()`\n * does that when the app starts — so injecting it has no side effects.\n *\n * During server-side rendering every method is a no-op (A3).\n */\n@Injectable({providedIn: 'root'})\nexport class BloomsightService {\n\n\tprivate readonly config: BloomsightConfig = requireConfig();\n\n\tprivate readonly isBrowser: boolean = isPlatformBrowser(inject(PLATFORM_ID));\n\n\t/**\n\t * Every failure the SDK reports after you subscribe — rejected or\n\t * unreachable requests, with the server's own code where there is one.\n\t * The configured `onError` is called as well.\n\t *\n\t * Mail failures are not included: they are the error of that `sendMail()`\n\t * call, where a caller is already waiting for them.\n\t */\n\treadonly errors$: Observable<BloomsightError> = inject(BloomsightErrors).errors$;\n\n\t/** Record a simple event. */\n\ttrack(eventToken: string): void {\n\t\tif (this.isBrowser) resolveSimpleEvent(eventToken);\n\t}\n\n\t/** Record a data event, with its payload. */\n\ttrackData(eventToken: string, data: BloomsightEventData): void {\n\t\tif (this.isBrowser) resolveDataEvent(eventToken, data);\n\t}\n\n\t/**\n\t * Send a mail through the standalone Bloomsight server. Mail always goes\n\t * there, whatever `target` is set to.\n\t *\n\t * Nothing is sent until the result is subscribed to, and it is sent once per\n\t * subscription — the same as `HttpClient`. It emits once and completes when\n\t * the server accepts the mail, and errors with a `BloomsightError`\n\t * (`code: 'MAIL_FAILED'`) when it does not.\n\t *\n\t * Under `logOnly` the mail is handed to the core, which logs it and sends\n\t * nothing, and the result still emits and completes, so a UI waiting on it\n\t * is released (F2). During server-side rendering it completes without\n\t * sending or emitting.\n\t */\n\tsendMail(request: BloomsightMailRequest): Observable<void> {\n\t\treturn new Observable<void>((subscriber) => {\n\t\t\tif (!this.isBrowser) {\n\t\t\t\tsubscriber.complete();\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst succeed = (): void => {\n\t\t\t\tsubscriber.next();\n\t\t\t\tsubscriber.complete();\n\t\t\t};\n\t\t\tconst fail = (cause?: unknown): void => subscriber.error(mailFailure(cause));\n\n\t\t\ttry {\n\t\t\t\tsendEmail(\n\t\t\t\t\trequest.engine ?? 'GMAIL',\n\t\t\t\t\trequest.engineId,\n\t\t\t\t\trequest.templateId,\n\t\t\t\t\trequest.templateOwnerId,\n\t\t\t\t\trequest.data,\n\t\t\t\t\tsucceed,\n\t\t\t\t\t() => fail()\n\t\t\t\t);\n\t\t\t} catch (cause) {\n\t\t\t\tfail(cause);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// The core returns before sending under logOnly, and calls neither\n\t\t\t// callback, so it is up to this wrapper to finish the call.\n\t\t\tif (this.config.logOnly) succeed();\n\t\t});\n\t}\n}\n\nfunction requireConfig(): BloomsightConfig {\n\tconst config = inject(BLOOMSIGHT_CONFIG, {optional: true});\n\tif (!config) {\n\t\tthrow new Error(\n\t\t\t'ngx-bloomsight: BloomsightService was injected, but the application never called provideBloomsight(). ' +\n\t\t\t'Add provideBloomsight({ propertyToken, isDevelopmentMode }) to the root providers.'\n\t\t);\n\t}\n\treturn config;\n}\n\nfunction mailFailure(cause?: unknown): BloomsightError {\n\tif (cause === undefined) {\n\t\treturn {code: 'MAIL_FAILED', message: 'The mail was not sent: the server rejected it or could not be reached'};\n\t}\n\treturn {\n\t\tcode: 'MAIL_FAILED',\n\t\tmessage: `The mail was not sent: ${cause instanceof Error ? cause.message : String(cause)}`\n\t};\n}\n"],"mappings":"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"}
|