flagdrop-stage 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cache.d.ts +30 -0
- package/dist/cache.d.ts.map +1 -0
- package/dist/cache.js +59 -0
- package/dist/cache.js.map +1 -0
- package/dist/client.d.ts +25 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +84 -0
- package/dist/client.js.map +1 -0
- package/dist/evaluator.d.ts +16 -0
- package/dist/evaluator.d.ts.map +1 -0
- package/dist/evaluator.js +111 -0
- package/dist/evaluator.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +10 -0
- package/dist/index.js.map +1 -0
- package/dist/storage/azure.d.ts +8 -0
- package/dist/storage/azure.d.ts.map +1 -0
- package/dist/storage/azure.js +28 -0
- package/dist/storage/azure.js.map +1 -0
- package/dist/storage/gcs.d.ts +6 -0
- package/dist/storage/gcs.d.ts.map +1 -0
- package/dist/storage/gcs.js +14 -0
- package/dist/storage/gcs.js.map +1 -0
- package/dist/storage/index.d.ts +4 -0
- package/dist/storage/index.d.ts.map +1 -0
- package/dist/storage/index.js +10 -0
- package/dist/storage/index.js.map +1 -0
- package/dist/storage/s3.d.ts +7 -0
- package/dist/storage/s3.d.ts.map +1 -0
- package/dist/storage/s3.js +19 -0
- package/dist/storage/s3.js.map +1 -0
- package/dist/telemetry.d.ts +30 -0
- package/dist/telemetry.d.ts.map +1 -0
- package/dist/telemetry.js +67 -0
- package/dist/telemetry.js.map +1 -0
- package/dist/types.d.ts +79 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +13 -0
- package/dist/types.js.map +1 -0
- package/package.json +42 -0
package/dist/cache.d.ts
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { ConfigFile } from './types';
|
|
2
|
+
export interface CacheOptions {
|
|
3
|
+
/** TTL in ms — default 30000 */
|
|
4
|
+
ttlMs: number;
|
|
5
|
+
/** Async function to fetch fresh config */
|
|
6
|
+
fetcher: () => Promise<ConfigFile>;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* In-memory config cache with TTL-based refresh.
|
|
10
|
+
* Serves stale data on fetch failure — never blocks evaluation.
|
|
11
|
+
*/
|
|
12
|
+
export declare class ConfigCache {
|
|
13
|
+
private config;
|
|
14
|
+
private refreshTimer;
|
|
15
|
+
private refreshing;
|
|
16
|
+
private readonly ttlMs;
|
|
17
|
+
private readonly fetcher;
|
|
18
|
+
constructor(options: CacheOptions);
|
|
19
|
+
/** Initial load — must succeed or throw */
|
|
20
|
+
initialize(): Promise<void>;
|
|
21
|
+
/** Start periodic background refresh */
|
|
22
|
+
private startRefreshTimer;
|
|
23
|
+
/** Lazy async refresh — serve stale on failure */
|
|
24
|
+
private refresh;
|
|
25
|
+
/** Get the current cached config (may be stale) */
|
|
26
|
+
getConfig(): ConfigFile | null;
|
|
27
|
+
/** Stop the refresh timer */
|
|
28
|
+
stop(): void;
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=cache.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cache.d.ts","sourceRoot":"","sources":["../src/cache.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAE1C,MAAM,WAAW,YAAY;IAC3B,gCAAgC;IAChC,KAAK,EAAE,MAAM,CAAC;IACd,2CAA2C;IAC3C,OAAO,EAAE,MAAM,OAAO,CAAC,UAAU,CAAC,CAAC;CACpC;AAED;;;GAGG;AACH,qBAAa,WAAW;IACtB,OAAO,CAAC,MAAM,CAA2B;IACzC,OAAO,CAAC,YAAY,CAA+C;IACnE,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAS;IAC/B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA4B;gBAExC,OAAO,EAAE,YAAY;IAKjC,2CAA2C;IACrC,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAKjC,wCAAwC;IACxC,OAAO,CAAC,iBAAiB;IAUzB,kDAAkD;YACpC,OAAO;IAYrB,mDAAmD;IACnD,SAAS,IAAI,UAAU,GAAG,IAAI;IAI9B,6BAA6B;IAC7B,IAAI,IAAI,IAAI;CAMb"}
|
package/dist/cache.js
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ConfigCache = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* In-memory config cache with TTL-based refresh.
|
|
6
|
+
* Serves stale data on fetch failure — never blocks evaluation.
|
|
7
|
+
*/
|
|
8
|
+
class ConfigCache {
|
|
9
|
+
constructor(options) {
|
|
10
|
+
this.config = null;
|
|
11
|
+
this.refreshTimer = null;
|
|
12
|
+
this.refreshing = false;
|
|
13
|
+
this.ttlMs = options.ttlMs;
|
|
14
|
+
this.fetcher = options.fetcher;
|
|
15
|
+
}
|
|
16
|
+
/** Initial load — must succeed or throw */
|
|
17
|
+
async initialize() {
|
|
18
|
+
this.config = await this.fetcher();
|
|
19
|
+
this.startRefreshTimer();
|
|
20
|
+
}
|
|
21
|
+
/** Start periodic background refresh */
|
|
22
|
+
startRefreshTimer() {
|
|
23
|
+
this.refreshTimer = setInterval(() => {
|
|
24
|
+
void this.refresh();
|
|
25
|
+
}, this.ttlMs);
|
|
26
|
+
// Unref so the timer doesn't prevent Node.js from exiting
|
|
27
|
+
if (this.refreshTimer && typeof this.refreshTimer === 'object' && 'unref' in this.refreshTimer) {
|
|
28
|
+
this.refreshTimer.unref();
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
/** Lazy async refresh — serve stale on failure */
|
|
32
|
+
async refresh() {
|
|
33
|
+
if (this.refreshing)
|
|
34
|
+
return;
|
|
35
|
+
this.refreshing = true;
|
|
36
|
+
try {
|
|
37
|
+
this.config = await this.fetcher();
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
// Serve stale — do not update config
|
|
41
|
+
}
|
|
42
|
+
finally {
|
|
43
|
+
this.refreshing = false;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
/** Get the current cached config (may be stale) */
|
|
47
|
+
getConfig() {
|
|
48
|
+
return this.config;
|
|
49
|
+
}
|
|
50
|
+
/** Stop the refresh timer */
|
|
51
|
+
stop() {
|
|
52
|
+
if (this.refreshTimer) {
|
|
53
|
+
clearInterval(this.refreshTimer);
|
|
54
|
+
this.refreshTimer = null;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
exports.ConfigCache = ConfigCache;
|
|
59
|
+
//# sourceMappingURL=cache.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cache.js","sourceRoot":"","sources":["../src/cache.ts"],"names":[],"mappings":";;;AASA;;;GAGG;AACH,MAAa,WAAW;IAOtB,YAAY,OAAqB;QANzB,WAAM,GAAsB,IAAI,CAAC;QACjC,iBAAY,GAA0C,IAAI,CAAC;QAC3D,eAAU,GAAG,KAAK,CAAC;QAKzB,IAAI,CAAC,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC;QAC3B,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC;IACjC,CAAC;IAED,2CAA2C;IAC3C,KAAK,CAAC,UAAU;QACd,IAAI,CAAC,MAAM,GAAG,MAAM,IAAI,CAAC,OAAO,EAAE,CAAC;QACnC,IAAI,CAAC,iBAAiB,EAAE,CAAC;IAC3B,CAAC;IAED,wCAAwC;IAChC,iBAAiB;QACvB,IAAI,CAAC,YAAY,GAAG,WAAW,CAAC,GAAG,EAAE;YACnC,KAAK,IAAI,CAAC,OAAO,EAAE,CAAC;QACtB,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC;QACf,0DAA0D;QAC1D,IAAI,IAAI,CAAC,YAAY,IAAI,OAAO,IAAI,CAAC,YAAY,KAAK,QAAQ,IAAI,OAAO,IAAI,IAAI,CAAC,YAAY,EAAE,CAAC;YAC9F,IAAI,CAAC,YAA+B,CAAC,KAAK,EAAE,CAAC;QAChD,CAAC;IACH,CAAC;IAED,kDAAkD;IAC1C,KAAK,CAAC,OAAO;QACnB,IAAI,IAAI,CAAC,UAAU;YAAE,OAAO;QAC5B,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC;QACvB,IAAI,CAAC;YACH,IAAI,CAAC,MAAM,GAAG,MAAM,IAAI,CAAC,OAAO,EAAE,CAAC;QACrC,CAAC;QAAC,MAAM,CAAC;YACP,qCAAqC;QACvC,CAAC;gBAAS,CAAC;YACT,IAAI,CAAC,UAAU,GAAG,KAAK,CAAC;QAC1B,CAAC;IACH,CAAC;IAED,mDAAmD;IACnD,SAAS;QACP,OAAO,IAAI,CAAC,MAAM,CAAC;IACrB,CAAC;IAED,6BAA6B;IAC7B,IAAI;QACF,IAAI,IAAI,CAAC,YAAY,EAAE,CAAC;YACtB,aAAa,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;YACjC,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC;QAC3B,CAAC;IACH,CAAC;CACF;AAtDD,kCAsDC"}
|
package/dist/client.d.ts
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { FlagClientOptions, IFlagClient, EvaluationContext } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* FlagDrop backend SDK client.
|
|
4
|
+
*
|
|
5
|
+
* Fetches config from cloud storage, caches in-memory, evaluates flags locally.
|
|
6
|
+
*/
|
|
7
|
+
export declare class FlagClient implements IFlagClient {
|
|
8
|
+
private cache;
|
|
9
|
+
private telemetry;
|
|
10
|
+
private readonly options;
|
|
11
|
+
constructor(options: FlagClientOptions);
|
|
12
|
+
/**
|
|
13
|
+
* Initialize the client — fetches config and starts timers.
|
|
14
|
+
* Must be called before evaluating flags.
|
|
15
|
+
*/
|
|
16
|
+
initialize(): Promise<void>;
|
|
17
|
+
getBool(key: string, defaultValue: boolean, context?: EvaluationContext): boolean;
|
|
18
|
+
getString(key: string, defaultValue: string, context?: EvaluationContext): string;
|
|
19
|
+
getNumber(key: string, defaultValue: number, context?: EvaluationContext): number;
|
|
20
|
+
getJSON<T = unknown>(key: string, defaultValue: T, context?: EvaluationContext): T;
|
|
21
|
+
close(): Promise<void>;
|
|
22
|
+
private evaluateFlag;
|
|
23
|
+
private createFetcher;
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,iBAAiB,EACjB,WAAW,EACX,iBAAiB,EAElB,MAAM,SAAS,CAAC;AAQjB;;;;GAIG;AACH,qBAAa,UAAW,YAAW,WAAW;IAC5C,OAAO,CAAC,KAAK,CAAc;IAC3B,OAAO,CAAC,SAAS,CAAmC;IACpD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAoB;gBAEhC,OAAO,EAAE,iBAAiB;IAkBtC;;;OAGG;IACG,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAKjC,OAAO,CACL,GAAG,EAAE,MAAM,EACX,YAAY,EAAE,OAAO,EACrB,OAAO,CAAC,EAAE,iBAAiB,GAC1B,OAAO;IAIV,SAAS,CACP,GAAG,EAAE,MAAM,EACX,YAAY,EAAE,MAAM,EACpB,OAAO,CAAC,EAAE,iBAAiB,GAC1B,MAAM;IAIT,SAAS,CACP,GAAG,EAAE,MAAM,EACX,YAAY,EAAE,MAAM,EACpB,OAAO,CAAC,EAAE,iBAAiB,GAC1B,MAAM;IAIT,OAAO,CAAC,CAAC,GAAG,OAAO,EACjB,GAAG,EAAE,MAAM,EACX,YAAY,EAAE,CAAC,EACf,OAAO,CAAC,EAAE,iBAAiB,GAC1B,CAAC;IAIE,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAO5B,OAAO,CAAC,YAAY;IAepB,OAAO,CAAC,aAAa;CActB"}
|
package/dist/client.js
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.FlagClient = void 0;
|
|
4
|
+
const cache_1 = require("./cache");
|
|
5
|
+
const telemetry_1 = require("./telemetry");
|
|
6
|
+
const evaluator_1 = require("./evaluator");
|
|
7
|
+
const s3_1 = require("./storage/s3");
|
|
8
|
+
const gcs_1 = require("./storage/gcs");
|
|
9
|
+
const azure_1 = require("./storage/azure");
|
|
10
|
+
/**
|
|
11
|
+
* FlagDrop backend SDK client.
|
|
12
|
+
*
|
|
13
|
+
* Fetches config from cloud storage, caches in-memory, evaluates flags locally.
|
|
14
|
+
*/
|
|
15
|
+
class FlagClient {
|
|
16
|
+
constructor(options) {
|
|
17
|
+
this.telemetry = null;
|
|
18
|
+
this.options = options;
|
|
19
|
+
const fetcher = this.createFetcher();
|
|
20
|
+
this.cache = new cache_1.ConfigCache({
|
|
21
|
+
ttlMs: options.refreshTtlMs ?? 30000,
|
|
22
|
+
fetcher,
|
|
23
|
+
});
|
|
24
|
+
if (options.telemetryUrl) {
|
|
25
|
+
this.telemetry = new telemetry_1.TelemetryCollector({
|
|
26
|
+
telemetryUrl: options.telemetryUrl,
|
|
27
|
+
environment: options.environment,
|
|
28
|
+
intervalMs: options.telemetryIntervalMs ?? 60000,
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Initialize the client — fetches config and starts timers.
|
|
34
|
+
* Must be called before evaluating flags.
|
|
35
|
+
*/
|
|
36
|
+
async initialize() {
|
|
37
|
+
await this.cache.initialize();
|
|
38
|
+
this.telemetry?.start();
|
|
39
|
+
}
|
|
40
|
+
getBool(key, defaultValue, context) {
|
|
41
|
+
return this.evaluateFlag(key, defaultValue, context);
|
|
42
|
+
}
|
|
43
|
+
getString(key, defaultValue, context) {
|
|
44
|
+
return this.evaluateFlag(key, defaultValue, context);
|
|
45
|
+
}
|
|
46
|
+
getNumber(key, defaultValue, context) {
|
|
47
|
+
return this.evaluateFlag(key, defaultValue, context);
|
|
48
|
+
}
|
|
49
|
+
getJSON(key, defaultValue, context) {
|
|
50
|
+
return this.evaluateFlag(key, defaultValue, context);
|
|
51
|
+
}
|
|
52
|
+
async close() {
|
|
53
|
+
this.cache.stop();
|
|
54
|
+
if (this.telemetry) {
|
|
55
|
+
await this.telemetry.stop();
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
evaluateFlag(key, defaultValue, context) {
|
|
59
|
+
try {
|
|
60
|
+
const config = this.cache.getConfig();
|
|
61
|
+
const flag = config?.flags[key];
|
|
62
|
+
this.telemetry?.record(key);
|
|
63
|
+
return (0, evaluator_1.evaluate)(key, flag, defaultValue, context);
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
return defaultValue;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
createFetcher() {
|
|
70
|
+
const { provider, bucket, region, environment } = this.options;
|
|
71
|
+
switch (provider) {
|
|
72
|
+
case 'aws':
|
|
73
|
+
return () => (0, s3_1.readFromS3)({ bucket, region }, environment);
|
|
74
|
+
case 'gcp':
|
|
75
|
+
return () => (0, gcs_1.readFromGCS)({ bucket }, environment);
|
|
76
|
+
case 'azure':
|
|
77
|
+
return () => (0, azure_1.readFromAzure)({ bucket }, environment);
|
|
78
|
+
default:
|
|
79
|
+
throw new Error(`Unsupported provider: ${provider}`);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
exports.FlagClient = FlagClient;
|
|
84
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { ConfigFlag, Rule, EvaluationContext } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* Evaluate a flag against the given context.
|
|
4
|
+
* Returns the resolved value, or `defaultValue` if the flag is missing/disabled/error.
|
|
5
|
+
*/
|
|
6
|
+
export declare function evaluate<T>(flagKey: string, flag: ConfigFlag | undefined, defaultValue: T, context?: EvaluationContext): T;
|
|
7
|
+
/**
|
|
8
|
+
* Check whether a single rule matches the given context.
|
|
9
|
+
*/
|
|
10
|
+
export declare function matchesRule(rule: Rule, context: EvaluationContext): boolean;
|
|
11
|
+
/**
|
|
12
|
+
* Deterministic bucketing via MurmurHash3.
|
|
13
|
+
* Returns a number 0-99 (inclusive).
|
|
14
|
+
*/
|
|
15
|
+
export declare function bucketUser(flagKey: string, attributeValue: string): number;
|
|
16
|
+
//# sourceMappingURL=evaluator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"evaluator.d.ts","sourceRoot":"","sources":["../src/evaluator.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAC;AAEnE;;;GAGG;AACH,wBAAgB,QAAQ,CAAC,CAAC,EACxB,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,UAAU,GAAG,SAAS,EAC5B,YAAY,EAAE,CAAC,EACf,OAAO,GAAE,iBAAsB,GAC9B,CAAC,CAgCH;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAwD3E;AAED;;;GAGG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,GAAG,MAAM,CAG1E"}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.evaluate = evaluate;
|
|
7
|
+
exports.matchesRule = matchesRule;
|
|
8
|
+
exports.bucketUser = bucketUser;
|
|
9
|
+
/**
|
|
10
|
+
* Rule evaluator — shared logic between Node and browser SDKs.
|
|
11
|
+
*
|
|
12
|
+
* CRITICAL: This module must produce IDENTICAL results in both environments.
|
|
13
|
+
* Same hash, same mod, same matching order. Never throws during evaluation.
|
|
14
|
+
*/
|
|
15
|
+
const murmurhash3js_1 = __importDefault(require("murmurhash3js"));
|
|
16
|
+
/**
|
|
17
|
+
* Evaluate a flag against the given context.
|
|
18
|
+
* Returns the resolved value, or `defaultValue` if the flag is missing/disabled/error.
|
|
19
|
+
*/
|
|
20
|
+
function evaluate(flagKey, flag, defaultValue, context = {}) {
|
|
21
|
+
try {
|
|
22
|
+
if (!flag || !flag.enabled) {
|
|
23
|
+
return defaultValue;
|
|
24
|
+
}
|
|
25
|
+
// Evaluate rules in order — first match wins
|
|
26
|
+
for (const rule of flag.rules) {
|
|
27
|
+
if (matchesRule(rule, context)) {
|
|
28
|
+
return rule.serveValue;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
// Rollout check — if a rollout is defined, use it to decide
|
|
32
|
+
if (flag.rollout && flag.rollout.attribute) {
|
|
33
|
+
const attrValue = context[flag.rollout.attribute] ?? '';
|
|
34
|
+
if (attrValue !== '') {
|
|
35
|
+
const bucket = bucketUser(flagKey, attrValue);
|
|
36
|
+
if (bucket < flag.rollout.percentage) {
|
|
37
|
+
return flag.defaultValue;
|
|
38
|
+
}
|
|
39
|
+
// Outside rollout percentage — return caller default
|
|
40
|
+
return defaultValue;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
// No rules matched, no rollout or missing attribute — return flag default
|
|
44
|
+
return flag.defaultValue;
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
// SDK NEVER throws during evaluation
|
|
48
|
+
return defaultValue;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Check whether a single rule matches the given context.
|
|
53
|
+
*/
|
|
54
|
+
function matchesRule(rule, context) {
|
|
55
|
+
try {
|
|
56
|
+
const attrValue = context[rule.attribute];
|
|
57
|
+
// If the attribute is not present in context, rule does not match
|
|
58
|
+
if (attrValue === undefined || attrValue === null) {
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
const { operator, value } = rule;
|
|
62
|
+
switch (operator) {
|
|
63
|
+
case 'eq':
|
|
64
|
+
return attrValue === String(value);
|
|
65
|
+
case 'neq':
|
|
66
|
+
return attrValue !== String(value);
|
|
67
|
+
case 'in':
|
|
68
|
+
if (Array.isArray(value)) {
|
|
69
|
+
return value.some((v) => String(v) === attrValue);
|
|
70
|
+
}
|
|
71
|
+
return false;
|
|
72
|
+
case 'notIn':
|
|
73
|
+
if (Array.isArray(value)) {
|
|
74
|
+
return !value.some((v) => String(v) === attrValue);
|
|
75
|
+
}
|
|
76
|
+
return true;
|
|
77
|
+
case 'lt': {
|
|
78
|
+
const numAttr = Number(attrValue);
|
|
79
|
+
const numVal = Number(value);
|
|
80
|
+
if (isNaN(numAttr) || isNaN(numVal))
|
|
81
|
+
return false;
|
|
82
|
+
return numAttr < numVal;
|
|
83
|
+
}
|
|
84
|
+
case 'gt': {
|
|
85
|
+
const numAttr = Number(attrValue);
|
|
86
|
+
const numVal = Number(value);
|
|
87
|
+
if (isNaN(numAttr) || isNaN(numVal))
|
|
88
|
+
return false;
|
|
89
|
+
return numAttr > numVal;
|
|
90
|
+
}
|
|
91
|
+
case 'startsWith':
|
|
92
|
+
return attrValue.startsWith(String(value));
|
|
93
|
+
case 'endsWith':
|
|
94
|
+
return attrValue.endsWith(String(value));
|
|
95
|
+
default:
|
|
96
|
+
return false;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
catch {
|
|
100
|
+
return false;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Deterministic bucketing via MurmurHash3.
|
|
105
|
+
* Returns a number 0-99 (inclusive).
|
|
106
|
+
*/
|
|
107
|
+
function bucketUser(flagKey, attributeValue) {
|
|
108
|
+
const hash = murmurhash3js_1.default.x86.hash32(flagKey + attributeValue);
|
|
109
|
+
return Math.abs(hash) % 100;
|
|
110
|
+
}
|
|
111
|
+
//# sourceMappingURL=evaluator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"evaluator.js","sourceRoot":"","sources":["../src/evaluator.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { FlagClient } from './client';
|
|
2
|
+
export { evaluate, matchesRule, bucketUser } from './evaluator';
|
|
3
|
+
export type { FlagClientOptions, BrowserFlagClientOptions, ConfigFile, ConfigFlag, Rule, Rollout, EvaluationContext, TelemetryBatch, IFlagClient, IBrowserFlagClient, } from './types';
|
|
4
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AACtC,OAAO,EAAE,QAAQ,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAChE,YAAY,EACV,iBAAiB,EACjB,wBAAwB,EACxB,UAAU,EACV,UAAU,EACV,IAAI,EACJ,OAAO,EACP,iBAAiB,EACjB,cAAc,EACd,WAAW,EACX,kBAAkB,GACnB,MAAM,SAAS,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.bucketUser = exports.matchesRule = exports.evaluate = exports.FlagClient = void 0;
|
|
4
|
+
var client_1 = require("./client");
|
|
5
|
+
Object.defineProperty(exports, "FlagClient", { enumerable: true, get: function () { return client_1.FlagClient; } });
|
|
6
|
+
var evaluator_1 = require("./evaluator");
|
|
7
|
+
Object.defineProperty(exports, "evaluate", { enumerable: true, get: function () { return evaluator_1.evaluate; } });
|
|
8
|
+
Object.defineProperty(exports, "matchesRule", { enumerable: true, get: function () { return evaluator_1.matchesRule; } });
|
|
9
|
+
Object.defineProperty(exports, "bucketUser", { enumerable: true, get: function () { return evaluator_1.bucketUser; } });
|
|
10
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,mCAAsC;AAA7B,oGAAA,UAAU,OAAA;AACnB,yCAAgE;AAAvD,qGAAA,QAAQ,OAAA;AAAE,wGAAA,WAAW,OAAA;AAAE,uGAAA,UAAU,OAAA"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { ConfigFile } from '../types';
|
|
2
|
+
export interface AzureReaderOptions {
|
|
3
|
+
bucket: string;
|
|
4
|
+
/** Azure storage account connection string or URL */
|
|
5
|
+
connectionString?: string;
|
|
6
|
+
}
|
|
7
|
+
export declare function readFromAzure(options: AzureReaderOptions, environment: string, scope?: 'backend' | 'frontend'): Promise<ConfigFile>;
|
|
8
|
+
//# sourceMappingURL=azure.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"azure.d.ts","sourceRoot":"","sources":["../../src/storage/azure.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAE3C,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,qDAAqD;IACrD,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,wBAAsB,aAAa,CACjC,OAAO,EAAE,kBAAkB,EAC3B,WAAW,EAAE,MAAM,EACnB,KAAK,GAAE,SAAS,GAAG,UAAsB,GACxC,OAAO,CAAC,UAAU,CAAC,CAwBrB"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.readFromAzure = readFromAzure;
|
|
4
|
+
const storage_blob_1 = require("@azure/storage-blob");
|
|
5
|
+
async function readFromAzure(options, environment, scope = 'backend') {
|
|
6
|
+
const blobName = `flags-${environment}-${scope}.json`;
|
|
7
|
+
let blobServiceClient;
|
|
8
|
+
if (options.connectionString) {
|
|
9
|
+
blobServiceClient = storage_blob_1.BlobServiceClient.fromConnectionString(options.connectionString);
|
|
10
|
+
}
|
|
11
|
+
else {
|
|
12
|
+
// Fall back to default Azure identity (env vars, managed identity, etc.)
|
|
13
|
+
blobServiceClient = storage_blob_1.BlobServiceClient.fromConnectionString(process.env.AZURE_STORAGE_CONNECTION_STRING ?? '');
|
|
14
|
+
}
|
|
15
|
+
const containerClient = blobServiceClient.getContainerClient(options.bucket);
|
|
16
|
+
const blobClient = containerClient.getBlobClient(blobName);
|
|
17
|
+
const downloadResponse = await blobClient.download(0);
|
|
18
|
+
const body = await streamToString(downloadResponse.readableStreamBody);
|
|
19
|
+
return JSON.parse(body);
|
|
20
|
+
}
|
|
21
|
+
async function streamToString(stream) {
|
|
22
|
+
const chunks = [];
|
|
23
|
+
for await (const chunk of stream) {
|
|
24
|
+
chunks.push(Buffer.from(chunk));
|
|
25
|
+
}
|
|
26
|
+
return Buffer.concat(chunks).toString('utf-8');
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=azure.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"azure.js","sourceRoot":"","sources":["../../src/storage/azure.ts"],"names":[],"mappings":";;AAYA,sCA4BC;AAxCD,sDAG6B;AAStB,KAAK,UAAU,aAAa,CACjC,OAA2B,EAC3B,WAAmB,EACnB,QAAgC,SAAS;IAEzC,MAAM,QAAQ,GAAG,SAAS,WAAW,IAAI,KAAK,OAAO,CAAC;IAEtD,IAAI,iBAAoC,CAAC;IACzC,IAAI,OAAO,CAAC,gBAAgB,EAAE,CAAC;QAC7B,iBAAiB,GAAG,gCAAiB,CAAC,oBAAoB,CACxD,OAAO,CAAC,gBAAgB,CACzB,CAAC;IACJ,CAAC;SAAM,CAAC;QACN,yEAAyE;QACzE,iBAAiB,GAAG,gCAAiB,CAAC,oBAAoB,CACxD,OAAO,CAAC,GAAG,CAAC,+BAA+B,IAAI,EAAE,CAClD,CAAC;IACJ,CAAC;IAED,MAAM,eAAe,GAAG,iBAAiB,CAAC,kBAAkB,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;IAC7E,MAAM,UAAU,GAAG,eAAe,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC;IAC3D,MAAM,gBAAgB,GAAG,MAAM,UAAU,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;IAEtD,MAAM,IAAI,GAAG,MAAM,cAAc,CAC/B,gBAAgB,CAAC,kBAA2C,CAC7D,CAAC;IAEF,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAe,CAAC;AACxC,CAAC;AAED,KAAK,UAAU,cAAc,CAAC,MAA6B;IACzD,MAAM,MAAM,GAAa,EAAE,CAAC;IAC5B,IAAI,KAAK,EAAE,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QACjC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;IAClC,CAAC;IACD,OAAO,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;AACjD,CAAC"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { ConfigFile } from '../types';
|
|
2
|
+
export interface GCSReaderOptions {
|
|
3
|
+
bucket: string;
|
|
4
|
+
}
|
|
5
|
+
export declare function readFromGCS(options: GCSReaderOptions, environment: string, scope?: 'backend' | 'frontend'): Promise<ConfigFile>;
|
|
6
|
+
//# sourceMappingURL=gcs.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gcs.d.ts","sourceRoot":"","sources":["../../src/storage/gcs.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAE3C,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,wBAAsB,WAAW,CAC/B,OAAO,EAAE,gBAAgB,EACzB,WAAW,EAAE,MAAM,EACnB,KAAK,GAAE,SAAS,GAAG,UAAsB,GACxC,OAAO,CAAC,UAAU,CAAC,CAUrB"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.readFromGCS = readFromGCS;
|
|
4
|
+
const storage_1 = require("@google-cloud/storage");
|
|
5
|
+
async function readFromGCS(options, environment, scope = 'backend') {
|
|
6
|
+
const storage = new storage_1.Storage();
|
|
7
|
+
const fileName = `flags-${environment}-${scope}.json`;
|
|
8
|
+
const [contents] = await storage
|
|
9
|
+
.bucket(options.bucket)
|
|
10
|
+
.file(fileName)
|
|
11
|
+
.download();
|
|
12
|
+
return JSON.parse(contents.toString());
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=gcs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gcs.js","sourceRoot":"","sources":["../../src/storage/gcs.ts"],"names":[],"mappings":";;AAOA,kCAcC;AArBD,mDAAgD;AAOzC,KAAK,UAAU,WAAW,CAC/B,OAAyB,EACzB,WAAmB,EACnB,QAAgC,SAAS;IAEzC,MAAM,OAAO,GAAG,IAAI,iBAAO,EAAE,CAAC;IAC9B,MAAM,QAAQ,GAAG,SAAS,WAAW,IAAI,KAAK,OAAO,CAAC;IAEtD,MAAM,CAAC,QAAQ,CAAC,GAAG,MAAM,OAAO;SAC7B,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC;SACtB,IAAI,CAAC,QAAQ,CAAC;SACd,QAAQ,EAAE,CAAC;IAEd,OAAO,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,QAAQ,EAAE,CAAe,CAAC;AACvD,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/storage/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,KAAK,eAAe,EAAE,MAAM,MAAM,CAAC;AACxD,OAAO,EAAE,WAAW,EAAE,KAAK,gBAAgB,EAAE,MAAM,OAAO,CAAC;AAC3D,OAAO,EAAE,aAAa,EAAE,KAAK,kBAAkB,EAAE,MAAM,SAAS,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.readFromAzure = exports.readFromGCS = exports.readFromS3 = void 0;
|
|
4
|
+
var s3_1 = require("./s3");
|
|
5
|
+
Object.defineProperty(exports, "readFromS3", { enumerable: true, get: function () { return s3_1.readFromS3; } });
|
|
6
|
+
var gcs_1 = require("./gcs");
|
|
7
|
+
Object.defineProperty(exports, "readFromGCS", { enumerable: true, get: function () { return gcs_1.readFromGCS; } });
|
|
8
|
+
var azure_1 = require("./azure");
|
|
9
|
+
Object.defineProperty(exports, "readFromAzure", { enumerable: true, get: function () { return azure_1.readFromAzure; } });
|
|
10
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/storage/index.ts"],"names":[],"mappings":";;;AAAA,2BAAwD;AAA/C,gGAAA,UAAU,OAAA;AACnB,6BAA2D;AAAlD,kGAAA,WAAW,OAAA;AACpB,iCAAiE;AAAxD,sGAAA,aAAa,OAAA"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { ConfigFile } from '../types';
|
|
2
|
+
export interface S3ReaderOptions {
|
|
3
|
+
bucket: string;
|
|
4
|
+
region: string;
|
|
5
|
+
}
|
|
6
|
+
export declare function readFromS3(options: S3ReaderOptions, environment: string, scope?: 'backend' | 'frontend'): Promise<ConfigFile>;
|
|
7
|
+
//# sourceMappingURL=s3.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"s3.d.ts","sourceRoot":"","sources":["../../src/storage/s3.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAE3C,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,wBAAsB,UAAU,CAC9B,OAAO,EAAE,eAAe,EACxB,WAAW,EAAE,MAAM,EACnB,KAAK,GAAE,SAAS,GAAG,UAAsB,GACxC,OAAO,CAAC,UAAU,CAAC,CAgBrB"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.readFromS3 = readFromS3;
|
|
4
|
+
const client_s3_1 = require("@aws-sdk/client-s3");
|
|
5
|
+
async function readFromS3(options, environment, scope = 'backend') {
|
|
6
|
+
const client = new client_s3_1.S3Client({ region: options.region });
|
|
7
|
+
const key = `flags-${environment}-${scope}.json`;
|
|
8
|
+
const command = new client_s3_1.GetObjectCommand({
|
|
9
|
+
Bucket: options.bucket,
|
|
10
|
+
Key: key,
|
|
11
|
+
});
|
|
12
|
+
const response = await client.send(command);
|
|
13
|
+
const body = await response.Body?.transformToString();
|
|
14
|
+
if (!body) {
|
|
15
|
+
throw new Error(`Empty response from S3 for key: ${key}`);
|
|
16
|
+
}
|
|
17
|
+
return JSON.parse(body);
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=s3.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"s3.js","sourceRoot":"","sources":["../../src/storage/s3.ts"],"names":[],"mappings":";;AAQA,gCAoBC;AA5BD,kDAAgE;AAQzD,KAAK,UAAU,UAAU,CAC9B,OAAwB,EACxB,WAAmB,EACnB,QAAgC,SAAS;IAEzC,MAAM,MAAM,GAAG,IAAI,oBAAQ,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IACxD,MAAM,GAAG,GAAG,SAAS,WAAW,IAAI,KAAK,OAAO,CAAC;IAEjD,MAAM,OAAO,GAAG,IAAI,4BAAgB,CAAC;QACnC,MAAM,EAAE,OAAO,CAAC,MAAM;QACtB,GAAG,EAAE,GAAG;KACT,CAAC,CAAC;IAEH,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAC5C,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,iBAAiB,EAAE,CAAC;IACtD,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,MAAM,IAAI,KAAK,CAAC,mCAAmC,GAAG,EAAE,CAAC,CAAC;IAC5D,CAAC;IAED,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAe,CAAC;AACxC,CAAC"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
export interface TelemetryOptions {
|
|
2
|
+
/** URL to POST telemetry batches to */
|
|
3
|
+
telemetryUrl: string;
|
|
4
|
+
/** Environment name */
|
|
5
|
+
environment: string;
|
|
6
|
+
/** Flush interval in ms — default 60000 */
|
|
7
|
+
intervalMs: number;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Opt-in telemetry: counts flag evaluations and flushes periodically.
|
|
11
|
+
* Fire-and-forget — never blocks evaluation.
|
|
12
|
+
*/
|
|
13
|
+
export declare class TelemetryCollector {
|
|
14
|
+
private counts;
|
|
15
|
+
private flushTimer;
|
|
16
|
+
private periodStart;
|
|
17
|
+
private readonly url;
|
|
18
|
+
private readonly environment;
|
|
19
|
+
private readonly intervalMs;
|
|
20
|
+
constructor(options: TelemetryOptions);
|
|
21
|
+
/** Start the periodic flush timer */
|
|
22
|
+
start(): void;
|
|
23
|
+
/** Record a flag evaluation */
|
|
24
|
+
record(flagKey: string): void;
|
|
25
|
+
/** Flush current counts — fire-and-forget */
|
|
26
|
+
flush(): Promise<void>;
|
|
27
|
+
/** Stop flushing and send final batch */
|
|
28
|
+
stop(): Promise<void>;
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=telemetry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"telemetry.d.ts","sourceRoot":"","sources":["../src/telemetry.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,gBAAgB;IAC/B,uCAAuC;IACvC,YAAY,EAAE,MAAM,CAAC;IACrB,uBAAuB;IACvB,WAAW,EAAE,MAAM,CAAC;IACpB,2CAA2C;IAC3C,UAAU,EAAE,MAAM,CAAC;CACpB;AAED;;;GAGG;AACH,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,MAAM,CAAkC;IAChD,OAAO,CAAC,UAAU,CAA+C;IACjE,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAS;IAC7B,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IACrC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;gBAExB,OAAO,EAAE,gBAAgB;IAOrC,qCAAqC;IACrC,KAAK,IAAI,IAAI;IASb,+BAA+B;IAC/B,MAAM,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAI7B,6CAA6C;IACvC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IA4B5B,yCAAyC;IACnC,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;CAO5B"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.TelemetryCollector = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Opt-in telemetry: counts flag evaluations and flushes periodically.
|
|
6
|
+
* Fire-and-forget — never blocks evaluation.
|
|
7
|
+
*/
|
|
8
|
+
class TelemetryCollector {
|
|
9
|
+
constructor(options) {
|
|
10
|
+
this.counts = new Map();
|
|
11
|
+
this.flushTimer = null;
|
|
12
|
+
this.url = options.telemetryUrl;
|
|
13
|
+
this.environment = options.environment;
|
|
14
|
+
this.intervalMs = options.intervalMs;
|
|
15
|
+
this.periodStart = new Date().toISOString();
|
|
16
|
+
}
|
|
17
|
+
/** Start the periodic flush timer */
|
|
18
|
+
start() {
|
|
19
|
+
this.flushTimer = setInterval(() => {
|
|
20
|
+
void this.flush();
|
|
21
|
+
}, this.intervalMs);
|
|
22
|
+
if (this.flushTimer && typeof this.flushTimer === 'object' && 'unref' in this.flushTimer) {
|
|
23
|
+
this.flushTimer.unref();
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
/** Record a flag evaluation */
|
|
27
|
+
record(flagKey) {
|
|
28
|
+
this.counts.set(flagKey, (this.counts.get(flagKey) ?? 0) + 1);
|
|
29
|
+
}
|
|
30
|
+
/** Flush current counts — fire-and-forget */
|
|
31
|
+
async flush() {
|
|
32
|
+
if (this.counts.size === 0)
|
|
33
|
+
return;
|
|
34
|
+
const countsSnapshot = Object.fromEntries(this.counts);
|
|
35
|
+
const periodEnd = new Date().toISOString();
|
|
36
|
+
const batch = {
|
|
37
|
+
projectId: '', // Filled by relay/backend from auth
|
|
38
|
+
environment: this.environment,
|
|
39
|
+
counts: countsSnapshot,
|
|
40
|
+
periodStart: this.periodStart,
|
|
41
|
+
periodEnd,
|
|
42
|
+
};
|
|
43
|
+
// Reset for next period
|
|
44
|
+
this.counts.clear();
|
|
45
|
+
this.periodStart = periodEnd;
|
|
46
|
+
try {
|
|
47
|
+
await fetch(this.url, {
|
|
48
|
+
method: 'POST',
|
|
49
|
+
headers: { 'Content-Type': 'application/json' },
|
|
50
|
+
body: JSON.stringify(batch),
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
// Fire-and-forget — telemetry failures are silent
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
/** Stop flushing and send final batch */
|
|
58
|
+
async stop() {
|
|
59
|
+
if (this.flushTimer) {
|
|
60
|
+
clearInterval(this.flushTimer);
|
|
61
|
+
this.flushTimer = null;
|
|
62
|
+
}
|
|
63
|
+
await this.flush();
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
exports.TelemetryCollector = TelemetryCollector;
|
|
67
|
+
//# sourceMappingURL=telemetry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"telemetry.js","sourceRoot":"","sources":["../src/telemetry.ts"],"names":[],"mappings":";;;AAWA;;;GAGG;AACH,MAAa,kBAAkB;IAQ7B,YAAY,OAAyB;QAP7B,WAAM,GAAwB,IAAI,GAAG,EAAE,CAAC;QACxC,eAAU,GAA0C,IAAI,CAAC;QAO/D,IAAI,CAAC,GAAG,GAAG,OAAO,CAAC,YAAY,CAAC;QAChC,IAAI,CAAC,WAAW,GAAG,OAAO,CAAC,WAAW,CAAC;QACvC,IAAI,CAAC,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC;QACrC,IAAI,CAAC,WAAW,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IAC9C,CAAC;IAED,qCAAqC;IACrC,KAAK;QACH,IAAI,CAAC,UAAU,GAAG,WAAW,CAAC,GAAG,EAAE;YACjC,KAAK,IAAI,CAAC,KAAK,EAAE,CAAC;QACpB,CAAC,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC;QACpB,IAAI,IAAI,CAAC,UAAU,IAAI,OAAO,IAAI,CAAC,UAAU,KAAK,QAAQ,IAAI,OAAO,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;YACxF,IAAI,CAAC,UAA6B,CAAC,KAAK,EAAE,CAAC;QAC9C,CAAC;IACH,CAAC;IAED,+BAA+B;IAC/B,MAAM,CAAC,OAAe;QACpB,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;IAChE,CAAC;IAED,6CAA6C;IAC7C,KAAK,CAAC,KAAK;QACT,IAAI,IAAI,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC;YAAE,OAAO;QAEnC,MAAM,cAAc,GAAG,MAAM,CAAC,WAAW,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACvD,MAAM,SAAS,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;QAC3C,MAAM,KAAK,GAAmB;YAC5B,SAAS,EAAE,EAAE,EAAE,oCAAoC;YACnD,WAAW,EAAE,IAAI,CAAC,WAAW;YAC7B,MAAM,EAAE,cAAc;YACtB,WAAW,EAAE,IAAI,CAAC,WAAW;YAC7B,SAAS;SACV,CAAC;QAEF,wBAAwB;QACxB,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;QACpB,IAAI,CAAC,WAAW,GAAG,SAAS,CAAC;QAE7B,IAAI,CAAC;YACH,MAAM,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE;gBACpB,MAAM,EAAE,MAAM;gBACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;gBAC/C,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC;aAC5B,CAAC,CAAC;QACL,CAAC;QAAC,MAAM,CAAC;YACP,kDAAkD;QACpD,CAAC;IACH,CAAC;IAED,yCAAyC;IACzC,KAAK,CAAC,IAAI;QACR,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;YACpB,aAAa,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;YAC/B,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC;QACzB,CAAC;QACD,MAAM,IAAI,CAAC,KAAK,EAAE,CAAC;IACrB,CAAC;CACF;AAnED,gDAmEC"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @flagdrop/sdk — Shared type definitions
|
|
3
|
+
*
|
|
4
|
+
* These types are the contract between:
|
|
5
|
+
* - The Go config file generator (produces the JSON)
|
|
6
|
+
* - The Node.js backend SDK (reads the JSON)
|
|
7
|
+
* - The Python backend SDK (reads the JSON)
|
|
8
|
+
* - The browser SDK (reads the JSON via relay)
|
|
9
|
+
* - The OpenFeature provider (wraps the SDK)
|
|
10
|
+
*/
|
|
11
|
+
export interface FlagClientOptions {
|
|
12
|
+
/** Customer's storage bucket name */
|
|
13
|
+
bucket: string;
|
|
14
|
+
/** Environment name (e.g., 'prod', 'staging') */
|
|
15
|
+
environment: string;
|
|
16
|
+
/** Cloud provider: 'aws' | 'gcp' | 'azure' */
|
|
17
|
+
provider: 'aws' | 'gcp' | 'azure';
|
|
18
|
+
/** Cloud region (e.g., 'us-east-1') */
|
|
19
|
+
region: string;
|
|
20
|
+
/** Cache refresh interval in ms (default: 30000) */
|
|
21
|
+
refreshTtlMs?: number;
|
|
22
|
+
/** FlagDrop telemetry API URL (optional, opt-in) */
|
|
23
|
+
telemetryUrl?: string;
|
|
24
|
+
/** Telemetry flush interval in ms (default: 60000) */
|
|
25
|
+
telemetryIntervalMs?: number;
|
|
26
|
+
}
|
|
27
|
+
export interface BrowserFlagClientOptions {
|
|
28
|
+
/** URL of the Web Relay SSE endpoint */
|
|
29
|
+
relayUrl: string;
|
|
30
|
+
/** Environment name */
|
|
31
|
+
environment: string;
|
|
32
|
+
/** API key for relay authentication (optional) */
|
|
33
|
+
apiKey?: string;
|
|
34
|
+
}
|
|
35
|
+
export interface ConfigFile {
|
|
36
|
+
version: number;
|
|
37
|
+
updatedAt: string;
|
|
38
|
+
scope: 'backend' | 'frontend';
|
|
39
|
+
flags: Record<string, ConfigFlag>;
|
|
40
|
+
}
|
|
41
|
+
export interface ConfigFlag {
|
|
42
|
+
type: 'boolean' | 'string' | 'number' | 'json';
|
|
43
|
+
enabled: boolean;
|
|
44
|
+
defaultValue: unknown;
|
|
45
|
+
rules: Rule[];
|
|
46
|
+
rollout?: Rollout;
|
|
47
|
+
description?: string;
|
|
48
|
+
}
|
|
49
|
+
export interface Rule {
|
|
50
|
+
attribute: string;
|
|
51
|
+
operator: 'eq' | 'neq' | 'in' | 'notIn' | 'lt' | 'gt' | 'startsWith' | 'endsWith' | 'segment';
|
|
52
|
+
value: string | string[] | number;
|
|
53
|
+
serveValue: unknown;
|
|
54
|
+
}
|
|
55
|
+
export interface Rollout {
|
|
56
|
+
percentage: number;
|
|
57
|
+
attribute: string;
|
|
58
|
+
}
|
|
59
|
+
/** Arbitrary key-value pairs passed at eval time for targeting */
|
|
60
|
+
export type EvaluationContext = Record<string, string>;
|
|
61
|
+
export interface TelemetryBatch {
|
|
62
|
+
projectId: string;
|
|
63
|
+
environment: string;
|
|
64
|
+
counts: Record<string, number>;
|
|
65
|
+
periodStart: string;
|
|
66
|
+
periodEnd: string;
|
|
67
|
+
}
|
|
68
|
+
export interface IFlagClient {
|
|
69
|
+
getBool(key: string, defaultValue: boolean, context?: EvaluationContext): boolean;
|
|
70
|
+
getString(key: string, defaultValue: string, context?: EvaluationContext): string;
|
|
71
|
+
getNumber(key: string, defaultValue: number, context?: EvaluationContext): number;
|
|
72
|
+
getJSON<T = unknown>(key: string, defaultValue: T, context?: EvaluationContext): T;
|
|
73
|
+
close(): Promise<void>;
|
|
74
|
+
}
|
|
75
|
+
export interface IBrowserFlagClient extends IFlagClient {
|
|
76
|
+
on(event: 'change', handler: (flagKey: string, newValue: unknown) => void): void;
|
|
77
|
+
off(event: 'change', handler: (flagKey: string, newValue: unknown) => void): void;
|
|
78
|
+
}
|
|
79
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAMH,MAAM,WAAW,iBAAiB;IAChC,qCAAqC;IACrC,MAAM,EAAE,MAAM,CAAC;IACf,iDAAiD;IACjD,WAAW,EAAE,MAAM,CAAC;IACpB,8CAA8C;IAC9C,QAAQ,EAAE,KAAK,GAAG,KAAK,GAAG,OAAO,CAAC;IAClC,uCAAuC;IACvC,MAAM,EAAE,MAAM,CAAC;IACf,oDAAoD;IACpD,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,oDAAoD;IACpD,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,sDAAsD;IACtD,mBAAmB,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED,MAAM,WAAW,wBAAwB;IACvC,wCAAwC;IACxC,QAAQ,EAAE,MAAM,CAAC;IACjB,uBAAuB;IACvB,WAAW,EAAE,MAAM,CAAC;IACpB,kDAAkD;IAClD,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAMD,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,SAAS,GAAG,UAAU,CAAC;IAC9B,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;CACnC;AAED,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,SAAS,GAAG,QAAQ,GAAG,QAAQ,GAAG,MAAM,CAAC;IAC/C,OAAO,EAAE,OAAO,CAAC;IACjB,YAAY,EAAE,OAAO,CAAC;IACtB,KAAK,EAAE,IAAI,EAAE,CAAC;IACd,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,IAAI;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,IAAI,GAAG,KAAK,GAAG,IAAI,GAAG,OAAO,GAAG,IAAI,GAAG,IAAI,GAAG,YAAY,GAAG,UAAU,GAAG,SAAS,CAAC;IAC9F,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,MAAM,CAAC;IAClC,UAAU,EAAE,OAAO,CAAC;CACrB;AAED,MAAM,WAAW,OAAO;IACtB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;CACnB;AAMD,kEAAkE;AAClE,MAAM,MAAM,iBAAiB,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AAMvD,MAAM,WAAW,cAAc;IAC7B,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/B,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;CACnB;AAMD,MAAM,WAAW,WAAW;IAC1B,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,YAAY,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,iBAAiB,GAAG,OAAO,CAAC;IAClF,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,iBAAiB,GAAG,MAAM,CAAC;IAClF,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,iBAAiB,GAAG,MAAM,CAAC;IAClF,OAAO,CAAC,CAAC,GAAG,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,YAAY,EAAE,CAAC,EAAE,OAAO,CAAC,EAAE,iBAAiB,GAAG,CAAC,CAAC;IACnF,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB;AAED,MAAM,WAAW,kBAAmB,SAAQ,WAAW;IACrD,EAAE,CAAC,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,KAAK,IAAI,GAAG,IAAI,CAAC;IACjF,GAAG,CAAC,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,KAAK,IAAI,GAAG,IAAI,CAAC;CACnF"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* @flagdrop/sdk — Shared type definitions
|
|
4
|
+
*
|
|
5
|
+
* These types are the contract between:
|
|
6
|
+
* - The Go config file generator (produces the JSON)
|
|
7
|
+
* - The Node.js backend SDK (reads the JSON)
|
|
8
|
+
* - The Python backend SDK (reads the JSON)
|
|
9
|
+
* - The browser SDK (reads the JSON via relay)
|
|
10
|
+
* - The OpenFeature provider (wraps the SDK)
|
|
11
|
+
*/
|
|
12
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":";AAAA;;;;;;;;;GASG"}
|
package/package.json
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "flagdrop-stage",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "FlagDrop backend SDK for Node.js — read feature flags from your cloud storage bucket",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"files": ["dist"],
|
|
8
|
+
"scripts": {
|
|
9
|
+
"build": "tsc",
|
|
10
|
+
"test": "jest",
|
|
11
|
+
"prepublishOnly": "npm run build"
|
|
12
|
+
},
|
|
13
|
+
"keywords": ["feature-flags", "flagdrop", "sdk"],
|
|
14
|
+
"license": "MIT",
|
|
15
|
+
"repository": {
|
|
16
|
+
"type": "git",
|
|
17
|
+
"url": "https://github.com/flagdrop-io/sdk-node.git"
|
|
18
|
+
},
|
|
19
|
+
"dependencies": {
|
|
20
|
+
"murmurhash3js": "^3.0.1"
|
|
21
|
+
},
|
|
22
|
+
"peerDependencies": {
|
|
23
|
+
"@aws-sdk/client-s3": "^3.0.0",
|
|
24
|
+
"@google-cloud/storage": "^7.0.0",
|
|
25
|
+
"@azure/storage-blob": "^12.0.0"
|
|
26
|
+
},
|
|
27
|
+
"peerDependenciesMeta": {
|
|
28
|
+
"@aws-sdk/client-s3": { "optional": true },
|
|
29
|
+
"@google-cloud/storage": { "optional": true },
|
|
30
|
+
"@azure/storage-blob": { "optional": true }
|
|
31
|
+
},
|
|
32
|
+
"devDependencies": {
|
|
33
|
+
"@aws-sdk/client-s3": "^3.500.0",
|
|
34
|
+
"@google-cloud/storage": "^7.0.0",
|
|
35
|
+
"@azure/storage-blob": "^12.0.0",
|
|
36
|
+
"@types/jest": "^29.5.0",
|
|
37
|
+
"@types/node": "^20.0.0",
|
|
38
|
+
"jest": "^29.7.0",
|
|
39
|
+
"ts-jest": "^29.1.0",
|
|
40
|
+
"typescript": "^5.4.0"
|
|
41
|
+
}
|
|
42
|
+
}
|