@ops-ai/astro-feature-flags-toggly 1.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 (48) hide show
  1. package/LICENSE +23 -0
  2. package/README.md +462 -0
  3. package/dist/chunk-354E3C57.js +173 -0
  4. package/dist/chunk-354E3C57.js.map +1 -0
  5. package/dist/chunk-4UKIT2NP.js +44 -0
  6. package/dist/chunk-4UKIT2NP.js.map +1 -0
  7. package/dist/chunk-FK633ULF.js +200 -0
  8. package/dist/chunk-FK633ULF.js.map +1 -0
  9. package/dist/chunk-XCJSQJHR.js +74 -0
  10. package/dist/chunk-XCJSQJHR.js.map +1 -0
  11. package/dist/chunk-XQGKGTBK.js +161 -0
  12. package/dist/chunk-XQGKGTBK.js.map +1 -0
  13. package/dist/client/setup.d.ts +3 -0
  14. package/dist/client/setup.js +21 -0
  15. package/dist/client/setup.js.map +1 -0
  16. package/dist/client/store.d.ts +59 -0
  17. package/dist/client/store.js +25 -0
  18. package/dist/client/store.js.map +1 -0
  19. package/dist/components/Feature.astro +79 -0
  20. package/dist/components/FeatureClient.astro +144 -0
  21. package/dist/frameworks/react/Feature.d.ts +86 -0
  22. package/dist/frameworks/react/Feature.js +14 -0
  23. package/dist/frameworks/react/Feature.js.map +1 -0
  24. package/dist/frameworks/react/index.d.ts +3 -0
  25. package/dist/frameworks/react/index.js +12 -0
  26. package/dist/frameworks/react/index.js.map +1 -0
  27. package/dist/frameworks/svelte/Feature.svelte +75 -0
  28. package/dist/frameworks/svelte/stores.d.ts +54 -0
  29. package/dist/frameworks/svelte/stores.js +34 -0
  30. package/dist/frameworks/svelte/stores.js.map +1 -0
  31. package/dist/frameworks/vue/Feature.vue +93 -0
  32. package/dist/frameworks/vue/composables.d.ts +56 -0
  33. package/dist/frameworks/vue/composables.js +39 -0
  34. package/dist/frameworks/vue/composables.js.map +1 -0
  35. package/dist/index-S3g0i0FH.d.ts +102 -0
  36. package/dist/index.d.ts +7 -0
  37. package/dist/index.js +47 -0
  38. package/dist/index.js.map +1 -0
  39. package/dist/integration/index.d.ts +24 -0
  40. package/dist/integration/index.js +10 -0
  41. package/dist/integration/index.js.map +1 -0
  42. package/dist/server/toggly-server.d.ts +53 -0
  43. package/dist/server/toggly-server.js +9 -0
  44. package/dist/server/toggly-server.js.map +1 -0
  45. package/dist/server/utils.d.ts +43 -0
  46. package/dist/server/utils.js +13 -0
  47. package/dist/server/utils.js.map +1 -0
  48. package/package.json +105 -0
@@ -0,0 +1,79 @@
1
+ ---
2
+ /**
3
+ * Feature - Server-rendered feature flag component for Astro
4
+ *
5
+ * This component evaluates feature flags on the server side during SSR/SSG.
6
+ * Use this for content that should be evaluated at build time or on each request.
7
+ *
8
+ * @example
9
+ * ```astro
10
+ * <Feature flag="new-dashboard">
11
+ * <p>This content is only shown when the feature is enabled</p>
12
+ * </Feature>
13
+ * ```
14
+ *
15
+ * @example Multiple flags with 'any' requirement
16
+ * ```astro
17
+ * <Feature flags={['feature1', 'feature2']} requirement="any">
18
+ * <p>Shown if at least one feature is enabled</p>
19
+ * </Feature>
20
+ * ```
21
+ *
22
+ * @example With fallback content
23
+ * ```astro
24
+ * <Feature flag="beta-feature">
25
+ * <p>Beta content</p>
26
+ * <div slot="fallback">
27
+ * <p>Beta feature not available yet</p>
28
+ * </div>
29
+ * </Feature>
30
+ * ```
31
+ */
32
+
33
+ interface Props {
34
+ /** Single feature flag key to check */
35
+ flag?: string;
36
+ /** Multiple feature flag keys to check */
37
+ flags?: string[];
38
+ /** Requirement for multiple flags: 'all' or 'any' (default: 'all') */
39
+ requirement?: 'all' | 'any';
40
+ /** If true, negates the result (default: false) */
41
+ negate?: boolean;
42
+ }
43
+
44
+ const { flag, flags, requirement = 'all', negate = false } = Astro.props;
45
+
46
+ // Get Toggly client from Astro.locals
47
+ const toggly = Astro.locals.toggly;
48
+
49
+ if (!toggly) {
50
+ console.warn(
51
+ '[Toggly Feature] Client not found in Astro.locals. ' +
52
+ 'Make sure the Toggly integration is properly configured.'
53
+ );
54
+ }
55
+
56
+ // Build feature keys array
57
+ const flagKeys: string[] = [];
58
+ if (flag) {
59
+ flagKeys.push(flag);
60
+ }
61
+ if (flags && Array.isArray(flags)) {
62
+ flagKeys.push(...flags);
63
+ }
64
+
65
+ // Evaluate feature gate
66
+ let isEnabled = false;
67
+ if (toggly && flagKeys.length > 0) {
68
+ try {
69
+ isEnabled = await toggly.evaluateGate(flagKeys, requirement, negate);
70
+ } catch (error) {
71
+ console.error('[Toggly Feature] Error evaluating feature gate:', error);
72
+ isEnabled = false;
73
+ }
74
+ }
75
+ ---
76
+
77
+ {isEnabled ? <slot /> : <slot name="fallback" />}
78
+
79
+
@@ -0,0 +1,144 @@
1
+ ---
2
+ /**
3
+ * FeatureClient - Client-side hydrated feature flag component
4
+ *
5
+ * This component evaluates feature flags on the client side using Astro islands.
6
+ * Use this when you need dynamic, client-side feature flag evaluation or when
7
+ * you want to update the UI based on flag changes without a page reload.
8
+ *
9
+ * @example Basic usage with client:load
10
+ * ```astro
11
+ * <FeatureClient flag="new-feature" client="load">
12
+ * <p>This content hydrates and checks the flag on the client</p>
13
+ * </FeatureClient>
14
+ * ```
15
+ *
16
+ * @example With client:visible for lazy loading
17
+ * ```astro
18
+ * <FeatureClient flag="beta-widget" client="visible">
19
+ * <BetaWidget />
20
+ * </FeatureClient>
21
+ * ```
22
+ *
23
+ * @example With fallback
24
+ * ```astro
25
+ * <FeatureClient flag="premium-feature" client="load">
26
+ * <PremiumContent />
27
+ * <div slot="fallback">
28
+ * <p>Upgrade to access premium features</p>
29
+ * </div>
30
+ * </FeatureClient>
31
+ * ```
32
+ */
33
+
34
+ interface Props {
35
+ /** Feature flag key to check */
36
+ flag: string;
37
+ /** Hydration strategy: 'load', 'idle', or 'visible' (default: 'load') */
38
+ client?: 'load' | 'idle' | 'visible';
39
+ /** Requirement for multiple flags: 'all' or 'any' (default: 'all') */
40
+ requirement?: 'all' | 'any';
41
+ /** If true, negates the result (default: false) */
42
+ negate?: boolean;
43
+ }
44
+
45
+ const { flag, client = 'load', requirement = 'all', negate = false } = Astro.props;
46
+ ---
47
+
48
+ <div
49
+ data-toggly-feature={flag}
50
+ data-toggly-requirement={requirement}
51
+ data-toggly-negate={negate}
52
+ data-toggly-hydrate={client}
53
+ class="toggly-feature-client"
54
+ >
55
+ <div data-toggly-content>
56
+ <slot />
57
+ </div>
58
+ <div data-toggly-fallback style="display: none;">
59
+ <slot name="fallback" />
60
+ </div>
61
+ </div>
62
+
63
+ <script>
64
+ import { $flags, $isReady } from '@ops-ai/astro-feature-flags-toggly/client/store';
65
+
66
+ // Hydrate all FeatureClient components
67
+ function hydrateFeatureClients() {
68
+ const elements = document.querySelectorAll<HTMLElement>('.toggly-feature-client');
69
+
70
+ elements.forEach((element) => {
71
+ const flag = element.dataset.togglyFeature;
72
+ const requirement = (element.dataset.togglyRequirement as 'all' | 'any') || 'all';
73
+ const negate = element.dataset.togglyNegate === 'true';
74
+
75
+ if (!flag) return;
76
+
77
+ const content = element.querySelector<HTMLElement>('[data-toggly-content]');
78
+ const fallback = element.querySelector<HTMLElement>('[data-toggly-fallback]');
79
+
80
+ if (!content || !fallback) return;
81
+
82
+ // Subscribe to flags changes
83
+ const unsubscribe = $flags.subscribe((flags) => {
84
+ // Wait for flags to be ready
85
+ const ready = $isReady.get();
86
+ if (!ready) return;
87
+
88
+ // Evaluate flag
89
+ const flagKeys = flag.split(',').map((k) => k.trim());
90
+ let isEnabled: boolean;
91
+
92
+ if (requirement === 'any') {
93
+ isEnabled = flagKeys.some((key) => flags[key] === true);
94
+ } else {
95
+ isEnabled = flagKeys.every((key) => flags[key] === true);
96
+ }
97
+
98
+ if (negate) {
99
+ isEnabled = !isEnabled;
100
+ }
101
+
102
+ // Show/hide content based on flag
103
+ if (isEnabled) {
104
+ content.style.display = '';
105
+ fallback.style.display = 'none';
106
+ } else {
107
+ content.style.display = 'none';
108
+ fallback.style.display = '';
109
+ }
110
+ });
111
+
112
+ // Store unsubscribe function for cleanup
113
+ (element as any).__togglyUnsubscribe = unsubscribe;
114
+ });
115
+ }
116
+
117
+ // Hydrate on page load
118
+ if (document.readyState === 'loading') {
119
+ document.addEventListener('DOMContentLoaded', hydrateFeatureClients);
120
+ } else {
121
+ hydrateFeatureClients();
122
+ }
123
+
124
+ // Re-hydrate on navigation (for view transitions)
125
+ document.addEventListener('astro:page-load', hydrateFeatureClients);
126
+
127
+ // Cleanup on navigation
128
+ document.addEventListener('astro:before-swap', () => {
129
+ document.querySelectorAll('.toggly-feature-client').forEach((element) => {
130
+ const unsubscribe = (element as any).__togglyUnsubscribe;
131
+ if (unsubscribe) {
132
+ unsubscribe();
133
+ }
134
+ });
135
+ });
136
+ </script>
137
+
138
+ <style>
139
+ .toggly-feature-client {
140
+ display: contents;
141
+ }
142
+ </style>
143
+
144
+
@@ -0,0 +1,86 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import { ReactNode } from 'react';
3
+
4
+ interface FeatureProps {
5
+ /** Single feature flag key to check */
6
+ flag?: string;
7
+ /** Multiple feature flag keys to check */
8
+ flags?: string[];
9
+ /** Requirement for multiple flags: 'all' or 'any' (default: 'all') */
10
+ requirement?: 'all' | 'any';
11
+ /** If true, negates the result (default: false) */
12
+ negate?: boolean;
13
+ /** Content to render when flag is enabled */
14
+ children: ReactNode;
15
+ /** Content to render when flag is disabled (optional) */
16
+ fallback?: ReactNode;
17
+ }
18
+ /**
19
+ * Feature - React component for conditional rendering based on feature flags
20
+ *
21
+ * @example
22
+ * ```tsx
23
+ * <Feature flag="new-dashboard">
24
+ * <Dashboard />
25
+ * </Feature>
26
+ * ```
27
+ *
28
+ * @example Multiple flags with 'any' requirement
29
+ * ```tsx
30
+ * <Feature flags={['feature1', 'feature2']} requirement="any">
31
+ * <Content />
32
+ * </Feature>
33
+ * ```
34
+ *
35
+ * @example With fallback
36
+ * ```tsx
37
+ * <Feature flag="premium-feature" fallback={<UpgradePrompt />}>
38
+ * <PremiumContent />
39
+ * </Feature>
40
+ * ```
41
+ */
42
+ declare function Feature({ flag, flags, requirement, negate, children, fallback, }: FeatureProps): react_jsx_runtime.JSX.Element;
43
+ /**
44
+ * Hook to check if a feature flag is enabled
45
+ *
46
+ * @param flagKey - Feature flag key to check
47
+ * @param defaultValue - Default value if flag not found (default: false)
48
+ * @returns Object with enabled state and ready state
49
+ *
50
+ * @example
51
+ * ```tsx
52
+ * function MyComponent() {
53
+ * const { enabled, isReady } = useFeatureFlag('new-dashboard');
54
+ *
55
+ * if (!isReady) return <Loading />;
56
+ * if (!enabled) return <OldDashboard />;
57
+ * return <NewDashboard />;
58
+ * }
59
+ * ```
60
+ */
61
+ declare function useFeatureFlag(flagKey: string, defaultValue?: boolean): {
62
+ enabled: boolean;
63
+ isReady: boolean;
64
+ };
65
+ /**
66
+ * Hook to check if multiple feature flags are enabled
67
+ *
68
+ * @param flagKeys - Array of feature flag keys to check
69
+ * @param requirement - 'all' or 'any' (default: 'all')
70
+ * @param negate - If true, negates the result (default: false)
71
+ * @returns Object with enabled state and ready state
72
+ *
73
+ * @example
74
+ * ```tsx
75
+ * function MyComponent() {
76
+ * const { enabled } = useFeatureGate(['feature1', 'feature2'], 'any');
77
+ * return enabled ? <NewFeatures /> : <OldFeatures />;
78
+ * }
79
+ * ```
80
+ */
81
+ declare function useFeatureGate(flagKeys: string[], requirement?: 'all' | 'any', negate?: boolean): {
82
+ enabled: boolean;
83
+ isReady: boolean;
84
+ };
85
+
86
+ export { Feature, type FeatureProps, Feature as default, useFeatureFlag, useFeatureGate };
@@ -0,0 +1,14 @@
1
+ import {
2
+ Feature,
3
+ Feature_default,
4
+ useFeatureFlag,
5
+ useFeatureGate
6
+ } from "../../chunk-XCJSQJHR.js";
7
+ import "../../chunk-FK633ULF.js";
8
+ export {
9
+ Feature,
10
+ Feature_default as default,
11
+ useFeatureFlag,
12
+ useFeatureGate
13
+ };
14
+ //# sourceMappingURL=Feature.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -0,0 +1,3 @@
1
+ export { default as Feature, FeatureProps, useFeatureFlag, useFeatureGate } from './Feature.js';
2
+ import 'react/jsx-runtime';
3
+ import 'react';
@@ -0,0 +1,12 @@
1
+ import {
2
+ Feature,
3
+ useFeatureFlag,
4
+ useFeatureGate
5
+ } from "../../chunk-XCJSQJHR.js";
6
+ import "../../chunk-FK633ULF.js";
7
+ export {
8
+ Feature,
9
+ useFeatureFlag,
10
+ useFeatureGate
11
+ };
12
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -0,0 +1,75 @@
1
+ <script lang="ts">
2
+ /**
3
+ * Svelte Feature Component for Astro Islands
4
+ *
5
+ * Use this component in Svelte islands within Astro for client-side feature flagging.
6
+ * Integrates with nanostores for reactive state management.
7
+ *
8
+ * @example
9
+ * ```svelte
10
+ * <Feature flag="new-dashboard">
11
+ * <Dashboard />
12
+ * </Feature>
13
+ * ```
14
+ *
15
+ * @example Multiple flags with 'any' requirement
16
+ * ```svelte
17
+ * <Feature flags={['feature1', 'feature2']} requirement="any">
18
+ * <Content />
19
+ * </Feature>
20
+ * ```
21
+ *
22
+ * @example With fallback
23
+ * ```svelte
24
+ * <Feature flag="premium-feature">
25
+ * <PremiumContent />
26
+ * <svelte:fragment slot="fallback">
27
+ * <UpgradePrompt />
28
+ * </svelte:fragment>
29
+ * </Feature>
30
+ * ```
31
+ */
32
+
33
+ import { $flags, $isReady } from '../../client/store.js';
34
+
35
+ export let flag: string | undefined = undefined;
36
+ export let flags: string[] | undefined = undefined;
37
+ export let requirement: 'all' | 'any' = 'all';
38
+ export let negate: boolean = false;
39
+
40
+ // Reactively compute whether the feature is enabled
41
+ $: flagKeys = (() => {
42
+ const keys: string[] = [];
43
+ if (flag) keys.push(flag);
44
+ if (flags && Array.isArray(flags)) keys.push(...flags);
45
+ return keys;
46
+ })();
47
+
48
+ $: isEnabled = (() => {
49
+ if (flagKeys.length === 0) {
50
+ return !negate;
51
+ }
52
+
53
+ let enabled: boolean;
54
+
55
+ if (requirement === 'any') {
56
+ enabled = flagKeys.some((key) => $flags[key] === true);
57
+ } else {
58
+ enabled = flagKeys.every((key) => $flags[key] === true);
59
+ }
60
+
61
+ if (negate) {
62
+ enabled = !enabled;
63
+ }
64
+
65
+ return enabled;
66
+ })();
67
+ </script>
68
+
69
+ {#if $isReady && isEnabled}
70
+ <slot />
71
+ {:else}
72
+ <slot name="fallback" />
73
+ {/if}
74
+
75
+
@@ -0,0 +1,54 @@
1
+ import * as svelte_store from 'svelte/store';
2
+ export { $flags as flags, $isReady as isReady } from '../../client/store.js';
3
+ import 'nanostores';
4
+ import '../../index-S3g0i0FH.js';
5
+
6
+ /**
7
+ * Create a derived store for a specific feature flag
8
+ *
9
+ * @param flagKey - Feature flag key to check
10
+ * @param defaultValue - Default value if flag not found (default: false)
11
+ * @returns Svelte-compatible derived store
12
+ *
13
+ * @example
14
+ * ```svelte
15
+ * <script>
16
+ * import { featureFlag } from '@ops-ai/astro-feature-flags-toggly/svelte';
17
+ *
18
+ * const newDashboard = featureFlag('new-dashboard');
19
+ * </script>
20
+ *
21
+ * {#if $newDashboard}
22
+ * <NewDashboard />
23
+ * {:else}
24
+ * <OldDashboard />
25
+ * {/if}
26
+ * ```
27
+ */
28
+ declare function featureFlag(flagKey: string, defaultValue?: boolean): svelte_store.Readable<boolean>;
29
+ /**
30
+ * Create a derived store that evaluates multiple feature flags
31
+ *
32
+ * @param flagKeys - Array of feature flag keys to check
33
+ * @param requirement - 'all' or 'any' (default: 'all')
34
+ * @param negate - If true, negates the result (default: false)
35
+ * @returns Svelte-compatible derived store
36
+ *
37
+ * @example
38
+ * ```svelte
39
+ * <script>
40
+ * import { featureGate } from '@ops-ai/astro-feature-flags-toggly/svelte';
41
+ *
42
+ * const hasAnyFeature = featureGate(['feature1', 'feature2'], 'any');
43
+ * </script>
44
+ *
45
+ * {#if $hasAnyFeature}
46
+ * <NewFeatures />
47
+ * {:else}
48
+ * <OldFeatures />
49
+ * {/if}
50
+ * ```
51
+ */
52
+ declare function featureGate(flagKeys: string[], requirement?: 'all' | 'any', negate?: boolean): svelte_store.Readable<boolean>;
53
+
54
+ export { featureFlag, featureGate };
@@ -0,0 +1,34 @@
1
+ import {
2
+ $flags,
3
+ $isReady
4
+ } from "../../chunk-FK633ULF.js";
5
+
6
+ // src/frameworks/svelte/stores.ts
7
+ import { derived } from "svelte/store";
8
+ function featureFlag(flagKey, defaultValue = false) {
9
+ return derived($flags, ($flags2) => $flags2[flagKey] ?? defaultValue);
10
+ }
11
+ function featureGate(flagKeys, requirement = "all", negate = false) {
12
+ return derived($flags, ($flags2) => {
13
+ if (flagKeys.length === 0) {
14
+ return !negate;
15
+ }
16
+ let isEnabled;
17
+ if (requirement === "any") {
18
+ isEnabled = flagKeys.some((key) => $flags2[key] === true);
19
+ } else {
20
+ isEnabled = flagKeys.every((key) => $flags2[key] === true);
21
+ }
22
+ if (negate) {
23
+ isEnabled = !isEnabled;
24
+ }
25
+ return isEnabled;
26
+ });
27
+ }
28
+ export {
29
+ featureFlag,
30
+ featureGate,
31
+ $flags as flags,
32
+ $isReady as isReady
33
+ };
34
+ //# sourceMappingURL=stores.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/frameworks/svelte/stores.ts"],"sourcesContent":["/**\n * Svelte-specific store utilities for Toggly\n * \n * Re-exports nanostores for easy use in Svelte components\n */\n\nimport { derived } from 'svelte/store';\nimport { $flags, $isReady } from '../../client/store.js';\n\n/**\n * Create a derived store for a specific feature flag\n * \n * @param flagKey - Feature flag key to check\n * @param defaultValue - Default value if flag not found (default: false)\n * @returns Svelte-compatible derived store\n * \n * @example\n * ```svelte\n * <script>\n * import { featureFlag } from '@ops-ai/astro-feature-flags-toggly/svelte';\n * \n * const newDashboard = featureFlag('new-dashboard');\n * </script>\n * \n * {#if $newDashboard}\n * <NewDashboard />\n * {:else}\n * <OldDashboard />\n * {/if}\n * ```\n */\nexport function featureFlag(flagKey: string, defaultValue: boolean = false) {\n return derived($flags, ($flags) => $flags[flagKey] ?? defaultValue);\n}\n\n/**\n * Create a derived store that evaluates multiple feature flags\n * \n * @param flagKeys - Array of feature flag keys to check\n * @param requirement - 'all' or 'any' (default: 'all')\n * @param negate - If true, negates the result (default: false)\n * @returns Svelte-compatible derived store\n * \n * @example\n * ```svelte\n * <script>\n * import { featureGate } from '@ops-ai/astro-feature-flags-toggly/svelte';\n * \n * const hasAnyFeature = featureGate(['feature1', 'feature2'], 'any');\n * </script>\n * \n * {#if $hasAnyFeature}\n * <NewFeatures />\n * {:else}\n * <OldFeatures />\n * {/if}\n * ```\n */\nexport function featureGate(\n flagKeys: string[],\n requirement: 'all' | 'any' = 'all',\n negate: boolean = false\n) {\n return derived($flags, ($flags) => {\n if (flagKeys.length === 0) {\n return !negate;\n }\n\n let isEnabled: boolean;\n\n if (requirement === 'any') {\n isEnabled = flagKeys.some((key) => $flags[key] === true);\n } else {\n isEnabled = flagKeys.every((key) => $flags[key] === true);\n }\n\n if (negate) {\n isEnabled = !isEnabled;\n }\n\n return isEnabled;\n });\n}\n\n// Re-export base stores for direct use\nexport { $flags as flags, $isReady as isReady };\n\n\n"],"mappings":";;;;;;AAMA,SAAS,eAAe;AAyBjB,SAAS,YAAY,SAAiB,eAAwB,OAAO;AAC1E,SAAO,QAAQ,QAAQ,CAACA,YAAWA,QAAO,OAAO,KAAK,YAAY;AACpE;AAyBO,SAAS,YACd,UACA,cAA6B,OAC7B,SAAkB,OAClB;AACA,SAAO,QAAQ,QAAQ,CAACA,YAAW;AACjC,QAAI,SAAS,WAAW,GAAG;AACzB,aAAO,CAAC;AAAA,IACV;AAEA,QAAI;AAEJ,QAAI,gBAAgB,OAAO;AACzB,kBAAY,SAAS,KAAK,CAAC,QAAQA,QAAO,GAAG,MAAM,IAAI;AAAA,IACzD,OAAO;AACL,kBAAY,SAAS,MAAM,CAAC,QAAQA,QAAO,GAAG,MAAM,IAAI;AAAA,IAC1D;AAEA,QAAI,QAAQ;AACV,kBAAY,CAAC;AAAA,IACf;AAEA,WAAO;AAAA,EACT,CAAC;AACH;","names":["$flags"]}
@@ -0,0 +1,93 @@
1
+ <template>
2
+ <slot v-if="isReady && isEnabled" />
3
+ <slot v-else name="fallback" />
4
+ </template>
5
+
6
+ <script setup lang="ts">
7
+ /**
8
+ * Vue Feature Component for Astro Islands
9
+ *
10
+ * Use this component in Vue islands within Astro for client-side feature flagging.
11
+ * Integrates with nanostores for reactive state management.
12
+ *
13
+ * @example
14
+ * ```vue
15
+ * <Feature flag="new-dashboard">
16
+ * <Dashboard />
17
+ * </Feature>
18
+ * ```
19
+ *
20
+ * @example Multiple flags with 'any' requirement
21
+ * ```vue
22
+ * <Feature :flags="['feature1', 'feature2']" requirement="any">
23
+ * <Content />
24
+ * </Feature>
25
+ * ```
26
+ *
27
+ * @example With fallback
28
+ * ```vue
29
+ * <Feature flag="premium-feature">
30
+ * <PremiumContent />
31
+ * <template #fallback>
32
+ * <UpgradePrompt />
33
+ * </template>
34
+ * </Feature>
35
+ * ```
36
+ */
37
+
38
+ import { computed } from 'vue';
39
+ import { useStore } from '@nanostores/vue';
40
+ import { $flags, $isReady } from '../../client/store.js';
41
+
42
+ export interface FeatureProps {
43
+ /** Single feature flag key to check */
44
+ flag?: string;
45
+ /** Multiple feature flag keys to check */
46
+ flags?: string[];
47
+ /** Requirement for multiple flags: 'all' or 'any' (default: 'all') */
48
+ requirement?: 'all' | 'any';
49
+ /** If true, negates the result (default: false) */
50
+ negate?: boolean;
51
+ }
52
+
53
+ const props = withDefaults(defineProps<FeatureProps>(), {
54
+ requirement: 'all',
55
+ negate: false,
56
+ });
57
+
58
+ const allFlags = useStore($flags);
59
+ const isReady = useStore($isReady);
60
+
61
+ const isEnabled = computed(() => {
62
+ // Build flag keys array
63
+ const flagKeys: string[] = [];
64
+ if (props.flag) {
65
+ flagKeys.push(props.flag);
66
+ }
67
+ if (props.flags && Array.isArray(props.flags)) {
68
+ flagKeys.push(...props.flags);
69
+ }
70
+
71
+ // No flags specified
72
+ if (flagKeys.length === 0) {
73
+ return !props.negate;
74
+ }
75
+
76
+ // Evaluate flags
77
+ let enabled: boolean;
78
+
79
+ if (props.requirement === 'any') {
80
+ enabled = flagKeys.some((key) => allFlags.value[key] === true);
81
+ } else {
82
+ enabled = flagKeys.every((key) => allFlags.value[key] === true);
83
+ }
84
+
85
+ if (props.negate) {
86
+ enabled = !enabled;
87
+ }
88
+
89
+ return enabled;
90
+ });
91
+ </script>
92
+
93
+
@@ -0,0 +1,56 @@
1
+ import { Ref } from 'vue';
2
+
3
+ /**
4
+ * Vue Composables for Toggly
5
+ */
6
+
7
+ /**
8
+ * Hook to check if a feature flag is enabled
9
+ *
10
+ * @param flagKey - Feature flag key to check
11
+ * @param defaultValue - Default value if flag not found (default: false)
12
+ * @returns Object with enabled state and ready state
13
+ *
14
+ * @example
15
+ * ```vue
16
+ * <script setup>
17
+ * const { enabled, isReady } = useFeatureFlag('new-dashboard');
18
+ * </script>
19
+ *
20
+ * <template>
21
+ * <Loading v-if="!isReady" />
22
+ * <NewDashboard v-else-if="enabled" />
23
+ * <OldDashboard v-else />
24
+ * </template>
25
+ * ```
26
+ */
27
+ declare function useFeatureFlag(flagKey: string, defaultValue?: boolean): {
28
+ enabled: Readonly<Ref<boolean>>;
29
+ isReady: Readonly<Ref<boolean>>;
30
+ };
31
+ /**
32
+ * Hook to check if multiple feature flags are enabled
33
+ *
34
+ * @param flagKeys - Array of feature flag keys to check
35
+ * @param requirement - 'all' or 'any' (default: 'all')
36
+ * @param negate - If true, negates the result (default: false)
37
+ * @returns Object with enabled state and ready state
38
+ *
39
+ * @example
40
+ * ```vue
41
+ * <script setup>
42
+ * const { enabled } = useFeatureGate(['feature1', 'feature2'], 'any');
43
+ * </script>
44
+ *
45
+ * <template>
46
+ * <NewFeatures v-if="enabled" />
47
+ * <OldFeatures v-else />
48
+ * </template>
49
+ * ```
50
+ */
51
+ declare function useFeatureGate(flagKeys: string[], requirement?: 'all' | 'any', negate?: boolean): {
52
+ enabled: Readonly<Ref<boolean>>;
53
+ isReady: Readonly<Ref<boolean>>;
54
+ };
55
+
56
+ export { useFeatureFlag, useFeatureGate };