featurefly 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.
@@ -0,0 +1,136 @@
1
+ // ═══════════════════════════════════════════════════════════════════════════════
2
+ // FeatureFly — Vue Composables
3
+ // ═══════════════════════════════════════════════════════════════════════════════
4
+ //
5
+ // Thin wrapper providing Vue 3 composables for FeatureFly SDK.
6
+ // Requires Vue 3.x (Composition API).
7
+ //
8
+ // Usage:
9
+ // import { FeatureFlyPlugin, useFeatureFlag, useAllFlags } from 'featurefly/vue';
10
+ //
11
+ // ═══════════════════════════════════════════════════════════════════════════════
12
+ import { inject, ref, onMounted, onUnmounted, watch, } from 'vue';
13
+ // ─── Plugin & Injection Key ─────────────────────────────────────────────────────
14
+ const FEATUREFLY_KEY = Symbol('featurefly');
15
+ /**
16
+ * Vue Plugin that provides the FeatureFlagsClient to the entire app.
17
+ *
18
+ * @example
19
+ * ```ts
20
+ * import { createApp } from 'vue';
21
+ * import { FeatureFlyPlugin } from 'featurefly/vue';
22
+ *
23
+ * const app = createApp(App);
24
+ * app.use(FeatureFlyPlugin, { client: featureFlyClient });
25
+ * ```
26
+ */
27
+ export const FeatureFlyPlugin = {
28
+ install(app, options) {
29
+ app.provide(FEATUREFLY_KEY, options.client);
30
+ },
31
+ };
32
+ function useClient() {
33
+ const client = inject(FEATUREFLY_KEY);
34
+ if (!client) {
35
+ throw new Error('useFeatureFlag must be called inside a component where FeatureFlyPlugin is installed. ' +
36
+ 'Use app.use(FeatureFlyPlugin, { client }) in your main.ts.');
37
+ }
38
+ return client;
39
+ }
40
+ // ─── useFeatureFlag ─────────────────────────────────────────────────────────────
41
+ /**
42
+ * Vue composable for evaluating a single feature flag.
43
+ * Returns a reactive `Ref` that auto-updates on flag changes.
44
+ *
45
+ * @param slug Flag slug identifier
46
+ * @param defaultValue Default value while loading
47
+ * @param context Optional reactive evaluation context
48
+ *
49
+ * @example
50
+ * ```vue
51
+ * <script setup>
52
+ * import { useFeatureFlag } from 'featurefly/vue';
53
+ *
54
+ * const darkMode = useFeatureFlag('dark-mode', false);
55
+ * </script>
56
+ *
57
+ * <template>
58
+ * <div :class="{ dark: darkMode.value }">...</div>
59
+ * </template>
60
+ * ```
61
+ */
62
+ export function useFeatureFlag(slug, defaultValue, context) {
63
+ const client = useClient();
64
+ const value = ref(defaultValue);
65
+ const unsubs = [];
66
+ const evaluate = async () => {
67
+ try {
68
+ const ctx = context && 'value' in context ? context.value : context;
69
+ const result = await client.evaluateFlag(slug, ctx);
70
+ value.value = result;
71
+ }
72
+ catch {
73
+ // Keep current value on error
74
+ }
75
+ };
76
+ onMounted(() => {
77
+ evaluate();
78
+ unsubs.push(client.on('flagsUpdated', () => evaluate()));
79
+ unsubs.push(client.on('flagChanged', (payload) => {
80
+ if (payload.slug === slug)
81
+ evaluate();
82
+ }));
83
+ // If context is reactive, re-evaluate when it changes
84
+ if (context && 'value' in context) {
85
+ watch(context, () => evaluate(), { deep: true });
86
+ }
87
+ });
88
+ onUnmounted(() => {
89
+ unsubs.forEach((u) => u());
90
+ });
91
+ return value;
92
+ }
93
+ // ─── useAllFlags ────────────────────────────────────────────────────────────────
94
+ /**
95
+ * Vue composable for batch-evaluating all feature flags.
96
+ * Returns a reactive `Ref<Record<string, FlagValue>>` that auto-updates.
97
+ *
98
+ * @example
99
+ * ```vue
100
+ * <script setup>
101
+ * import { useAllFlags } from 'featurefly/vue';
102
+ *
103
+ * const flags = useAllFlags({ workspaceId: 'ws-123' });
104
+ * </script>
105
+ *
106
+ * <template>
107
+ * <NewFeature v-if="flags['new-feature']" />
108
+ * </template>
109
+ * ```
110
+ */
111
+ export function useAllFlags(context) {
112
+ const client = useClient();
113
+ const flags = ref({});
114
+ const unsubs = [];
115
+ const evaluate = async () => {
116
+ try {
117
+ const ctx = context && 'value' in context ? context.value : context;
118
+ const result = await client.evaluateAllFlags(ctx);
119
+ flags.value = result;
120
+ }
121
+ catch {
122
+ // Keep current value on error
123
+ }
124
+ };
125
+ onMounted(() => {
126
+ evaluate();
127
+ unsubs.push(client.on('flagsUpdated', () => evaluate()));
128
+ if (context && 'value' in context) {
129
+ watch(context, () => evaluate(), { deep: true });
130
+ }
131
+ });
132
+ onUnmounted(() => {
133
+ unsubs.forEach((u) => u());
134
+ });
135
+ return flags;
136
+ }
package/package.json ADDED
@@ -0,0 +1,97 @@
1
+ {
2
+ "name": "featurefly",
3
+ "version": "0.1.0",
4
+ "description": "Lightweight, framework-agnostic Feature Flags SDK with built-in caching, retry, circuit breaker, targeting, A/B testing, and real-time streaming",
5
+ "main": "dist/index.js",
6
+ "module": "dist/index.esm.js",
7
+ "types": "dist/index.d.ts",
8
+ "files": [
9
+ "dist"
10
+ ],
11
+ "sideEffects": false,
12
+ "scripts": {
13
+ "build": "tsc && tsc --project tsconfig.esm.json",
14
+ "dev": "tsc --watch",
15
+ "test": "jest --coverage",
16
+ "test:watch": "jest --watch",
17
+ "lint": "eslint src --ext .ts",
18
+ "clean": "rm -rf dist",
19
+ "prepublishOnly": "npm run clean && npm run build && npm test"
20
+ },
21
+ "keywords": [
22
+ "feature-flags",
23
+ "feature-toggles",
24
+ "featurefly",
25
+ "sdk",
26
+ "typescript",
27
+ "framework-agnostic",
28
+ "feature-management",
29
+ "ab-testing",
30
+ "gradual-rollout",
31
+ "circuit-breaker",
32
+ "targeting",
33
+ "streaming",
34
+ "edge-evaluation",
35
+ "react-hooks",
36
+ "vue-composables"
37
+ ],
38
+ "author": "Arrua Platform Team",
39
+ "license": "MIT",
40
+ "homepage": "https://github.com/ArruaBrian/featurefly#readme",
41
+ "bugs": {
42
+ "url": "https://github.com/ArruaBrian/featurefly/issues"
43
+ },
44
+ "repository": {
45
+ "type": "git",
46
+ "url": "https://github.com/ArruaBrian/featurefly.git"
47
+ },
48
+ "dependencies": {
49
+ "axios": "^1.6.0"
50
+ },
51
+ "devDependencies": {
52
+ "@types/jest": "^29.0.0",
53
+ "@types/node": "^20.0.0",
54
+ "@types/react": "^18.3.28",
55
+ "@typescript-eslint/eslint-plugin": "^6.21.0",
56
+ "@typescript-eslint/parser": "^6.21.0",
57
+ "eslint": "^8.57.1",
58
+ "eslint-config-prettier": "^10.1.8",
59
+ "eslint-plugin-prettier": "^5.5.5",
60
+ "jest": "^29.0.0",
61
+ "ts-jest": "^29.0.0",
62
+ "typescript": "^5.0.0",
63
+ "vue": "^3.5.29"
64
+ },
65
+ "peerDependencies": {
66
+ "react": ">=18.0.0",
67
+ "vue": ">=3.0.0"
68
+ },
69
+ "peerDependenciesMeta": {
70
+ "react": {
71
+ "optional": true
72
+ },
73
+ "vue": {
74
+ "optional": true
75
+ }
76
+ },
77
+ "exports": {
78
+ ".": {
79
+ "import": "./dist/index.esm.js",
80
+ "require": "./dist/index.js",
81
+ "types": "./dist/index.d.ts"
82
+ },
83
+ "./react": {
84
+ "import": "./dist/react/index.js",
85
+ "require": "./dist/react/index.js",
86
+ "types": "./dist/react/index.d.ts"
87
+ },
88
+ "./vue": {
89
+ "import": "./dist/vue/index.js",
90
+ "require": "./dist/vue/index.js",
91
+ "types": "./dist/vue/index.d.ts"
92
+ }
93
+ },
94
+ "engines": {
95
+ "node": ">=18.0.0"
96
+ }
97
+ }