Skip to content
Draft
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
78ba573
feat(browser-sdk,react-sdk): extend the web SDKs to support the new `…
pavkam Jan 29, 2025
ecb8a4c
Forward `toolbar` config in react-sdk (#299)
laander Jan 29, 2025
f384857
Bump react + browser SDK to v3.0.0-alpha.2 (#300)
laander Jan 29, 2025
81f5ddc
feat(node-sdk): support for remote configuration (#295)
pavkam Jan 29, 2025
ee38109
chore(node-sdk): bump version (#302)
pavkam Feb 3, 2025
eca331d
chore: allow publishing of non `main` branches` (#304)
roncohen Feb 4, 2025
ca22529
fix: docs ci script (#298)
matus-vacula Jan 28, 2025
2bb5088
chore(deps): bump next from 14.2.15 to 14.2.21 in /packages/react-sdk…
dependabot[bot] Jan 28, 2025
e81ec95
Bump year (#281)
makwarth Jan 28, 2025
f13880a
feat(browser-sdk,node-sdk): add avatar support for user and company c…
pavkam Jan 28, 2025
15119f8
chore(deps): bump nanoid from 3.3.7 to 3.3.8 in /packages/node-sdk/ex…
dependabot[bot] Jan 20, 2025
579eb1c
fix(node-sdk): ensure timers don't keep process alive (#303)
roncohen Feb 3, 2025
d9381e4
chore(browser-sdk,node-sdk): version 2.5.2 (#306)
roncohen Feb 4, 2025
6b1800a
Remove deprecated and flatten options (#308)
roncohen Feb 6, 2025
de93d76
feat(node-sdk): allow passing `meta` to `getFeature(s)` (#312)
pavkam Feb 9, 2025
bae0be7
chore(deps-dev): bump vitest from 2.0.4 to 2.1.9 (#314)
dependabot[bot] Feb 10, 2025
2fcef62
chore(deps-dev): bump vitest from 2.1.4 to 2.1.9 in /packages/node-sd…
dependabot[bot] Feb 10, 2025
f366edb
feat: use remote feature list instead of locally defined list (#315)
roncohen Feb 12, 2025
87e78a5
Merge branch 'main' into browser-react-3.0.alpha
pavkam Feb 12, 2025
28ffa72
chore(deps): upgrade @bucketco/browser-sdk to 3.0.0-alpha.2
pavkam Feb 12, 2025
c463ebf
feat(browser-sdk,react-sdk): check events (#316)
pavkam Feb 12, 2025
f1ef0f7
feat(openfeature-web-provider): improve flag resolution and context h…
pavkam Feb 12, 2025
f4e579c
chore(openfeature-browser-provider): bump version to 0.4.0-alpha.1
pavkam Feb 12, 2025
ab5ba42
refactor(react-sdk): simplify BucketProvider props and improve type d…
pavkam Feb 13, 2025
a92f028
chore(browser-sdk,react-sdk): bump SDK versions to 3.0.0-alpha.4 and …
pavkam Feb 13, 2025
adf6fe8
fix(browser-sdk,react-sdk): remove featureId option for requestFeedba…
roncohen Feb 13, 2025
bff834c
fix(react-sdk): throw error on missing provider (#322)
roncohen Feb 13, 2025
bc0dbde
fix(browser-sdk): remove featureDefinition list (#323)
roncohen Feb 13, 2025
c6fa42a
fix(react-sdk): improve type definitions for useFeature hook (#324)
pavkam Feb 17, 2025
1e21da8
feat(browser-sdk): Event listeners (#325)
roncohen Feb 17, 2025
7340206
feat: hooks
roncohen Feb 15, 2025
672b6c0
more hooks
roncohen Feb 15, 2025
568c5f9
missing files
roncohen Feb 15, 2025
c769a8b
docs
roncohen Feb 15, 2025
f19dd98
update tests
roncohen Feb 15, 2025
b622416
export hook types, better examples etc.
roncohen Feb 16, 2025
f7e2899
refactored hooks system
roncohen Feb 16, 2025
1e3f599
updated hooks
roncohen Feb 16, 2025
b2fb888
missed a type import
roncohen Feb 16, 2025
869c1a1
missed the example index.html
roncohen Feb 16, 2025
fad9666
one more
roncohen Feb 16, 2025
7a84fe4
update react sdk too
roncohen Feb 16, 2025
f6e592c
fix test
roncohen Feb 17, 2025
121a36e
feat: in use
roncohen Feb 15, 2025
9d09ff4
working on tests
roncohen Feb 15, 2025
a5949f2
wip
roncohen Feb 16, 2025
2de4ab2
introduce: in-use API for use with React
roncohen Feb 17, 2025
32cafbf
fix tests
roncohen Feb 17, 2025
e1079a3
Merge branch 'browser-react-3.0.alpha' into in-use
roncohen Feb 17, 2025
7e020bf
fix: docs
roncohen Feb 17, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
feat(browser-sdk,react-sdk): check events (#316)
  • Loading branch information
pavkam authored Feb 12, 2025
commit c463ebf2fc86e57a0b3f4c93d9a7666fc61e635b
43 changes: 39 additions & 4 deletions docs.sh
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,46 @@ typedoc
# We can fix this by removing the number at the end of the anchor.
SEDCOMMAND='s/globals.md#(.*)-[0-9]+/globals.md#\1/g'

FILES=$(find dist/docs/@bucketco -name "globals.md")
# Find all markdown files including globals.md
FILES=$(find dist/docs/@bucketco -name "*.md")

echo "Processing markdown files..."
for file in $FILES
do
sed -r $SEDCOMMAND $file > $file.fixed
rm $file
mv $file.fixed $file
echo "Processing $file..."

# Fix anchor links in globals.md files
if [[ "$file" == *"globals.md" ]]; then
sed -r "$SEDCOMMAND" "$file" > "$file.fixed"
rm "$file"
mv "$file.fixed" "$file"
fi

# Create a temporary file for processing
tmp_file="${file}.tmp"

# Process NOTE blocks - handle multi-line
awk '
BEGIN { in_block = 0; content = ""; }
/^> \[!NOTE\]/ { in_block = 1; print "{% hint style=\"info\" %}"; next; }
/^> \[!TIP\]/ { in_block = 1; print "{% hint style=\"success\" %}"; next; }
/^> \[!IMPORTANT\]/ { in_block = 1; print "{% hint style=\"warning\" %}"; next; }
/^> \[!WARNING\]/ { in_block = 1; print "{% hint style=\"warning\" %}"; next; }
/^> \[!CAUTION\]/ { in_block = 1; print "{% hint style=\"danger\" %}"; next; }
in_block && /^>/ {
content = content substr($0, 3) "\n";
next;
}
in_block && !/^>/ {
printf "%s", content;
print "{% endhint %}";
in_block = 0;
content = "";
}
!in_block { print; }
' "$file" > "$tmp_file"

mv "$tmp_file" "$file"
done

echo "Processing complete!"
12 changes: 5 additions & 7 deletions packages/browser-sdk/FEEDBACK.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ the viewport, displayed in English, and with a [light-mode theme](#custom-stylin

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

```javascript
```typescript
const bucket = new BucketClient({
publishableKey: "bucket-publishable-key",
user: { id: "42" },
Expand All @@ -39,11 +39,9 @@ const bucket = new BucketClient({

See also:

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

## Automated feedback surveys

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

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

```javascript
```typescript
const bucket = new BucketClient({
publishableKey: "bucket-publishable-key",
user: { id: "42" },
Expand Down
99 changes: 69 additions & 30 deletions packages/browser-sdk/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ First find your `publishableKey` under [environment settings](https://app.bucket

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

A. Import module
A. Import module:

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

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

Supply these to the constructor call:

```ts
```typescript
type Configuration = {
logger: console; // by default only logs warn/error, by passing `console` you'll log everything
apiBaseUrl?: "https://front.bucket.co";
sseBaseUrl?: "https://livemessaging.bucket.co";
feedback?: undefined; // See FEEDBACK.md
enableTracking?: true; // set to `false` to stop sending track events and user/company updates to Bucket servers. Useful when you're impersonating a user.
enableTracking?: true; // set to `false` to stop sending track events and user/company updates to Bucket servers. Useful when you're impersonating a user
featureOptions?: {
fallbackFeatures?: string[]; // Enable these features if unable to contact bucket.co
timeoutMs?: number; // Timeout for fetching features
staleWhileRevalidate?: boolean; // Revalidate in the background when cached features turn stale to avoid latency in the UI
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.
expireTimeMs?: number; // In case we're unable to fetch features from Bucket, cached/stale features will be used instead until they expire after `expireTimeMs`.
fallbackFeatures?:
| string[]
| 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
timeoutMs?: number; // Timeout for fetching features (default: 5000ms)
staleWhileRevalidate?: boolean; // Revalidate in the background when cached features turn stale to avoid latency in the UI (default: false)
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
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
};
};
```
Expand All @@ -120,9 +122,9 @@ In addition to the `id`, you must also supply anything additional that you want
Attributes cannot be nested (multiple levels) and must be either strings, integers or booleans.
Some attributes are special and used in Bucket UI:

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

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

### Feature Overrides

You can override feature flags locally for testing purposes using `setFeatureOverride`:

```ts
// Override a feature to be enabled
bucketClient.setFeatureOverride("huddle", true);

// Override a feature to be disabled
bucketClient.setFeatureOverride("huddle", false);

// Remove the override
bucketClient.setFeatureOverride("huddle", null);

// Get current override value
const override = bucketClient.getFeatureOverride("huddle"); // returns boolean | null
```

Feature overrides are persisted in `localStorage` and will be restored when the page is reloaded.

### Feature Updates

You can listen for feature updates using `onFeaturesUpdated`:

```ts
// Register a callback for feature updates
const unsubscribe = bucketClient.onFeaturesUpdated(() => {
console.log("Features were updated");
});

// Later, stop listening for updates
unsubscribe();
```

> [!NOTE]
> Note that the callback may be called even if features haven't actually changed.

### Remote config

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

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.
> [!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.

### Qualitative feedback

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

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.

Note: To get started with automatic feedback collection, make sure you've set `user` in the `BucketClient` constructor.
> [!NOTE]
> To get started with automatic feedback collection, make sure you've set `user` in the `BucketClient` constructor.

Automated feedback surveys work even if you're not using the SDK to send events to Bucket.
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.
Expand All @@ -262,7 +302,7 @@ bucketClient.feedback({
});
```

#### Bucket feedback API
### Bucket feedback API

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

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

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:

```
```ts
import bucket from "@bucketco/browser-sdk";
import { sha256 } from 'crypto-hash';
import { sha256 } from "crypto-hash";

bucket.user(await sha256("john_doe"));
```
Expand All @@ -290,28 +330,27 @@ The two cookies are:
- `bucket-prompt-${userId}`: store the last automated feedback prompt message ID received to avoid repeating surveys
- `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.

### Typescript
### TypeScript

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

## Content Security Policy (CSP)

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:

| Directive | Values | Reason |
| ----------- | ------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- |
| connect-src | https://front.bucket.co | Basic functionality` |
| 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. |
| style-src | 'unsafe-inline' | The feedback UI is styled with inline styles. Not having this directive results unstyled HTML elements. |
| Directive | Values | Reason |
| ----------- | ------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------- |
| connect-src | [https://front.bucket.co](https://front.bucket.co) | Basic functionality` |
| 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. |
| style-src | 'unsafe-inline' | The feedback UI is styled with inline styles. Not having this directive results unstyled HTML elements. |

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

| Directive | Values | Reason |
| --------------- | ------------------------ | ------------------------------- |
| script-src-elem | https://cdn.jsdelivr.net | Loads the Bucket SDK from a CDN |

# License
| Directive | Values | Reason |
| --------------- | ---------------------------------------------------- | ------------------------------- |
| script-src-elem | [https://cdn.jsdelivr.net](https://cdn.jsdelivr.net) | Loads the Bucket SDK from a CDN |

MIT License
## License

Copyright (c) 2025 Bucket ApS
> MIT License
> Copyright (c) 2025 Bucket ApS
2 changes: 1 addition & 1 deletion packages/browser-sdk/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@bucketco/browser-sdk",
"version": "3.0.0-alpha.2",
"version": "3.0.0-alpha.3",
"packageManager": "[email protected]",
"license": "MIT",
"repository": {
Expand Down
42 changes: 28 additions & 14 deletions packages/browser-sdk/src/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -707,25 +707,39 @@ export class BucketClient {
}
: { key: undefined, payload: undefined };

function sendCheckEvent() {
fClient
.sendCheckEvent({
key,
version: f?.targetingVersion,
value,
})
.catch(() => {
// ignore
});
}

return {
get isEnabled() {
sendCheckEvent();
fClient
.sendCheckEvent({
action: "check",
key,
version: f?.targetingVersion,
ruleEvaluationResults: f?.ruleEvaluationResults,
missingContextFields: f?.missingContextFields,
value,
})
.catch(() => {
// ignore
});
return value;
},
get config() {
sendCheckEvent();
fClient
.sendCheckEvent({
action: "check-config",
key,
version: f?.config?.version,
ruleEvaluationResults: f?.config?.ruleEvaluationResults,
missingContextFields: f?.config?.missingContextFields,
value: f?.config && {
key: f.config.key,
payload: f.config.payload,
},
})
.catch(() => {
// ignore
});

return config;
},
track: () => this.track(key),
Expand Down
8 changes: 7 additions & 1 deletion packages/browser-sdk/src/feature/featureCache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,11 @@ export function parseAPIFeaturesResponse(
typeof feature.isEnabled !== "boolean" ||
feature.key !== key ||
typeof feature.targetingVersion !== "number" ||
(feature.config && typeof feature.config !== "object")
(feature.config && typeof feature.config !== "object") ||
(feature.missingContextFields &&
!Array.isArray(feature.missingContextFields)) ||
(feature.ruleEvaluationResults &&
!Array.isArray(feature.ruleEvaluationResults))
) {
return;
}
Expand All @@ -37,6 +41,8 @@ export function parseAPIFeaturesResponse(
targetingVersion: feature.targetingVersion,
key,
config: feature.config,
missingContextFields: feature.missingContextFields,
ruleEvaluationResults: feature.ruleEvaluationResults,
};
}

Expand Down
Loading