@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.
Files changed (42) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +226 -0
  3. package/dist/FtureXClient.d.ts +72 -0
  4. package/dist/FtureXClient.js +427 -0
  5. package/dist/angular/feature-toggle.directive.d.ts +32 -0
  6. package/dist/angular/feature-toggle.directive.js +77 -0
  7. package/dist/angular/feature-toggle.pipe.d.ts +20 -0
  8. package/dist/angular/feature-toggle.pipe.js +37 -0
  9. package/dist/angular/fturex.config.d.ts +7 -0
  10. package/dist/angular/fturex.config.js +2 -0
  11. package/dist/angular/fturex.module.d.ts +23 -0
  12. package/dist/angular/fturex.module.js +48 -0
  13. package/dist/angular/fturex.service.d.ts +31 -0
  14. package/dist/angular/fturex.service.js +56 -0
  15. package/dist/angular/index.d.ts +6 -0
  16. package/dist/angular/index.js +5 -0
  17. package/dist/index.d.ts +2 -0
  18. package/dist/index.js +2 -0
  19. package/dist/opentelemetry/FtureXOtelHook.d.ts +58 -0
  20. package/dist/opentelemetry/FtureXOtelHook.js +86 -0
  21. package/dist/opentelemetry/index.d.ts +2 -0
  22. package/dist/opentelemetry/index.js +1 -0
  23. package/dist/react/FeatureToggle.d.ts +14 -0
  24. package/dist/react/FeatureToggle.js +16 -0
  25. package/dist/react/FeatureToggleProvider.d.ts +15 -0
  26. package/dist/react/FeatureToggleProvider.js +17 -0
  27. package/dist/react/index.d.ts +3 -0
  28. package/dist/react/index.js +3 -0
  29. package/dist/react/useFeatureToggle.d.ts +27 -0
  30. package/dist/react/useFeatureToggle.js +104 -0
  31. package/dist/svelte/index.d.ts +2 -0
  32. package/dist/svelte/index.js +1 -0
  33. package/dist/svelte/useFeatureToggle.d.ts +54 -0
  34. package/dist/svelte/useFeatureToggle.js +85 -0
  35. package/dist/types.d.ts +122 -0
  36. package/dist/types.js +1 -0
  37. package/dist/vue/index.d.ts +1 -0
  38. package/dist/vue/index.js +2 -0
  39. package/dist/vue/useFeatureToggle.d.ts +32 -0
  40. package/dist/vue/useFeatureToggle.js +104 -0
  41. package/package.json +99 -0
  42. package/src/vue/FeatureToggle.vue +28 -0
@@ -0,0 +1,31 @@
1
+ import { OnDestroy } from '@angular/core';
2
+ import { Observable } from 'rxjs';
3
+ import { ContextProperties } from '../types.js';
4
+ import { FtureXModuleConfig } from './fturex.config.js';
5
+ /**
6
+ * Angular injectable service wrapping FtureXClient.
7
+ * Provide via FtureXModule.forRoot() in your AppModule.
8
+ *
9
+ * @example
10
+ * constructor(private ftureX: FtureXService) {}
11
+ *
12
+ * ngOnInit() {
13
+ * this.ftureX.isEnabled('new-dashboard').subscribe(enabled => {
14
+ * this.showNewDashboard = enabled;
15
+ * });
16
+ * }
17
+ */
18
+ export declare class FtureXService implements OnDestroy {
19
+ private readonly client;
20
+ readonly initialized: Promise<void>;
21
+ constructor(moduleConfig: FtureXModuleConfig);
22
+ /**
23
+ * Check if a feature is enabled (no context)
24
+ */
25
+ isEnabled(featureName: string): Observable<boolean>;
26
+ /**
27
+ * Check if a feature is enabled with context properties
28
+ */
29
+ isEnabledWithContext(featureName: string, context: ContextProperties): Observable<boolean>;
30
+ ngOnDestroy(): void;
31
+ }
@@ -0,0 +1,56 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ var __metadata = (this && this.__metadata) || function (k, v) {
8
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
9
+ };
10
+ var __param = (this && this.__param) || function (paramIndex, decorator) {
11
+ return function (target, key) { decorator(target, key, paramIndex); }
12
+ };
13
+ import { Injectable, Inject } from '@angular/core';
14
+ import { from } from 'rxjs';
15
+ import { FtureXClient } from '../FtureXClient.js';
16
+ import { FTUREX_CONFIG } from './fturex.config.js';
17
+ /**
18
+ * Angular injectable service wrapping FtureXClient.
19
+ * Provide via FtureXModule.forRoot() in your AppModule.
20
+ *
21
+ * @example
22
+ * constructor(private ftureX: FtureXService) {}
23
+ *
24
+ * ngOnInit() {
25
+ * this.ftureX.isEnabled('new-dashboard').subscribe(enabled => {
26
+ * this.showNewDashboard = enabled;
27
+ * });
28
+ * }
29
+ */
30
+ let FtureXService = class FtureXService {
31
+ constructor(moduleConfig) {
32
+ this.client = new FtureXClient(moduleConfig.config, moduleConfig.cacheOptions);
33
+ this.initialized = this.client.initialize();
34
+ }
35
+ /**
36
+ * Check if a feature is enabled (no context)
37
+ */
38
+ isEnabled(featureName) {
39
+ return from(this.client.isEnabled(featureName));
40
+ }
41
+ /**
42
+ * Check if a feature is enabled with context properties
43
+ */
44
+ isEnabledWithContext(featureName, context) {
45
+ return from(this.client.isEnabledWithContext(featureName, context));
46
+ }
47
+ ngOnDestroy() {
48
+ this.client.dispose();
49
+ }
50
+ };
51
+ FtureXService = __decorate([
52
+ Injectable(),
53
+ __param(0, Inject(FTUREX_CONFIG)),
54
+ __metadata("design:paramtypes", [Object])
55
+ ], FtureXService);
56
+ export { FtureXService };
@@ -0,0 +1,6 @@
1
+ export { FtureXModule } from './fturex.module.js';
2
+ export { FtureXService } from './fturex.service.js';
3
+ export { FeatureToggleDirective } from './feature-toggle.directive.js';
4
+ export { FeatureTogglePipe } from './feature-toggle.pipe.js';
5
+ export { FTUREX_CONFIG } from './fturex.config.js';
6
+ export type { FtureXModuleConfig } from './fturex.config.js';
@@ -0,0 +1,5 @@
1
+ export { FtureXModule } from './fturex.module.js';
2
+ export { FtureXService } from './fturex.service.js';
3
+ export { FeatureToggleDirective } from './feature-toggle.directive.js';
4
+ export { FeatureTogglePipe } from './feature-toggle.pipe.js';
5
+ export { FTUREX_CONFIG } from './fturex.config.js';
@@ -0,0 +1,2 @@
1
+ export { FtureXClient } from './FtureXClient.js';
2
+ export type { FtureXConfiguration, FeatureCacheOptions, ContextProperties, FeatureStatistics, StatisticsReport, FtureXHook, HookContext, EvaluationResult, EvaluationReason } from './types.js';
package/dist/index.js ADDED
@@ -0,0 +1,2 @@
1
+ // Core client
2
+ export { FtureXClient } from './FtureXClient.js';
@@ -0,0 +1,58 @@
1
+ import type { FtureXHook, HookContext, EvaluationResult } from '../types.js';
2
+ /**
3
+ * Options for the FtureX OpenTelemetry hook.
4
+ */
5
+ export interface FtureXOtelOptions {
6
+ /** Add span events to the current active span on each evaluation. Default: true */
7
+ addSpanEvents?: boolean;
8
+ /** Emit evaluation counters as OTel metrics. Default: true */
9
+ addMetrics?: boolean;
10
+ /** Include the evaluated flag value in span event attributes. Default: false */
11
+ recordFlagValue?: boolean;
12
+ /** Optional identifier for the feature flag set (e.g. environment name). */
13
+ setId?: string;
14
+ }
15
+ /**
16
+ * Minimal OTel API surface used by this hook.
17
+ * Compatible with `@opentelemetry/api` — pass the module directly.
18
+ */
19
+ export interface OtelApi {
20
+ trace: {
21
+ getActiveSpan(): {
22
+ addEvent(name: string, attributes?: Record<string, string>): void;
23
+ } | undefined;
24
+ };
25
+ metrics: {
26
+ getMeter(name: string): {
27
+ createCounter(name: string, options?: {
28
+ description?: string;
29
+ }): {
30
+ add(value: number, attributes?: Record<string, string>): void;
31
+ };
32
+ };
33
+ };
34
+ }
35
+ /**
36
+ * OpenTelemetry hook for FtureX.
37
+ * Emits span events and metrics following OTel semantic conventions for feature flags.
38
+ *
39
+ * Requires `@opentelemetry/api` as a peer dependency.
40
+ *
41
+ * @example
42
+ * ```ts
43
+ * import { FtureXOtelHook } from 'fturex/opentelemetry';
44
+ * import * as api from '@opentelemetry/api';
45
+ *
46
+ * const otelHook = new FtureXOtelHook(api, { recordFlagValue: true });
47
+ * client.addHook(otelHook);
48
+ * ```
49
+ */
50
+ export declare class FtureXOtelHook implements FtureXHook {
51
+ private readonly options;
52
+ private readonly otel;
53
+ private evalSuccessCounter?;
54
+ private evalErrorCounter?;
55
+ constructor(otel: OtelApi, options?: FtureXOtelOptions);
56
+ after(context: HookContext, result: EvaluationResult): void;
57
+ error(context: HookContext, error: unknown): void;
58
+ }
@@ -0,0 +1,86 @@
1
+ /**
2
+ * OpenTelemetry hook for FtureX.
3
+ * Emits span events and metrics following OTel semantic conventions for feature flags.
4
+ *
5
+ * Requires `@opentelemetry/api` as a peer dependency.
6
+ *
7
+ * @example
8
+ * ```ts
9
+ * import { FtureXOtelHook } from 'fturex/opentelemetry';
10
+ * import * as api from '@opentelemetry/api';
11
+ *
12
+ * const otelHook = new FtureXOtelHook(api, { recordFlagValue: true });
13
+ * client.addHook(otelHook);
14
+ * ```
15
+ */
16
+ export class FtureXOtelHook {
17
+ constructor(otel, options = {}) {
18
+ this.otel = otel;
19
+ this.options = {
20
+ addSpanEvents: options.addSpanEvents ?? true,
21
+ addMetrics: options.addMetrics ?? true,
22
+ recordFlagValue: options.recordFlagValue ?? false,
23
+ setId: options.setId ?? '',
24
+ };
25
+ if (this.options.addMetrics) {
26
+ const meter = otel.metrics.getMeter('fturex');
27
+ this.evalSuccessCounter = meter.createCounter('feature_flag.evaluation_success_total', {
28
+ description: 'Number of successful feature flag evaluations',
29
+ });
30
+ this.evalErrorCounter = meter.createCounter('feature_flag.evaluation_error_total', {
31
+ description: 'Number of failed feature flag evaluations',
32
+ });
33
+ }
34
+ }
35
+ after(context, result) {
36
+ if (this.options.addSpanEvents) {
37
+ const span = this.otel.trace.getActiveSpan();
38
+ if (span) {
39
+ const attributes = {
40
+ 'feature_flag.key': context.flagKey,
41
+ 'feature_flag.provider.name': 'FtureX',
42
+ 'feature_flag.result.reason': result.reason,
43
+ };
44
+ if (this.options.recordFlagValue) {
45
+ attributes['feature_flag.result.value'] = String(result.value);
46
+ }
47
+ if (this.options.setId) {
48
+ attributes['feature_flag.set.id'] = this.options.setId;
49
+ }
50
+ if (context.evaluationContext) {
51
+ const contextId = context.evaluationContext['userId'] ?? context.evaluationContext['tenantId'];
52
+ if (contextId) {
53
+ attributes['feature_flag.context.id'] = contextId;
54
+ }
55
+ }
56
+ span.addEvent('feature_flag.evaluation', attributes);
57
+ }
58
+ }
59
+ if (this.options.addMetrics && this.evalSuccessCounter) {
60
+ this.evalSuccessCounter.add(1, {
61
+ 'feature_flag.key': context.flagKey,
62
+ 'feature_flag.provider.name': 'FtureX',
63
+ });
64
+ }
65
+ }
66
+ error(context, error) {
67
+ if (this.options.addSpanEvents) {
68
+ const span = this.otel.trace.getActiveSpan();
69
+ if (span) {
70
+ span.addEvent('feature_flag.evaluation', {
71
+ 'feature_flag.key': context.flagKey,
72
+ 'feature_flag.provider.name': 'FtureX',
73
+ 'feature_flag.result.reason': 'error',
74
+ 'error.type': error instanceof Error ? error.constructor.name : 'Error',
75
+ });
76
+ }
77
+ }
78
+ if (this.options.addMetrics && this.evalErrorCounter) {
79
+ this.evalErrorCounter.add(1, {
80
+ 'feature_flag.key': context.flagKey,
81
+ 'feature_flag.provider.name': 'FtureX',
82
+ 'error.type': error instanceof Error ? error.constructor.name : 'Error',
83
+ });
84
+ }
85
+ }
86
+ }
@@ -0,0 +1,2 @@
1
+ export { FtureXOtelHook } from './FtureXOtelHook.js';
2
+ export type { FtureXOtelOptions } from './FtureXOtelHook.js';
@@ -0,0 +1 @@
1
+ export { FtureXOtelHook } from './FtureXOtelHook.js';
@@ -0,0 +1,14 @@
1
+ import React, { ReactNode } from "react";
2
+ import { ContextProperties } from "../types.js";
3
+ interface FeatureToggleProps {
4
+ featureName: string;
5
+ context?: ContextProperties;
6
+ children: ReactNode;
7
+ fallback?: ReactNode;
8
+ loading?: ReactNode;
9
+ }
10
+ /**
11
+ * React component for conditional rendering based on feature toggles
12
+ */
13
+ export declare const FeatureToggle: React.FC<FeatureToggleProps>;
14
+ export {};
@@ -0,0 +1,16 @@
1
+ import React from "react";
2
+ import { useFeatureToggle } from "./useFeatureToggle.js";
3
+ /**
4
+ * React component for conditional rendering based on feature toggles
5
+ */
6
+ export const FeatureToggle = ({ featureName, context, children, fallback = null, loading: loadingComponent = null, }) => {
7
+ const { isEnabled, loading, error } = useFeatureToggle(featureName, context);
8
+ if (loading) {
9
+ return React.createElement(React.Fragment, null, loadingComponent);
10
+ }
11
+ if (error) {
12
+ console.error(`Error checking feature '${featureName}':`, error);
13
+ return React.createElement(React.Fragment, null, fallback);
14
+ }
15
+ return React.createElement(React.Fragment, null, isEnabled ? children : fallback);
16
+ };
@@ -0,0 +1,15 @@
1
+ import React, { ReactNode } from 'react';
2
+ import { FtureXClient } from '../FtureXClient.js';
3
+ import { FtureXConfiguration, FeatureCacheOptions } from '../types.js';
4
+ export declare const FeatureToggleContext: React.Context<FtureXClient | null>;
5
+ interface FeatureToggleProviderProps {
6
+ config: FtureXConfiguration;
7
+ cacheOptions?: FeatureCacheOptions;
8
+ children: ReactNode;
9
+ }
10
+ /**
11
+ * React Provider for Feature Toggle Client
12
+ * Wrap your app with this provider to enable feature toggle hooks
13
+ */
14
+ export declare const FeatureToggleProvider: React.FC<FeatureToggleProviderProps>;
15
+ export {};
@@ -0,0 +1,17 @@
1
+ import React, { createContext, useEffect, useState } from 'react';
2
+ import { FtureXClient } from '../FtureXClient.js';
3
+ export const FeatureToggleContext = createContext(null);
4
+ /**
5
+ * React Provider for Feature Toggle Client
6
+ * Wrap your app with this provider to enable feature toggle hooks
7
+ */
8
+ export const FeatureToggleProvider = ({ config, cacheOptions, children }) => {
9
+ const [client] = useState(() => new FtureXClient(config, cacheOptions));
10
+ useEffect(() => {
11
+ client.initialize();
12
+ return () => {
13
+ client.dispose();
14
+ };
15
+ }, [client]);
16
+ return (React.createElement(FeatureToggleContext.Provider, { value: client }, children));
17
+ };
@@ -0,0 +1,3 @@
1
+ export { FeatureToggleProvider, FeatureToggleContext } from './FeatureToggleProvider.js';
2
+ export { useFeatureToggle, useFeatureToggles } from './useFeatureToggle.js';
3
+ export { FeatureToggle } from './FeatureToggle.js';
@@ -0,0 +1,3 @@
1
+ export { FeatureToggleProvider, FeatureToggleContext } from './FeatureToggleProvider.js';
2
+ export { useFeatureToggle, useFeatureToggles } from './useFeatureToggle.js';
3
+ export { FeatureToggle } from './FeatureToggle.js';
@@ -0,0 +1,27 @@
1
+ import { ContextProperties } from '../types.js';
2
+ /**
3
+ * React hook for checking feature toggles.
4
+ * Re-evaluates automatically whenever the client refreshes its manifest,
5
+ * so the UI updates without a page reload.
6
+ *
7
+ * @param featureName - Name of the feature to check
8
+ * @param context - Optional context properties for the feature check
9
+ * @returns Object with isEnabled state, loading state, and error
10
+ */
11
+ export declare function useFeatureToggle(featureName: string, context?: ContextProperties): {
12
+ isEnabled: boolean;
13
+ loading: boolean;
14
+ error: Error | null;
15
+ };
16
+ /**
17
+ * React hook for checking multiple feature toggles at once.
18
+ * Re-evaluates automatically whenever the client refreshes its manifest.
19
+ *
20
+ * @param featureNames - Array of feature names to check
21
+ * @returns Object with features map, loading state, and error
22
+ */
23
+ export declare function useFeatureToggles(featureNames: string[]): {
24
+ features: Record<string, boolean>;
25
+ loading: boolean;
26
+ error: Error | null;
27
+ };
@@ -0,0 +1,104 @@
1
+ import { useState, useEffect, useContext } from 'react';
2
+ import { FeatureToggleContext } from './FeatureToggleProvider.js';
3
+ /**
4
+ * React hook 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 Object with isEnabled state, loading state, and error
11
+ */
12
+ export function useFeatureToggle(featureName, context) {
13
+ const client = useContext(FeatureToggleContext);
14
+ const [isEnabled, setIsEnabled] = useState(false);
15
+ const [loading, setLoading] = useState(true);
16
+ const [error, setError] = useState(null);
17
+ useEffect(() => {
18
+ if (!client) {
19
+ setError(new Error('FeatureToggleClient not provided. Wrap your app with FeatureToggleProvider.'));
20
+ setLoading(false);
21
+ return;
22
+ }
23
+ let mounted = true;
24
+ const evaluate = async () => {
25
+ try {
26
+ const enabled = context
27
+ ? await client.isEnabledWithContext(featureName, context)
28
+ : await client.isEnabled(featureName);
29
+ if (mounted) {
30
+ setIsEnabled(enabled);
31
+ setError(null);
32
+ setLoading(false);
33
+ }
34
+ }
35
+ catch (err) {
36
+ if (mounted) {
37
+ setError(err);
38
+ setIsEnabled(false);
39
+ setLoading(false);
40
+ }
41
+ }
42
+ };
43
+ // Subscribe to manifest updates — re-evaluate whenever the client fetches new data
44
+ client.on('update', evaluate);
45
+ client.on('ready', evaluate);
46
+ // Also evaluate immediately in case the manifest is already loaded
47
+ evaluate();
48
+ return () => {
49
+ mounted = false;
50
+ client.off('update', evaluate);
51
+ client.off('ready', evaluate);
52
+ };
53
+ }, [client, featureName, JSON.stringify(context)]);
54
+ return { isEnabled, loading, error };
55
+ }
56
+ /**
57
+ * React hook for checking multiple feature toggles at once.
58
+ * Re-evaluates automatically whenever the client refreshes its manifest.
59
+ *
60
+ * @param featureNames - Array of feature names to check
61
+ * @returns Object with features map, loading state, and error
62
+ */
63
+ export function useFeatureToggles(featureNames) {
64
+ const client = useContext(FeatureToggleContext);
65
+ const [features, setFeatures] = useState({});
66
+ const [loading, setLoading] = useState(true);
67
+ const [error, setError] = useState(null);
68
+ useEffect(() => {
69
+ if (!client) {
70
+ setError(new Error('FeatureToggleClient not provided. Wrap your app with FeatureToggleProvider.'));
71
+ setLoading(false);
72
+ return;
73
+ }
74
+ let mounted = true;
75
+ const evaluate = async () => {
76
+ try {
77
+ const results = {};
78
+ await Promise.all(featureNames.map(async (name) => {
79
+ results[name] = await client.isEnabled(name);
80
+ }));
81
+ if (mounted) {
82
+ setFeatures(results);
83
+ setError(null);
84
+ setLoading(false);
85
+ }
86
+ }
87
+ catch (err) {
88
+ if (mounted) {
89
+ setError(err);
90
+ setLoading(false);
91
+ }
92
+ }
93
+ };
94
+ client.on('update', evaluate);
95
+ client.on('ready', evaluate);
96
+ evaluate();
97
+ return () => {
98
+ mounted = false;
99
+ client.off('update', evaluate);
100
+ client.off('ready', evaluate);
101
+ };
102
+ }, [client, JSON.stringify(featureNames)]);
103
+ return { features, loading, error };
104
+ }
@@ -0,0 +1,2 @@
1
+ export { useFeatureToggle, useFeatureToggles } from './useFeatureToggle.js';
2
+ export type { FeatureToggleState, FeatureTogglesState } from './useFeatureToggle.js';
@@ -0,0 +1 @@
1
+ export { useFeatureToggle, useFeatureToggles } from './useFeatureToggle.js';
@@ -0,0 +1,54 @@
1
+ import { type Readable } from 'svelte/store';
2
+ import { FtureXClient } from '../FtureXClient.js';
3
+ import { ContextProperties } from '../types.js';
4
+ export interface FeatureToggleState {
5
+ isEnabled: boolean;
6
+ loading: boolean;
7
+ error: Error | null;
8
+ }
9
+ export interface FeatureTogglesState {
10
+ features: Record<string, boolean>;
11
+ loading: boolean;
12
+ error: Error | null;
13
+ }
14
+ /**
15
+ * Creates a Svelte readable store that resolves to a feature toggle state.
16
+ * Re-evaluates automatically whenever the client refreshes its manifest,
17
+ * so the UI updates without a page reload.
18
+ *
19
+ * @example
20
+ * <script>
21
+ * import { useFeatureToggle } from 'fturex/svelte';
22
+ * import { client } from './fturex';
23
+ *
24
+ * const feature = useFeatureToggle(client, 'new-dashboard');
25
+ * </script>
26
+ *
27
+ * {#if $feature.loading}
28
+ * <Spinner />
29
+ * {:else if $feature.isEnabled}
30
+ * <NewDashboard />
31
+ * {:else}
32
+ * <LegacyDashboard />
33
+ * {/if}
34
+ */
35
+ export declare function useFeatureToggle(client: FtureXClient, featureName: string, context?: ContextProperties): Readable<FeatureToggleState>;
36
+ /**
37
+ * Creates a Svelte readable store that resolves to a map of feature toggle states.
38
+ * Re-evaluates automatically whenever the client refreshes its manifest.
39
+ *
40
+ * @example
41
+ * <script>
42
+ * import { useFeatureToggles } from 'fturex/svelte';
43
+ * import { client } from './fturex';
44
+ *
45
+ * const features = useFeatureToggles(client, ['feature-a', 'feature-b']);
46
+ * </script>
47
+ *
48
+ * {#if !$features.loading}
49
+ * {#if $features.features['feature-a']}
50
+ * <FeatureA />
51
+ * {/if}
52
+ * {/if}
53
+ */
54
+ export declare function useFeatureToggles(client: FtureXClient, featureNames: string[]): Readable<FeatureTogglesState>;
@@ -0,0 +1,85 @@
1
+ import { readable } from 'svelte/store';
2
+ /**
3
+ * Creates a Svelte readable store that resolves to a feature toggle state.
4
+ * Re-evaluates automatically whenever the client refreshes its manifest,
5
+ * so the UI updates without a page reload.
6
+ *
7
+ * @example
8
+ * <script>
9
+ * import { useFeatureToggle } from 'fturex/svelte';
10
+ * import { client } from './fturex';
11
+ *
12
+ * const feature = useFeatureToggle(client, 'new-dashboard');
13
+ * </script>
14
+ *
15
+ * {#if $feature.loading}
16
+ * <Spinner />
17
+ * {:else if $feature.isEnabled}
18
+ * <NewDashboard />
19
+ * {:else}
20
+ * <LegacyDashboard />
21
+ * {/if}
22
+ */
23
+ export function useFeatureToggle(client, featureName, context) {
24
+ return readable({ isEnabled: false, loading: true, error: null }, (set) => {
25
+ const evaluate = async () => {
26
+ try {
27
+ const enabled = context
28
+ ? await client.isEnabledWithContext(featureName, context)
29
+ : await client.isEnabled(featureName);
30
+ set({ isEnabled: enabled, loading: false, error: null });
31
+ }
32
+ catch (err) {
33
+ set({ isEnabled: false, loading: false, error: err });
34
+ }
35
+ };
36
+ client.on('update', evaluate);
37
+ client.on('ready', evaluate);
38
+ evaluate();
39
+ return () => {
40
+ client.off('update', evaluate);
41
+ client.off('ready', evaluate);
42
+ };
43
+ });
44
+ }
45
+ /**
46
+ * Creates a Svelte readable store that resolves to a map of feature toggle states.
47
+ * Re-evaluates automatically whenever the client refreshes its manifest.
48
+ *
49
+ * @example
50
+ * <script>
51
+ * import { useFeatureToggles } from 'fturex/svelte';
52
+ * import { client } from './fturex';
53
+ *
54
+ * const features = useFeatureToggles(client, ['feature-a', 'feature-b']);
55
+ * </script>
56
+ *
57
+ * {#if !$features.loading}
58
+ * {#if $features.features['feature-a']}
59
+ * <FeatureA />
60
+ * {/if}
61
+ * {/if}
62
+ */
63
+ export function useFeatureToggles(client, featureNames) {
64
+ return readable({ features: {}, loading: true, error: null }, (set) => {
65
+ const evaluate = async () => {
66
+ try {
67
+ const results = {};
68
+ await Promise.all(featureNames.map(async (name) => {
69
+ results[name] = await client.isEnabled(name);
70
+ }));
71
+ set({ features: results, loading: false, error: null });
72
+ }
73
+ catch (err) {
74
+ set({ features: {}, loading: false, error: err });
75
+ }
76
+ };
77
+ client.on('update', evaluate);
78
+ client.on('ready', evaluate);
79
+ evaluate();
80
+ return () => {
81
+ client.off('update', evaluate);
82
+ client.off('ready', evaluate);
83
+ };
84
+ });
85
+ }