@anterprize/fturex 0.0.1
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/LICENSE +21 -0
- package/README.md +226 -0
- package/dist/FtureXClient.d.ts +72 -0
- package/dist/FtureXClient.js +427 -0
- package/dist/angular/feature-toggle.directive.d.ts +32 -0
- package/dist/angular/feature-toggle.directive.js +77 -0
- package/dist/angular/feature-toggle.pipe.d.ts +20 -0
- package/dist/angular/feature-toggle.pipe.js +37 -0
- package/dist/angular/fturex.config.d.ts +7 -0
- package/dist/angular/fturex.config.js +2 -0
- package/dist/angular/fturex.module.d.ts +23 -0
- package/dist/angular/fturex.module.js +48 -0
- package/dist/angular/fturex.service.d.ts +31 -0
- package/dist/angular/fturex.service.js +56 -0
- package/dist/angular/index.d.ts +6 -0
- package/dist/angular/index.js +5 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/opentelemetry/FtureXOtelHook.d.ts +58 -0
- package/dist/opentelemetry/FtureXOtelHook.js +86 -0
- package/dist/opentelemetry/index.d.ts +2 -0
- package/dist/opentelemetry/index.js +1 -0
- package/dist/react/FeatureToggle.d.ts +14 -0
- package/dist/react/FeatureToggle.js +16 -0
- package/dist/react/FeatureToggleProvider.d.ts +15 -0
- package/dist/react/FeatureToggleProvider.js +17 -0
- package/dist/react/index.d.ts +3 -0
- package/dist/react/index.js +3 -0
- package/dist/react/useFeatureToggle.d.ts +27 -0
- package/dist/react/useFeatureToggle.js +104 -0
- package/dist/svelte/index.d.ts +2 -0
- package/dist/svelte/index.js +1 -0
- package/dist/svelte/useFeatureToggle.d.ts +54 -0
- package/dist/svelte/useFeatureToggle.js +85 -0
- package/dist/types.d.ts +122 -0
- package/dist/types.js +1 -0
- package/dist/vue/index.d.ts +1 -0
- package/dist/vue/index.js +2 -0
- package/dist/vue/useFeatureToggle.d.ts +32 -0
- package/dist/vue/useFeatureToggle.js +104 -0
- package/package.json +99 -0
- package/src/vue/FeatureToggle.vue +28 -0
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configuration for the FtureX Client
|
|
3
|
+
*/
|
|
4
|
+
export interface FtureXConfiguration {
|
|
5
|
+
/**
|
|
6
|
+
* Base URL of the feature toggle API
|
|
7
|
+
*/
|
|
8
|
+
baseUrl: string;
|
|
9
|
+
/**
|
|
10
|
+
* Application API key for authentication
|
|
11
|
+
*/
|
|
12
|
+
appKey: string;
|
|
13
|
+
/**
|
|
14
|
+
* Enable sending statistics to the API
|
|
15
|
+
* @default true
|
|
16
|
+
*/
|
|
17
|
+
sendStatistics?: boolean;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Cache options for feature toggles
|
|
21
|
+
*/
|
|
22
|
+
export interface FeatureCacheOptions {
|
|
23
|
+
/**
|
|
24
|
+
* Interval in seconds to refresh the feature manifest from the API
|
|
25
|
+
* @default 30
|
|
26
|
+
*/
|
|
27
|
+
refreshIntervalSeconds?: number;
|
|
28
|
+
/**
|
|
29
|
+
* Enable persisting the manifest to localStorage
|
|
30
|
+
* @default true
|
|
31
|
+
*/
|
|
32
|
+
enableLocalStoragePersistence?: boolean;
|
|
33
|
+
/**
|
|
34
|
+
* Key used for localStorage persistence
|
|
35
|
+
* @default 'feature-toggle-cache'
|
|
36
|
+
*/
|
|
37
|
+
localStorageKey?: string;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Context properties for feature evaluation
|
|
41
|
+
*/
|
|
42
|
+
export interface ContextProperties {
|
|
43
|
+
[key: string]: string;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* A single condition rule as returned by the server manifest
|
|
47
|
+
*/
|
|
48
|
+
export interface ConditionRule {
|
|
49
|
+
contextProperty: string;
|
|
50
|
+
operator: string;
|
|
51
|
+
value?: string;
|
|
52
|
+
conditionGroupValues?: string[];
|
|
53
|
+
/**
|
|
54
|
+
* Logical operator connecting this rule to the NEXT one.
|
|
55
|
+
* 'AND' (default when absent) — both this and the next rule must match.
|
|
56
|
+
* 'OR' — either this AND-segment or the next must match.
|
|
57
|
+
* Ignored on the last rule in the array.
|
|
58
|
+
*/
|
|
59
|
+
logicAfter?: 'AND' | 'OR';
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* A single feature entry in the manifest
|
|
63
|
+
*/
|
|
64
|
+
export interface ManifestEntry {
|
|
65
|
+
name: string;
|
|
66
|
+
enabled: boolean;
|
|
67
|
+
conditions: ConditionRule[];
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Full feature manifest returned by GET /feature/manifest
|
|
71
|
+
*/
|
|
72
|
+
export interface FeatureManifest {
|
|
73
|
+
features: ManifestEntry[];
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Statistics for a feature
|
|
77
|
+
*/
|
|
78
|
+
export interface FeatureStatistics {
|
|
79
|
+
featureName: string;
|
|
80
|
+
hitCount: number;
|
|
81
|
+
enabledCount: number;
|
|
82
|
+
disabledCount: number;
|
|
83
|
+
lastAccessed: Date;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Statistics report sent to the API
|
|
87
|
+
*/
|
|
88
|
+
export interface StatisticsReport {
|
|
89
|
+
appKey: string;
|
|
90
|
+
features: FeatureStatistics[];
|
|
91
|
+
reportTimestamp: Date;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Describes how the flag value was determined.
|
|
95
|
+
* Follows OTel semantic conventions for feature flags.
|
|
96
|
+
*/
|
|
97
|
+
export type EvaluationReason = 'static' | 'targeting_match' | 'default' | 'error' | 'stale' | 'disabled';
|
|
98
|
+
/**
|
|
99
|
+
* Context passed to hooks for each flag evaluation.
|
|
100
|
+
*/
|
|
101
|
+
export interface HookContext {
|
|
102
|
+
flagKey: string;
|
|
103
|
+
evaluationContext?: ContextProperties;
|
|
104
|
+
defaultValue: boolean;
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Result of a feature flag evaluation, passed to hooks.
|
|
108
|
+
*/
|
|
109
|
+
export interface EvaluationResult {
|
|
110
|
+
value: boolean;
|
|
111
|
+
reason: EvaluationReason;
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Hook interface for observing feature flag evaluations.
|
|
115
|
+
* Implement this to add custom telemetry, logging, or analytics.
|
|
116
|
+
* All methods are optional.
|
|
117
|
+
*/
|
|
118
|
+
export interface FtureXHook {
|
|
119
|
+
before?(context: HookContext): void | Promise<void>;
|
|
120
|
+
after?(context: HookContext, result: EvaluationResult): void | Promise<void>;
|
|
121
|
+
error?(context: HookContext, error: unknown): void | Promise<void>;
|
|
122
|
+
}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { FeatureToggleClientKey, useFeatureToggle, useFeatureToggles } from './useFeatureToggle.js';
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { Ref, InjectionKey } from 'vue';
|
|
2
|
+
import { FtureXClient } from '../FtureXClient.js';
|
|
3
|
+
import { ContextProperties } from '../types.js';
|
|
4
|
+
export declare const FeatureToggleClientKey: InjectionKey<FtureXClient>;
|
|
5
|
+
/**
|
|
6
|
+
* Vue composable for checking feature toggles.
|
|
7
|
+
* Re-evaluates automatically whenever the client refreshes its manifest,
|
|
8
|
+
* so the UI updates without a page reload.
|
|
9
|
+
*
|
|
10
|
+
* @param featureName - Name of the feature to check
|
|
11
|
+
* @param context - Optional context properties for the feature check
|
|
12
|
+
* @returns Reactive object with isEnabled, loading, and error states
|
|
13
|
+
*/
|
|
14
|
+
export declare function useFeatureToggle(featureName: string, context?: ContextProperties): {
|
|
15
|
+
isEnabled: Ref<boolean, boolean>;
|
|
16
|
+
loading: Ref<boolean, boolean>;
|
|
17
|
+
error: Ref<Error | null, Error | null>;
|
|
18
|
+
refresh: () => Promise<void>;
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* Vue composable for checking multiple feature toggles at once.
|
|
22
|
+
* Re-evaluates automatically whenever the client refreshes its manifest.
|
|
23
|
+
*
|
|
24
|
+
* @param featureNames - Array of feature names to check
|
|
25
|
+
* @returns Reactive object with features map, loading state, and error
|
|
26
|
+
*/
|
|
27
|
+
export declare function useFeatureToggles(featureNames: string[]): {
|
|
28
|
+
features: Ref<Record<string, boolean>, Record<string, boolean>>;
|
|
29
|
+
loading: Ref<boolean, boolean>;
|
|
30
|
+
error: Ref<Error | null, Error | null>;
|
|
31
|
+
refresh: () => Promise<void>;
|
|
32
|
+
};
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { ref, inject, onMounted, onUnmounted } from 'vue';
|
|
2
|
+
export const FeatureToggleClientKey = Symbol('FeatureToggleClient');
|
|
3
|
+
/**
|
|
4
|
+
* Vue composable for checking feature toggles.
|
|
5
|
+
* Re-evaluates automatically whenever the client refreshes its manifest,
|
|
6
|
+
* so the UI updates without a page reload.
|
|
7
|
+
*
|
|
8
|
+
* @param featureName - Name of the feature to check
|
|
9
|
+
* @param context - Optional context properties for the feature check
|
|
10
|
+
* @returns Reactive object with isEnabled, loading, and error states
|
|
11
|
+
*/
|
|
12
|
+
export function useFeatureToggle(featureName, context) {
|
|
13
|
+
const client = inject(FeatureToggleClientKey);
|
|
14
|
+
const isEnabled = ref(false);
|
|
15
|
+
const loading = ref(true);
|
|
16
|
+
const error = ref(null);
|
|
17
|
+
const evaluate = async () => {
|
|
18
|
+
if (!client) {
|
|
19
|
+
error.value = new Error('FeatureToggleClient not provided. Use app.provide() to provide the client.');
|
|
20
|
+
loading.value = false;
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
try {
|
|
24
|
+
loading.value = true;
|
|
25
|
+
const enabled = context
|
|
26
|
+
? await client.isEnabledWithContext(featureName, context)
|
|
27
|
+
: await client.isEnabled(featureName);
|
|
28
|
+
isEnabled.value = enabled;
|
|
29
|
+
error.value = null;
|
|
30
|
+
}
|
|
31
|
+
catch (err) {
|
|
32
|
+
error.value = err;
|
|
33
|
+
isEnabled.value = false;
|
|
34
|
+
}
|
|
35
|
+
finally {
|
|
36
|
+
loading.value = false;
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
onMounted(() => {
|
|
40
|
+
client?.on('update', evaluate);
|
|
41
|
+
client?.on('ready', evaluate);
|
|
42
|
+
evaluate();
|
|
43
|
+
});
|
|
44
|
+
onUnmounted(() => {
|
|
45
|
+
client?.off('update', evaluate);
|
|
46
|
+
client?.off('ready', evaluate);
|
|
47
|
+
});
|
|
48
|
+
return {
|
|
49
|
+
isEnabled,
|
|
50
|
+
loading,
|
|
51
|
+
error,
|
|
52
|
+
refresh: evaluate
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Vue composable for checking multiple feature toggles at once.
|
|
57
|
+
* Re-evaluates automatically whenever the client refreshes its manifest.
|
|
58
|
+
*
|
|
59
|
+
* @param featureNames - Array of feature names to check
|
|
60
|
+
* @returns Reactive object with features map, loading state, and error
|
|
61
|
+
*/
|
|
62
|
+
export function useFeatureToggles(featureNames) {
|
|
63
|
+
const client = inject(FeatureToggleClientKey);
|
|
64
|
+
const features = ref({});
|
|
65
|
+
const loading = ref(true);
|
|
66
|
+
const error = ref(null);
|
|
67
|
+
const evaluate = async () => {
|
|
68
|
+
if (!client) {
|
|
69
|
+
error.value = new Error('FeatureToggleClient not provided. Use app.provide() to provide the client.');
|
|
70
|
+
loading.value = false;
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
try {
|
|
74
|
+
loading.value = true;
|
|
75
|
+
const results = {};
|
|
76
|
+
await Promise.all(featureNames.map(async (name) => {
|
|
77
|
+
results[name] = await client.isEnabled(name);
|
|
78
|
+
}));
|
|
79
|
+
features.value = results;
|
|
80
|
+
error.value = null;
|
|
81
|
+
}
|
|
82
|
+
catch (err) {
|
|
83
|
+
error.value = err;
|
|
84
|
+
}
|
|
85
|
+
finally {
|
|
86
|
+
loading.value = false;
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
onMounted(() => {
|
|
90
|
+
client?.on('update', evaluate);
|
|
91
|
+
client?.on('ready', evaluate);
|
|
92
|
+
evaluate();
|
|
93
|
+
});
|
|
94
|
+
onUnmounted(() => {
|
|
95
|
+
client?.off('update', evaluate);
|
|
96
|
+
client?.off('ready', evaluate);
|
|
97
|
+
});
|
|
98
|
+
return {
|
|
99
|
+
features,
|
|
100
|
+
loading,
|
|
101
|
+
error,
|
|
102
|
+
refresh: evaluate
|
|
103
|
+
};
|
|
104
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@anterprize/fturex",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "Feature toggle client for React, Vue, Angular and Svelte applications",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"import": "./dist/index.js",
|
|
11
|
+
"types": "./dist/index.d.ts"
|
|
12
|
+
},
|
|
13
|
+
"./react": {
|
|
14
|
+
"import": "./dist/react/index.js",
|
|
15
|
+
"types": "./dist/react/index.d.ts"
|
|
16
|
+
},
|
|
17
|
+
"./vue": {
|
|
18
|
+
"import": "./dist/vue/index.js",
|
|
19
|
+
"types": "./dist/vue/index.d.ts"
|
|
20
|
+
},
|
|
21
|
+
"./vue/FeatureToggle.vue": "./src/vue/FeatureToggle.vue",
|
|
22
|
+
"./angular": {
|
|
23
|
+
"import": "./dist/angular/index.js",
|
|
24
|
+
"types": "./dist/angular/index.d.ts"
|
|
25
|
+
},
|
|
26
|
+
"./svelte": {
|
|
27
|
+
"import": "./dist/svelte/index.js",
|
|
28
|
+
"types": "./dist/svelte/index.d.ts"
|
|
29
|
+
},
|
|
30
|
+
"./opentelemetry": {
|
|
31
|
+
"import": "./dist/opentelemetry/index.js",
|
|
32
|
+
"types": "./dist/opentelemetry/index.d.ts"
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
"scripts": {
|
|
36
|
+
"build": "tsc",
|
|
37
|
+
"prepublishOnly": "npm run build",
|
|
38
|
+
"test": "jest",
|
|
39
|
+
"lint": "eslint src/**/*.ts"
|
|
40
|
+
},
|
|
41
|
+
"keywords": [
|
|
42
|
+
"feature-toggle",
|
|
43
|
+
"feature-flag",
|
|
44
|
+
"feature-flags",
|
|
45
|
+
"react",
|
|
46
|
+
"vue",
|
|
47
|
+
"angular",
|
|
48
|
+
"svelte",
|
|
49
|
+
"typescript"
|
|
50
|
+
],
|
|
51
|
+
"author": "Anterprize",
|
|
52
|
+
"license": "MIT",
|
|
53
|
+
"peerDependencies": {
|
|
54
|
+
"@angular/core": ">=15.0.0",
|
|
55
|
+
"react": "^16.8.0 || ^17.0.0 || ^18.0.0",
|
|
56
|
+
"svelte": ">=4.0.0",
|
|
57
|
+
"vue": "^3.0.0"
|
|
58
|
+
},
|
|
59
|
+
"peerDependenciesMeta": {
|
|
60
|
+
"@angular/core": {
|
|
61
|
+
"optional": true
|
|
62
|
+
},
|
|
63
|
+
"vue": {
|
|
64
|
+
"optional": true
|
|
65
|
+
},
|
|
66
|
+
"react": {
|
|
67
|
+
"optional": true
|
|
68
|
+
},
|
|
69
|
+
"svelte": {
|
|
70
|
+
"optional": true
|
|
71
|
+
}
|
|
72
|
+
},
|
|
73
|
+
"devDependencies": {
|
|
74
|
+
"@angular/core": "^17.0.0",
|
|
75
|
+
"@types/jest": "^30.0.0",
|
|
76
|
+
"@types/node": "^25.5.2",
|
|
77
|
+
"@types/react": "^18.0.0",
|
|
78
|
+
"@typescript-eslint/eslint-plugin": "^6.0.0",
|
|
79
|
+
"@typescript-eslint/parser": "^6.0.0",
|
|
80
|
+
"eslint": "^8.0.0",
|
|
81
|
+
"jest": "^29.0.0",
|
|
82
|
+
"jest-environment-jsdom": "^30.3.0",
|
|
83
|
+
"react": "^18.0.0",
|
|
84
|
+
"rxjs": "^7.8.0",
|
|
85
|
+
"svelte": "^5.55.1",
|
|
86
|
+
"ts-jest": "^29.4.6",
|
|
87
|
+
"typescript": "^5.0.0",
|
|
88
|
+
"vue": "^3.5.32"
|
|
89
|
+
},
|
|
90
|
+
"files": [
|
|
91
|
+
"dist",
|
|
92
|
+
"src/vue/FeatureToggle.vue",
|
|
93
|
+
"README.md"
|
|
94
|
+
],
|
|
95
|
+
"repository": {
|
|
96
|
+
"type": "git",
|
|
97
|
+
"url": "https://github.com/Anterprize/fturex-sdk"
|
|
98
|
+
}
|
|
99
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div v-if="loading">
|
|
3
|
+
<slot name="loading"></slot>
|
|
4
|
+
</div>
|
|
5
|
+
<div v-else-if="error">
|
|
6
|
+
<slot name="error" :error="error"></slot>
|
|
7
|
+
</div>
|
|
8
|
+
<div v-else>
|
|
9
|
+
<slot v-if="isEnabled"></slot>
|
|
10
|
+
</div>
|
|
11
|
+
</template>
|
|
12
|
+
|
|
13
|
+
<script setup lang="ts">
|
|
14
|
+
import { useFeatureToggle } from "./useFeatureToggle.js";
|
|
15
|
+
import { ContextProperties } from "../types.js";
|
|
16
|
+
|
|
17
|
+
interface Props {
|
|
18
|
+
featureName: string;
|
|
19
|
+
context?: ContextProperties;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const props = withDefaults(defineProps<Props>(), {});
|
|
23
|
+
|
|
24
|
+
const { isEnabled, loading, error } = useFeatureToggle(
|
|
25
|
+
props.featureName,
|
|
26
|
+
props.context,
|
|
27
|
+
);
|
|
28
|
+
</script>
|