Skip to content

Commit 74a1ffc

Browse files
pavkamroncohen
authored andcommitted
feat(browser-sdk,react-sdk): check events (#316)
1 parent 6133f41 commit 74a1ffc

File tree

14 files changed

+433
-179
lines changed

14 files changed

+433
-179
lines changed

packages/browser-sdk/FEEDBACK.md

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ the viewport, displayed in English, and with a [light-mode theme](#custom-stylin
1313

1414
These settings can be overwritten when initializing the Bucket Browser SDK:
1515

16-
```javascript
16+
```typescript
1717
const bucket = new BucketClient({
1818
publishableKey: "bucket-publishable-key",
1919
user: { id: "42" },
@@ -39,11 +39,9 @@ const bucket = new BucketClient({
3939

4040
See also:
4141

42-
- [Positioning and behavior](#positioning-and-behavior) for the position option.
43-
- [Static language configuration](#static-language-configuration) if you want to
44-
translate the feedback UI.
45-
- [Automated feedback surveys](#automated-feedback-surveys) to
46-
override default configuration.
42+
- [Positioning and behavior](#positioning-and-behavior) for the position option,
43+
- [Static language configuration](#static-language-configuration) if you want to translate the feedback UI,
44+
- [Automated feedback surveys](#automated-feedback-surveys) to override default configuration.
4745

4846
## Automated feedback surveys
4947

@@ -63,7 +61,7 @@ The live connection for automated feedback is established when the
6361

6462
You can disable automated collection in the `BucketClient` constructor:
6563

66-
```javascript
64+
```typescript
6765
const bucket = new BucketClient({
6866
publishableKey: "bucket-publishable-key",
6967
user: { id: "42" },

packages/browser-sdk/README.md

Lines changed: 69 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@ First find your `publishableKey` under [environment settings](https://app.bucket
88

99
The package can be imported or used directly in a HTML script tag:
1010

11-
A. Import module
11+
A. Import module:
1212

13-
```ts
13+
```typescript
1414
import { BucketClient } from "@bucketco/browser-sdk";
1515

1616
const user = {
@@ -93,19 +93,21 @@ See [example/browser.html](https://github.com/bucketco/bucket-javascript-sdk/tre
9393

9494
Supply these to the constructor call:
9595

96-
```ts
96+
```typescript
9797
type Configuration = {
9898
logger: console; // by default only logs warn/error, by passing `console` you'll log everything
9999
apiBaseUrl?: "https://front.bucket.co";
100100
sseBaseUrl?: "https://livemessaging.bucket.co";
101101
feedback?: undefined; // See FEEDBACK.md
102-
enableTracking?: true; // set to `false` to stop sending track events and user/company updates to Bucket servers. Useful when you're impersonating a user.
102+
enableTracking?: true; // set to `false` to stop sending track events and user/company updates to Bucket servers. Useful when you're impersonating a user
103103
featureOptions?: {
104-
fallbackFeatures?: string[]; // Enable these features if unable to contact bucket.co
105-
timeoutMs?: number; // Timeout for fetching features
106-
staleWhileRevalidate?: boolean; // Revalidate in the background when cached features turn stale to avoid latency in the UI
107-
staleTimeMs?: number; // at initialization time features are loaded from the cache unless they have gone stale. Defaults to 0 which means the cache is disabled. Increase in the case of a non-SPA.
108-
expireTimeMs?: number; // In case we're unable to fetch features from Bucket, cached/stale features will be used instead until they expire after `expireTimeMs`.
104+
fallbackFeatures?:
105+
| string[]
106+
| Record<string, { key: string; payload: any } | true>; // Enable these features if unable to contact bucket.co. Can be a list of feature keys or a record with configuration values
107+
timeoutMs?: number; // Timeout for fetching features (default: 5000ms)
108+
staleWhileRevalidate?: boolean; // Revalidate in the background when cached features turn stale to avoid latency in the UI (default: false)
109+
staleTimeMs?: number; // at initialization time features are loaded from the cache unless they have gone stale. Defaults to 0 which means the cache is disabled. Increase in the case of a non-SPA
110+
expireTimeMs?: number; // In case we're unable to fetch features from Bucket, cached/stale features will be used instead until they expire after `expireTimeMs`. Default is 30 days
109111
};
110112
};
111113
```
@@ -120,9 +122,9 @@ In addition to the `id`, you must also supply anything additional that you want
120122
Attributes cannot be nested (multiple levels) and must be either strings, integers or booleans.
121123
Some attributes are special and used in Bucket UI:
122124

123-
- `name` is used to display name for `user`/`company`,
124-
- `email` is accepted for `user`s and will be highlighted in the Bucket UI if available,
125-
- `avatar` can be provided for both `user` and `company` and should be an URL to an image.
125+
- `name` -- display name for `user`/`company`,
126+
- `email` -- is accepted for `user`s and will be highlighted in the Bucket UI if available,
127+
- `avatar` -- can be provided for both `user` and `company` and should be an URL to an image.
126128

127129
```ts
128130
const bucketClient = new BucketClient({
@@ -172,6 +174,43 @@ by down-stream clients, like the React SDK.
172174
Note that accessing `isEnabled` on the object returned by `getFeatures` does not automatically
173175
generate a `check` event, contrary to the `isEnabled` property on the object returned by `getFeature`.
174176

177+
### Feature Overrides
178+
179+
You can override feature flags locally for testing purposes using `setFeatureOverride`:
180+
181+
```ts
182+
// Override a feature to be enabled
183+
bucketClient.setFeatureOverride("huddle", true);
184+
185+
// Override a feature to be disabled
186+
bucketClient.setFeatureOverride("huddle", false);
187+
188+
// Remove the override
189+
bucketClient.setFeatureOverride("huddle", null);
190+
191+
// Get current override value
192+
const override = bucketClient.getFeatureOverride("huddle"); // returns boolean | null
193+
```
194+
195+
Feature overrides are persisted in `localStorage` and will be restored when the page is reloaded.
196+
197+
### Feature Updates
198+
199+
You can listen for feature updates using `onFeaturesUpdated`:
200+
201+
```ts
202+
// Register a callback for feature updates
203+
const unsubscribe = bucketClient.onFeaturesUpdated(() => {
204+
console.log("Features were updated");
205+
});
206+
207+
// Later, stop listening for updates
208+
unsubscribe();
209+
```
210+
211+
> [!NOTE]
212+
> Note that the callback may be called even if features haven't actually changed.
213+
175214
### Remote config
176215

177216
Similar to `isEnabled`, each feature has a `config` property. This configuration is managed from within Bucket.
@@ -225,7 +264,7 @@ const { isEnabled } = bucketClient.getFeature("voiceHuddle");
225264
await bucketClient.updateUser({ voiceHuddleOptIn: (!isEnabled).toString() });
226265
```
227266

228-
Note that user/company attributes are also stored remotely on the Bucket servers and will automatically be used to evaluate feature targeting if the page is refreshed.
267+
> [!NOTE] > `user`/`company` attributes are also stored remotely on the Bucket servers and will automatically be used to evaluate feature targeting if the page is refreshed.
229268
230269
### Qualitative feedback
231270

@@ -235,7 +274,8 @@ Bucket can collect qualitative feedback from your users in the form of a [Custom
235274

236275
The Bucket Browser SDK comes with automated feedback collection mode enabled by default, which lets the Bucket service ask your users for feedback for relevant features just after they've used them.
237276

238-
Note: To get started with automatic feedback collection, make sure you've set `user` in the `BucketClient` constructor.
277+
> [!NOTE]
278+
> To get started with automatic feedback collection, make sure you've set `user` in the `BucketClient` constructor.
239279
240280
Automated feedback surveys work even if you're not using the SDK to send events to Bucket.
241281
It works because the Bucket Browser SDK maintains a live connection to Bucket's servers and can automatically show a feedback prompt whenever the Bucket servers determines that an event should trigger a prompt - regardless of how this event is sent to Bucket.
@@ -262,7 +302,7 @@ bucketClient.feedback({
262302
});
263303
```
264304

265-
#### Bucket feedback API
305+
### Bucket feedback API
266306

267307
If you are not using the Bucket Browser SDK, you can still submit feedback using the HTTP API.
268308

@@ -274,9 +314,9 @@ The Bucket Browser SDK doesn't collect any metadata and HTTP IP addresses are _n
274314

275315
For tracking individual users, we recommend using something like database ID as userId, as it's unique and doesn't include any PII (personal identifiable information). If, however, you're using e.g. email address as userId, but prefer not to send any PII to Bucket, you can hash the sensitive data before sending it to Bucket:
276316

277-
```
317+
```ts
278318
import bucket from "@bucketco/browser-sdk";
279-
import { sha256 } from 'crypto-hash';
319+
import { sha256 } from "crypto-hash";
280320

281321
bucket.user(await sha256("john_doe"));
282322
```
@@ -290,28 +330,27 @@ The two cookies are:
290330
- `bucket-prompt-${userId}`: store the last automated feedback prompt message ID received to avoid repeating surveys
291331
- `bucket-token-${userId}`: caching a token used to connect to Bucket's live messaging infrastructure that is used to deliver automated feedback surveys in real time.
292332

293-
### Typescript
333+
### TypeScript
294334

295335
Types are bundled together with the library and exposed automatically when importing through a package manager.
296336

297337
## Content Security Policy (CSP)
298338

299339
If you are running with strict Content Security Policies active on your website, you will need to enable these directives in order to use the SDK:
300340

301-
| Directive | Values | Reason |
302-
| ----------- | ------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- |
303-
| connect-src | https://front.bucket.co | Basic functionality` |
304-
| connect-src | https://livemessaging.bucket.co | Server sent events for use in automated feedback surveys, which allows for automatically collecting feedback when a user used a feature. |
305-
| style-src | 'unsafe-inline' | The feedback UI is styled with inline styles. Not having this directive results unstyled HTML elements. |
341+
| Directive | Values | Reason |
342+
| ----------- | ------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------- |
343+
| connect-src | [https://front.bucket.co](https://front.bucket.co) | Basic functionality` |
344+
| connect-src | [https://livemessaging.bucket.co](https://livemessaging.bucket.co) | Server sent events for use in automated feedback surveys, which allows for automatically collecting feedback when a user used a feature. |
345+
| style-src | 'unsafe-inline' | The feedback UI is styled with inline styles. Not having this directive results unstyled HTML elements. |
306346

307347
If you are including the Bucket tracking SDK with a `<script>`-tag from `jsdelivr.net` you will also need:
308348

309-
| Directive | Values | Reason |
310-
| --------------- | ------------------------ | ------------------------------- |
311-
| script-src-elem | https://cdn.jsdelivr.net | Loads the Bucket SDK from a CDN |
312-
313-
# License
349+
| Directive | Values | Reason |
350+
| --------------- | ---------------------------------------------------- | ------------------------------- |
351+
| script-src-elem | [https://cdn.jsdelivr.net](https://cdn.jsdelivr.net) | Loads the Bucket SDK from a CDN |
314352

315-
MIT License
353+
## License
316354

317-
Copyright (c) 2025 Bucket ApS
355+
> MIT License
356+
> Copyright (c) 2025 Bucket ApS

packages/browser-sdk/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@bucketco/browser-sdk",
3-
"version": "3.0.0-alpha.2",
3+
"version": "3.0.0-alpha.3",
44
"packageManager": "yarn@4.1.1",
55
"license": "MIT",
66
"repository": {

packages/browser-sdk/src/client.ts

Lines changed: 28 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -707,25 +707,39 @@ export class BucketClient {
707707
}
708708
: { key: undefined, payload: undefined };
709709

710-
function sendCheckEvent() {
711-
fClient
712-
.sendCheckEvent({
713-
key,
714-
version: f?.targetingVersion,
715-
value,
716-
})
717-
.catch(() => {
718-
// ignore
719-
});
720-
}
721-
722710
return {
723711
get isEnabled() {
724-
sendCheckEvent();
712+
fClient
713+
.sendCheckEvent({
714+
action: "check",
715+
key,
716+
version: f?.targetingVersion,
717+
ruleEvaluationResults: f?.ruleEvaluationResults,
718+
missingContextFields: f?.missingContextFields,
719+
value,
720+
})
721+
.catch(() => {
722+
// ignore
723+
});
725724
return value;
726725
},
727726
get config() {
728-
sendCheckEvent();
727+
fClient
728+
.sendCheckEvent({
729+
action: "check-config",
730+
key,
731+
version: f?.config?.version,
732+
ruleEvaluationResults: f?.config?.ruleEvaluationResults,
733+
missingContextFields: f?.config?.missingContextFields,
734+
value: f?.config && {
735+
key: f.config.key,
736+
payload: f.config.payload,
737+
},
738+
})
739+
.catch(() => {
740+
// ignore
741+
});
742+
729743
return config;
730744
},
731745
track: () => this.track(key),

packages/browser-sdk/src/feature/featureCache.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,11 @@ export function parseAPIFeaturesResponse(
2727
typeof feature.isEnabled !== "boolean" ||
2828
feature.key !== key ||
2929
typeof feature.targetingVersion !== "number" ||
30-
(feature.config && typeof feature.config !== "object")
30+
(feature.config && typeof feature.config !== "object") ||
31+
(feature.missingContextFields &&
32+
!Array.isArray(feature.missingContextFields)) ||
33+
(feature.ruleEvaluationResults &&
34+
!Array.isArray(feature.ruleEvaluationResults))
3135
) {
3236
return;
3337
}
@@ -37,6 +41,8 @@ export function parseAPIFeaturesResponse(
3741
targetingVersion: feature.targetingVersion,
3842
key,
3943
config: feature.config,
44+
missingContextFields: feature.missingContextFields,
45+
ruleEvaluationResults: feature.ruleEvaluationResults,
4046
};
4147
}
4248

0 commit comments

Comments
 (0)