Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
3 changes: 1 addition & 2 deletions packages/browser-sdk/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -274,8 +274,7 @@ See details in [Feedback HTTP API](https://docs.bucket.co/reference/http-trackin

Event listeners allow for capturing various events occurring in the `BucketClient`. This is useful to build integrations with other system or for various debugging purposes. There are 5 kinds of events:

- `configCheck`: Your code used a feature config
- `enabledCheck`: Your code checked whether a specific feature should be enabled
- `check`: Your code used `isEnabled` or `config` for a feature
- `featuresUpdated`: Features were updated. Either because they were loaded as part of initialization or because the user/company updated
- `user`: User information updated (similar to the `identify` call used in tracking terminology)
- `company`: Company information updated (sometimes to the `group` call used in tracking terminology)
Expand Down
2 changes: 1 addition & 1 deletion packages/browser-sdk/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@
document.getElementById("loading").style.display = "none";
});

bucket.on("enabledCheck", (check) =>
bucket.on("check", (check) =>
console.log(`Check event for ${check.key}`),
);

Expand Down
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.1.0",
"version": "3.1.1",
"packageManager": "yarn@4.1.1",
"license": "MIT",
"repository": {
Expand Down
18 changes: 10 additions & 8 deletions packages/browser-sdk/src/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -480,30 +480,31 @@ export class BucketClient {
}

/**
* Add a hook to the client.
* Add an event listener
*
* @param hook Hook to add.
* @param type Type of events to listen for
* @param handler The function to call when the event is triggered.
* @returns A function to remove the hook.
*/
on<THookType extends keyof HookArgs>(
type: THookType,
handler: (args0: HookArgs[THookType]) => void,
) {
this.hooks.addHook(type, handler);
return () => this.hooks.removeHook(type, handler);
return this.hooks.addHook(type, handler);
}

/**
* Remove a hook from the client.
* Remove an event listener
*
* @param type Type of event to remove.
* @param handler The same function that was passed to `on`.
*
* @param hook Hook to add.
* @returns A function to remove the hook.
*/
off<THookType extends keyof HookArgs>(
type: THookType,
handler: (args0: HookArgs[THookType]) => void,
) {
return this.hooks.removeHook(type, handler);
this.hooks.removeHook(type, handler);
}

/**
Expand Down Expand Up @@ -799,6 +800,7 @@ export class BucketClient {
checkEvent.action == "check-config" ? "configCheck" : "enabledCheck",
checkEvent,
);
this.hooks.trigger("check", checkEvent);
});
}

Expand Down
11 changes: 11 additions & 0 deletions packages/browser-sdk/src/hooksManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,17 @@ import { CompanyContext, UserContext } from "./context";
* @internal
*/
export interface HookArgs {
/**
* Deprecated: Use `check` instead.
* @deprecated
*/
configCheck: CheckEvent;
/**
* Deprecated: Use `check` instead.
* @deprecated
*/
enabledCheck: CheckEvent;
check: CheckEvent;
featuresUpdated: RawFeatures;
user: UserContext;
company: CompanyContext;
Expand All @@ -28,13 +37,15 @@ export class HooksManager {
private hooks: {
enabledCheck: ((arg0: CheckEvent) => void)[];
configCheck: ((arg0: CheckEvent) => void)[];
check: ((arg0: CheckEvent) => void)[];
featuresUpdated: ((arg0: RawFeatures) => void)[];
user: ((arg0: UserContext) => void)[];
company: ((arg0: CompanyContext) => void)[];
track: ((arg0: TrackEvent) => void)[];
} = {
enabledCheck: [],
configCheck: [],
check: [],
featuresUpdated: [],
user: [],
company: [],
Expand Down
9 changes: 9 additions & 0 deletions packages/browser-sdk/test/client.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,13 +80,15 @@ describe("BucketClient", () => {
const trackHook = vi.fn();
const userHook = vi.fn();
const companyHook = vi.fn();
const checkHook = vi.fn();
const checkHookIsEnabled = vi.fn();
const checkHookConfig = vi.fn();
const featuresUpdated = vi.fn();

client.on("track", trackHook);
client.on("user", userHook);
client.on("company", companyHook);
client.on("check", checkHook);
client.on("configCheck", checkHookConfig);
client.on("enabledCheck", checkHookIsEnabled);
client.on("featuresUpdated", featuresUpdated);
Expand All @@ -108,10 +110,14 @@ describe("BucketClient", () => {
// eslint-disable-next-line @typescript-eslint/no-unused-expressions -- special getter triggering event
client.getFeature("featureA").isEnabled;
expect(checkHookIsEnabled).toHaveBeenCalled();
expect(checkHook).toHaveBeenCalled();

checkHook.mockReset();

// eslint-disable-next-line @typescript-eslint/no-unused-expressions -- special getter triggering event
client.getFeature("featureA").config;
expect(checkHookConfig).toHaveBeenCalled();
expect(checkHook).toHaveBeenCalled();

expect(featuresUpdated).not.toHaveBeenCalled();
await client.updateOtherContext({ key: "value" });
Expand All @@ -121,6 +127,7 @@ describe("BucketClient", () => {
client.off("track", trackHook);
client.off("user", userHook);
client.off("company", companyHook);
client.off("check", checkHook);
client.off("configCheck", checkHookConfig);
client.off("enabledCheck", checkHookIsEnabled);
client.off("featuresUpdated", featuresUpdated);
Expand All @@ -129,6 +136,7 @@ describe("BucketClient", () => {
trackHook.mockReset();
userHook.mockReset();
companyHook.mockReset();
checkHook.mockReset();
checkHookIsEnabled.mockReset();
checkHookConfig.mockReset();
featuresUpdated.mockReset();
Expand All @@ -147,6 +155,7 @@ describe("BucketClient", () => {
expect(trackHook).not.toHaveBeenCalled();
expect(userHook).not.toHaveBeenCalled();
expect(companyHook).not.toHaveBeenCalled();
expect(checkHook).not.toHaveBeenCalled();
expect(checkHookIsEnabled).not.toHaveBeenCalled();
expect(checkHookConfig).not.toHaveBeenCalled();
expect(featuresUpdated).not.toHaveBeenCalled();
Expand Down
46 changes: 30 additions & 16 deletions packages/browser-sdk/test/hooksManager.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,21 @@ describe("HookManager", () => {
hookManager = new HooksManager();
});

it("should add and trigger check-is-enabled hooks", () => {
it("should add and trigger `check` hooks", () => {
const callback = vi.fn();
hookManager.addHook("check", callback);

const checkEvent: CheckEvent = {
action: "check-is-enabled",
key: "test-key",
value: true,
};
hookManager.trigger("check", checkEvent);

expect(callback).toHaveBeenCalledWith(checkEvent);
});

it("should add and trigger `enabledCheck` hooks", () => {
const callback = vi.fn();
hookManager.addHook("enabledCheck", callback);

Expand All @@ -25,7 +39,7 @@ describe("HookManager", () => {
expect(callback).toHaveBeenCalledWith(checkEvent);
});

it("should add and trigger configCheck hooks", () => {
it("should add and trigger `configCheck` hooks", () => {
const callback = vi.fn();
hookManager.addHook("configCheck", callback);

Expand All @@ -39,7 +53,7 @@ describe("HookManager", () => {
expect(callback).toHaveBeenCalledWith(checkEvent);
});

it("should add and trigger features-updated hooks", () => {
it("should add and trigger `featuresUpdated` hooks", () => {
const callback = vi.fn();
hookManager.addHook("featuresUpdated", callback);

Expand All @@ -51,7 +65,7 @@ describe("HookManager", () => {
expect(callback).toHaveBeenCalledWith(features);
});

it("should add and trigger track hooks", () => {
it("should add and trigger `track` hooks", () => {
const callback = vi.fn();
const user: UserContext = { id: "user-id", name: "user-name" };
const company: CompanyContext = { id: "company-id", name: "company-name" };
Expand All @@ -69,7 +83,7 @@ describe("HookManager", () => {
});
});

it("should add and trigger user hooks", () => {
it("should add and trigger `user` hooks", () => {
const callback = vi.fn();

hookManager.addHook("user", callback);
Expand All @@ -80,7 +94,7 @@ describe("HookManager", () => {
expect(callback).toHaveBeenCalledWith(user);
});

it("should add and trigger company hooks", () => {
it("should add and trigger `company` hooks", () => {
const callback = vi.fn();
hookManager.addHook("company", callback);

Expand All @@ -94,15 +108,15 @@ describe("HookManager", () => {
const callback1 = vi.fn();
const callback2 = vi.fn();

hookManager.addHook("enabledCheck", callback1);
hookManager.addHook("enabledCheck", callback2);
hookManager.addHook("check", callback1);
hookManager.addHook("check", callback2);

const checkEvent: CheckEvent = {
action: "check-is-enabled",
key: "test-key",
value: true,
};
hookManager.trigger("enabledCheck", checkEvent);
hookManager.trigger("check", checkEvent);

expect(callback1).toHaveBeenCalledWith(checkEvent);
expect(callback2).toHaveBeenCalledWith(checkEvent);
Expand All @@ -112,16 +126,16 @@ describe("HookManager", () => {
const callback1 = vi.fn();
const callback2 = vi.fn();

hookManager.addHook("enabledCheck", callback1);
hookManager.addHook("enabledCheck", callback2);
hookManager.removeHook("enabledCheck", callback1);
hookManager.addHook("check", callback1);
hookManager.addHook("check", callback2);
hookManager.removeHook("check", callback1);

const checkEvent: CheckEvent = {
action: "check-is-enabled",
key: "test-key",
value: true,
};
hookManager.trigger("enabledCheck", checkEvent);
hookManager.trigger("check", checkEvent);

expect(callback1).not.toHaveBeenCalled();
expect(callback2).toHaveBeenCalledWith(checkEvent);
Expand All @@ -131,16 +145,16 @@ describe("HookManager", () => {
const callback1 = vi.fn();
const callback2 = vi.fn();

const removeHook1 = hookManager.addHook("enabledCheck", callback1);
hookManager.addHook("enabledCheck", callback2);
const removeHook1 = hookManager.addHook("check", callback1);
hookManager.addHook("check", callback2);
removeHook1();

const checkEvent: CheckEvent = {
action: "check-is-enabled",
key: "test-key",
value: true,
};
hookManager.trigger("enabledCheck", checkEvent);
hookManager.trigger("check", checkEvent);

expect(callback1).not.toHaveBeenCalled();
expect(callback2).toHaveBeenCalledWith(checkEvent);
Expand Down
4 changes: 2 additions & 2 deletions packages/openfeature-browser-provider/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,9 @@
"types": "./dist/index.d.ts",
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/bucket-openfeature-browser-provider.mjs",
"require": "./dist/bucket-openfeature-browser-provider.umd.js",
"types": "./dist/index.d.ts"
"require": "./dist/bucket-openfeature-browser-provider.umd.js"
}
},
"dependencies": {
Expand Down
2 changes: 1 addition & 1 deletion packages/react-sdk/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -409,7 +409,7 @@ function LoggingWrapper({ children }: { children: ReactNode }) {
const client = useClient();

useEffect(() => {
client.on("enabledCheck", (evt) => {
client.on("check", (evt) => {
console.log(`The feature ${evt.key} is ${evt.value} for user.`);
});
}, [client]);
Expand Down
4 changes: 2 additions & 2 deletions packages/react-sdk/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@bucketco/react-sdk",
"version": "3.1.0",
"version": "3.1.1",
"license": "MIT",
"repository": {
"type": "git",
Expand Down Expand Up @@ -34,7 +34,7 @@
}
},
"dependencies": {
"@bucketco/browser-sdk": "3.1.0",
"@bucketco/browser-sdk": "3.1.1",
"canonical-json": "^0.0.4",
"rollup": "^4.2.0"
},
Expand Down
4 changes: 2 additions & 2 deletions packages/react-sdk/src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -362,8 +362,8 @@ export function useUpdateOtherContext() {
* ```ts
* const client = useClient();
* useEffect(() => {
* return client?.on("enabledCheck", () => {
* console.log("enabledCheck hook called");
* return client?.on("check", () => {
* console.log("check hook called");
* });
* }, [client]);
* ```
Expand Down
16 changes: 14 additions & 2 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -417,7 +417,19 @@ __metadata:
languageName: node
linkType: hard

"@bucketco/browser-sdk@npm:3.1.0, @bucketco/browser-sdk@workspace:packages/browser-sdk":
"@bucketco/browser-sdk@npm:3.1.0":
version: 3.1.0
resolution: "@bucketco/browser-sdk@npm:3.1.0"
dependencies:
"@floating-ui/dom": "npm:^1.6.8"
canonical-json: "npm:^0.0.4"
js-cookie: "npm:^3.0.5"
preact: "npm:^10.22.1"
checksum: 10c0/a53174fa826b990087ef434f3d9cb09dbd80b707783fa208d872f499b42e7dcebe326ff78225aa621670138c0059d60361e68903fd129dd25af1ed0d85a1428d
languageName: node
linkType: hard

"@bucketco/browser-sdk@npm:3.1.1, @bucketco/browser-sdk@workspace:packages/browser-sdk":
version: 0.0.0-use.local
resolution: "@bucketco/browser-sdk@workspace:packages/browser-sdk"
dependencies:
Expand Down Expand Up @@ -580,7 +592,7 @@ __metadata:
version: 0.0.0-use.local
resolution: "@bucketco/react-sdk@workspace:packages/react-sdk"
dependencies:
"@bucketco/browser-sdk": "npm:3.1.0"
"@bucketco/browser-sdk": "npm:3.1.1"
"@bucketco/eslint-config": "workspace:^"
"@bucketco/tsconfig": "workspace:^"
"@testing-library/react": "npm:^15.0.7"
Expand Down