@bucketco/node-sdk
Advanced tools
Comparing version 1.1.1 to 1.2.0
{ | ||
"name": "@bucketco/node-sdk", | ||
"version": "1.1.1", | ||
"version": "1.2.0", | ||
"license": "MIT", | ||
@@ -5,0 +5,0 @@ "repository": { |
@@ -346,10 +346,3 @@ "use strict"; | ||
} | ||
/** | ||
* Gets the evaluated feature for the current context which includes the user, company, and custom context. | ||
* | ||
* @returns The evaluated features. | ||
* @remarks | ||
* Call `initialize` before calling this method to ensure the feature definitions are cached, no features will be returned otherwise. | ||
**/ | ||
getFeatures(context) { | ||
_getFeatures(context) { | ||
var _a, _b; | ||
@@ -388,27 +381,66 @@ const featureDefinitions = this.getFeaturesCache().get(); | ||
} | ||
return (0, utils_1.maskedProxy)(evaluatedFeatures, (features, key) => { | ||
var _a; | ||
void this.sendFeatureEvent({ | ||
action: "check", | ||
key: key, | ||
targetingVersion: features[key].targetingVersion, | ||
evalResult: features[key].isEnabled, | ||
}).catch((err) => { | ||
var _a; | ||
(_a = this._config.logger) === null || _a === void 0 ? void 0 : _a.error(`failed to send check event for "${key}": ${err}`, err); | ||
}); | ||
const feature = features[key]; | ||
return { | ||
key, | ||
isEnabled: (_a = feature === null || feature === void 0 ? void 0 : feature.isEnabled) !== null && _a !== void 0 ? _a : false, | ||
track: () => __awaiter(this, void 0, void 0, function* () { | ||
var _a, _b, _c; | ||
const userId = (_a = context.user) === null || _a === void 0 ? void 0 : _a.id; | ||
if (!userId) { | ||
(_b = this._config.logger) === null || _b === void 0 ? void 0 : _b.warn("feature.track(): no user set, cannot track event"); | ||
return; | ||
} | ||
yield this.track(userId, key, { companyId: (_c = context.company) === null || _c === void 0 ? void 0 : _c.id }); | ||
}), | ||
}; | ||
return evaluatedFeatures; | ||
} | ||
_wrapRawFeature(context, { key, isEnabled, targetingVersion }) { | ||
// eslint-disable-next-line @typescript-eslint/no-this-alias | ||
const client = this; | ||
return { | ||
get isEnabled() { | ||
void client | ||
.sendFeatureEvent({ | ||
action: "check", | ||
key, | ||
targetingVersion, | ||
evalResult: isEnabled, | ||
}) | ||
.catch((err) => { | ||
var _a; | ||
(_a = client._config.logger) === null || _a === void 0 ? void 0 : _a.error(`failed to send check event for "${key}": ${err}`, err); | ||
}); | ||
return isEnabled; | ||
}, | ||
key, | ||
track: () => __awaiter(this, void 0, void 0, function* () { | ||
var _a, _b, _c; | ||
const userId = (_a = context.user) === null || _a === void 0 ? void 0 : _a.id; | ||
if (!userId) { | ||
(_b = this._config.logger) === null || _b === void 0 ? void 0 : _b.warn("feature.track(): no user set, cannot track event"); | ||
return; | ||
} | ||
yield this.track(userId, key, { | ||
companyId: (_c = context.company) === null || _c === void 0 ? void 0 : _c.id, | ||
}); | ||
}), | ||
}; | ||
} | ||
/** | ||
* Gets the evaluated feature for the current context which includes the user, company, and custom context. | ||
* | ||
* @returns The evaluated features. | ||
* @remarks | ||
* Call `initialize` before calling this method to ensure the feature definitions are cached, no features will be returned otherwise. | ||
**/ | ||
getFeatures(context) { | ||
const features = this._getFeatures(context); | ||
return Object.fromEntries(Object.entries(features).map(([k, v]) => [ | ||
k, | ||
this._wrapRawFeature(context, v), | ||
])); | ||
} | ||
/** | ||
* Gets the evaluated feature for the current context which includes the user, company, and custom context. | ||
* Using the `isEnabled` property sends a `check` event to Bucket. | ||
* | ||
* @returns The evaluated features. | ||
* @remarks | ||
* Call `initialize` before calling this method to ensure the feature definitions are cached, no features will be returned otherwise. | ||
**/ | ||
getFeature(context, key) { | ||
var _a; | ||
const features = this._getFeatures(context); | ||
const feature = features[key]; | ||
return this._wrapRawFeature(context, { | ||
key, | ||
isEnabled: (_a = feature === null || feature === void 0 ? void 0 : feature.isEnabled) !== null && _a !== void 0 ? _a : false, | ||
targetingVersion: feature === null || feature === void 0 ? void 0 : feature.targetingVersion, | ||
}); | ||
@@ -460,2 +492,3 @@ } | ||
* Get features for the user/company/other context bound to this client. | ||
* Meant for use in serialization of features for transferring to the client-side/browser. | ||
* | ||
@@ -468,2 +501,11 @@ * @returns Features for the given user/company and whether each one is enabled or not | ||
/** | ||
* Get a specific feature for the user/company/other context bound to this client. | ||
* Using the `isEnabled` property sends a `check` event to Bucket. | ||
* | ||
* @returns Features for the given user/company and whether each one is enabled or not | ||
*/ | ||
getFeature(key) { | ||
return this._client.getFeature(this._context, key); | ||
} | ||
/** | ||
* Track an event in Bucket. | ||
@@ -504,4 +546,12 @@ * | ||
} | ||
/** | ||
* Flushes the batch buffer. | ||
*/ | ||
flush() { | ||
return __awaiter(this, void 0, void 0, function* () { | ||
yield this._client.flush(); | ||
}); | ||
} | ||
} | ||
exports.BoundBucketClient = BoundBucketClient; | ||
//# sourceMappingURL=client.js.map |
@@ -5,3 +5,2 @@ "use strict"; | ||
exports.checkWithinAllottedTimeWindow = checkWithinAllottedTimeWindow; | ||
exports.maskedProxy = maskedProxy; | ||
exports.ok = ok; | ||
@@ -31,27 +30,2 @@ exports.isObject = isObject; | ||
/** | ||
* Create a read-only masked proxy for the given object that notifies a | ||
* callback when a property is accessed. The callback is then responsible | ||
* for returning the masked value for the given property. | ||
* | ||
* @param obj - The object to proxy. | ||
* @param callback - The callback to notify. | ||
* | ||
* @returns The proxy object. | ||
**/ | ||
function maskedProxy(obj, valueFunc) { | ||
return new Proxy(obj, { | ||
get(target, prop) { | ||
const val = target[prop]; | ||
if (val !== undefined) { | ||
return valueFunc(target, prop); | ||
} | ||
return undefined; | ||
}, | ||
set(_target, prop, _value) { | ||
console.error(`Cannot modify property '${String(prop)}' of the object.`); | ||
return true; | ||
}, | ||
}); | ||
} | ||
/** | ||
* Assert that the given condition is `true`. | ||
@@ -58,0 +32,0 @@ * |
@@ -1,2 +0,2 @@ | ||
import { ClientOptions, Context, Logger, TrackOptions, TypedFeatures } from "./types"; | ||
import { ClientOptions, Context, Feature, Logger, TrackOptions, TypedFeatures } from "./types"; | ||
/** | ||
@@ -134,2 +134,4 @@ * The SDK client. | ||
flush(): Promise<void>; | ||
private _getFeatures; | ||
private _wrapRawFeature; | ||
/** | ||
@@ -143,2 +145,11 @@ * Gets the evaluated feature for the current context which includes the user, company, and custom context. | ||
getFeatures(context: Context): TypedFeatures; | ||
/** | ||
* Gets the evaluated feature for the current context which includes the user, company, and custom context. | ||
* Using the `isEnabled` property sends a `check` event to Bucket. | ||
* | ||
* @returns The evaluated features. | ||
* @remarks | ||
* Call `initialize` before calling this method to ensure the feature definitions are cached, no features will be returned otherwise. | ||
**/ | ||
getFeature(context: Context, key: keyof TypedFeatures): Feature; | ||
} | ||
@@ -179,7 +190,15 @@ /** | ||
* Get features for the user/company/other context bound to this client. | ||
* Meant for use in serialization of features for transferring to the client-side/browser. | ||
* | ||
* @returns Features for the given user/company and whether each one is enabled or not | ||
*/ | ||
getFeatures(): Record<string, import("./types").Feature>; | ||
getFeatures(): Record<string, Feature>; | ||
/** | ||
* Get a specific feature for the user/company/other context bound to this client. | ||
* Using the `isEnabled` property sends a `check` event to Bucket. | ||
* | ||
* @returns Features for the given user/company and whether each one is enabled or not | ||
*/ | ||
getFeature(key: keyof TypedFeatures): Feature; | ||
/** | ||
* Track an event in Bucket. | ||
@@ -205,2 +224,6 @@ * | ||
bindClient({ user, company, other }: Context): BoundBucketClient; | ||
/** | ||
* Flushes the batch buffer. | ||
*/ | ||
flush(): Promise<void>; | ||
} |
export { BoundBucketClient, BucketClient } from "./client"; | ||
export type { Attributes, ClientOptions, Context, Features, HttpClient, Logger, TrackingMeta, } from "./types"; | ||
export type { Attributes, ClientOptions, Context, Features, HttpClient, Logger, RawFeature, TrackingMeta, } from "./types"; |
@@ -51,3 +51,3 @@ import { FeatureData } from "@bucketco/flag-evaluation"; | ||
*/ | ||
export interface InternalFeature { | ||
export interface RawFeature { | ||
/** | ||
@@ -54,0 +54,0 @@ * The key of the feature. |
@@ -5,13 +5,2 @@ import { Logger } from "./types"; | ||
/** | ||
* Create a read-only masked proxy for the given object that notifies a | ||
* callback when a property is accessed. The callback is then responsible | ||
* for returning the masked value for the given property. | ||
* | ||
* @param obj - The object to proxy. | ||
* @param callback - The callback to notify. | ||
* | ||
* @returns The proxy object. | ||
**/ | ||
export declare function maskedProxy<T extends object, K extends keyof T, O>(obj: T, valueFunc: (target: T, prop: K) => O): Readonly<Record<K, O>>; | ||
/** | ||
* Assert that the given condition is `true`. | ||
@@ -18,0 +7,0 @@ * |
{ | ||
"name": "@bucketco/node-sdk", | ||
"version": "1.1.1", | ||
"version": "1.2.0", | ||
"license": "MIT", | ||
@@ -49,3 +49,3 @@ "repository": { | ||
}, | ||
"gitHead": "dc3944c6f7ad6a63f403b8ed0c12a1f1de3f919f" | ||
"gitHead": "a20fbcbe0216c1c99a155eb92b0723a7ceb87b2b" | ||
} |
@@ -53,3 +53,3 @@ # Bucket Node.js SDK | ||
Once the client is initialized, you can obtain features along with the isEnabled status to indicate whether the feature is targeted for this user/company: | ||
Once the client is initialized, you can obtain features along with the `isEnabled` status to indicate whether the feature is targeted for this user/company: | ||
@@ -63,16 +63,45 @@ ```ts | ||
// get the current features (uses company, user and custom context to evaluate the features). | ||
const { huddle } = boundClient.getFeatures(); | ||
// get the huddle feature using company, user and custom context to evaluate the targeting. | ||
const { isEnabled, track } = boundClient.getFeature("huddle"); | ||
if (huddle.isEnabled) { | ||
if (isEnabled) { | ||
// this is your feature gated code ... | ||
// send an event when the feature is used: | ||
huddle.track(); | ||
track(); | ||
// CAUTION: if need the track event to be sent to Bucket as soon as possible, | ||
// always call `flush`. It can optionally be awaited to guarantee the sent happened. | ||
client.flush(); | ||
// CAUTION: if you plan to use the event for automated feedback surveys call `flush` immediately | ||
// after `track`. It can optionally be awaited to guarantee the sent happened. | ||
boundClient.flush(); | ||
} | ||
``` | ||
You can also use the `getFeatures` method which returns a map of all features: | ||
```ts | ||
// get the current features (uses company, user and custom context to evaluate the features). | ||
const features = boundClient.getFeatures(); | ||
const bothEnabled = | ||
features.huddle?.isEnabled && features.voiceHuddle?.isEnabled; | ||
``` | ||
When using `getFeatures` be careful not to assume that a feature exists, this could be a dangerous pattern: | ||
```ts | ||
// warning: if the `huddle` feature does not exist because it wasn't created in Bucket | ||
// or because the client was unable to reach our servers for some reason, this will cause an exception: | ||
const { isEnabled } = boundClient.getFeatures()["huddle"]; | ||
``` | ||
## High performance feature targeting | ||
The Bucket Node SDK contacts the Bucket servers when you call `initialize` | ||
and downloads the features with their targeting rules. | ||
These rules are then matched against the user/company information you provide | ||
to `getFeatures` (or through `bindClient(..).getFeatures()`). That means the | ||
`getFeatures` call does not need to contact the Bucket servers once initialize | ||
has completed. `BucketClient` will continue to periodically download the | ||
targeting rules from the Bucket servers in the background. | ||
## Flushing | ||
It is highly recommended that users of this SDK manually call `client.flush()` method on process shutdown. The SDK employs | ||
@@ -84,10 +113,11 @@ a batching technique to minimize the number of calls that are sent to Bucket's servers. During process shutdown, some | ||
````ts | ||
```ts | ||
process.on("SIGINT", () => { | ||
console.log("Flushing batch buffer..."); | ||
client.flush().then(() => { | ||
process.exit(0) | ||
}) | ||
process.exit(0); | ||
}); | ||
}); | ||
``` | ||
When you bind a client to a user/company, this data is matched against the targeting rules. | ||
@@ -98,12 +128,2 @@ To get accurate targeting, you must ensure that the user/company information provided is sufficient to match against the targeting rules you've created. | ||
## High performance feature targeting | ||
The Bucket Node SDK contacts the Bucket servers when you call `initialize` | ||
and downloads the features with their targeting rules. | ||
These rules are then matched against the user/company information you provide | ||
to `getFeatures` (or through `bindClient(..).getFeatures()`). That means the | ||
`getFeatures` call does not need to contact the Bucket servers once initialize | ||
has completed. `BucketClient` will continue to periodically download the | ||
targeting rules from the Bucket servers in the background. | ||
## Tracking custom events and setting custom attributes | ||
@@ -128,3 +148,3 @@ | ||
client.track("user_id", "huddle", { attributes: { voice: true } }); | ||
```` | ||
``` | ||
@@ -186,3 +206,3 @@ It's also possible to achieve the same through a bound client in the following manner: | ||
secretKey: string, | ||
// The host to send requests to (optional). | ||
// Override Bucket server address | ||
host?: string = "https://front.bucket.co", | ||
@@ -193,3 +213,3 @@ // The logger you can supply. By default no logging is performed. | ||
httpClient?: HttpClient = fetchClient, | ||
// A list of fallback features that will be enabled the Bucket servers | ||
// A list of fallback features that will be enabled if the Bucket servers | ||
// have not been contacted yet. | ||
@@ -224,1 +244,5 @@ fallbackFeatures?: string[] | ||
> Copyright (c) 2024 Bucket ApS | ||
``` | ||
``` |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
93454
1451
241