@featurevisor/sdk 1.35.3 → 2.0.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.
Files changed (85) hide show
  1. package/README.md +2 -381
  2. package/coverage/clover.xml +707 -645
  3. package/coverage/coverage-final.json +11 -9
  4. package/coverage/lcov-report/{segments.ts.html → bucketer.ts.html} +155 -77
  5. package/coverage/lcov-report/child.ts.html +940 -0
  6. package/coverage/lcov-report/conditions.ts.html +107 -158
  7. package/coverage/lcov-report/datafileReader.ts.html +763 -103
  8. package/coverage/lcov-report/emitter.ts.html +77 -59
  9. package/coverage/lcov-report/evaluate.ts.html +689 -416
  10. package/coverage/lcov-report/events.ts.html +334 -0
  11. package/coverage/lcov-report/helpers.ts.html +184 -0
  12. package/coverage/lcov-report/{bucket.ts.html → hooks.ts.html} +86 -239
  13. package/coverage/lcov-report/index.html +119 -89
  14. package/coverage/lcov-report/instance.ts.html +341 -773
  15. package/coverage/lcov-report/logger.ts.html +64 -64
  16. package/coverage/lcov.info +1433 -1226
  17. package/dist/bucketer.d.ts +11 -0
  18. package/dist/child.d.ts +26 -0
  19. package/dist/compareVersions.d.ts +4 -0
  20. package/dist/conditions.d.ts +4 -4
  21. package/dist/datafileReader.d.ts +26 -6
  22. package/dist/emitter.d.ts +8 -9
  23. package/dist/evaluate.d.ts +31 -29
  24. package/dist/events.d.ts +5 -0
  25. package/dist/helpers.d.ts +5 -0
  26. package/dist/hooks.d.ts +45 -0
  27. package/dist/index.d.ts +3 -2
  28. package/dist/index.js +1 -1
  29. package/dist/index.js.map +1 -1
  30. package/dist/index.mjs +1 -1
  31. package/dist/index.mjs.gz +0 -0
  32. package/dist/index.mjs.map +1 -1
  33. package/dist/instance.d.ts +40 -72
  34. package/dist/logger.d.ts +6 -5
  35. package/dist/murmurhash.d.ts +1 -0
  36. package/jest.config.js +2 -0
  37. package/lib/bucketer.d.ts +11 -0
  38. package/lib/child.d.ts +26 -0
  39. package/lib/compareVersions.d.ts +4 -0
  40. package/lib/conditions.d.ts +4 -4
  41. package/lib/datafileReader.d.ts +26 -6
  42. package/lib/emitter.d.ts +8 -9
  43. package/lib/evaluate.d.ts +31 -29
  44. package/lib/events.d.ts +5 -0
  45. package/lib/helpers.d.ts +5 -0
  46. package/lib/hooks.d.ts +45 -0
  47. package/lib/index.d.ts +3 -2
  48. package/lib/instance.d.ts +40 -72
  49. package/lib/logger.d.ts +6 -5
  50. package/lib/murmurhash.d.ts +1 -0
  51. package/package.json +3 -5
  52. package/src/bucketer.spec.ts +165 -0
  53. package/src/bucketer.ts +84 -0
  54. package/src/child.spec.ts +267 -0
  55. package/src/child.ts +285 -0
  56. package/src/compareVersions.ts +93 -0
  57. package/src/conditions.spec.ts +563 -353
  58. package/src/conditions.ts +46 -63
  59. package/src/datafileReader.spec.ts +396 -84
  60. package/src/datafileReader.ts +280 -60
  61. package/src/emitter.spec.ts +27 -86
  62. package/src/emitter.ts +38 -32
  63. package/src/evaluate.ts +349 -258
  64. package/src/events.spec.ts +154 -0
  65. package/src/events.ts +83 -0
  66. package/src/helpers.ts +33 -0
  67. package/src/hooks.ts +88 -0
  68. package/src/index.ts +3 -2
  69. package/src/instance.spec.ts +305 -489
  70. package/src/instance.ts +247 -391
  71. package/src/logger.spec.ts +212 -134
  72. package/src/logger.ts +36 -36
  73. package/src/murmurhash.ts +71 -0
  74. package/coverage/lcov-report/feature.ts.html +0 -508
  75. package/dist/bucket.d.ts +0 -30
  76. package/dist/feature.d.ts +0 -16
  77. package/dist/segments.d.ts +0 -5
  78. package/lib/bucket.d.ts +0 -30
  79. package/lib/feature.d.ts +0 -16
  80. package/lib/segments.d.ts +0 -5
  81. package/src/bucket.spec.ts +0 -37
  82. package/src/bucket.ts +0 -139
  83. package/src/feature.ts +0 -141
  84. package/src/segments.spec.ts +0 -468
  85. package/src/segments.ts +0 -58
@@ -1,99 +1,67 @@
1
- import { Context, DatafileContent, Feature, FeatureKey, InitialFeatures, StickyFeatures, VariableType, VariableValue, VariationValue, VariableKey } from "@featurevisor/types";
1
+ import type { Context, Feature, FeatureKey, StickyFeatures, EvaluatedFeatures, VariableValue, VariationValue, VariableKey, DatafileContent } from "@featurevisor/types";
2
2
  import { Logger, LogLevel } from "./logger";
3
- import { Emitter } from "./emitter";
4
- import { ConfigureBucketKey, ConfigureBucketValue } from "./bucket";
3
+ import { Hook } from "./hooks";
4
+ import { EventCallback, EventName } from "./emitter";
5
5
  import { Evaluation } from "./evaluate";
6
- export type ReadyCallback = () => void;
7
- export type ActivationCallback = (featureName: string, variation: VariationValue, context: Context, captureContext: Context) => void;
8
- export interface Statuses {
9
- ready: boolean;
10
- refreshInProgress: boolean;
6
+ import { FeaturevisorChildInstance } from "./child";
7
+ export interface OverrideOptions {
8
+ sticky?: StickyFeatures;
9
+ defaultVariationValue?: VariationValue;
10
+ defaultVariableValue?: VariableValue;
11
11
  }
12
- export type InterceptContext = (context: Context) => Context;
13
12
  export interface InstanceOptions {
14
- bucketKeySeparator?: string;
15
- configureBucketKey?: ConfigureBucketKey;
16
- configureBucketValue?: ConfigureBucketValue;
17
13
  datafile?: DatafileContent | string;
18
- datafileUrl?: string;
19
- handleDatafileFetch?: (datafileUrl: string) => Promise<DatafileContent>;
20
- initialFeatures?: InitialFeatures;
21
- interceptContext?: InterceptContext;
14
+ context?: Context;
15
+ logLevel?: LogLevel;
22
16
  logger?: Logger;
23
- onActivation?: ActivationCallback;
24
- onReady?: ReadyCallback;
25
- onRefresh?: () => void;
26
- onUpdate?: () => void;
27
- refreshInterval?: number;
28
- stickyFeatures?: StickyFeatures;
17
+ sticky?: StickyFeatures;
18
+ hooks?: Hook[];
29
19
  }
30
- export type DatafileFetchHandler = (datafileUrl: string) => Promise<DatafileContent>;
31
- type FieldType = string | VariableType;
32
- type ValueType = VariableValue;
33
- export declare function getValueByType(value: ValueType, fieldType: FieldType): ValueType;
34
20
  export declare class FeaturevisorInstance {
35
- private bucketKeySeparator;
36
- private configureBucketKey?;
37
- private configureBucketValue?;
38
- private datafileUrl?;
39
- private handleDatafileFetch?;
40
- private initialFeatures?;
41
- private interceptContext?;
21
+ private context;
42
22
  private logger;
43
- private refreshInterval?;
44
- private stickyFeatures?;
23
+ private sticky?;
45
24
  private datafileReader;
25
+ private hooksManager;
46
26
  private emitter;
47
- private statuses;
48
- private intervalId?;
49
- on: Emitter["addListener"];
50
- addListener: Emitter["addListener"];
51
- off: Emitter["removeListener"];
52
- removeListener: Emitter["removeListener"];
53
- removeAllListeners: Emitter["removeAllListeners"];
54
27
  constructor(options: InstanceOptions);
55
- setLogLevels(levels: LogLevel[]): void;
56
- onReady(): Promise<FeaturevisorInstance>;
28
+ setLogLevel(level: LogLevel): void;
57
29
  setDatafile(datafile: DatafileContent | string): void;
58
- setStickyFeatures(stickyFeatures: StickyFeatures | undefined): void;
30
+ setSticky(sticky: StickyFeatures, replace?: boolean): void;
59
31
  getRevision(): string;
60
- getFeature(featureKey: string | Feature): Feature | undefined;
32
+ getFeature(featureKey: string): Feature | undefined;
33
+ addHook(hook: Hook): () => void;
34
+ on(eventName: EventName, callback: EventCallback): () => void;
35
+ close(): void;
61
36
  /**
62
- * Statuses
37
+ * Context
63
38
  */
64
- isReady(): boolean;
65
- /**
66
- * Refresh
67
- */
68
- refresh(): void;
69
- startRefreshing(): void;
70
- stopRefreshing(): void;
39
+ setContext(context: Context, replace?: boolean): void;
40
+ getContext(context?: Context): Context;
41
+ spawn(context?: Context, options?: OverrideOptions): FeaturevisorChildInstance;
71
42
  /**
72
43
  * Flag
73
44
  */
74
- evaluateFlag(featureKey: FeatureKey | Feature, context?: Context): Evaluation;
75
- isEnabled(featureKey: FeatureKey | Feature, context?: Context): boolean;
45
+ private getEvaluationDependencies;
46
+ evaluateFlag(featureKey: FeatureKey, context?: Context, options?: OverrideOptions): Evaluation;
47
+ isEnabled(featureKey: FeatureKey, context?: Context, options?: OverrideOptions): boolean;
76
48
  /**
77
49
  * Variation
78
50
  */
79
- evaluateVariation(featureKey: FeatureKey | Feature, context?: Context): Evaluation;
80
- getVariation(featureKey: FeatureKey | Feature, context?: Context): VariationValue | undefined;
81
- /**
82
- * Activate
83
- */
84
- activate(featureKey: FeatureKey, context?: Context): VariationValue | undefined;
51
+ evaluateVariation(featureKey: FeatureKey, context?: Context, options?: OverrideOptions): Evaluation;
52
+ getVariation(featureKey: FeatureKey, context?: Context, options?: OverrideOptions): VariationValue | null;
85
53
  /**
86
54
  * Variable
87
55
  */
88
- evaluateVariable(featureKey: FeatureKey | Feature, variableKey: VariableKey, context?: Context): Evaluation;
89
- getVariable(featureKey: FeatureKey | Feature, variableKey: string, context?: Context): VariableValue | undefined;
90
- getVariableBoolean(featureKey: FeatureKey | Feature, variableKey: string, context?: Context): boolean | undefined;
91
- getVariableString(featureKey: FeatureKey | Feature, variableKey: string, context?: Context): string | undefined;
92
- getVariableInteger(featureKey: FeatureKey | Feature, variableKey: string, context?: Context): number | undefined;
93
- getVariableDouble(featureKey: FeatureKey | Feature, variableKey: string, context?: Context): number | undefined;
94
- getVariableArray(featureKey: FeatureKey | Feature, variableKey: string, context?: Context): string[] | undefined;
95
- getVariableObject<T>(featureKey: FeatureKey | Feature, variableKey: string, context?: Context): T | undefined;
96
- getVariableJSON<T>(featureKey: FeatureKey | Feature, variableKey: string, context?: Context): T | undefined;
56
+ evaluateVariable(featureKey: FeatureKey, variableKey: VariableKey, context?: Context, options?: OverrideOptions): Evaluation;
57
+ getVariable(featureKey: FeatureKey, variableKey: string, context?: Context, options?: OverrideOptions): VariableValue | null;
58
+ getVariableBoolean(featureKey: FeatureKey, variableKey: string, context?: Context, options?: OverrideOptions): boolean | null;
59
+ getVariableString(featureKey: FeatureKey, variableKey: string, context?: Context, options?: OverrideOptions): string | null;
60
+ getVariableInteger(featureKey: FeatureKey, variableKey: string, context?: Context, options?: OverrideOptions): number | null;
61
+ getVariableDouble(featureKey: FeatureKey, variableKey: string, context?: Context, options?: OverrideOptions): number | null;
62
+ getVariableArray(featureKey: FeatureKey, variableKey: string, context?: Context, options?: OverrideOptions): string[] | null;
63
+ getVariableObject<T>(featureKey: FeatureKey, variableKey: string, context?: Context, options?: OverrideOptions): T | null;
64
+ getVariableJSON<T>(featureKey: FeatureKey, variableKey: string, context?: Context, options?: OverrideOptions): T | null;
65
+ getAllEvaluations(context?: Context, featureKeys?: string[], options?: OverrideOptions): EvaluatedFeatures;
97
66
  }
98
67
  export declare function createInstance(options: InstanceOptions): FeaturevisorInstance;
99
- export {};
package/dist/logger.d.ts CHANGED
@@ -1,21 +1,22 @@
1
- export type LogLevel = "debug" | "info" | "warn" | "error";
1
+ export type LogLevel = "fatal" | "error" | "warn" | "info" | "debug";
2
2
  export type LogMessage = string;
3
3
  export interface LogDetails {
4
4
  [key: string]: any;
5
5
  }
6
6
  export type LogHandler = (level: LogLevel, message: LogMessage, details?: LogDetails) => void;
7
7
  export interface CreateLoggerOptions {
8
- levels?: LogLevel[];
8
+ level?: LogLevel;
9
9
  handler?: LogHandler;
10
10
  }
11
11
  export declare const loggerPrefix = "[Featurevisor]";
12
- export declare const defaultLogLevels: LogLevel[];
13
12
  export declare const defaultLogHandler: LogHandler;
14
13
  export declare class Logger {
15
- private levels;
14
+ static allLevels: LogLevel[];
15
+ static defaultLevel: LogLevel;
16
+ private level;
16
17
  private handle;
17
18
  constructor(options: CreateLoggerOptions);
18
- setLevels(levels: LogLevel[]): void;
19
+ setLevel(level: LogLevel): void;
19
20
  log(level: LogLevel, message: LogMessage, details?: LogDetails): void;
20
21
  debug(message: LogMessage, details?: LogDetails): void;
21
22
  info(message: LogMessage, details?: LogDetails): void;
@@ -0,0 +1 @@
1
+ export declare function MurmurHashV3(key: any, seed: any): number;
package/jest.config.js CHANGED
@@ -1,4 +1,6 @@
1
1
  module.exports = {
2
2
  preset: "ts-jest",
3
3
  bail: true,
4
+ collectCoverageFrom: ["src/**/*.ts"],
5
+ coveragePathIgnorePatterns: ["src/index.ts", "src/murmurhash.ts", "src/compareVersions.ts"],
4
6
  };
@@ -0,0 +1,11 @@
1
+ import type { BucketKey, Context, FeatureKey, BucketBy } from "@featurevisor/types";
2
+ import { Logger } from "./logger";
3
+ export declare const MAX_BUCKETED_NUMBER = 100000;
4
+ export declare function getBucketedNumber(bucketKey: string): number;
5
+ export interface GetBucketKeyOptions {
6
+ featureKey: FeatureKey;
7
+ bucketBy: BucketBy;
8
+ context: Context;
9
+ logger: Logger;
10
+ }
11
+ export declare function getBucketKey(options: GetBucketKeyOptions): BucketKey;
package/lib/child.d.ts ADDED
@@ -0,0 +1,26 @@
1
+ import type { Context, StickyFeatures, FeatureKey, VariationValue, VariableValue, EvaluatedFeatures } from "@featurevisor/types";
2
+ import { EventName, EventCallback } from "./emitter";
3
+ import type { OverrideOptions } from "./instance";
4
+ export declare class FeaturevisorChildInstance {
5
+ private parent;
6
+ private context;
7
+ private sticky;
8
+ private emitter;
9
+ constructor(options: any);
10
+ on(eventName: EventName, callback: EventCallback): () => void;
11
+ close(): void;
12
+ setContext(context: Context, replace?: boolean): void;
13
+ getContext(context?: Context): Context;
14
+ setSticky(sticky: StickyFeatures, replace?: boolean): void;
15
+ isEnabled(featureKey: FeatureKey, context?: Context, options?: OverrideOptions): boolean;
16
+ getVariation(featureKey: FeatureKey, context?: Context, options?: OverrideOptions): VariationValue | null;
17
+ getVariable(featureKey: FeatureKey, variableKey: string, context?: Context, options?: OverrideOptions): VariableValue | null;
18
+ getVariableBoolean(featureKey: FeatureKey, variableKey: string, context?: Context, options?: OverrideOptions): boolean | null;
19
+ getVariableString(featureKey: FeatureKey, variableKey: string, context?: Context, options?: OverrideOptions): string | null;
20
+ getVariableInteger(featureKey: FeatureKey, variableKey: string, context?: Context, options?: OverrideOptions): number | null;
21
+ getVariableDouble(featureKey: FeatureKey, variableKey: string, context?: Context, options?: OverrideOptions): number | null;
22
+ getVariableArray(featureKey: FeatureKey, variableKey: string, context?: Context, options?: OverrideOptions): string[] | null;
23
+ getVariableObject<T>(featureKey: FeatureKey, variableKey: string, context?: Context, options?: OverrideOptions): T | null;
24
+ getVariableJSON<T>(featureKey: FeatureKey, variableKey: string, context?: Context, options?: OverrideOptions): T | null;
25
+ getAllEvaluations(context?: Context, featureKeys?: string[], options?: OverrideOptions): EvaluatedFeatures;
26
+ }
@@ -0,0 +1,4 @@
1
+ export declare const semver: RegExp;
2
+ export declare const validateAndParse: (version: string) => RegExpMatchArray;
3
+ export declare const compareSegments: (a: string | string[] | RegExpMatchArray, b: string | string[] | RegExpMatchArray) => 1 | 0 | -1;
4
+ export declare const compareVersions: (v1: string, v2: string) => 1 | 0 | -1;
@@ -1,4 +1,4 @@
1
- import { Context, Condition, PlainCondition } from "@featurevisor/types";
2
- import { Logger } from "./logger";
3
- export declare function conditionIsMatched(condition: PlainCondition, context: Context): boolean;
4
- export declare function allConditionsAreMatched(conditions: Condition[] | Condition, context: Context, logger: Logger): boolean;
1
+ import type { Context, PlainCondition, AttributeValue } from "@featurevisor/types";
2
+ import { GetRegex } from "./datafileReader";
3
+ export declare function getValueFromContext(obj: any, path: any): AttributeValue;
4
+ export declare function conditionIsMatched(condition: PlainCondition, context: Context, getRegex: GetRegex): boolean;
@@ -1,16 +1,36 @@
1
- import { Feature, Segment, DatafileContentV1, DatafileContentV2, Attribute, AttributeKey, SegmentKey, FeatureKey } from "@featurevisor/types";
2
- export declare function parseJsonConditionsIfStringified<T>(record: T, key: string): T;
1
+ import type { Feature, Segment, DatafileContent, SegmentKey, FeatureKey, Context, Traffic, Allocation, GroupSegment, Condition, Force } from "@featurevisor/types";
2
+ import { Logger } from "./logger";
3
+ export type GetRegex = (regexString: string, regexFlags: string) => RegExp;
4
+ export interface DatafileReaderOptions {
5
+ datafile: DatafileContent;
6
+ logger: Logger;
7
+ }
8
+ export interface ForceResult {
9
+ force?: Force;
10
+ forceIndex?: number;
11
+ }
3
12
  export declare class DatafileReader {
4
13
  private schemaVersion;
5
14
  private revision;
6
- private attributes;
7
15
  private segments;
8
16
  private features;
9
- constructor(datafileJson: DatafileContentV1 | DatafileContentV2);
17
+ private logger;
18
+ private regexCache;
19
+ constructor(options: DatafileReaderOptions);
10
20
  getRevision(): string;
11
21
  getSchemaVersion(): string;
12
- getAllAttributes(): Attribute[];
13
- getAttribute(attributeKey: AttributeKey): Attribute | undefined;
14
22
  getSegment(segmentKey: SegmentKey): Segment | undefined;
23
+ getFeatureKeys(): string[];
15
24
  getFeature(featureKey: FeatureKey): Feature | undefined;
25
+ getVariableKeys(featureKey: FeatureKey): string[];
26
+ hasVariations(featureKey: FeatureKey): boolean;
27
+ getRegex(regexString: string, regexFlags?: string): RegExp;
28
+ allConditionsAreMatched(conditions: Condition[] | Condition, context: Context): boolean;
29
+ segmentIsMatched(segment: Segment, context: Context): boolean;
30
+ allSegmentsAreMatched(groupSegments: GroupSegment | GroupSegment[] | "*", context: Context): boolean;
31
+ getMatchedTraffic(traffic: Traffic[], context: Context): Traffic | undefined;
32
+ getMatchedAllocation(traffic: Traffic, bucketValue: number): Allocation | undefined;
33
+ getMatchedForce(featureKey: FeatureKey | Feature, context: Context): ForceResult;
34
+ parseConditionsIfStringified(conditions: Condition | Condition[]): Condition | Condition[];
35
+ parseSegmentsIfStringified(segments: GroupSegment | GroupSegment[]): GroupSegment | GroupSegment[];
16
36
  }
package/lib/emitter.d.ts CHANGED
@@ -1,12 +1,11 @@
1
- export type EventName = "ready" | "refresh" | "update" | "activation";
2
- export interface Listeners {
3
- [key: string]: Function[];
4
- }
1
+ export type EventName = "datafile_set" | "context_set" | "sticky_set";
2
+ export type EventDetails = Record<string, unknown>;
3
+ export type EventCallback = (details: EventDetails) => void;
4
+ export type Listeners = Record<EventName, EventCallback[]> | {};
5
5
  export declare class Emitter {
6
- private _listeners;
6
+ listeners: Listeners;
7
7
  constructor();
8
- addListener(eventName: EventName, fn: Function): void;
9
- removeListener(eventName: EventName, fn: Function): void;
10
- removeAllListeners(eventName?: EventName): void;
11
- emit(eventName: EventName, ...args: any[]): void;
8
+ on(eventName: EventName, callback: EventCallback): () => void;
9
+ trigger(eventName: EventName, details?: EventDetails): void;
10
+ clearAll(): void;
12
11
  }
package/lib/evaluate.d.ts CHANGED
@@ -1,26 +1,28 @@
1
- import { Feature, FeatureKey, Context, BucketKey, BucketValue, RuleKey, Traffic, Force, Required, OverrideFeature, Variation, VariationValue, VariableKey, VariableValue, VariableSchema, StickyFeatures, InitialFeatures } from "@featurevisor/types";
1
+ import type { FeatureKey, Context, BucketKey, BucketValue, RuleKey, Traffic, Force, Required, Variation, VariationValue, VariableKey, VariableValue, VariableSchema, EvaluatedFeature, StickyFeatures } from "@featurevisor/types";
2
2
  import { Logger } from "./logger";
3
+ import { HooksManager } from "./hooks";
3
4
  import { DatafileReader } from "./datafileReader";
4
- import { ConfigureBucketKey, ConfigureBucketValue } from "./bucket";
5
- import type { Statuses, InterceptContext } from "./instance";
6
5
  export declare enum EvaluationReason {
7
- NOT_FOUND = "not_found",
8
- NO_VARIATIONS = "no_variations",
9
- NO_MATCH = "no_match",
10
- DISABLED = "disabled",
11
- REQUIRED = "required",
12
- OUT_OF_RANGE = "out_of_range",
13
- FORCED = "forced",
14
- INITIAL = "initial",
15
- STICKY = "sticky",
16
- RULE = "rule",
17
- ALLOCATED = "allocated",
18
- DEFAULTED = "defaulted",
19
- OVERRIDE = "override",
6
+ FEATURE_NOT_FOUND = "feature_not_found",// feature is not found in datafile
7
+ DISABLED = "disabled",// feature is disabled
8
+ REQUIRED = "required",// required features are not enabled
9
+ OUT_OF_RANGE = "out_of_range",// out of range when mutually exclusive experiments are involved via Groups
10
+ NO_VARIATIONS = "no_variations",// feature has no variations
11
+ VARIATION_DISABLED = "variation_disabled",// feature is disabled, and variation's disabledVariationValue is used
12
+ VARIABLE_NOT_FOUND = "variable_not_found",// variable's schema is not defined in the feature
13
+ VARIABLE_DEFAULT = "variable_default",// default variable value used
14
+ VARIABLE_DISABLED = "variable_disabled",// feature is disabled, and variable's disabledValue is used
15
+ VARIABLE_OVERRIDE = "variable_override",// variable overridden from inside a variation
16
+ NO_MATCH = "no_match",// no rules matched
17
+ FORCED = "forced",// against a forced rule
18
+ STICKY = "sticky",// against a sticky feature
19
+ RULE = "rule",// against a regular rule
20
+ ALLOCATED = "allocated",// regular allocation based on bucketing
20
21
  ERROR = "error"
21
22
  }
22
23
  type EvaluationType = "flag" | "variation" | "variable";
23
24
  export interface Evaluation {
25
+ type: EvaluationType;
24
26
  featureKey: FeatureKey;
25
27
  reason: EvaluationReason;
26
28
  bucketKey?: BucketKey;
@@ -32,28 +34,28 @@ export interface Evaluation {
32
34
  forceIndex?: number;
33
35
  force?: Force;
34
36
  required?: Required[];
35
- sticky?: OverrideFeature;
36
- initial?: OverrideFeature;
37
+ sticky?: EvaluatedFeature;
37
38
  variation?: Variation;
38
39
  variationValue?: VariationValue;
39
40
  variableKey?: VariableKey;
40
41
  variableValue?: VariableValue;
41
42
  variableSchema?: VariableSchema;
42
43
  }
43
- export interface EvaluateOptions {
44
- type: EvaluationType;
45
- featureKey: FeatureKey | Feature;
46
- variableKey?: VariableKey;
44
+ export interface EvaluateDependencies {
47
45
  context: Context;
48
46
  logger: Logger;
47
+ hooksManager: HooksManager;
49
48
  datafileReader: DatafileReader;
50
- statuses?: Statuses;
51
- interceptContext?: InterceptContext;
52
- stickyFeatures?: StickyFeatures;
53
- initialFeatures?: InitialFeatures;
54
- bucketKeySeparator?: string;
55
- configureBucketKey?: ConfigureBucketKey;
56
- configureBucketValue?: ConfigureBucketValue;
49
+ sticky?: StickyFeatures;
50
+ defaultVariationValue?: VariationValue;
51
+ defaultVariableValue?: VariableValue;
52
+ }
53
+ export interface EvaluateParams {
54
+ type: EvaluationType;
55
+ featureKey: FeatureKey;
56
+ variableKey?: VariableKey;
57
57
  }
58
+ export type EvaluateOptions = EvaluateParams & EvaluateDependencies;
59
+ export declare function evaluateWithHooks(opts: EvaluateOptions): Evaluation;
58
60
  export declare function evaluate(options: EvaluateOptions): Evaluation;
59
61
  export {};
@@ -0,0 +1,5 @@
1
+ import type { StickyFeatures } from "@featurevisor/types";
2
+ import type { EventDetails } from "./emitter";
3
+ import type { DatafileReader } from "./datafileReader";
4
+ export declare function getParamsForStickySetEvent(previousStickyFeatures: StickyFeatures, newStickyFeatures: StickyFeatures, replace: any): EventDetails;
5
+ export declare function getParamsForDatafileSetEvent(previousDatafileReader: DatafileReader, newDatafileReader: DatafileReader): EventDetails;
@@ -0,0 +1,5 @@
1
+ import type { VariableType, VariableValue } from "@featurevisor/types";
2
+ type FieldType = string | VariableType;
3
+ type ValueType = VariableValue;
4
+ export declare function getValueByType(value: ValueType, fieldType: FieldType): ValueType;
5
+ export {};
package/lib/hooks.d.ts ADDED
@@ -0,0 +1,45 @@
1
+ import type { BucketBy, BucketKey, BucketValue, Context, FeatureKey } from "@featurevisor/types";
2
+ import type { EvaluateOptions, Evaluation } from "./evaluate";
3
+ import type { Logger } from "./logger";
4
+ /**
5
+ * bucketKey
6
+ */
7
+ export interface ConfigureBucketKeyOptions {
8
+ featureKey: FeatureKey;
9
+ context: Context;
10
+ bucketBy: BucketBy;
11
+ bucketKey: string;
12
+ }
13
+ export type ConfigureBucketKey = (options: ConfigureBucketKeyOptions) => BucketKey;
14
+ /**
15
+ * bucketValue
16
+ */
17
+ export interface ConfigureBucketValueOptions {
18
+ featureKey: FeatureKey;
19
+ bucketKey: string;
20
+ context: Context;
21
+ bucketValue: number;
22
+ }
23
+ export type ConfigureBucketValue = (options: ConfigureBucketValueOptions) => BucketValue;
24
+ /**
25
+ * Hooks
26
+ */
27
+ export interface Hook {
28
+ name: string;
29
+ before?: (options: EvaluateOptions) => EvaluateOptions;
30
+ bucketKey?: ConfigureBucketKey;
31
+ bucketValue?: ConfigureBucketValue;
32
+ after?: (evaluation: Evaluation, options: EvaluateOptions) => Evaluation;
33
+ }
34
+ export interface HooksManagerOptions {
35
+ hooks?: Hook[];
36
+ logger: Logger;
37
+ }
38
+ export declare class HooksManager {
39
+ private hooks;
40
+ private logger;
41
+ constructor(options: HooksManagerOptions);
42
+ add(hook: Hook): (() => void) | undefined;
43
+ remove(name: string): void;
44
+ getAll(): Hook[];
45
+ }
package/lib/index.d.ts CHANGED
@@ -1,6 +1,7 @@
1
- export * from "./bucket";
1
+ export * from "./bucketer";
2
2
  export * from "./instance";
3
3
  export * from "./logger";
4
4
  export * from "./conditions";
5
- export * from "./emitter";
6
5
  export * from "./evaluate";
6
+ export * from "./datafileReader";
7
+ export * from "./child";
package/lib/instance.d.ts CHANGED
@@ -1,99 +1,67 @@
1
- import { Context, DatafileContent, Feature, FeatureKey, InitialFeatures, StickyFeatures, VariableType, VariableValue, VariationValue, VariableKey } from "@featurevisor/types";
1
+ import type { Context, Feature, FeatureKey, StickyFeatures, EvaluatedFeatures, VariableValue, VariationValue, VariableKey, DatafileContent } from "@featurevisor/types";
2
2
  import { Logger, LogLevel } from "./logger";
3
- import { Emitter } from "./emitter";
4
- import { ConfigureBucketKey, ConfigureBucketValue } from "./bucket";
3
+ import { Hook } from "./hooks";
4
+ import { EventCallback, EventName } from "./emitter";
5
5
  import { Evaluation } from "./evaluate";
6
- export type ReadyCallback = () => void;
7
- export type ActivationCallback = (featureName: string, variation: VariationValue, context: Context, captureContext: Context) => void;
8
- export interface Statuses {
9
- ready: boolean;
10
- refreshInProgress: boolean;
6
+ import { FeaturevisorChildInstance } from "./child";
7
+ export interface OverrideOptions {
8
+ sticky?: StickyFeatures;
9
+ defaultVariationValue?: VariationValue;
10
+ defaultVariableValue?: VariableValue;
11
11
  }
12
- export type InterceptContext = (context: Context) => Context;
13
12
  export interface InstanceOptions {
14
- bucketKeySeparator?: string;
15
- configureBucketKey?: ConfigureBucketKey;
16
- configureBucketValue?: ConfigureBucketValue;
17
13
  datafile?: DatafileContent | string;
18
- datafileUrl?: string;
19
- handleDatafileFetch?: (datafileUrl: string) => Promise<DatafileContent>;
20
- initialFeatures?: InitialFeatures;
21
- interceptContext?: InterceptContext;
14
+ context?: Context;
15
+ logLevel?: LogLevel;
22
16
  logger?: Logger;
23
- onActivation?: ActivationCallback;
24
- onReady?: ReadyCallback;
25
- onRefresh?: () => void;
26
- onUpdate?: () => void;
27
- refreshInterval?: number;
28
- stickyFeatures?: StickyFeatures;
17
+ sticky?: StickyFeatures;
18
+ hooks?: Hook[];
29
19
  }
30
- export type DatafileFetchHandler = (datafileUrl: string) => Promise<DatafileContent>;
31
- type FieldType = string | VariableType;
32
- type ValueType = VariableValue;
33
- export declare function getValueByType(value: ValueType, fieldType: FieldType): ValueType;
34
20
  export declare class FeaturevisorInstance {
35
- private bucketKeySeparator;
36
- private configureBucketKey?;
37
- private configureBucketValue?;
38
- private datafileUrl?;
39
- private handleDatafileFetch?;
40
- private initialFeatures?;
41
- private interceptContext?;
21
+ private context;
42
22
  private logger;
43
- private refreshInterval?;
44
- private stickyFeatures?;
23
+ private sticky?;
45
24
  private datafileReader;
25
+ private hooksManager;
46
26
  private emitter;
47
- private statuses;
48
- private intervalId?;
49
- on: Emitter["addListener"];
50
- addListener: Emitter["addListener"];
51
- off: Emitter["removeListener"];
52
- removeListener: Emitter["removeListener"];
53
- removeAllListeners: Emitter["removeAllListeners"];
54
27
  constructor(options: InstanceOptions);
55
- setLogLevels(levels: LogLevel[]): void;
56
- onReady(): Promise<FeaturevisorInstance>;
28
+ setLogLevel(level: LogLevel): void;
57
29
  setDatafile(datafile: DatafileContent | string): void;
58
- setStickyFeatures(stickyFeatures: StickyFeatures | undefined): void;
30
+ setSticky(sticky: StickyFeatures, replace?: boolean): void;
59
31
  getRevision(): string;
60
- getFeature(featureKey: string | Feature): Feature | undefined;
32
+ getFeature(featureKey: string): Feature | undefined;
33
+ addHook(hook: Hook): () => void;
34
+ on(eventName: EventName, callback: EventCallback): () => void;
35
+ close(): void;
61
36
  /**
62
- * Statuses
37
+ * Context
63
38
  */
64
- isReady(): boolean;
65
- /**
66
- * Refresh
67
- */
68
- refresh(): void;
69
- startRefreshing(): void;
70
- stopRefreshing(): void;
39
+ setContext(context: Context, replace?: boolean): void;
40
+ getContext(context?: Context): Context;
41
+ spawn(context?: Context, options?: OverrideOptions): FeaturevisorChildInstance;
71
42
  /**
72
43
  * Flag
73
44
  */
74
- evaluateFlag(featureKey: FeatureKey | Feature, context?: Context): Evaluation;
75
- isEnabled(featureKey: FeatureKey | Feature, context?: Context): boolean;
45
+ private getEvaluationDependencies;
46
+ evaluateFlag(featureKey: FeatureKey, context?: Context, options?: OverrideOptions): Evaluation;
47
+ isEnabled(featureKey: FeatureKey, context?: Context, options?: OverrideOptions): boolean;
76
48
  /**
77
49
  * Variation
78
50
  */
79
- evaluateVariation(featureKey: FeatureKey | Feature, context?: Context): Evaluation;
80
- getVariation(featureKey: FeatureKey | Feature, context?: Context): VariationValue | undefined;
81
- /**
82
- * Activate
83
- */
84
- activate(featureKey: FeatureKey, context?: Context): VariationValue | undefined;
51
+ evaluateVariation(featureKey: FeatureKey, context?: Context, options?: OverrideOptions): Evaluation;
52
+ getVariation(featureKey: FeatureKey, context?: Context, options?: OverrideOptions): VariationValue | null;
85
53
  /**
86
54
  * Variable
87
55
  */
88
- evaluateVariable(featureKey: FeatureKey | Feature, variableKey: VariableKey, context?: Context): Evaluation;
89
- getVariable(featureKey: FeatureKey | Feature, variableKey: string, context?: Context): VariableValue | undefined;
90
- getVariableBoolean(featureKey: FeatureKey | Feature, variableKey: string, context?: Context): boolean | undefined;
91
- getVariableString(featureKey: FeatureKey | Feature, variableKey: string, context?: Context): string | undefined;
92
- getVariableInteger(featureKey: FeatureKey | Feature, variableKey: string, context?: Context): number | undefined;
93
- getVariableDouble(featureKey: FeatureKey | Feature, variableKey: string, context?: Context): number | undefined;
94
- getVariableArray(featureKey: FeatureKey | Feature, variableKey: string, context?: Context): string[] | undefined;
95
- getVariableObject<T>(featureKey: FeatureKey | Feature, variableKey: string, context?: Context): T | undefined;
96
- getVariableJSON<T>(featureKey: FeatureKey | Feature, variableKey: string, context?: Context): T | undefined;
56
+ evaluateVariable(featureKey: FeatureKey, variableKey: VariableKey, context?: Context, options?: OverrideOptions): Evaluation;
57
+ getVariable(featureKey: FeatureKey, variableKey: string, context?: Context, options?: OverrideOptions): VariableValue | null;
58
+ getVariableBoolean(featureKey: FeatureKey, variableKey: string, context?: Context, options?: OverrideOptions): boolean | null;
59
+ getVariableString(featureKey: FeatureKey, variableKey: string, context?: Context, options?: OverrideOptions): string | null;
60
+ getVariableInteger(featureKey: FeatureKey, variableKey: string, context?: Context, options?: OverrideOptions): number | null;
61
+ getVariableDouble(featureKey: FeatureKey, variableKey: string, context?: Context, options?: OverrideOptions): number | null;
62
+ getVariableArray(featureKey: FeatureKey, variableKey: string, context?: Context, options?: OverrideOptions): string[] | null;
63
+ getVariableObject<T>(featureKey: FeatureKey, variableKey: string, context?: Context, options?: OverrideOptions): T | null;
64
+ getVariableJSON<T>(featureKey: FeatureKey, variableKey: string, context?: Context, options?: OverrideOptions): T | null;
65
+ getAllEvaluations(context?: Context, featureKeys?: string[], options?: OverrideOptions): EvaluatedFeatures;
97
66
  }
98
67
  export declare function createInstance(options: InstanceOptions): FeaturevisorInstance;
99
- export {};
package/lib/logger.d.ts CHANGED
@@ -1,21 +1,22 @@
1
- export type LogLevel = "debug" | "info" | "warn" | "error";
1
+ export type LogLevel = "fatal" | "error" | "warn" | "info" | "debug";
2
2
  export type LogMessage = string;
3
3
  export interface LogDetails {
4
4
  [key: string]: any;
5
5
  }
6
6
  export type LogHandler = (level: LogLevel, message: LogMessage, details?: LogDetails) => void;
7
7
  export interface CreateLoggerOptions {
8
- levels?: LogLevel[];
8
+ level?: LogLevel;
9
9
  handler?: LogHandler;
10
10
  }
11
11
  export declare const loggerPrefix = "[Featurevisor]";
12
- export declare const defaultLogLevels: LogLevel[];
13
12
  export declare const defaultLogHandler: LogHandler;
14
13
  export declare class Logger {
15
- private levels;
14
+ static allLevels: LogLevel[];
15
+ static defaultLevel: LogLevel;
16
+ private level;
16
17
  private handle;
17
18
  constructor(options: CreateLoggerOptions);
18
- setLevels(levels: LogLevel[]): void;
19
+ setLevel(level: LogLevel): void;
19
20
  log(level: LogLevel, message: LogMessage, details?: LogDetails): void;
20
21
  debug(message: LogMessage, details?: LogDetails): void;
21
22
  info(message: LogMessage, details?: LogDetails): void;
@@ -0,0 +1 @@
1
+ export declare function MurmurHashV3(key: any, seed: any): number;