@togglely/sdk 1.0.0 → 1.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.
- package/dist/index.d.ts +127 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.esm.js +222 -0
- package/dist/index.esm.js.map +1 -0
- package/dist/index.js +248 -0
- package/dist/index.js.map +1 -0
- package/dist/index.umd.js +584 -0
- package/dist/index.umd.js.map +1 -0
- package/dist/index.umd.min.js +2 -0
- package/dist/index.umd.min.js.map +1 -0
- package/package.json +1 -1
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Togglely Vanilla JavaScript SDK
|
|
3
|
+
*
|
|
4
|
+
* Simple wrapper around @togglely/sdk-core for vanilla JS projects
|
|
5
|
+
* Can be used directly in browser or Node.js
|
|
6
|
+
*
|
|
7
|
+
* CDN Usage:
|
|
8
|
+
* ```html
|
|
9
|
+
* <script src="https://unpkg.com/@togglely/sdk/dist/index.umd.min.js"></script>
|
|
10
|
+
* <script>
|
|
11
|
+
* const client = new Togglely.TogglelyClient({
|
|
12
|
+
* apiKey: 'your-api-key',
|
|
13
|
+
* environment: 'production',
|
|
14
|
+
* baseUrl: 'https://your-togglely-instance.com'
|
|
15
|
+
* });
|
|
16
|
+
*
|
|
17
|
+
* client.isEnabled('new-feature').then(enabled => {
|
|
18
|
+
* if (enabled) {
|
|
19
|
+
* document.getElementById('new-feature').style.display = 'block';
|
|
20
|
+
* }
|
|
21
|
+
* });
|
|
22
|
+
* </script>
|
|
23
|
+
* ```
|
|
24
|
+
*
|
|
25
|
+
* Module Usage:
|
|
26
|
+
* ```javascript
|
|
27
|
+
* import { TogglelyClient } from '@togglely/sdk';
|
|
28
|
+
*
|
|
29
|
+
* const client = new TogglelyClient({
|
|
30
|
+
* apiKey: 'your-api-key',
|
|
31
|
+
* environment: 'production',
|
|
32
|
+
* baseUrl: 'https://your-togglely-instance.com'
|
|
33
|
+
* });
|
|
34
|
+
*
|
|
35
|
+
* // Check toggle
|
|
36
|
+
* const isEnabled = await client.isEnabled('new-feature', false);
|
|
37
|
+
*
|
|
38
|
+
* // Listen to events
|
|
39
|
+
* client.on('ready', () => console.log('Toggles loaded!'));
|
|
40
|
+
* client.on('offline', () => console.log('Using offline toggles'));
|
|
41
|
+
* ```
|
|
42
|
+
*
|
|
43
|
+
* Offline Mode with Environment Variables:
|
|
44
|
+
*
|
|
45
|
+
* Node.js:
|
|
46
|
+
* ```bash
|
|
47
|
+
* TOGGLELY_NEW_FEATURE=true
|
|
48
|
+
* TOGGLELY_MAX_ITEMS=100
|
|
49
|
+
* TOGGLELY_WELCOME_MESSAGE="Hello World"
|
|
50
|
+
* ```
|
|
51
|
+
*
|
|
52
|
+
* Browser (inject before SDK loads):
|
|
53
|
+
* ```html
|
|
54
|
+
* <script>
|
|
55
|
+
* window.__TOGGLELY_TOGGLES = {
|
|
56
|
+
* 'new-feature': true,
|
|
57
|
+
* 'max-items': 100,
|
|
58
|
+
* 'welcome-message': 'Hello World'
|
|
59
|
+
* };
|
|
60
|
+
* </script>
|
|
61
|
+
* ```
|
|
62
|
+
*/
|
|
63
|
+
export { TogglelyClient, TogglelyConfig, ToggleContext, TogglelyState, TogglelyEventType, TogglelyEventHandler, ToggleValue, AllTogglesResponse, createOfflineTogglesScript, togglesToEnvVars } from '@togglely/sdk-core';
|
|
64
|
+
import { TogglelyClient, TogglelyConfig } from '@togglely/sdk-core';
|
|
65
|
+
/**
|
|
66
|
+
* Initialize Togglely with global instance
|
|
67
|
+
* Creates a global `window.togglely` instance for easy access
|
|
68
|
+
*/
|
|
69
|
+
export declare function initTogglely(config: TogglelyConfig): TogglelyClient;
|
|
70
|
+
/**
|
|
71
|
+
* Get the global Togglely instance
|
|
72
|
+
*/
|
|
73
|
+
export declare function getGlobalTogglely(): TogglelyClient | null;
|
|
74
|
+
/**
|
|
75
|
+
* Check if a feature is enabled using the global instance
|
|
76
|
+
*/
|
|
77
|
+
export declare function isEnabled(key: string, defaultValue?: boolean): Promise<boolean>;
|
|
78
|
+
/**
|
|
79
|
+
* Get a string toggle value using the global instance
|
|
80
|
+
*/
|
|
81
|
+
export declare function getString(key: string, defaultValue?: string): Promise<string>;
|
|
82
|
+
/**
|
|
83
|
+
* Get a number toggle value using the global instance
|
|
84
|
+
*/
|
|
85
|
+
export declare function getNumber(key: string, defaultValue?: number): Promise<number>;
|
|
86
|
+
/**
|
|
87
|
+
* Get a JSON toggle value using the global instance
|
|
88
|
+
*/
|
|
89
|
+
export declare function getJSON<T = any>(key: string, defaultValue?: T): Promise<T>;
|
|
90
|
+
/**
|
|
91
|
+
* Show/hide DOM elements based on feature toggles
|
|
92
|
+
*
|
|
93
|
+
* Usage:
|
|
94
|
+
* ```javascript
|
|
95
|
+
* // Show element when toggle is enabled
|
|
96
|
+
* togglelyToggle('#new-feature', 'new-feature');
|
|
97
|
+
*
|
|
98
|
+
* // Hide element when toggle is disabled
|
|
99
|
+
* togglelyToggle('#old-feature', 'new-feature', { invert: true });
|
|
100
|
+
* ```
|
|
101
|
+
*/
|
|
102
|
+
export declare function togglelyToggle(selector: string, toggleKey: string, options?: {
|
|
103
|
+
defaultValue?: boolean;
|
|
104
|
+
invert?: boolean;
|
|
105
|
+
hideClass?: string;
|
|
106
|
+
showClass?: string;
|
|
107
|
+
}): Promise<void>;
|
|
108
|
+
/**
|
|
109
|
+
* Initialize togglely toggle for multiple elements
|
|
110
|
+
* Automatically updates when toggles change
|
|
111
|
+
*
|
|
112
|
+
* Usage:
|
|
113
|
+
* ```javascript
|
|
114
|
+
* togglelyInit({
|
|
115
|
+
* 'new-feature': ['.new-feature', '.new-banner'],
|
|
116
|
+
* 'dark-mode': ['body'],
|
|
117
|
+
* 'premium': { selector: '.premium-content', defaultValue: false }
|
|
118
|
+
* });
|
|
119
|
+
* ```
|
|
120
|
+
*/
|
|
121
|
+
export declare function togglelyInit(config: Record<string, string | string[] | {
|
|
122
|
+
selector: string;
|
|
123
|
+
defaultValue?: boolean;
|
|
124
|
+
invert?: boolean;
|
|
125
|
+
}>): () => void;
|
|
126
|
+
export default TogglelyClient;
|
|
127
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6DG;AAGH,OAAO,EACL,cAAc,EACd,cAAc,EACd,aAAa,EACb,aAAa,EACb,iBAAiB,EACjB,oBAAoB,EACpB,WAAW,EACX,kBAAkB,EAClB,0BAA0B,EAC1B,gBAAgB,EACjB,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAIpE;;;GAGG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,cAAc,GAAG,cAAc,CASnE;AAED;;GAEG;AACH,wBAAgB,iBAAiB,IAAI,cAAc,GAAG,IAAI,CAKzD;AAED;;GAEG;AACH,wBAAsB,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,YAAY,GAAE,OAAe,GAAG,OAAO,CAAC,OAAO,CAAC,CAO5F;AAED;;GAEG;AACH,wBAAsB,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,YAAY,GAAE,MAAW,GAAG,OAAO,CAAC,MAAM,CAAC,CAOvF;AAED;;GAEG;AACH,wBAAsB,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,YAAY,GAAE,MAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CAOtF;AAED;;GAEG;AACH,wBAAsB,OAAO,CAAC,CAAC,GAAG,GAAG,EAAE,GAAG,EAAE,MAAM,EAAE,YAAY,GAAE,CAAW,GAAG,OAAO,CAAC,CAAC,CAAC,CAOzF;AAID;;;;;;;;;;;GAWG;AACH,wBAAsB,cAAc,CAClC,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,EACjB,OAAO,GAAE;IACP,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;CACf,GACL,OAAO,CAAC,IAAI,CAAC,CA6Bf;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,YAAY,CAC1B,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,YAAY,CAAC,EAAE,OAAO,CAAC;IAAC,MAAM,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC,GACzG,MAAM,IAAI,CA0CZ;AAID,eAAe,cAAc,CAAC"}
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
import { TogglelyClient } from '@togglely/sdk-core';
|
|
2
|
+
export { TogglelyClient, createOfflineTogglesScript, TogglelyClient as default, togglesToEnvVars } from '@togglely/sdk-core';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Togglely Vanilla JavaScript SDK
|
|
6
|
+
*
|
|
7
|
+
* Simple wrapper around @togglely/sdk-core for vanilla JS projects
|
|
8
|
+
* Can be used directly in browser or Node.js
|
|
9
|
+
*
|
|
10
|
+
* CDN Usage:
|
|
11
|
+
* ```html
|
|
12
|
+
* <script src="https://unpkg.com/@togglely/sdk/dist/index.umd.min.js"></script>
|
|
13
|
+
* <script>
|
|
14
|
+
* const client = new Togglely.TogglelyClient({
|
|
15
|
+
* apiKey: 'your-api-key',
|
|
16
|
+
* environment: 'production',
|
|
17
|
+
* baseUrl: 'https://your-togglely-instance.com'
|
|
18
|
+
* });
|
|
19
|
+
*
|
|
20
|
+
* client.isEnabled('new-feature').then(enabled => {
|
|
21
|
+
* if (enabled) {
|
|
22
|
+
* document.getElementById('new-feature').style.display = 'block';
|
|
23
|
+
* }
|
|
24
|
+
* });
|
|
25
|
+
* </script>
|
|
26
|
+
* ```
|
|
27
|
+
*
|
|
28
|
+
* Module Usage:
|
|
29
|
+
* ```javascript
|
|
30
|
+
* import { TogglelyClient } from '@togglely/sdk';
|
|
31
|
+
*
|
|
32
|
+
* const client = new TogglelyClient({
|
|
33
|
+
* apiKey: 'your-api-key',
|
|
34
|
+
* environment: 'production',
|
|
35
|
+
* baseUrl: 'https://your-togglely-instance.com'
|
|
36
|
+
* });
|
|
37
|
+
*
|
|
38
|
+
* // Check toggle
|
|
39
|
+
* const isEnabled = await client.isEnabled('new-feature', false);
|
|
40
|
+
*
|
|
41
|
+
* // Listen to events
|
|
42
|
+
* client.on('ready', () => console.log('Toggles loaded!'));
|
|
43
|
+
* client.on('offline', () => console.log('Using offline toggles'));
|
|
44
|
+
* ```
|
|
45
|
+
*
|
|
46
|
+
* Offline Mode with Environment Variables:
|
|
47
|
+
*
|
|
48
|
+
* Node.js:
|
|
49
|
+
* ```bash
|
|
50
|
+
* TOGGLELY_NEW_FEATURE=true
|
|
51
|
+
* TOGGLELY_MAX_ITEMS=100
|
|
52
|
+
* TOGGLELY_WELCOME_MESSAGE="Hello World"
|
|
53
|
+
* ```
|
|
54
|
+
*
|
|
55
|
+
* Browser (inject before SDK loads):
|
|
56
|
+
* ```html
|
|
57
|
+
* <script>
|
|
58
|
+
* window.__TOGGLELY_TOGGLES = {
|
|
59
|
+
* 'new-feature': true,
|
|
60
|
+
* 'max-items': 100,
|
|
61
|
+
* 'welcome-message': 'Hello World'
|
|
62
|
+
* };
|
|
63
|
+
* </script>
|
|
64
|
+
* ```
|
|
65
|
+
*/
|
|
66
|
+
// Re-export everything from core
|
|
67
|
+
// ==================== Helper Functions for Vanilla JS ====================
|
|
68
|
+
/**
|
|
69
|
+
* Initialize Togglely with global instance
|
|
70
|
+
* Creates a global `window.togglely` instance for easy access
|
|
71
|
+
*/
|
|
72
|
+
function initTogglely(config) {
|
|
73
|
+
const client = new TogglelyClient(config);
|
|
74
|
+
// Make available globally in browser
|
|
75
|
+
if (typeof window !== 'undefined') {
|
|
76
|
+
window.togglely = client;
|
|
77
|
+
}
|
|
78
|
+
return client;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Get the global Togglely instance
|
|
82
|
+
*/
|
|
83
|
+
function getGlobalTogglely() {
|
|
84
|
+
if (typeof window !== 'undefined') {
|
|
85
|
+
return window.togglely || null;
|
|
86
|
+
}
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Check if a feature is enabled using the global instance
|
|
91
|
+
*/
|
|
92
|
+
async function isEnabled(key, defaultValue = false) {
|
|
93
|
+
const client = getGlobalTogglely();
|
|
94
|
+
if (!client) {
|
|
95
|
+
console.error('[Togglely] No global instance found. Call initTogglely() first.');
|
|
96
|
+
return defaultValue;
|
|
97
|
+
}
|
|
98
|
+
return client.isEnabled(key, defaultValue);
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Get a string toggle value using the global instance
|
|
102
|
+
*/
|
|
103
|
+
async function getString(key, defaultValue = '') {
|
|
104
|
+
const client = getGlobalTogglely();
|
|
105
|
+
if (!client) {
|
|
106
|
+
console.error('[Togglely] No global instance found. Call initTogglely() first.');
|
|
107
|
+
return defaultValue;
|
|
108
|
+
}
|
|
109
|
+
return client.getString(key, defaultValue);
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Get a number toggle value using the global instance
|
|
113
|
+
*/
|
|
114
|
+
async function getNumber(key, defaultValue = 0) {
|
|
115
|
+
const client = getGlobalTogglely();
|
|
116
|
+
if (!client) {
|
|
117
|
+
console.error('[Togglely] No global instance found. Call initTogglely() first.');
|
|
118
|
+
return defaultValue;
|
|
119
|
+
}
|
|
120
|
+
return client.getNumber(key, defaultValue);
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Get a JSON toggle value using the global instance
|
|
124
|
+
*/
|
|
125
|
+
async function getJSON(key, defaultValue = {}) {
|
|
126
|
+
const client = getGlobalTogglely();
|
|
127
|
+
if (!client) {
|
|
128
|
+
console.error('[Togglely] No global instance found. Call initTogglely() first.');
|
|
129
|
+
return defaultValue;
|
|
130
|
+
}
|
|
131
|
+
return client.getJSON(key, defaultValue);
|
|
132
|
+
}
|
|
133
|
+
// ==================== DOM Helpers ====================
|
|
134
|
+
/**
|
|
135
|
+
* Show/hide DOM elements based on feature toggles
|
|
136
|
+
*
|
|
137
|
+
* Usage:
|
|
138
|
+
* ```javascript
|
|
139
|
+
* // Show element when toggle is enabled
|
|
140
|
+
* togglelyToggle('#new-feature', 'new-feature');
|
|
141
|
+
*
|
|
142
|
+
* // Hide element when toggle is disabled
|
|
143
|
+
* togglelyToggle('#old-feature', 'new-feature', { invert: true });
|
|
144
|
+
* ```
|
|
145
|
+
*/
|
|
146
|
+
async function togglelyToggle(selector, toggleKey, options = {}) {
|
|
147
|
+
const { defaultValue = false, invert = false, hideClass = 'togglely-hidden', showClass = 'togglely-visible' } = options;
|
|
148
|
+
const client = getGlobalTogglely();
|
|
149
|
+
if (!client) {
|
|
150
|
+
console.error('[Togglely] No global instance found. Call initTogglely() first.');
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
const isToggleEnabled = await client.isEnabled(toggleKey, defaultValue);
|
|
154
|
+
const shouldShow = invert ? !isToggleEnabled : isToggleEnabled;
|
|
155
|
+
const elements = document.querySelectorAll(selector);
|
|
156
|
+
elements.forEach(el => {
|
|
157
|
+
if (shouldShow) {
|
|
158
|
+
el.classList.remove(hideClass);
|
|
159
|
+
el.classList.add(showClass);
|
|
160
|
+
el.style.display = '';
|
|
161
|
+
}
|
|
162
|
+
else {
|
|
163
|
+
el.classList.remove(showClass);
|
|
164
|
+
el.classList.add(hideClass);
|
|
165
|
+
el.style.display = 'none';
|
|
166
|
+
}
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Initialize togglely toggle for multiple elements
|
|
171
|
+
* Automatically updates when toggles change
|
|
172
|
+
*
|
|
173
|
+
* Usage:
|
|
174
|
+
* ```javascript
|
|
175
|
+
* togglelyInit({
|
|
176
|
+
* 'new-feature': ['.new-feature', '.new-banner'],
|
|
177
|
+
* 'dark-mode': ['body'],
|
|
178
|
+
* 'premium': { selector: '.premium-content', defaultValue: false }
|
|
179
|
+
* });
|
|
180
|
+
* ```
|
|
181
|
+
*/
|
|
182
|
+
function togglelyInit(config) {
|
|
183
|
+
const client = getGlobalTogglely();
|
|
184
|
+
if (!client) {
|
|
185
|
+
console.error('[Togglely] No global instance found. Call initTogglely() first.');
|
|
186
|
+
return () => { };
|
|
187
|
+
}
|
|
188
|
+
const updateAll = async () => {
|
|
189
|
+
for (const [toggleKey, value] of Object.entries(config)) {
|
|
190
|
+
let selectors;
|
|
191
|
+
let defaultValue = false;
|
|
192
|
+
let invert = false;
|
|
193
|
+
if (typeof value === 'string') {
|
|
194
|
+
selectors = [value];
|
|
195
|
+
}
|
|
196
|
+
else if (Array.isArray(value)) {
|
|
197
|
+
selectors = value;
|
|
198
|
+
}
|
|
199
|
+
else {
|
|
200
|
+
selectors = [value.selector];
|
|
201
|
+
defaultValue = value.defaultValue ?? false;
|
|
202
|
+
invert = value.invert ?? false;
|
|
203
|
+
}
|
|
204
|
+
const isToggleEnabled = await client.isEnabled(toggleKey, defaultValue);
|
|
205
|
+
const shouldShow = invert ? !isToggleEnabled : isToggleEnabled;
|
|
206
|
+
selectors.forEach(selector => {
|
|
207
|
+
const elements = document.querySelectorAll(selector);
|
|
208
|
+
elements.forEach(el => {
|
|
209
|
+
el.style.display = shouldShow ? '' : 'none';
|
|
210
|
+
});
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
};
|
|
214
|
+
// Initial update
|
|
215
|
+
updateAll();
|
|
216
|
+
// Subscribe to updates
|
|
217
|
+
const unsubscribe = client.on('update', updateAll);
|
|
218
|
+
return unsubscribe;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
export { getGlobalTogglely, getJSON, getNumber, getString, initTogglely, isEnabled, togglelyInit, togglelyToggle };
|
|
222
|
+
//# sourceMappingURL=index.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.esm.js","sources":["../src/index.ts"],"sourcesContent":[null],"names":[],"mappings":"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"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
|
+
|
|
5
|
+
var sdkCore = require('@togglely/sdk-core');
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Togglely Vanilla JavaScript SDK
|
|
9
|
+
*
|
|
10
|
+
* Simple wrapper around @togglely/sdk-core for vanilla JS projects
|
|
11
|
+
* Can be used directly in browser or Node.js
|
|
12
|
+
*
|
|
13
|
+
* CDN Usage:
|
|
14
|
+
* ```html
|
|
15
|
+
* <script src="https://unpkg.com/@togglely/sdk/dist/index.umd.min.js"></script>
|
|
16
|
+
* <script>
|
|
17
|
+
* const client = new Togglely.TogglelyClient({
|
|
18
|
+
* apiKey: 'your-api-key',
|
|
19
|
+
* environment: 'production',
|
|
20
|
+
* baseUrl: 'https://your-togglely-instance.com'
|
|
21
|
+
* });
|
|
22
|
+
*
|
|
23
|
+
* client.isEnabled('new-feature').then(enabled => {
|
|
24
|
+
* if (enabled) {
|
|
25
|
+
* document.getElementById('new-feature').style.display = 'block';
|
|
26
|
+
* }
|
|
27
|
+
* });
|
|
28
|
+
* </script>
|
|
29
|
+
* ```
|
|
30
|
+
*
|
|
31
|
+
* Module Usage:
|
|
32
|
+
* ```javascript
|
|
33
|
+
* import { TogglelyClient } from '@togglely/sdk';
|
|
34
|
+
*
|
|
35
|
+
* const client = new TogglelyClient({
|
|
36
|
+
* apiKey: 'your-api-key',
|
|
37
|
+
* environment: 'production',
|
|
38
|
+
* baseUrl: 'https://your-togglely-instance.com'
|
|
39
|
+
* });
|
|
40
|
+
*
|
|
41
|
+
* // Check toggle
|
|
42
|
+
* const isEnabled = await client.isEnabled('new-feature', false);
|
|
43
|
+
*
|
|
44
|
+
* // Listen to events
|
|
45
|
+
* client.on('ready', () => console.log('Toggles loaded!'));
|
|
46
|
+
* client.on('offline', () => console.log('Using offline toggles'));
|
|
47
|
+
* ```
|
|
48
|
+
*
|
|
49
|
+
* Offline Mode with Environment Variables:
|
|
50
|
+
*
|
|
51
|
+
* Node.js:
|
|
52
|
+
* ```bash
|
|
53
|
+
* TOGGLELY_NEW_FEATURE=true
|
|
54
|
+
* TOGGLELY_MAX_ITEMS=100
|
|
55
|
+
* TOGGLELY_WELCOME_MESSAGE="Hello World"
|
|
56
|
+
* ```
|
|
57
|
+
*
|
|
58
|
+
* Browser (inject before SDK loads):
|
|
59
|
+
* ```html
|
|
60
|
+
* <script>
|
|
61
|
+
* window.__TOGGLELY_TOGGLES = {
|
|
62
|
+
* 'new-feature': true,
|
|
63
|
+
* 'max-items': 100,
|
|
64
|
+
* 'welcome-message': 'Hello World'
|
|
65
|
+
* };
|
|
66
|
+
* </script>
|
|
67
|
+
* ```
|
|
68
|
+
*/
|
|
69
|
+
// Re-export everything from core
|
|
70
|
+
// ==================== Helper Functions for Vanilla JS ====================
|
|
71
|
+
/**
|
|
72
|
+
* Initialize Togglely with global instance
|
|
73
|
+
* Creates a global `window.togglely` instance for easy access
|
|
74
|
+
*/
|
|
75
|
+
function initTogglely(config) {
|
|
76
|
+
const client = new sdkCore.TogglelyClient(config);
|
|
77
|
+
// Make available globally in browser
|
|
78
|
+
if (typeof window !== 'undefined') {
|
|
79
|
+
window.togglely = client;
|
|
80
|
+
}
|
|
81
|
+
return client;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Get the global Togglely instance
|
|
85
|
+
*/
|
|
86
|
+
function getGlobalTogglely() {
|
|
87
|
+
if (typeof window !== 'undefined') {
|
|
88
|
+
return window.togglely || null;
|
|
89
|
+
}
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Check if a feature is enabled using the global instance
|
|
94
|
+
*/
|
|
95
|
+
async function isEnabled(key, defaultValue = false) {
|
|
96
|
+
const client = getGlobalTogglely();
|
|
97
|
+
if (!client) {
|
|
98
|
+
console.error('[Togglely] No global instance found. Call initTogglely() first.');
|
|
99
|
+
return defaultValue;
|
|
100
|
+
}
|
|
101
|
+
return client.isEnabled(key, defaultValue);
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Get a string toggle value using the global instance
|
|
105
|
+
*/
|
|
106
|
+
async function getString(key, defaultValue = '') {
|
|
107
|
+
const client = getGlobalTogglely();
|
|
108
|
+
if (!client) {
|
|
109
|
+
console.error('[Togglely] No global instance found. Call initTogglely() first.');
|
|
110
|
+
return defaultValue;
|
|
111
|
+
}
|
|
112
|
+
return client.getString(key, defaultValue);
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Get a number toggle value using the global instance
|
|
116
|
+
*/
|
|
117
|
+
async function getNumber(key, defaultValue = 0) {
|
|
118
|
+
const client = getGlobalTogglely();
|
|
119
|
+
if (!client) {
|
|
120
|
+
console.error('[Togglely] No global instance found. Call initTogglely() first.');
|
|
121
|
+
return defaultValue;
|
|
122
|
+
}
|
|
123
|
+
return client.getNumber(key, defaultValue);
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Get a JSON toggle value using the global instance
|
|
127
|
+
*/
|
|
128
|
+
async function getJSON(key, defaultValue = {}) {
|
|
129
|
+
const client = getGlobalTogglely();
|
|
130
|
+
if (!client) {
|
|
131
|
+
console.error('[Togglely] No global instance found. Call initTogglely() first.');
|
|
132
|
+
return defaultValue;
|
|
133
|
+
}
|
|
134
|
+
return client.getJSON(key, defaultValue);
|
|
135
|
+
}
|
|
136
|
+
// ==================== DOM Helpers ====================
|
|
137
|
+
/**
|
|
138
|
+
* Show/hide DOM elements based on feature toggles
|
|
139
|
+
*
|
|
140
|
+
* Usage:
|
|
141
|
+
* ```javascript
|
|
142
|
+
* // Show element when toggle is enabled
|
|
143
|
+
* togglelyToggle('#new-feature', 'new-feature');
|
|
144
|
+
*
|
|
145
|
+
* // Hide element when toggle is disabled
|
|
146
|
+
* togglelyToggle('#old-feature', 'new-feature', { invert: true });
|
|
147
|
+
* ```
|
|
148
|
+
*/
|
|
149
|
+
async function togglelyToggle(selector, toggleKey, options = {}) {
|
|
150
|
+
const { defaultValue = false, invert = false, hideClass = 'togglely-hidden', showClass = 'togglely-visible' } = options;
|
|
151
|
+
const client = getGlobalTogglely();
|
|
152
|
+
if (!client) {
|
|
153
|
+
console.error('[Togglely] No global instance found. Call initTogglely() first.');
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
const isToggleEnabled = await client.isEnabled(toggleKey, defaultValue);
|
|
157
|
+
const shouldShow = invert ? !isToggleEnabled : isToggleEnabled;
|
|
158
|
+
const elements = document.querySelectorAll(selector);
|
|
159
|
+
elements.forEach(el => {
|
|
160
|
+
if (shouldShow) {
|
|
161
|
+
el.classList.remove(hideClass);
|
|
162
|
+
el.classList.add(showClass);
|
|
163
|
+
el.style.display = '';
|
|
164
|
+
}
|
|
165
|
+
else {
|
|
166
|
+
el.classList.remove(showClass);
|
|
167
|
+
el.classList.add(hideClass);
|
|
168
|
+
el.style.display = 'none';
|
|
169
|
+
}
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Initialize togglely toggle for multiple elements
|
|
174
|
+
* Automatically updates when toggles change
|
|
175
|
+
*
|
|
176
|
+
* Usage:
|
|
177
|
+
* ```javascript
|
|
178
|
+
* togglelyInit({
|
|
179
|
+
* 'new-feature': ['.new-feature', '.new-banner'],
|
|
180
|
+
* 'dark-mode': ['body'],
|
|
181
|
+
* 'premium': { selector: '.premium-content', defaultValue: false }
|
|
182
|
+
* });
|
|
183
|
+
* ```
|
|
184
|
+
*/
|
|
185
|
+
function togglelyInit(config) {
|
|
186
|
+
const client = getGlobalTogglely();
|
|
187
|
+
if (!client) {
|
|
188
|
+
console.error('[Togglely] No global instance found. Call initTogglely() first.');
|
|
189
|
+
return () => { };
|
|
190
|
+
}
|
|
191
|
+
const updateAll = async () => {
|
|
192
|
+
for (const [toggleKey, value] of Object.entries(config)) {
|
|
193
|
+
let selectors;
|
|
194
|
+
let defaultValue = false;
|
|
195
|
+
let invert = false;
|
|
196
|
+
if (typeof value === 'string') {
|
|
197
|
+
selectors = [value];
|
|
198
|
+
}
|
|
199
|
+
else if (Array.isArray(value)) {
|
|
200
|
+
selectors = value;
|
|
201
|
+
}
|
|
202
|
+
else {
|
|
203
|
+
selectors = [value.selector];
|
|
204
|
+
defaultValue = value.defaultValue ?? false;
|
|
205
|
+
invert = value.invert ?? false;
|
|
206
|
+
}
|
|
207
|
+
const isToggleEnabled = await client.isEnabled(toggleKey, defaultValue);
|
|
208
|
+
const shouldShow = invert ? !isToggleEnabled : isToggleEnabled;
|
|
209
|
+
selectors.forEach(selector => {
|
|
210
|
+
const elements = document.querySelectorAll(selector);
|
|
211
|
+
elements.forEach(el => {
|
|
212
|
+
el.style.display = shouldShow ? '' : 'none';
|
|
213
|
+
});
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
};
|
|
217
|
+
// Initial update
|
|
218
|
+
updateAll();
|
|
219
|
+
// Subscribe to updates
|
|
220
|
+
const unsubscribe = client.on('update', updateAll);
|
|
221
|
+
return unsubscribe;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
Object.defineProperty(exports, "TogglelyClient", {
|
|
225
|
+
enumerable: true,
|
|
226
|
+
get: function () { return sdkCore.TogglelyClient; }
|
|
227
|
+
});
|
|
228
|
+
Object.defineProperty(exports, "createOfflineTogglesScript", {
|
|
229
|
+
enumerable: true,
|
|
230
|
+
get: function () { return sdkCore.createOfflineTogglesScript; }
|
|
231
|
+
});
|
|
232
|
+
Object.defineProperty(exports, "default", {
|
|
233
|
+
enumerable: true,
|
|
234
|
+
get: function () { return sdkCore.TogglelyClient; }
|
|
235
|
+
});
|
|
236
|
+
Object.defineProperty(exports, "togglesToEnvVars", {
|
|
237
|
+
enumerable: true,
|
|
238
|
+
get: function () { return sdkCore.togglesToEnvVars; }
|
|
239
|
+
});
|
|
240
|
+
exports.getGlobalTogglely = getGlobalTogglely;
|
|
241
|
+
exports.getJSON = getJSON;
|
|
242
|
+
exports.getNumber = getNumber;
|
|
243
|
+
exports.getString = getString;
|
|
244
|
+
exports.initTogglely = initTogglely;
|
|
245
|
+
exports.isEnabled = isEnabled;
|
|
246
|
+
exports.togglelyInit = togglelyInit;
|
|
247
|
+
exports.togglelyToggle = togglelyToggle;
|
|
248
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../src/index.ts"],"sourcesContent":[null],"names":["TogglelyClient"],"mappings":"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"}
|
|
@@ -0,0 +1,584 @@
|
|
|
1
|
+
(function (global, factory) {
|
|
2
|
+
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
|
|
3
|
+
typeof define === 'function' && define.amd ? define(['exports'], factory) :
|
|
4
|
+
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.Togglely = {}));
|
|
5
|
+
})(this, (function (exports) { 'use strict';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Togglely Core SDK - Framework agnostic
|
|
9
|
+
*
|
|
10
|
+
* Supports offline fallback via environment variables
|
|
11
|
+
*/
|
|
12
|
+
/**
|
|
13
|
+
* Core Togglely Client
|
|
14
|
+
*/
|
|
15
|
+
class TogglelyClient {
|
|
16
|
+
constructor(config) {
|
|
17
|
+
this.toggles = new Map();
|
|
18
|
+
this.context = {};
|
|
19
|
+
this.state = {
|
|
20
|
+
isReady: false,
|
|
21
|
+
isOffline: false,
|
|
22
|
+
lastError: null,
|
|
23
|
+
lastFetch: null
|
|
24
|
+
};
|
|
25
|
+
this.eventHandlers = new Map();
|
|
26
|
+
this.offlineTogglesLoaded = false;
|
|
27
|
+
this.config = {
|
|
28
|
+
refreshInterval: 60000,
|
|
29
|
+
timeout: 5000,
|
|
30
|
+
offlineFallback: true,
|
|
31
|
+
envPrefix: 'TOGGLELY_',
|
|
32
|
+
...config
|
|
33
|
+
};
|
|
34
|
+
// Initialize event handlers
|
|
35
|
+
this.eventHandlers.set('ready', new Set());
|
|
36
|
+
this.eventHandlers.set('update', new Set());
|
|
37
|
+
this.eventHandlers.set('error', new Set());
|
|
38
|
+
this.eventHandlers.set('offline', new Set());
|
|
39
|
+
this.eventHandlers.set('online', new Set());
|
|
40
|
+
// Load offline toggles first (if enabled)
|
|
41
|
+
if (this.config.offlineFallback) {
|
|
42
|
+
this.loadOfflineToggles();
|
|
43
|
+
}
|
|
44
|
+
// Start polling
|
|
45
|
+
this.startPolling();
|
|
46
|
+
}
|
|
47
|
+
// ==================== Event Handling ====================
|
|
48
|
+
on(event, handler) {
|
|
49
|
+
const handlers = this.eventHandlers.get(event);
|
|
50
|
+
if (handlers) {
|
|
51
|
+
handlers.add(handler);
|
|
52
|
+
}
|
|
53
|
+
return () => this.off(event, handler);
|
|
54
|
+
}
|
|
55
|
+
off(event, handler) {
|
|
56
|
+
const handlers = this.eventHandlers.get(event);
|
|
57
|
+
if (handlers) {
|
|
58
|
+
handlers.delete(handler);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
emit(event) {
|
|
62
|
+
const handlers = this.eventHandlers.get(event);
|
|
63
|
+
if (handlers) {
|
|
64
|
+
handlers.forEach(handler => handler({ ...this.state }));
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
// ==================== Context ====================
|
|
68
|
+
setContext(context) {
|
|
69
|
+
this.context = { ...this.context, ...context };
|
|
70
|
+
}
|
|
71
|
+
getContext() {
|
|
72
|
+
return { ...this.context };
|
|
73
|
+
}
|
|
74
|
+
clearContext() {
|
|
75
|
+
this.context = {};
|
|
76
|
+
}
|
|
77
|
+
// ==================== State ====================
|
|
78
|
+
getState() {
|
|
79
|
+
return { ...this.state };
|
|
80
|
+
}
|
|
81
|
+
isReady() {
|
|
82
|
+
return this.state.isReady;
|
|
83
|
+
}
|
|
84
|
+
isOffline() {
|
|
85
|
+
return this.state.isOffline;
|
|
86
|
+
}
|
|
87
|
+
// ==================== Toggle Accessors ====================
|
|
88
|
+
async isEnabled(key, defaultValue = false) {
|
|
89
|
+
const value = await this.getValue(key);
|
|
90
|
+
if (value === null)
|
|
91
|
+
return defaultValue;
|
|
92
|
+
return value.enabled && value.value === true;
|
|
93
|
+
}
|
|
94
|
+
async getString(key, defaultValue = '') {
|
|
95
|
+
const value = await this.getValue(key);
|
|
96
|
+
if (value === null || !value.enabled)
|
|
97
|
+
return defaultValue;
|
|
98
|
+
return String(value.value);
|
|
99
|
+
}
|
|
100
|
+
async getNumber(key, defaultValue = 0) {
|
|
101
|
+
const value = await this.getValue(key);
|
|
102
|
+
if (value === null || !value.enabled)
|
|
103
|
+
return defaultValue;
|
|
104
|
+
return Number(value.value);
|
|
105
|
+
}
|
|
106
|
+
async getJSON(key, defaultValue = {}) {
|
|
107
|
+
const value = await this.getValue(key);
|
|
108
|
+
if (value === null || !value.enabled)
|
|
109
|
+
return defaultValue;
|
|
110
|
+
if (typeof value.value === 'string') {
|
|
111
|
+
try {
|
|
112
|
+
return JSON.parse(value.value);
|
|
113
|
+
}
|
|
114
|
+
catch {
|
|
115
|
+
return defaultValue;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
return value.value;
|
|
119
|
+
}
|
|
120
|
+
async getValue(key) {
|
|
121
|
+
// Try cache first
|
|
122
|
+
const cached = this.toggles.get(key);
|
|
123
|
+
if (cached) {
|
|
124
|
+
return cached;
|
|
125
|
+
}
|
|
126
|
+
// Fetch from server
|
|
127
|
+
try {
|
|
128
|
+
const response = await this.fetchWithTimeout(`${this.config.baseUrl}/sdk/toggles/${this.config.environment}/${key}`, {
|
|
129
|
+
headers: {
|
|
130
|
+
'Authorization': `Bearer ${this.config.apiKey}`,
|
|
131
|
+
'Content-Type': 'application/json'
|
|
132
|
+
}
|
|
133
|
+
});
|
|
134
|
+
if (!response.ok) {
|
|
135
|
+
if (response.status === 404) {
|
|
136
|
+
return null;
|
|
137
|
+
}
|
|
138
|
+
throw new Error(`HTTP ${response.status}`);
|
|
139
|
+
}
|
|
140
|
+
const data = await response.json();
|
|
141
|
+
this.toggles.set(key, data);
|
|
142
|
+
// Update state if we were offline
|
|
143
|
+
if (this.state.isOffline) {
|
|
144
|
+
this.state.isOffline = false;
|
|
145
|
+
this.emit('online');
|
|
146
|
+
}
|
|
147
|
+
return data;
|
|
148
|
+
}
|
|
149
|
+
catch (error) {
|
|
150
|
+
// Try offline fallback
|
|
151
|
+
const offlineValue = this.getOfflineToggle(key);
|
|
152
|
+
if (offlineValue !== null) {
|
|
153
|
+
return offlineValue;
|
|
154
|
+
}
|
|
155
|
+
console.error(`[Togglely] Failed to fetch toggle "${key}":`, error);
|
|
156
|
+
return null;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
getAllToggles() {
|
|
160
|
+
const result = {};
|
|
161
|
+
this.toggles.forEach((value, key) => {
|
|
162
|
+
result[key] = value;
|
|
163
|
+
});
|
|
164
|
+
return result;
|
|
165
|
+
}
|
|
166
|
+
// ==================== Offline Fallback ====================
|
|
167
|
+
/**
|
|
168
|
+
* Load toggles from environment variables
|
|
169
|
+
* Format: TOGGLELY_<TOGGLE_KEY>=<value> or TOGGLELY_<TOGGLE_KEY>_ENABLED=true
|
|
170
|
+
*/
|
|
171
|
+
loadOfflineToggles() {
|
|
172
|
+
try {
|
|
173
|
+
// Browser environment - check window.__TOGGLELY_TOGGLES
|
|
174
|
+
if (typeof window !== 'undefined' && window.__TOGGLELY_TOGGLES) {
|
|
175
|
+
const offlineToggles = window.__TOGGLELY_TOGGLES;
|
|
176
|
+
for (const [key, value] of Object.entries(offlineToggles)) {
|
|
177
|
+
this.toggles.set(key, this.parseOfflineValue(value));
|
|
178
|
+
}
|
|
179
|
+
this.offlineTogglesLoaded = true;
|
|
180
|
+
console.log('[Togglely] Loaded offline toggles from window.__TOGGLELY_TOGGLES');
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
// Node.js / Bun / Deno environment - check process.env
|
|
184
|
+
if (typeof process !== 'undefined' && process.env) {
|
|
185
|
+
const prefix = this.config.envPrefix;
|
|
186
|
+
for (const [envKey, envValue] of Object.entries(process.env)) {
|
|
187
|
+
if (envKey?.startsWith(prefix) && envValue !== undefined) {
|
|
188
|
+
// Parse toggle key: TOGGLELY_MY_FEATURE -> my-feature
|
|
189
|
+
const toggleKey = envKey
|
|
190
|
+
.slice(prefix.length)
|
|
191
|
+
.toLowerCase()
|
|
192
|
+
.replace(/_/g, '-');
|
|
193
|
+
this.toggles.set(toggleKey, this.parseOfflineValue(envValue));
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
this.offlineTogglesLoaded = true;
|
|
197
|
+
console.log('[Togglely] Loaded offline toggles from environment variables');
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
catch (error) {
|
|
201
|
+
console.warn('[Togglely] Failed to load offline toggles:', error);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
getOfflineToggle(key) {
|
|
205
|
+
if (!this.config.offlineFallback)
|
|
206
|
+
return null;
|
|
207
|
+
// Try to get from already loaded offline toggles
|
|
208
|
+
const cached = this.toggles.get(key);
|
|
209
|
+
if (cached) {
|
|
210
|
+
// If we haven't emitted offline event yet, do it now
|
|
211
|
+
if (!this.state.isOffline) {
|
|
212
|
+
this.state.isOffline = true;
|
|
213
|
+
this.emit('offline');
|
|
214
|
+
}
|
|
215
|
+
return cached;
|
|
216
|
+
}
|
|
217
|
+
return null;
|
|
218
|
+
}
|
|
219
|
+
parseOfflineValue(value) {
|
|
220
|
+
// Handle boolean strings
|
|
221
|
+
if (typeof value === 'string') {
|
|
222
|
+
const lower = value.toLowerCase();
|
|
223
|
+
if (lower === 'true')
|
|
224
|
+
return { value: true, enabled: true };
|
|
225
|
+
if (lower === 'false')
|
|
226
|
+
return { value: false, enabled: true };
|
|
227
|
+
// Try to parse as number
|
|
228
|
+
if (!isNaN(Number(value))) {
|
|
229
|
+
return { value: Number(value), enabled: true };
|
|
230
|
+
}
|
|
231
|
+
// Try to parse as JSON
|
|
232
|
+
try {
|
|
233
|
+
const parsed = JSON.parse(value);
|
|
234
|
+
return { value: parsed, enabled: true };
|
|
235
|
+
}
|
|
236
|
+
catch {
|
|
237
|
+
// Return as string
|
|
238
|
+
return { value, enabled: true };
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
return { value, enabled: true };
|
|
242
|
+
}
|
|
243
|
+
// ==================== Refresh / Polling ====================
|
|
244
|
+
async refresh() {
|
|
245
|
+
try {
|
|
246
|
+
const response = await this.fetchWithTimeout(`${this.config.baseUrl}/sdk/toggles/${this.config.environment}`, {
|
|
247
|
+
headers: {
|
|
248
|
+
'Authorization': `Bearer ${this.config.apiKey}`,
|
|
249
|
+
'Content-Type': 'application/json'
|
|
250
|
+
}
|
|
251
|
+
});
|
|
252
|
+
if (!response.ok) {
|
|
253
|
+
throw new Error(`HTTP ${response.status}`);
|
|
254
|
+
}
|
|
255
|
+
const data = await response.json();
|
|
256
|
+
this.toggles.clear();
|
|
257
|
+
for (const [key, value] of Object.entries(data)) {
|
|
258
|
+
this.toggles.set(key, value);
|
|
259
|
+
}
|
|
260
|
+
this.state.lastFetch = new Date();
|
|
261
|
+
this.state.lastError = null;
|
|
262
|
+
if (!this.state.isReady) {
|
|
263
|
+
this.state.isReady = true;
|
|
264
|
+
this.emit('ready');
|
|
265
|
+
}
|
|
266
|
+
// If we were offline, go online
|
|
267
|
+
if (this.state.isOffline) {
|
|
268
|
+
this.state.isOffline = false;
|
|
269
|
+
this.emit('online');
|
|
270
|
+
}
|
|
271
|
+
this.emit('update');
|
|
272
|
+
}
|
|
273
|
+
catch (error) {
|
|
274
|
+
this.state.lastError = error;
|
|
275
|
+
// If we have offline toggles, switch to offline mode
|
|
276
|
+
if (this.config.offlineFallback && this.offlineTogglesLoaded) {
|
|
277
|
+
if (!this.state.isOffline) {
|
|
278
|
+
this.state.isOffline = true;
|
|
279
|
+
this.emit('offline');
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
this.emit('error');
|
|
283
|
+
console.error('[Togglely] Failed to refresh toggles:', error);
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
forceOfflineMode() {
|
|
287
|
+
this.state.isOffline = true;
|
|
288
|
+
this.emit('offline');
|
|
289
|
+
}
|
|
290
|
+
forceOnlineMode() {
|
|
291
|
+
this.state.isOffline = false;
|
|
292
|
+
this.refresh();
|
|
293
|
+
this.emit('online');
|
|
294
|
+
}
|
|
295
|
+
// ==================== Cleanup ====================
|
|
296
|
+
destroy() {
|
|
297
|
+
if (this.refreshTimer) {
|
|
298
|
+
clearInterval(this.refreshTimer);
|
|
299
|
+
}
|
|
300
|
+
this.toggles.clear();
|
|
301
|
+
this.eventHandlers.forEach(handlers => handlers.clear());
|
|
302
|
+
}
|
|
303
|
+
// ==================== Private Helpers ====================
|
|
304
|
+
startPolling() {
|
|
305
|
+
// Initial fetch
|
|
306
|
+
this.refresh();
|
|
307
|
+
// Set up polling
|
|
308
|
+
this.refreshTimer = setInterval(() => {
|
|
309
|
+
if (!this.state.isOffline) {
|
|
310
|
+
this.refresh();
|
|
311
|
+
}
|
|
312
|
+
}, this.config.refreshInterval);
|
|
313
|
+
}
|
|
314
|
+
fetchWithTimeout(url, options) {
|
|
315
|
+
return new Promise((resolve, reject) => {
|
|
316
|
+
const timeoutId = setTimeout(() => {
|
|
317
|
+
reject(new Error('Request timeout'));
|
|
318
|
+
}, this.config.timeout);
|
|
319
|
+
fetch(url, options)
|
|
320
|
+
.then(response => {
|
|
321
|
+
clearTimeout(timeoutId);
|
|
322
|
+
resolve(response);
|
|
323
|
+
})
|
|
324
|
+
.catch(error => {
|
|
325
|
+
clearTimeout(timeoutId);
|
|
326
|
+
reject(error);
|
|
327
|
+
});
|
|
328
|
+
});
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
// ==================== Utility Functions ====================
|
|
332
|
+
/**
|
|
333
|
+
* Create a client-side toggle loader script
|
|
334
|
+
* Use this to inject offline toggles into your HTML
|
|
335
|
+
*/
|
|
336
|
+
function createOfflineTogglesScript(toggles) {
|
|
337
|
+
return `<script>window.__TOGGLELY_TOGGLES = ${JSON.stringify(toggles)};</script>`;
|
|
338
|
+
}
|
|
339
|
+
/**
|
|
340
|
+
* Helper to convert toggles to environment variables
|
|
341
|
+
*/
|
|
342
|
+
function togglesToEnvVars(toggles, prefix = 'TOGGLELY_') {
|
|
343
|
+
const envVars = {};
|
|
344
|
+
for (const [key, value] of Object.entries(toggles)) {
|
|
345
|
+
const envKey = prefix + key.toUpperCase().replace(/-/g, '_');
|
|
346
|
+
envVars[envKey] = typeof value === 'object' ? JSON.stringify(value) : String(value);
|
|
347
|
+
}
|
|
348
|
+
return envVars;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
/**
|
|
352
|
+
* Togglely Vanilla JavaScript SDK
|
|
353
|
+
*
|
|
354
|
+
* Simple wrapper around @togglely/sdk-core for vanilla JS projects
|
|
355
|
+
* Can be used directly in browser or Node.js
|
|
356
|
+
*
|
|
357
|
+
* CDN Usage:
|
|
358
|
+
* ```html
|
|
359
|
+
* <script src="https://unpkg.com/@togglely/sdk/dist/index.umd.min.js"></script>
|
|
360
|
+
* <script>
|
|
361
|
+
* const client = new Togglely.TogglelyClient({
|
|
362
|
+
* apiKey: 'your-api-key',
|
|
363
|
+
* environment: 'production',
|
|
364
|
+
* baseUrl: 'https://your-togglely-instance.com'
|
|
365
|
+
* });
|
|
366
|
+
*
|
|
367
|
+
* client.isEnabled('new-feature').then(enabled => {
|
|
368
|
+
* if (enabled) {
|
|
369
|
+
* document.getElementById('new-feature').style.display = 'block';
|
|
370
|
+
* }
|
|
371
|
+
* });
|
|
372
|
+
* </script>
|
|
373
|
+
* ```
|
|
374
|
+
*
|
|
375
|
+
* Module Usage:
|
|
376
|
+
* ```javascript
|
|
377
|
+
* import { TogglelyClient } from '@togglely/sdk';
|
|
378
|
+
*
|
|
379
|
+
* const client = new TogglelyClient({
|
|
380
|
+
* apiKey: 'your-api-key',
|
|
381
|
+
* environment: 'production',
|
|
382
|
+
* baseUrl: 'https://your-togglely-instance.com'
|
|
383
|
+
* });
|
|
384
|
+
*
|
|
385
|
+
* // Check toggle
|
|
386
|
+
* const isEnabled = await client.isEnabled('new-feature', false);
|
|
387
|
+
*
|
|
388
|
+
* // Listen to events
|
|
389
|
+
* client.on('ready', () => console.log('Toggles loaded!'));
|
|
390
|
+
* client.on('offline', () => console.log('Using offline toggles'));
|
|
391
|
+
* ```
|
|
392
|
+
*
|
|
393
|
+
* Offline Mode with Environment Variables:
|
|
394
|
+
*
|
|
395
|
+
* Node.js:
|
|
396
|
+
* ```bash
|
|
397
|
+
* TOGGLELY_NEW_FEATURE=true
|
|
398
|
+
* TOGGLELY_MAX_ITEMS=100
|
|
399
|
+
* TOGGLELY_WELCOME_MESSAGE="Hello World"
|
|
400
|
+
* ```
|
|
401
|
+
*
|
|
402
|
+
* Browser (inject before SDK loads):
|
|
403
|
+
* ```html
|
|
404
|
+
* <script>
|
|
405
|
+
* window.__TOGGLELY_TOGGLES = {
|
|
406
|
+
* 'new-feature': true,
|
|
407
|
+
* 'max-items': 100,
|
|
408
|
+
* 'welcome-message': 'Hello World'
|
|
409
|
+
* };
|
|
410
|
+
* </script>
|
|
411
|
+
* ```
|
|
412
|
+
*/
|
|
413
|
+
// Re-export everything from core
|
|
414
|
+
// ==================== Helper Functions for Vanilla JS ====================
|
|
415
|
+
/**
|
|
416
|
+
* Initialize Togglely with global instance
|
|
417
|
+
* Creates a global `window.togglely` instance for easy access
|
|
418
|
+
*/
|
|
419
|
+
function initTogglely(config) {
|
|
420
|
+
const client = new TogglelyClient(config);
|
|
421
|
+
// Make available globally in browser
|
|
422
|
+
if (typeof window !== 'undefined') {
|
|
423
|
+
window.togglely = client;
|
|
424
|
+
}
|
|
425
|
+
return client;
|
|
426
|
+
}
|
|
427
|
+
/**
|
|
428
|
+
* Get the global Togglely instance
|
|
429
|
+
*/
|
|
430
|
+
function getGlobalTogglely() {
|
|
431
|
+
if (typeof window !== 'undefined') {
|
|
432
|
+
return window.togglely || null;
|
|
433
|
+
}
|
|
434
|
+
return null;
|
|
435
|
+
}
|
|
436
|
+
/**
|
|
437
|
+
* Check if a feature is enabled using the global instance
|
|
438
|
+
*/
|
|
439
|
+
async function isEnabled(key, defaultValue = false) {
|
|
440
|
+
const client = getGlobalTogglely();
|
|
441
|
+
if (!client) {
|
|
442
|
+
console.error('[Togglely] No global instance found. Call initTogglely() first.');
|
|
443
|
+
return defaultValue;
|
|
444
|
+
}
|
|
445
|
+
return client.isEnabled(key, defaultValue);
|
|
446
|
+
}
|
|
447
|
+
/**
|
|
448
|
+
* Get a string toggle value using the global instance
|
|
449
|
+
*/
|
|
450
|
+
async function getString(key, defaultValue = '') {
|
|
451
|
+
const client = getGlobalTogglely();
|
|
452
|
+
if (!client) {
|
|
453
|
+
console.error('[Togglely] No global instance found. Call initTogglely() first.');
|
|
454
|
+
return defaultValue;
|
|
455
|
+
}
|
|
456
|
+
return client.getString(key, defaultValue);
|
|
457
|
+
}
|
|
458
|
+
/**
|
|
459
|
+
* Get a number toggle value using the global instance
|
|
460
|
+
*/
|
|
461
|
+
async function getNumber(key, defaultValue = 0) {
|
|
462
|
+
const client = getGlobalTogglely();
|
|
463
|
+
if (!client) {
|
|
464
|
+
console.error('[Togglely] No global instance found. Call initTogglely() first.');
|
|
465
|
+
return defaultValue;
|
|
466
|
+
}
|
|
467
|
+
return client.getNumber(key, defaultValue);
|
|
468
|
+
}
|
|
469
|
+
/**
|
|
470
|
+
* Get a JSON toggle value using the global instance
|
|
471
|
+
*/
|
|
472
|
+
async function getJSON(key, defaultValue = {}) {
|
|
473
|
+
const client = getGlobalTogglely();
|
|
474
|
+
if (!client) {
|
|
475
|
+
console.error('[Togglely] No global instance found. Call initTogglely() first.');
|
|
476
|
+
return defaultValue;
|
|
477
|
+
}
|
|
478
|
+
return client.getJSON(key, defaultValue);
|
|
479
|
+
}
|
|
480
|
+
// ==================== DOM Helpers ====================
|
|
481
|
+
/**
|
|
482
|
+
* Show/hide DOM elements based on feature toggles
|
|
483
|
+
*
|
|
484
|
+
* Usage:
|
|
485
|
+
* ```javascript
|
|
486
|
+
* // Show element when toggle is enabled
|
|
487
|
+
* togglelyToggle('#new-feature', 'new-feature');
|
|
488
|
+
*
|
|
489
|
+
* // Hide element when toggle is disabled
|
|
490
|
+
* togglelyToggle('#old-feature', 'new-feature', { invert: true });
|
|
491
|
+
* ```
|
|
492
|
+
*/
|
|
493
|
+
async function togglelyToggle(selector, toggleKey, options = {}) {
|
|
494
|
+
const { defaultValue = false, invert = false, hideClass = 'togglely-hidden', showClass = 'togglely-visible' } = options;
|
|
495
|
+
const client = getGlobalTogglely();
|
|
496
|
+
if (!client) {
|
|
497
|
+
console.error('[Togglely] No global instance found. Call initTogglely() first.');
|
|
498
|
+
return;
|
|
499
|
+
}
|
|
500
|
+
const isToggleEnabled = await client.isEnabled(toggleKey, defaultValue);
|
|
501
|
+
const shouldShow = invert ? !isToggleEnabled : isToggleEnabled;
|
|
502
|
+
const elements = document.querySelectorAll(selector);
|
|
503
|
+
elements.forEach(el => {
|
|
504
|
+
if (shouldShow) {
|
|
505
|
+
el.classList.remove(hideClass);
|
|
506
|
+
el.classList.add(showClass);
|
|
507
|
+
el.style.display = '';
|
|
508
|
+
}
|
|
509
|
+
else {
|
|
510
|
+
el.classList.remove(showClass);
|
|
511
|
+
el.classList.add(hideClass);
|
|
512
|
+
el.style.display = 'none';
|
|
513
|
+
}
|
|
514
|
+
});
|
|
515
|
+
}
|
|
516
|
+
/**
|
|
517
|
+
* Initialize togglely toggle for multiple elements
|
|
518
|
+
* Automatically updates when toggles change
|
|
519
|
+
*
|
|
520
|
+
* Usage:
|
|
521
|
+
* ```javascript
|
|
522
|
+
* togglelyInit({
|
|
523
|
+
* 'new-feature': ['.new-feature', '.new-banner'],
|
|
524
|
+
* 'dark-mode': ['body'],
|
|
525
|
+
* 'premium': { selector: '.premium-content', defaultValue: false }
|
|
526
|
+
* });
|
|
527
|
+
* ```
|
|
528
|
+
*/
|
|
529
|
+
function togglelyInit(config) {
|
|
530
|
+
const client = getGlobalTogglely();
|
|
531
|
+
if (!client) {
|
|
532
|
+
console.error('[Togglely] No global instance found. Call initTogglely() first.');
|
|
533
|
+
return () => { };
|
|
534
|
+
}
|
|
535
|
+
const updateAll = async () => {
|
|
536
|
+
for (const [toggleKey, value] of Object.entries(config)) {
|
|
537
|
+
let selectors;
|
|
538
|
+
let defaultValue = false;
|
|
539
|
+
let invert = false;
|
|
540
|
+
if (typeof value === 'string') {
|
|
541
|
+
selectors = [value];
|
|
542
|
+
}
|
|
543
|
+
else if (Array.isArray(value)) {
|
|
544
|
+
selectors = value;
|
|
545
|
+
}
|
|
546
|
+
else {
|
|
547
|
+
selectors = [value.selector];
|
|
548
|
+
defaultValue = value.defaultValue ?? false;
|
|
549
|
+
invert = value.invert ?? false;
|
|
550
|
+
}
|
|
551
|
+
const isToggleEnabled = await client.isEnabled(toggleKey, defaultValue);
|
|
552
|
+
const shouldShow = invert ? !isToggleEnabled : isToggleEnabled;
|
|
553
|
+
selectors.forEach(selector => {
|
|
554
|
+
const elements = document.querySelectorAll(selector);
|
|
555
|
+
elements.forEach(el => {
|
|
556
|
+
el.style.display = shouldShow ? '' : 'none';
|
|
557
|
+
});
|
|
558
|
+
});
|
|
559
|
+
}
|
|
560
|
+
};
|
|
561
|
+
// Initial update
|
|
562
|
+
updateAll();
|
|
563
|
+
// Subscribe to updates
|
|
564
|
+
const unsubscribe = client.on('update', updateAll);
|
|
565
|
+
return unsubscribe;
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
exports.TogglelyClient = TogglelyClient;
|
|
569
|
+
exports.createOfflineTogglesScript = createOfflineTogglesScript;
|
|
570
|
+
exports.default = TogglelyClient;
|
|
571
|
+
exports.getGlobalTogglely = getGlobalTogglely;
|
|
572
|
+
exports.getJSON = getJSON;
|
|
573
|
+
exports.getNumber = getNumber;
|
|
574
|
+
exports.getString = getString;
|
|
575
|
+
exports.initTogglely = initTogglely;
|
|
576
|
+
exports.isEnabled = isEnabled;
|
|
577
|
+
exports.togglelyInit = togglelyInit;
|
|
578
|
+
exports.togglelyToggle = togglelyToggle;
|
|
579
|
+
exports.togglesToEnvVars = togglesToEnvVars;
|
|
580
|
+
|
|
581
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
582
|
+
|
|
583
|
+
}));
|
|
584
|
+
//# sourceMappingURL=index.umd.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.umd.js","sources":["../../core/dist/index.esm.js","../src/index.ts"],"sourcesContent":["/**\n * Togglely Core SDK - Framework agnostic\n *\n * Supports offline fallback via environment variables\n */\n/**\n * Core Togglely Client\n */\nclass TogglelyClient {\n constructor(config) {\n this.toggles = new Map();\n this.context = {};\n this.state = {\n isReady: false,\n isOffline: false,\n lastError: null,\n lastFetch: null\n };\n this.eventHandlers = new Map();\n this.offlineTogglesLoaded = false;\n this.config = {\n refreshInterval: 60000,\n timeout: 5000,\n offlineFallback: true,\n envPrefix: 'TOGGLELY_',\n ...config\n };\n // Initialize event handlers\n this.eventHandlers.set('ready', new Set());\n this.eventHandlers.set('update', new Set());\n this.eventHandlers.set('error', new Set());\n this.eventHandlers.set('offline', new Set());\n this.eventHandlers.set('online', new Set());\n // Load offline toggles first (if enabled)\n if (this.config.offlineFallback) {\n this.loadOfflineToggles();\n }\n // Start polling\n this.startPolling();\n }\n // ==================== Event Handling ====================\n on(event, handler) {\n const handlers = this.eventHandlers.get(event);\n if (handlers) {\n handlers.add(handler);\n }\n return () => this.off(event, handler);\n }\n off(event, handler) {\n const handlers = this.eventHandlers.get(event);\n if (handlers) {\n handlers.delete(handler);\n }\n }\n emit(event) {\n const handlers = this.eventHandlers.get(event);\n if (handlers) {\n handlers.forEach(handler => handler({ ...this.state }));\n }\n }\n // ==================== Context ====================\n setContext(context) {\n this.context = { ...this.context, ...context };\n }\n getContext() {\n return { ...this.context };\n }\n clearContext() {\n this.context = {};\n }\n // ==================== State ====================\n getState() {\n return { ...this.state };\n }\n isReady() {\n return this.state.isReady;\n }\n isOffline() {\n return this.state.isOffline;\n }\n // ==================== Toggle Accessors ====================\n async isEnabled(key, defaultValue = false) {\n const value = await this.getValue(key);\n if (value === null)\n return defaultValue;\n return value.enabled && value.value === true;\n }\n async getString(key, defaultValue = '') {\n const value = await this.getValue(key);\n if (value === null || !value.enabled)\n return defaultValue;\n return String(value.value);\n }\n async getNumber(key, defaultValue = 0) {\n const value = await this.getValue(key);\n if (value === null || !value.enabled)\n return defaultValue;\n return Number(value.value);\n }\n async getJSON(key, defaultValue = {}) {\n const value = await this.getValue(key);\n if (value === null || !value.enabled)\n return defaultValue;\n if (typeof value.value === 'string') {\n try {\n return JSON.parse(value.value);\n }\n catch {\n return defaultValue;\n }\n }\n return value.value;\n }\n async getValue(key) {\n // Try cache first\n const cached = this.toggles.get(key);\n if (cached) {\n return cached;\n }\n // Fetch from server\n try {\n const response = await this.fetchWithTimeout(`${this.config.baseUrl}/sdk/toggles/${this.config.environment}/${key}`, {\n headers: {\n 'Authorization': `Bearer ${this.config.apiKey}`,\n 'Content-Type': 'application/json'\n }\n });\n if (!response.ok) {\n if (response.status === 404) {\n return null;\n }\n throw new Error(`HTTP ${response.status}`);\n }\n const data = await response.json();\n this.toggles.set(key, data);\n // Update state if we were offline\n if (this.state.isOffline) {\n this.state.isOffline = false;\n this.emit('online');\n }\n return data;\n }\n catch (error) {\n // Try offline fallback\n const offlineValue = this.getOfflineToggle(key);\n if (offlineValue !== null) {\n return offlineValue;\n }\n console.error(`[Togglely] Failed to fetch toggle \"${key}\":`, error);\n return null;\n }\n }\n getAllToggles() {\n const result = {};\n this.toggles.forEach((value, key) => {\n result[key] = value;\n });\n return result;\n }\n // ==================== Offline Fallback ====================\n /**\n * Load toggles from environment variables\n * Format: TOGGLELY_<TOGGLE_KEY>=<value> or TOGGLELY_<TOGGLE_KEY>_ENABLED=true\n */\n loadOfflineToggles() {\n try {\n // Browser environment - check window.__TOGGLELY_TOGGLES\n if (typeof window !== 'undefined' && window.__TOGGLELY_TOGGLES) {\n const offlineToggles = window.__TOGGLELY_TOGGLES;\n for (const [key, value] of Object.entries(offlineToggles)) {\n this.toggles.set(key, this.parseOfflineValue(value));\n }\n this.offlineTogglesLoaded = true;\n console.log('[Togglely] Loaded offline toggles from window.__TOGGLELY_TOGGLES');\n return;\n }\n // Node.js / Bun / Deno environment - check process.env\n if (typeof process !== 'undefined' && process.env) {\n const prefix = this.config.envPrefix;\n for (const [envKey, envValue] of Object.entries(process.env)) {\n if (envKey?.startsWith(prefix) && envValue !== undefined) {\n // Parse toggle key: TOGGLELY_MY_FEATURE -> my-feature\n const toggleKey = envKey\n .slice(prefix.length)\n .toLowerCase()\n .replace(/_/g, '-');\n this.toggles.set(toggleKey, this.parseOfflineValue(envValue));\n }\n }\n this.offlineTogglesLoaded = true;\n console.log('[Togglely] Loaded offline toggles from environment variables');\n }\n }\n catch (error) {\n console.warn('[Togglely] Failed to load offline toggles:', error);\n }\n }\n getOfflineToggle(key) {\n if (!this.config.offlineFallback)\n return null;\n // Try to get from already loaded offline toggles\n const cached = this.toggles.get(key);\n if (cached) {\n // If we haven't emitted offline event yet, do it now\n if (!this.state.isOffline) {\n this.state.isOffline = true;\n this.emit('offline');\n }\n return cached;\n }\n return null;\n }\n parseOfflineValue(value) {\n // Handle boolean strings\n if (typeof value === 'string') {\n const lower = value.toLowerCase();\n if (lower === 'true')\n return { value: true, enabled: true };\n if (lower === 'false')\n return { value: false, enabled: true };\n // Try to parse as number\n if (!isNaN(Number(value))) {\n return { value: Number(value), enabled: true };\n }\n // Try to parse as JSON\n try {\n const parsed = JSON.parse(value);\n return { value: parsed, enabled: true };\n }\n catch {\n // Return as string\n return { value, enabled: true };\n }\n }\n return { value, enabled: true };\n }\n // ==================== Refresh / Polling ====================\n async refresh() {\n try {\n const response = await this.fetchWithTimeout(`${this.config.baseUrl}/sdk/toggles/${this.config.environment}`, {\n headers: {\n 'Authorization': `Bearer ${this.config.apiKey}`,\n 'Content-Type': 'application/json'\n }\n });\n if (!response.ok) {\n throw new Error(`HTTP ${response.status}`);\n }\n const data = await response.json();\n this.toggles.clear();\n for (const [key, value] of Object.entries(data)) {\n this.toggles.set(key, value);\n }\n this.state.lastFetch = new Date();\n this.state.lastError = null;\n if (!this.state.isReady) {\n this.state.isReady = true;\n this.emit('ready');\n }\n // If we were offline, go online\n if (this.state.isOffline) {\n this.state.isOffline = false;\n this.emit('online');\n }\n this.emit('update');\n }\n catch (error) {\n this.state.lastError = error;\n // If we have offline toggles, switch to offline mode\n if (this.config.offlineFallback && this.offlineTogglesLoaded) {\n if (!this.state.isOffline) {\n this.state.isOffline = true;\n this.emit('offline');\n }\n }\n this.emit('error');\n console.error('[Togglely] Failed to refresh toggles:', error);\n }\n }\n forceOfflineMode() {\n this.state.isOffline = true;\n this.emit('offline');\n }\n forceOnlineMode() {\n this.state.isOffline = false;\n this.refresh();\n this.emit('online');\n }\n // ==================== Cleanup ====================\n destroy() {\n if (this.refreshTimer) {\n clearInterval(this.refreshTimer);\n }\n this.toggles.clear();\n this.eventHandlers.forEach(handlers => handlers.clear());\n }\n // ==================== Private Helpers ====================\n startPolling() {\n // Initial fetch\n this.refresh();\n // Set up polling\n this.refreshTimer = setInterval(() => {\n if (!this.state.isOffline) {\n this.refresh();\n }\n }, this.config.refreshInterval);\n }\n fetchWithTimeout(url, options) {\n return new Promise((resolve, reject) => {\n const timeoutId = setTimeout(() => {\n reject(new Error('Request timeout'));\n }, this.config.timeout);\n fetch(url, options)\n .then(response => {\n clearTimeout(timeoutId);\n resolve(response);\n })\n .catch(error => {\n clearTimeout(timeoutId);\n reject(error);\n });\n });\n }\n}\n// ==================== Utility Functions ====================\n/**\n * Create a client-side toggle loader script\n * Use this to inject offline toggles into your HTML\n */\nfunction createOfflineTogglesScript(toggles) {\n return `<script>window.__TOGGLELY_TOGGLES = ${JSON.stringify(toggles)};</script>`;\n}\n/**\n * Helper to convert toggles to environment variables\n */\nfunction togglesToEnvVars(toggles, prefix = 'TOGGLELY_') {\n const envVars = {};\n for (const [key, value] of Object.entries(toggles)) {\n const envKey = prefix + key.toUpperCase().replace(/-/g, '_');\n envVars[envKey] = typeof value === 'object' ? JSON.stringify(value) : String(value);\n }\n return envVars;\n}\n\nexport { TogglelyClient, createOfflineTogglesScript, TogglelyClient as default, togglesToEnvVars };\n//# sourceMappingURL=index.esm.js.map\n",null],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).Flagify={})}(this,function(e){"use strict";class t{constructor(e){this.toggles=new Map,this.context={},this.state={isReady:!1,isOffline:!1,lastError:null,lastFetch:null},this.eventHandlers=new Map,this.offlineTogglesLoaded=!1,this.config={refreshInterval:6e4,timeout:5e3,offlineFallback:!0,envPrefix:"TOGGLELY_",...e},this.eventHandlers.set("ready",new Set),this.eventHandlers.set("update",new Set),this.eventHandlers.set("error",new Set),this.eventHandlers.set("offline",new Set),this.eventHandlers.set("online",new Set),this.config.offlineFallback&&this.loadOfflineToggles(),this.startPolling()}on(e,t){const n=this.eventHandlers.get(e);return n&&n.add(t),()=>this.off(e,t)}off(e,t){const n=this.eventHandlers.get(e);n&&n.delete(t)}emit(e){const t=this.eventHandlers.get(e);t&&t.forEach(e=>e({...this.state}))}setContext(e){this.context={...this.context,...e}}getContext(){return{...this.context}}clearContext(){this.context={}}getState(){return{...this.state}}isReady(){return this.state.isReady}isOffline(){return this.state.isOffline}async isEnabled(e,t=!1){const n=await this.getValue(e);return null===n?t:n.enabled&&!0===n.value}async getString(e,t=""){const n=await this.getValue(e);return null!==n&&n.enabled?String(n.value):t}async getNumber(e,t=0){const n=await this.getValue(e);return null!==n&&n.enabled?Number(n.value):t}async getJSON(e,t={}){const n=await this.getValue(e);if(null===n||!n.enabled)return t;if("string"==typeof n.value)try{return JSON.parse(n.value)}catch{return t}return n.value}async getValue(e){const t=this.toggles.get(e);if(t)return t;try{const t=await this.fetchWithTimeout(`${this.config.baseUrl}/sdk/toggles/${this.config.environment}/${e}`,{headers:{Authorization:`Bearer ${this.config.apiKey}`,"Content-Type":"application/json"}});if(!t.ok){if(404===t.status)return null;throw new Error(`HTTP ${t.status}`)}const n=await t.json();return this.toggles.set(e,n),this.state.isOffline&&(this.state.isOffline=!1,this.emit("online")),n}catch(t){const n=this.getOfflineToggle(e);return null!==n?n:(console.error(`[Togglely] Failed to fetch toggle "${e}":`,t),null)}}getAllToggles(){const e={};return this.toggles.forEach((t,n)=>{e[n]=t}),e}loadOfflineToggles(){try{if("undefined"!=typeof window&&window.__TOGGLELY_TOGGLES){const e=window.__TOGGLELY_TOGGLES;for(const[t,n]of Object.entries(e))this.toggles.set(t,this.parseOfflineValue(n));return this.offlineTogglesLoaded=!0,void console.log("[Togglely] Loaded offline toggles from window.__TOGGLELY_TOGGLES")}if("undefined"!=typeof process&&process.env){const e=this.config.envPrefix;for(const[t,n]of Object.entries(process.env))if(t?.startsWith(e)&&void 0!==n){const s=t.slice(e.length).toLowerCase().replace(/_/g,"-");this.toggles.set(s,this.parseOfflineValue(n))}this.offlineTogglesLoaded=!0,console.log("[Togglely] Loaded offline toggles from environment variables")}}catch(e){console.warn("[Togglely] Failed to load offline toggles:",e)}}getOfflineToggle(e){if(!this.config.offlineFallback)return null;const t=this.toggles.get(e);return t?(this.state.isOffline||(this.state.isOffline=!0,this.emit("offline")),t):null}parseOfflineValue(e){if("string"==typeof e){const t=e.toLowerCase();if("true"===t)return{value:!0,enabled:!0};if("false"===t)return{value:!1,enabled:!0};if(!isNaN(Number(e)))return{value:Number(e),enabled:!0};try{return{value:JSON.parse(e),enabled:!0}}catch{return{value:e,enabled:!0}}}return{value:e,enabled:!0}}async refresh(){try{const e=await this.fetchWithTimeout(`${this.config.baseUrl}/sdk/toggles/${this.config.environment}`,{headers:{Authorization:`Bearer ${this.config.apiKey}`,"Content-Type":"application/json"}});if(!e.ok)throw new Error(`HTTP ${e.status}`);const t=await e.json();this.toggles.clear();for(const[e,n]of Object.entries(t))this.toggles.set(e,n);this.state.lastFetch=new Date,this.state.lastError=null,this.state.isReady||(this.state.isReady=!0,this.emit("ready")),this.state.isOffline&&(this.state.isOffline=!1,this.emit("online")),this.emit("update")}catch(e){this.state.lastError=e,this.config.offlineFallback&&this.offlineTogglesLoaded&&(this.state.isOffline||(this.state.isOffline=!0,this.emit("offline"))),this.emit("error"),console.error("[Togglely] Failed to refresh toggles:",e)}}forceOfflineMode(){this.state.isOffline=!0,this.emit("offline")}forceOnlineMode(){this.state.isOffline=!1,this.refresh(),this.emit("online")}destroy(){this.refreshTimer&&clearInterval(this.refreshTimer),this.toggles.clear(),this.eventHandlers.forEach(e=>e.clear())}startPolling(){this.refresh(),this.refreshTimer=setInterval(()=>{this.state.isOffline||this.refresh()},this.config.refreshInterval)}fetchWithTimeout(e,t){return new Promise((n,s)=>{const i=setTimeout(()=>{s(new Error("Request timeout"))},this.config.timeout);fetch(e,t).then(e=>{clearTimeout(i),n(e)}).catch(e=>{clearTimeout(i),s(e)})})}}function n(){return"undefined"!=typeof window&&window.togglely||null}e.TogglelyClient=t,e.createOfflineTogglesScript=function(e){return`<script>window.__TOGGLELY_TOGGLES = ${JSON.stringify(e)};<\/script>`},e.default=t,e.getGlobalTogglely=n,e.getJSON=async function(e,t={}){const s=n();return s?s.getJSON(e,t):(console.error("[Togglely] No global instance found. Call initTogglely() first."),t)},e.getNumber=async function(e,t=0){const s=n();return s?s.getNumber(e,t):(console.error("[Togglely] No global instance found. Call initTogglely() first."),t)},e.getString=async function(e,t=""){const s=n();return s?s.getString(e,t):(console.error("[Togglely] No global instance found. Call initTogglely() first."),t)},e.initTogglely=function(e){const n=new t(e);return"undefined"!=typeof window&&(window.togglely=n),n},e.isEnabled=async function(e,t=!1){const s=n();return s?s.isEnabled(e,t):(console.error("[Togglely] No global instance found. Call initTogglely() first."),t)},e.togglelyInit=function(e){const t=n();if(!t)return console.error("[Togglely] No global instance found. Call initTogglely() first."),()=>{};const s=async()=>{for(const[n,s]of Object.entries(e)){let e,i=!1,o=!1;"string"==typeof s?e=[s]:Array.isArray(s)?e=s:(e=[s.selector],i=s.defaultValue??!1,o=s.invert??!1);const l=await t.isEnabled(n,i),r=o?!l:l;e.forEach(e=>{document.querySelectorAll(e).forEach(e=>{e.style.display=r?"":"none"})})}};return s(),t.on("update",s)},e.togglelyToggle=async function(e,t,s={}){const{defaultValue:i=!1,invert:o=!1,hideClass:l="togglely-hidden",showClass:r="togglely-visible"}=s,a=n();if(!a)return void console.error("[Togglely] No global instance found. Call initTogglely() first.");const f=await a.isEnabled(t,i),g=o?!f:f;document.querySelectorAll(e).forEach(e=>{g?(e.classList.remove(l),e.classList.add(r),e.style.display=""):(e.classList.remove(r),e.classList.add(l),e.style.display="none")})},e.togglesToEnvVars=function(e,t="TOGGLELY_"){const n={};for(const[s,i]of Object.entries(e)){n[t+s.toUpperCase().replace(/-/g,"_")]="object"==typeof i?JSON.stringify(i):String(i)}return n},Object.defineProperty(e,"__esModule",{value:!0})});
|
|
2
|
+
//# sourceMappingURL=index.umd.min.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.umd.min.js","sources":["../../core/dist/index.esm.js","../src/index.ts"],"sourcesContent":["/**\n * Togglely Core SDK - Framework agnostic\n *\n * Supports offline fallback via environment variables\n */\n/**\n * Core Togglely Client\n */\nclass TogglelyClient {\n constructor(config) {\n this.toggles = new Map();\n this.context = {};\n this.state = {\n isReady: false,\n isOffline: false,\n lastError: null,\n lastFetch: null\n };\n this.eventHandlers = new Map();\n this.offlineTogglesLoaded = false;\n this.config = {\n refreshInterval: 60000,\n timeout: 5000,\n offlineFallback: true,\n envPrefix: 'TOGGLELY_',\n ...config\n };\n // Initialize event handlers\n this.eventHandlers.set('ready', new Set());\n this.eventHandlers.set('update', new Set());\n this.eventHandlers.set('error', new Set());\n this.eventHandlers.set('offline', new Set());\n this.eventHandlers.set('online', new Set());\n // Load offline toggles first (if enabled)\n if (this.config.offlineFallback) {\n this.loadOfflineToggles();\n }\n // Start polling\n this.startPolling();\n }\n // ==================== Event Handling ====================\n on(event, handler) {\n const handlers = this.eventHandlers.get(event);\n if (handlers) {\n handlers.add(handler);\n }\n return () => this.off(event, handler);\n }\n off(event, handler) {\n const handlers = this.eventHandlers.get(event);\n if (handlers) {\n handlers.delete(handler);\n }\n }\n emit(event) {\n const handlers = this.eventHandlers.get(event);\n if (handlers) {\n handlers.forEach(handler => handler({ ...this.state }));\n }\n }\n // ==================== Context ====================\n setContext(context) {\n this.context = { ...this.context, ...context };\n }\n getContext() {\n return { ...this.context };\n }\n clearContext() {\n this.context = {};\n }\n // ==================== State ====================\n getState() {\n return { ...this.state };\n }\n isReady() {\n return this.state.isReady;\n }\n isOffline() {\n return this.state.isOffline;\n }\n // ==================== Toggle Accessors ====================\n async isEnabled(key, defaultValue = false) {\n const value = await this.getValue(key);\n if (value === null)\n return defaultValue;\n return value.enabled && value.value === true;\n }\n async getString(key, defaultValue = '') {\n const value = await this.getValue(key);\n if (value === null || !value.enabled)\n return defaultValue;\n return String(value.value);\n }\n async getNumber(key, defaultValue = 0) {\n const value = await this.getValue(key);\n if (value === null || !value.enabled)\n return defaultValue;\n return Number(value.value);\n }\n async getJSON(key, defaultValue = {}) {\n const value = await this.getValue(key);\n if (value === null || !value.enabled)\n return defaultValue;\n if (typeof value.value === 'string') {\n try {\n return JSON.parse(value.value);\n }\n catch {\n return defaultValue;\n }\n }\n return value.value;\n }\n async getValue(key) {\n // Try cache first\n const cached = this.toggles.get(key);\n if (cached) {\n return cached;\n }\n // Fetch from server\n try {\n const response = await this.fetchWithTimeout(`${this.config.baseUrl}/sdk/toggles/${this.config.environment}/${key}`, {\n headers: {\n 'Authorization': `Bearer ${this.config.apiKey}`,\n 'Content-Type': 'application/json'\n }\n });\n if (!response.ok) {\n if (response.status === 404) {\n return null;\n }\n throw new Error(`HTTP ${response.status}`);\n }\n const data = await response.json();\n this.toggles.set(key, data);\n // Update state if we were offline\n if (this.state.isOffline) {\n this.state.isOffline = false;\n this.emit('online');\n }\n return data;\n }\n catch (error) {\n // Try offline fallback\n const offlineValue = this.getOfflineToggle(key);\n if (offlineValue !== null) {\n return offlineValue;\n }\n console.error(`[Togglely] Failed to fetch toggle \"${key}\":`, error);\n return null;\n }\n }\n getAllToggles() {\n const result = {};\n this.toggles.forEach((value, key) => {\n result[key] = value;\n });\n return result;\n }\n // ==================== Offline Fallback ====================\n /**\n * Load toggles from environment variables\n * Format: TOGGLELY_<TOGGLE_KEY>=<value> or TOGGLELY_<TOGGLE_KEY>_ENABLED=true\n */\n loadOfflineToggles() {\n try {\n // Browser environment - check window.__TOGGLELY_TOGGLES\n if (typeof window !== 'undefined' && window.__TOGGLELY_TOGGLES) {\n const offlineToggles = window.__TOGGLELY_TOGGLES;\n for (const [key, value] of Object.entries(offlineToggles)) {\n this.toggles.set(key, this.parseOfflineValue(value));\n }\n this.offlineTogglesLoaded = true;\n console.log('[Togglely] Loaded offline toggles from window.__TOGGLELY_TOGGLES');\n return;\n }\n // Node.js / Bun / Deno environment - check process.env\n if (typeof process !== 'undefined' && process.env) {\n const prefix = this.config.envPrefix;\n for (const [envKey, envValue] of Object.entries(process.env)) {\n if (envKey?.startsWith(prefix) && envValue !== undefined) {\n // Parse toggle key: TOGGLELY_MY_FEATURE -> my-feature\n const toggleKey = envKey\n .slice(prefix.length)\n .toLowerCase()\n .replace(/_/g, '-');\n this.toggles.set(toggleKey, this.parseOfflineValue(envValue));\n }\n }\n this.offlineTogglesLoaded = true;\n console.log('[Togglely] Loaded offline toggles from environment variables');\n }\n }\n catch (error) {\n console.warn('[Togglely] Failed to load offline toggles:', error);\n }\n }\n getOfflineToggle(key) {\n if (!this.config.offlineFallback)\n return null;\n // Try to get from already loaded offline toggles\n const cached = this.toggles.get(key);\n if (cached) {\n // If we haven't emitted offline event yet, do it now\n if (!this.state.isOffline) {\n this.state.isOffline = true;\n this.emit('offline');\n }\n return cached;\n }\n return null;\n }\n parseOfflineValue(value) {\n // Handle boolean strings\n if (typeof value === 'string') {\n const lower = value.toLowerCase();\n if (lower === 'true')\n return { value: true, enabled: true };\n if (lower === 'false')\n return { value: false, enabled: true };\n // Try to parse as number\n if (!isNaN(Number(value))) {\n return { value: Number(value), enabled: true };\n }\n // Try to parse as JSON\n try {\n const parsed = JSON.parse(value);\n return { value: parsed, enabled: true };\n }\n catch {\n // Return as string\n return { value, enabled: true };\n }\n }\n return { value, enabled: true };\n }\n // ==================== Refresh / Polling ====================\n async refresh() {\n try {\n const response = await this.fetchWithTimeout(`${this.config.baseUrl}/sdk/toggles/${this.config.environment}`, {\n headers: {\n 'Authorization': `Bearer ${this.config.apiKey}`,\n 'Content-Type': 'application/json'\n }\n });\n if (!response.ok) {\n throw new Error(`HTTP ${response.status}`);\n }\n const data = await response.json();\n this.toggles.clear();\n for (const [key, value] of Object.entries(data)) {\n this.toggles.set(key, value);\n }\n this.state.lastFetch = new Date();\n this.state.lastError = null;\n if (!this.state.isReady) {\n this.state.isReady = true;\n this.emit('ready');\n }\n // If we were offline, go online\n if (this.state.isOffline) {\n this.state.isOffline = false;\n this.emit('online');\n }\n this.emit('update');\n }\n catch (error) {\n this.state.lastError = error;\n // If we have offline toggles, switch to offline mode\n if (this.config.offlineFallback && this.offlineTogglesLoaded) {\n if (!this.state.isOffline) {\n this.state.isOffline = true;\n this.emit('offline');\n }\n }\n this.emit('error');\n console.error('[Togglely] Failed to refresh toggles:', error);\n }\n }\n forceOfflineMode() {\n this.state.isOffline = true;\n this.emit('offline');\n }\n forceOnlineMode() {\n this.state.isOffline = false;\n this.refresh();\n this.emit('online');\n }\n // ==================== Cleanup ====================\n destroy() {\n if (this.refreshTimer) {\n clearInterval(this.refreshTimer);\n }\n this.toggles.clear();\n this.eventHandlers.forEach(handlers => handlers.clear());\n }\n // ==================== Private Helpers ====================\n startPolling() {\n // Initial fetch\n this.refresh();\n // Set up polling\n this.refreshTimer = setInterval(() => {\n if (!this.state.isOffline) {\n this.refresh();\n }\n }, this.config.refreshInterval);\n }\n fetchWithTimeout(url, options) {\n return new Promise((resolve, reject) => {\n const timeoutId = setTimeout(() => {\n reject(new Error('Request timeout'));\n }, this.config.timeout);\n fetch(url, options)\n .then(response => {\n clearTimeout(timeoutId);\n resolve(response);\n })\n .catch(error => {\n clearTimeout(timeoutId);\n reject(error);\n });\n });\n }\n}\n// ==================== Utility Functions ====================\n/**\n * Create a client-side toggle loader script\n * Use this to inject offline toggles into your HTML\n */\nfunction createOfflineTogglesScript(toggles) {\n return `<script>window.__TOGGLELY_TOGGLES = ${JSON.stringify(toggles)};</script>`;\n}\n/**\n * Helper to convert toggles to environment variables\n */\nfunction togglesToEnvVars(toggles, prefix = 'TOGGLELY_') {\n const envVars = {};\n for (const [key, value] of Object.entries(toggles)) {\n const envKey = prefix + key.toUpperCase().replace(/-/g, '_');\n envVars[envKey] = typeof value === 'object' ? JSON.stringify(value) : String(value);\n }\n return envVars;\n}\n\nexport { TogglelyClient, createOfflineTogglesScript, TogglelyClient as default, togglesToEnvVars };\n//# sourceMappingURL=index.esm.js.map\n",null],"names":["TogglelyClient","constructor","config","this","toggles","Map","context","state","isReady","isOffline","lastError","lastFetch","eventHandlers","offlineTogglesLoaded","refreshInterval","timeout","offlineFallback","envPrefix","set","Set","loadOfflineToggles","startPolling","on","event","handler","handlers","get","add","off","delete","emit","forEach","setContext","getContext","clearContext","getState","isEnabled","key","defaultValue","value","getValue","enabled","getString","String","getNumber","Number","getJSON","JSON","parse","cached","response","fetchWithTimeout","baseUrl","environment","headers","Authorization","apiKey","ok","status","Error","data","json","error","offlineValue","getOfflineToggle","console","getAllToggles","result","window","__TOGGLELY_TOGGLES","offlineToggles","Object","entries","parseOfflineValue","log","process","env","prefix","envKey","envValue","startsWith","undefined","toggleKey","slice","length","toLowerCase","replace","warn","lower","isNaN","refresh","clear","Date","forceOfflineMode","forceOnlineMode","destroy","refreshTimer","clearInterval","setInterval","url","options","Promise","resolve","reject","timeoutId","setTimeout","fetch","then","clearTimeout","catch","getGlobalTogglely","togglely","stringify","async","client","updateAll","selectors","invert","Array","isArray","selector","isToggleEnabled","shouldShow","document","querySelectorAll","el","style","display","hideClass","showClass","classList","remove","envVars","toUpperCase"],"mappings":"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"}
|