@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
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
93454
1451
241