@nexussdk/flags 0.0.3 → 0.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,95 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ startFlagsDevServer
4
+ } from "../chunk-VC3UFQ47.mjs";
5
+
6
+ // src/dev-server/cli.ts
7
+ function parseArgs(args) {
8
+ let port = 4568;
9
+ let host = "localhost";
10
+ let flagsFile = "./nexus-flags.json";
11
+ let showHelp = false;
12
+ for (let i = 0; i < args.length; i++) {
13
+ const arg = args[i];
14
+ if (arg === "--help" || arg === "-h") {
15
+ showHelp = true;
16
+ } else if (arg === "--port" || arg === "-p") {
17
+ const next = args[++i];
18
+ const val = next ? parseInt(next, 10) : NaN;
19
+ if (!isNaN(val) && val > 0 && val < 65536) port = val;
20
+ } else if (arg === "--host") {
21
+ const next = args[++i];
22
+ if (next) host = next;
23
+ } else if (arg === "--file" || arg === "-f") {
24
+ const next = args[++i];
25
+ if (next) flagsFile = next;
26
+ }
27
+ }
28
+ return { port, host, flagsFile, showHelp };
29
+ }
30
+ async function main() {
31
+ const { port, host, flagsFile, showHelp } = parseArgs(process.argv.slice(2));
32
+ if (showHelp) {
33
+ console.log(`
34
+ Nexus Flags Dev Server \u2014 Local Feature Flag Management
35
+
36
+ Usage:
37
+ nexus-flags-dev [options]
38
+
39
+ Options:
40
+ -p, --port <number> Port to listen on (default: 4568)
41
+ --host <string> Host interface to bind (default: localhost)
42
+ -f, --file <path> Path to JSON flags file (default: ./nexus-flags.json)
43
+ -h, --help Show this help message
44
+
45
+ Endpoints:
46
+ GET / Web GUI Dashboard (CRUD flags)
47
+ GET /health Health check
48
+ GET /api/v1/flags List all flags
49
+ POST /api/v1/flags Create a flag
50
+ GET /api/v1/flags/:key Get a flag
51
+ PATCH /api/v1/flags/:key Update a flag
52
+ DELETE /api/v1/flags/:key Delete a flag
53
+ POST /api/v1/flags/:key/toggle Toggle enabled state
54
+ GET /api/v1/flags/stream SSE realtime stream (subscribe for flag changes)
55
+ GET /api/v1/flags/eval Evaluation snapshot (used by SDK refresh)
56
+ GET /api/v1/flags/bootstrap Pre-load snapshot for SSR bootstrap
57
+ `);
58
+ process.exit(0);
59
+ }
60
+ try {
61
+ const instance = await startFlagsDevServer({ port, host, flagsFile });
62
+ const serverUrl = `http://${host}:${port}`;
63
+ console.log(`
64
+ \u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557
65
+ \u2551 NEXUS FEATURE FLAGS DEV SERVER \u2551
66
+ \u2551 Local Flag Management Dashboard \u2551
67
+ \u2551 \u2551
68
+ \u2551 Web Dashboard: ${serverUrl.padEnd(39)} \u2551
69
+ \u2551 Flags file: ${flagsFile.padEnd(39)} \u2551
70
+ \u2551 SSE stream: ${(serverUrl + "/api/v1/flags/stream").padEnd(39)} \u2551
71
+ \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D
72
+
73
+ [INFO] Configure your SDK to point to this server:
74
+
75
+ new NexusFlagsClient({
76
+ apiKey: 'nxs_dev_any_value',
77
+ baseUrl: '${serverUrl}',
78
+ realtime: true,
79
+ })
80
+
81
+ [INFO] Server running. Press Ctrl+C to stop.
82
+ `);
83
+ const onExit = async () => {
84
+ console.log("\n[INFO] Shutting down Nexus Flags Dev Server...");
85
+ await instance.close();
86
+ process.exit(0);
87
+ };
88
+ process.on("SIGINT", onExit);
89
+ process.on("SIGTERM", onExit);
90
+ } catch (err) {
91
+ console.error("[ERROR] Failed to start Nexus Flags Dev Server:", err);
92
+ process.exit(1);
93
+ }
94
+ }
95
+ main();
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ DevFlagStore,
4
+ FlagSseBroadcaster,
5
+ startFlagsDevServer
6
+ } from "../chunk-VC3UFQ47.mjs";
7
+ export {
8
+ DevFlagStore,
9
+ FlagSseBroadcaster,
10
+ startFlagsDevServer
11
+ };
package/dist/index.cjs CHANGED
@@ -1 +1 @@
1
- 'use strict';var core=require('@nexussdk/core');function p(r,e=0){let t=e>>>0,s=3432918353,n=461845907,a=0,f=Math.floor(r.length/4)*4;for(;a<f;){let o=r.charCodeAt(a)&255|(r.charCodeAt(a+1)&255)<<8|(r.charCodeAt(a+2)&255)<<16|(r.charCodeAt(a+3)&255)<<24;o=Math.imul(o,s),o=o<<15|o>>>17,o=Math.imul(o,n),t^=o,t=t<<13|t>>>19,t=Math.imul(t,5)+3864292196>>>0,a+=4;}let i=0,h=r.length&3;return h>=3&&(i^=(r.charCodeAt(a+2)&255)<<16),h>=2&&(i^=(r.charCodeAt(a+1)&255)<<8),h>=1&&(i^=r.charCodeAt(a)&255,i=Math.imul(i,s),i=i<<15|i>>>17,i=Math.imul(i,n),t^=i),t^=r.length,t^=t>>>16,t=Math.imul(t,2246822507),t^=t>>>13,t=Math.imul(t,3266489909),t^=t>>>16,t>>>0}function v(r,e){let t=`${r}:${e}`;return p(t)%100}function g(r,e){let t=r.replace(/^v/,"").split(".").map(Number),s=e.replace(/^v/,"").split(".").map(Number);for(let n=0;n<3;n++){let a=(t[n]??0)-(s[n]??0);if(a!==0)return a}return 0}function y(r,e){let t=e.split("."),s=r;for(let n of t){if(s==null||typeof s!="object")return;s=s[n];}return s}function b(r,e){let t=y(e,r.attribute),s=r.values;switch(r.operator){case "EQUALS":return t===s[0];case "NOT_EQUALS":return t!==s[0];case "IN":return s.includes(t);case "NOT_IN":return !s.includes(t);case "CONTAINS":return typeof t=="string"&&t.includes(String(s[0]));case "NOT_CONTAINS":return typeof t=="string"&&!t.includes(String(s[0]));case "STARTS_WITH":return typeof t=="string"&&t.startsWith(String(s[0]));case "ENDS_WITH":return typeof t=="string"&&t.endsWith(String(s[0]));case "GREATER_THAN":return typeof t=="number"&&t>Number(s[0]);case "LESS_THAN":return typeof t=="number"&&t<Number(s[0]);case "SEMVER_GTE":return typeof t=="string"&&typeof s[0]=="string"&&g(t,String(s[0]))>=0;case "SEMVER_LTE":return typeof t=="string"&&typeof s[0]=="string"&&g(t,String(s[0]))<=0;default:return false}}function u(r,e){if(!r.isEnabled)return {key:r.key,enabled:false,variants:{},reason:"KILL_SWITCH",version:r.version};if(r.targetingRules.length>0){if(!r.targetingRules.every(s=>b(s,e)))return {key:r.key,enabled:false,variants:{},reason:"FALLBACK",version:r.version};if(r.rolloutPercentage>=100)return {key:r.key,enabled:true,variants:r.variants,reason:"TARGETING_MATCH",version:r.version}}if(r.rolloutPercentage>0){let t=e.id??"anon";return v(t,r.key)<r.rolloutPercentage?{key:r.key,enabled:true,variants:r.variants,reason:r.targetingRules.length>0?"TARGETING_MATCH":"ROLLOUT_MATCH",version:r.version}:{key:r.key,enabled:false,variants:{},reason:"FALLBACK",version:r.version}}return {key:r.key,enabled:false,variants:{},reason:"KILL_SWITCH",version:r.version}}var l=class{options;eventSource=null;connected=false;destroyed=false;reconnectAttempt=0;reconnectTimer=null;maxReconnects;constructor(e){this.options=e,this.maxReconnects=e.maxReconnects??1/0;}get isConnected(){return this.connected}connect(){this.destroyed||this.connected||this.eventSource||this.openConnection();}disconnect(){this.destroyed=true,this.cleanup();}openConnection(){if(!this.destroyed)try{let e=new URL(this.options.url);e.searchParams.set("apiKey",this.options.apiKey),this.eventSource=new EventSource(e.toString()),this.eventSource.addEventListener("open",()=>{this.connected=!0,this.reconnectAttempt=0,this.options.onStateChange?.(!0);}),this.eventSource.addEventListener("message",t=>{this.handleMessage(t.data);}),this.eventSource.addEventListener("flag_update",t=>{this.handleMessage(t.data);}),this.eventSource.addEventListener("error",()=>{this.connected=!1,this.options.onStateChange?.(!1),this.cleanup(),this.scheduleReconnect();});}catch{this.scheduleReconnect();}}handleMessage(e){try{let t=JSON.parse(e);this.options.onEvent(t);}catch{}}cleanup(){this.eventSource&&(this.eventSource.close(),this.eventSource=null),this.reconnectTimer!==null&&(clearTimeout(this.reconnectTimer),this.reconnectTimer=null);}scheduleReconnect(){if(this.destroyed||this.reconnectAttempt>=this.maxReconnects)return;let e=core.computeBackoffMs(this.reconnectAttempt,1e3,3e4);this.reconnectAttempt++,this.reconnectTimer=setTimeout(()=>{this.reconnectTimer=null,this.destroyed||this.openConnection();},e);}};var E="nexus_flags_",m="nexus_anon_id",c=class{memory=new Map;storageKey;localStorageAvailable;broadcastChannel=null;constructor(e){this.storageKey=`${E}${e}`,this.localStorageAvailable=this.testLocalStorage(),this.hydrate(),this.setupBroadcastChannel();}set(e,t){this.memory.set(e,t),this.persist(),this.broadcastUpdate(e,t);}get(e){return this.memory.get(e)}setAll(e){for(let[t,s]of Object.entries(e))this.memory.set(t,s);this.persist();}delete(e){this.memory.delete(e),this.persist();}getAll(){return Object.fromEntries(this.memory.entries())}clear(){if(this.memory.clear(),this.localStorageAvailable)try{window.localStorage.removeItem(this.storageKey);}catch{}this.broadcastChannel?.close();}getOrCreateAnonymousId(){if(typeof window>"u")return "anon-ssr-node";if(this.localStorageAvailable)try{let e=window.localStorage.getItem(m);return e||(e=`anon_${Math.random().toString(36).substring(2,11)}`,window.localStorage.setItem(m,e)),e}catch{}return `anon_${Math.random().toString(36).substring(2,11)}`}testLocalStorage(){try{if(typeof window>"u")return !1;let e="__nexus_test__";return window.localStorage.setItem(e,"1"),window.localStorage.removeItem(e),!0}catch{return false}}hydrate(){if(this.localStorageAvailable)try{let e=window.localStorage.getItem(this.storageKey);if(e){let t=JSON.parse(e);for(let[s,n]of Object.entries(t))this.memory.set(s,n);}}catch{}}persist(){if(this.localStorageAvailable)try{window.localStorage.setItem(this.storageKey,JSON.stringify(Object.fromEntries(this.memory.entries())));}catch{}}setupBroadcastChannel(){try{typeof BroadcastChannel<"u"&&(this.broadcastChannel=new BroadcastChannel(`nexus_flags_${this.storageKey}`),this.broadcastChannel.onmessage=e=>{e.data?.key&&e.data?.result&&this.memory.set(e.data.key,e.data.result);});}catch{}}broadcastUpdate(e,t){try{this.broadcastChannel?.postMessage({key:e,result:t});}catch{}}};var d=class{apiKey;baseUrl;timeoutMs;user;storage;listeners=new Map;sseManager;flagDefinitions=new Map;constructor(e={}){this.apiKey=core.resolveApiKey(e.apiKey),this.baseUrl=core.resolveBaseUrl(e.baseUrl),this.timeoutMs=e.timeoutMs??3e3,this.storage=new c(this.apiKey.substring(0,16)),this.user=e.user??{id:this.storage.getOrCreateAnonymousId()},e.bootstrap&&this.storage.setAll(e.bootstrap),e.realtime!==false&&this.initRealtimeSync(),this.refreshFlags();}isEnabled(e,t=false){let s=this.storage.get(e);return s!==void 0?s.enabled:t}getVariant(e,t,s){let n=this.storage.get(e);if(!n?.enabled||!n.variants)return s;let a=n.variants[t];return a!==void 0?a:s}async identify(e){this.user={...this.user,...e},await this.refreshFlags();}reset(){this.user={id:this.storage.getOrCreateAnonymousId()},this.refreshFlags();}onFlagChange(e,t){return this.listeners.has(e)||this.listeners.set(e,new Set),this.listeners.get(e).add(t),()=>this.listeners.get(e)?.delete(t)}destroy(){this.sseManager?.disconnect(),this.listeners.clear(),this.storage.clear();}initRealtimeSync(){this.sseManager=new l({url:`${this.baseUrl}/api/v1/flags/stream`,apiKey:this.apiKey,onEvent:e=>{if(e.type==="FLAG_UPDATE"&&e.data){let t=this.flagDefinitions.get(e.key),s=t?u(t,this.user):e.data;this.storage.set(e.key,s),this.listeners.get(e.key)?.forEach(n=>n(s));}else e.type==="FLAG_DELETE"&&(this.storage.delete(e.key),this.flagDefinitions.delete(e.key));}}),this.sseManager.connect();}async refreshFlags(){try{let e=await core.fetchWithRetry({url:`${this.baseUrl}/api/v1/flags/eval`,method:"GET",headers:{Authorization:`Bearer ${this.apiKey}`,"X-Nexus-User-Id":this.user.id??"anon","X-Nexus-Country":this.user.country??""},timeoutMs:this.timeoutMs,maxRetries:2});for(let[t,s]of Object.entries(e.data)){this.flagDefinitions.set(t,s);let n=u(s,this.user);this.storage.set(t,n),this.listeners.get(t)?.forEach(a=>a(n));}}catch{}}};exports.FlagStorage=c;exports.NexusFlagsClient=d;exports.SSEManager=l;exports.computeRolloutBucket=v;exports.evaluateFlag=u;exports.murmur3=p;
1
+ 'use strict';var core=require('@nexussdk/core');function v(n,t=0){let e=t>>>0,s=3432918353,r=461845907,a=0,l=Math.floor(n.length/4)*4;for(;a<l;){let o=n.charCodeAt(a)&255|(n.charCodeAt(a+1)&255)<<8|(n.charCodeAt(a+2)&255)<<16|(n.charCodeAt(a+3)&255)<<24;o=Math.imul(o,s),o=o<<15|o>>>17,o=Math.imul(o,r),e^=o,e=e<<13|e>>>19,e=Math.imul(e,5)+3864292196>>>0,a+=4;}let i=0,g=n.length&3;return g>=3&&(i^=(n.charCodeAt(a+2)&255)<<16),g>=2&&(i^=(n.charCodeAt(a+1)&255)<<8),g>=1&&(i^=n.charCodeAt(a)&255,i=Math.imul(i,s),i=i<<15|i>>>17,i=Math.imul(i,r),e^=i),e^=n.length,e^=e>>>16,e=Math.imul(e,2246822507),e^=e>>>13,e=Math.imul(e,3266489909),e^=e>>>16,e>>>0}function f(n,t){let e=`${n}:${t}`;return v(e)%100}function p(n,t){let e=n.replace(/^v/,"").split(".").map(Number),s=t.replace(/^v/,"").split(".").map(Number);for(let r=0;r<3;r++){let a=(e[r]??0)-(s[r]??0);if(a!==0)return a}return 0}function y(n,t){let e=t.split("."),s=n;for(let r of e){if(s==null||typeof s!="object")return;s=s[r];}return s}function b(n,t){let e=y(t,n.attribute),s=n.values;switch(n.operator){case "EQUALS":return e===s[0];case "NOT_EQUALS":return e!==s[0];case "IN":return s.includes(e);case "NOT_IN":return !s.includes(e);case "CONTAINS":return typeof e=="string"&&e.includes(String(s[0]));case "NOT_CONTAINS":return typeof e=="string"&&!e.includes(String(s[0]));case "STARTS_WITH":return typeof e=="string"&&e.startsWith(String(s[0]));case "ENDS_WITH":return typeof e=="string"&&e.endsWith(String(s[0]));case "GREATER_THAN":return typeof e=="number"&&e>Number(s[0]);case "LESS_THAN":return typeof e=="number"&&e<Number(s[0]);case "SEMVER_GTE":return typeof e=="string"&&typeof s[0]=="string"&&p(e,String(s[0]))>=0;case "SEMVER_LTE":return typeof e=="string"&&typeof s[0]=="string"&&p(e,String(s[0]))<=0;default:return false}}function d(n,t){if(!n.isEnabled)return {key:n.key,enabled:false,variants:{},reason:"KILL_SWITCH",version:n.version};if(n.targetingRules.length>0){if(!n.targetingRules.every(s=>b(s,t)))return {key:n.key,enabled:false,variants:{},reason:"FALLBACK",version:n.version};if(n.rolloutPercentage>=100)return {key:n.key,enabled:true,variants:n.variants,reason:"TARGETING_MATCH",version:n.version}}if(n.rolloutPercentage>0){let e=t.id??"anon";return f(e,n.key)<n.rolloutPercentage?{key:n.key,enabled:true,variants:n.variants,reason:n.targetingRules.length>0?"TARGETING_MATCH":"ROLLOUT_MATCH",version:n.version}:{key:n.key,enabled:false,variants:{},reason:"FALLBACK",version:n.version}}return {key:n.key,enabled:false,variants:{},reason:"KILL_SWITCH",version:n.version}}var c=class{options;eventSource=null;connected=false;destroyed=false;reconnectAttempt=0;reconnectTimer=null;maxReconnects;constructor(t){this.options=t,this.maxReconnects=t.maxReconnects??1/0;}get isConnected(){return this.connected}connect(){this.destroyed||this.connected||this.eventSource||this.openConnection();}disconnect(){this.destroyed=true,this.cleanup();}openConnection(){if(!this.destroyed)try{let t=new URL(this.options.url);t.searchParams.set("apiKey",this.options.apiKey),this.eventSource=new EventSource(t.toString()),this.eventSource.addEventListener("open",()=>{this.connected=!0,this.reconnectAttempt=0,this.options.onStateChange?.(!0);}),this.eventSource.addEventListener("message",e=>{this.handleMessage(e.data);}),this.eventSource.addEventListener("flag_update",e=>{this.handleMessage(e.data);}),this.eventSource.addEventListener("FLAG_UPDATE",e=>{this.handleMessage(e.data);}),this.eventSource.addEventListener("FLAG_DELETE",e=>{this.handleMessage(e.data);}),this.eventSource.addEventListener("error",()=>{this.connected=!1,this.options.onStateChange?.(!1),this.cleanup(),this.scheduleReconnect();});}catch{this.scheduleReconnect();}}handleMessage(t){try{let e=JSON.parse(t);this.options.onEvent(e);}catch{}}cleanup(){this.eventSource&&(this.eventSource.close(),this.eventSource=null),this.reconnectTimer!==null&&(clearTimeout(this.reconnectTimer),this.reconnectTimer=null);}scheduleReconnect(){if(this.destroyed||this.reconnectAttempt>=this.maxReconnects)return;let t=core.computeBackoffMs(this.reconnectAttempt,1e3,3e4);this.reconnectAttempt++,this.reconnectTimer=setTimeout(()=>{this.reconnectTimer=null,this.destroyed||this.openConnection();},t);}};var S="nexus_flags_",m="nexus_anon_id",u=class{memory=new Map;storageKey;localStorageAvailable;broadcastChannel=null;constructor(t){this.storageKey=`${S}${t}`,this.localStorageAvailable=this.testLocalStorage(),this.hydrate(),this.setupBroadcastChannel();}set(t,e){this.memory.set(t,e),this.persist(),this.broadcastUpdate(t,e);}get(t){return this.memory.get(t)}setAll(t){for(let[e,s]of Object.entries(t))this.memory.set(e,s);this.persist();}delete(t){this.memory.delete(t),this.persist();}getAll(){return Object.fromEntries(this.memory.entries())}clear(){if(this.memory.clear(),this.localStorageAvailable)try{window.localStorage.removeItem(this.storageKey);}catch{}this.broadcastChannel?.close();}getOrCreateAnonymousId(){if(typeof window>"u")return "anon-ssr-node";if(this.localStorageAvailable)try{let t=window.localStorage.getItem(m);return t||(t=`anon_${Math.random().toString(36).substring(2,11)}`,window.localStorage.setItem(m,t)),t}catch{}return `anon_${Math.random().toString(36).substring(2,11)}`}testLocalStorage(){try{if(typeof window>"u")return !1;let t="__nexus_test__";return window.localStorage.setItem(t,"1"),window.localStorage.removeItem(t),!0}catch{return false}}hydrate(){if(this.localStorageAvailable)try{let t=window.localStorage.getItem(this.storageKey);if(t){let e=JSON.parse(t);for(let[s,r]of Object.entries(e))this.memory.set(s,r);}}catch{}}persist(){if(this.localStorageAvailable)try{window.localStorage.setItem(this.storageKey,JSON.stringify(Object.fromEntries(this.memory.entries())));}catch{}}setupBroadcastChannel(){try{typeof BroadcastChannel<"u"&&(this.broadcastChannel=new BroadcastChannel(`nexus_flags_${this.storageKey}`),this.broadcastChannel.onmessage=t=>{t.data?.key&&t.data?.result&&this.memory.set(t.data.key,t.data.result);});}catch{}}broadcastUpdate(t,e){try{this.broadcastChannel?.postMessage({key:t,result:e});}catch{}}};var h=class{apiKey;baseUrl;timeoutMs;user;storage;listeners=new Map;sseManager;flagDefinitions=new Map;constructor(t={}){if(this.apiKey=core.resolveApiKey(t.apiKey),this.baseUrl=core.resolveBaseUrl(t.baseUrl),this.timeoutMs=t.timeoutMs??3e3,this.storage=new u(this.apiKey.substring(0,16)),this.user=t.user??{id:this.storage.getOrCreateAnonymousId()},t.bootstrap){let e={};for(let[s,r]of Object.entries(t.bootstrap))if(typeof r=="boolean")e[s]={key:s,enabled:r,variants:{},reason:r?"DEFAULT_ENABLED":"KILL_SWITCH",version:1};else if(r&&typeof r=="object"){let a=r,l=typeof a.enabled=="boolean"?a.enabled:typeof a.isEnabled=="boolean"?a.isEnabled:true;e[s]={key:a.key??s,enabled:l,variants:a.variants??{},reason:l?"DEFAULT_ENABLED":"KILL_SWITCH",version:typeof a.version=="number"?a.version:1};}this.storage.setAll(e);}t.realtime!==false&&this.initRealtimeSync(),this.refreshFlags();}isEnabled(t,e=false){let s=this.storage.get(t);return s!==void 0?s.enabled:e}getVariant(t,e,s){let r=this.storage.get(t);if(!r?.enabled||!r.variants)return s;let a=r.variants[e];return a!==void 0?a:s}async identify(t){this.user={...this.user,...t},await this.refreshFlags();}reset(){this.user={id:this.storage.getOrCreateAnonymousId()},this.refreshFlags();}onFlagChange(t,e){return this.listeners.has(t)||this.listeners.set(t,new Set),this.listeners.get(t).add(e),()=>this.listeners.get(t)?.delete(e)}destroy(){this.sseManager?.disconnect(),this.listeners.clear(),this.storage.clear();}initRealtimeSync(){this.sseManager=new c({url:`${this.baseUrl}/api/v1/flags/stream`,apiKey:this.apiKey,onEvent:t=>{if(t.type==="FLAG_UPDATE"&&t.data){let e=this.flagDefinitions.get(t.key),s=e?d(e,this.user):t.data;this.storage.set(t.key,s),this.listeners.get(t.key)?.forEach(r=>r(s));}else t.type==="FLAG_DELETE"&&(this.storage.delete(t.key),this.flagDefinitions.delete(t.key));}}),this.sseManager.connect();}async refreshFlags(){try{let t=await core.fetchWithRetry({url:`${this.baseUrl}/api/v1/flags/eval`,method:"GET",headers:{Authorization:`Bearer ${this.apiKey}`,"X-Nexus-User-Id":this.user.id??"anon","X-Nexus-Country":this.user.country??""},timeoutMs:this.timeoutMs,maxRetries:2}),e=t.data,s=e&&typeof e=="object"&&"data"in e?e.data:t.data;if(s&&typeof s=="object")for(let[r,a]of Object.entries(s)){this.flagDefinitions.set(r,a);let l=d(a,this.user);this.storage.set(r,l),this.listeners.get(r)?.forEach(i=>i(l));}}catch{}}};exports.FlagStorage=u;exports.NexusFlagsClient=h;exports.SSEManager=c;exports.computeRolloutBucket=f;exports.evaluateFlag=d;exports.murmur3=v;
package/dist/index.d.mts CHANGED
@@ -32,9 +32,15 @@ interface NexusFlagsOptions {
32
32
  */
33
33
  user?: UserContext;
34
34
  /**
35
- * Pre-hydrated flags evaluated on the server (SSR) to prevent client-side UI flicker.
35
+ * Pre-hydrated flags evaluated on the server (SSR) or loaded from a local JSON file to prevent client-side UI flicker.
36
+ * Supports FlagEvaluationResult maps, raw boolean dictionaries, or DevFlag objects.
36
37
  */
37
- bootstrap?: Record<string, FlagEvaluationResult>;
38
+ bootstrap?: Record<string, FlagEvaluationResult | boolean | {
39
+ enabled?: boolean;
40
+ isEnabled?: boolean;
41
+ variants?: Record<string, unknown>;
42
+ [key: string]: unknown;
43
+ }>;
38
44
  /**
39
45
  * Toggle real-time SSE updates. Defaults to true.
40
46
  */
package/dist/index.d.ts CHANGED
@@ -32,9 +32,15 @@ interface NexusFlagsOptions {
32
32
  */
33
33
  user?: UserContext;
34
34
  /**
35
- * Pre-hydrated flags evaluated on the server (SSR) to prevent client-side UI flicker.
35
+ * Pre-hydrated flags evaluated on the server (SSR) or loaded from a local JSON file to prevent client-side UI flicker.
36
+ * Supports FlagEvaluationResult maps, raw boolean dictionaries, or DevFlag objects.
36
37
  */
37
- bootstrap?: Record<string, FlagEvaluationResult>;
38
+ bootstrap?: Record<string, FlagEvaluationResult | boolean | {
39
+ enabled?: boolean;
40
+ isEnabled?: boolean;
41
+ variants?: Record<string, unknown>;
42
+ [key: string]: unknown;
43
+ }>;
38
44
  /**
39
45
  * Toggle real-time SSE updates. Defaults to true.
40
46
  */
@@ -1,8 +1,8 @@
1
- var NexusFlags=(function(exports){'use strict';function m(r,e=1e3,t=3e4){let n=e*Math.pow(2,r),s=Math.min(t,n);return Math.random()*s}function _(r,e){return new Promise((t,n)=>{if(e?.aborted){n(new DOMException("Aborted","AbortError"));return}let s=setTimeout(t,r);e?.addEventListener("abort",()=>{clearTimeout(s),n(new DOMException("Aborted","AbortError"));});})}function U(r){return r>=500||r===429}async function x(r){let{url:e,method:t="GET",headers:n={},body:s,timeoutMs:a=5e3,maxRetries:d=3,retryBaseMs:i=1e3,retryMaxMs:c=3e4,signal:o}=r,y=new Error("Request failed");for(let h=0;h<=d;h++){if(o?.aborted)throw new DOMException("Request aborted by caller.","AbortError");let E=new AbortController,S=setTimeout(()=>E.abort(),a);o?.addEventListener("abort",()=>E.abort());try{let u={method:t,headers:{"Content-Type":"application/json",...n},signal:E.signal};s!==void 0&&(u.body=typeof s=="string"?s:JSON.stringify(s));let l=await fetch(e,u);if(clearTimeout(S),l.ok)return {data:await l.json(),status:l.status,headers:l.headers};if(U(l.status)&&h<d){y=new Error(`HTTP ${l.status}: ${l.statusText}`);let I=m(h,i,c);await _(I,o);continue}let M=await l.text().catch(()=>"");throw new Error(`HTTP ${l.status}: ${M}`)}catch(u){if(clearTimeout(S),u instanceof DOMException&&u.name==="AbortError")throw u;if(y=u instanceof Error?u:new Error(String(u)),h<d){let l=m(h,i,c);await _(l,o);}}}throw y}function p(r){try{let e=globalThis.process;if(typeof e<"u"&&e&&e.env)return e.env[r]??void 0}catch{}}function A(r){try{let e=typeof globalThis<"u"&&globalThis.__import_meta__;if(e&&e.env)return e.env[r]??void 0;let t=new Function("try { return import.meta; } catch(e) { return undefined; }")();if(t&&t.env)return t.env[r]??void 0}catch{}}function K(){try{if(typeof window<"u"&&window.__NEXUS_API_KEY__)return window.__NEXUS_API_KEY__}catch{}}function T(r){let e=r||K()||p("NEXUS_API_KEY")||p("NEXT_PUBLIC_NEXUS_API_KEY")||A("VITE_NEXUS_API_KEY")||p("NUXT_PUBLIC_NEXUS_API_KEY");if(!e||e.trim()==="")throw new Error(`[Nexus SDK] No API key found. Please provide one via:
1
+ var NexusFlags=(function(exports){'use strict';function v(r,e=1e3,t=3e4){let n=e*Math.pow(2,r),s=Math.min(t,n);return Math.random()*s}function _(r,e){return new Promise((t,n)=>{if(e?.aborted){n(new DOMException("Aborted","AbortError"));return}let s=setTimeout(t,r);e?.addEventListener("abort",()=>{clearTimeout(s),n(new DOMException("Aborted","AbortError"));});})}function U(r){return r>=500||r===429}async function A(r){let{url:e,method:t="GET",headers:n={},body:s,timeoutMs:a=5e3,maxRetries:l=3,retryBaseMs:i=1e3,retryMaxMs:h=3e4,signal:o}=r,m=new Error("Request failed");for(let d=0;d<=l;d++){if(o?.aborted)throw new DOMException("Request aborted by caller.","AbortError");let E=new AbortController,S=setTimeout(()=>E.abort(),a);o?.addEventListener("abort",()=>E.abort());try{let u={method:t,headers:{"Content-Type":"application/json",...n},signal:E.signal};s!==void 0&&(u.body=typeof s=="string"?s:JSON.stringify(s));let c=await fetch(e,u);if(clearTimeout(S),c.ok)return {data:await c.json(),status:c.status,headers:c.headers};if(U(c.status)&&d<l){m=new Error(`HTTP ${c.status}: ${c.statusText}`);let I=v(d,i,h);await _(I,o);continue}let M=await c.text().catch(()=>"");throw new Error(`HTTP ${c.status}: ${M}`)}catch(u){if(clearTimeout(S),u instanceof DOMException&&u.name==="AbortError")throw u;if(m=u instanceof Error?u:new Error(String(u)),d<l){let c=v(d,i,h);await _(c,o);}}}throw m}function p(r){try{let e=globalThis.process;if(typeof e<"u"&&e&&e.env)return e.env[r]??void 0}catch{}}function T(r){try{let e=typeof globalThis<"u"&&globalThis.__import_meta__;if(e&&e.env)return e.env[r]??void 0;let t=new Function("try { return import.meta; } catch(e) { return undefined; }")();if(t&&t.env)return t.env[r]??void 0}catch{}}function k(){try{if(typeof window<"u"&&window.__NEXUS_API_KEY__)return window.__NEXUS_API_KEY__}catch{}}function w(r){let e=r||k()||p("NEXUS_API_KEY")||p("NEXT_PUBLIC_NEXUS_API_KEY")||T("VITE_NEXUS_API_KEY")||p("NUXT_PUBLIC_NEXUS_API_KEY");if(!e||e.trim()==="")throw new Error(`[Nexus SDK] No API key found. Please provide one via:
2
2
  1. Nexus.init({ apiKey: "pk_live_..." })
3
3
  2. window.__NEXUS_API_KEY__ = "pk_live_..."
4
4
  3. NEXUS_API_KEY env var
5
5
  4. NEXT_PUBLIC_NEXUS_API_KEY (Next.js)
6
6
  5. VITE_NEXUS_API_KEY (Vite)
7
- 6. NUXT_PUBLIC_NEXUS_API_KEY (Nuxt)`);return e.trim()}function w(r,e="https://api.nexus.dev"){return (r||p("NEXT_PUBLIC_NEXUS_URL")||A("VITE_NEXUS_URL")||e).replace(/\/$/,"")}function C(r,e=0){let t=e>>>0,n=3432918353,s=461845907,a=0,d=Math.floor(r.length/4)*4;for(;a<d;){let o=r.charCodeAt(a)&255|(r.charCodeAt(a+1)&255)<<8|(r.charCodeAt(a+2)&255)<<16|(r.charCodeAt(a+3)&255)<<24;o=Math.imul(o,n),o=o<<15|o>>>17,o=Math.imul(o,s),t^=o,t=t<<13|t>>>19,t=Math.imul(t,5)+3864292196>>>0,a+=4;}let i=0,c=r.length&3;return c>=3&&(i^=(r.charCodeAt(a+2)&255)<<16),c>=2&&(i^=(r.charCodeAt(a+1)&255)<<8),c>=1&&(i^=r.charCodeAt(a)&255,i=Math.imul(i,n),i=i<<15|i>>>17,i=Math.imul(i,s),t^=i),t^=r.length,t^=t>>>16,t=Math.imul(t,2246822507),t^=t>>>13,t=Math.imul(t,3266489909),t^=t>>>16,t>>>0}function N(r,e){let t=`${r}:${e}`;return C(t)%100}function R(r,e){let t=r.replace(/^v/,"").split(".").map(Number),n=e.replace(/^v/,"").split(".").map(Number);for(let s=0;s<3;s++){let a=(t[s]??0)-(n[s]??0);if(a!==0)return a}return 0}function k(r,e){let t=e.split("."),n=r;for(let s of t){if(n==null||typeof n!="object")return;n=n[s];}return n}function O(r,e){let t=k(e,r.attribute),n=r.values;switch(r.operator){case "EQUALS":return t===n[0];case "NOT_EQUALS":return t!==n[0];case "IN":return n.includes(t);case "NOT_IN":return !n.includes(t);case "CONTAINS":return typeof t=="string"&&t.includes(String(n[0]));case "NOT_CONTAINS":return typeof t=="string"&&!t.includes(String(n[0]));case "STARTS_WITH":return typeof t=="string"&&t.startsWith(String(n[0]));case "ENDS_WITH":return typeof t=="string"&&t.endsWith(String(n[0]));case "GREATER_THAN":return typeof t=="number"&&t>Number(n[0]);case "LESS_THAN":return typeof t=="number"&&t<Number(n[0]);case "SEMVER_GTE":return typeof t=="string"&&typeof n[0]=="string"&&R(t,String(n[0]))>=0;case "SEMVER_LTE":return typeof t=="string"&&typeof n[0]=="string"&&R(t,String(n[0]))<=0;default:return false}}function v(r,e){if(!r.isEnabled)return {key:r.key,enabled:false,variants:{},reason:"KILL_SWITCH",version:r.version};if(r.targetingRules.length>0){if(!r.targetingRules.every(n=>O(n,e)))return {key:r.key,enabled:false,variants:{},reason:"FALLBACK",version:r.version};if(r.rolloutPercentage>=100)return {key:r.key,enabled:true,variants:r.variants,reason:"TARGETING_MATCH",version:r.version}}if(r.rolloutPercentage>0){let t=e.id??"anon";return N(t,r.key)<r.rolloutPercentage?{key:r.key,enabled:true,variants:r.variants,reason:r.targetingRules.length>0?"TARGETING_MATCH":"ROLLOUT_MATCH",version:r.version}:{key:r.key,enabled:false,variants:{},reason:"FALLBACK",version:r.version}}return {key:r.key,enabled:false,variants:{},reason:"KILL_SWITCH",version:r.version}}var f=class{options;eventSource=null;connected=false;destroyed=false;reconnectAttempt=0;reconnectTimer=null;maxReconnects;constructor(e){this.options=e,this.maxReconnects=e.maxReconnects??1/0;}get isConnected(){return this.connected}connect(){this.destroyed||this.connected||this.eventSource||this.openConnection();}disconnect(){this.destroyed=true,this.cleanup();}openConnection(){if(!this.destroyed)try{let e=new URL(this.options.url);e.searchParams.set("apiKey",this.options.apiKey),this.eventSource=new EventSource(e.toString()),this.eventSource.addEventListener("open",()=>{this.connected=!0,this.reconnectAttempt=0,this.options.onStateChange?.(!0);}),this.eventSource.addEventListener("message",t=>{this.handleMessage(t.data);}),this.eventSource.addEventListener("flag_update",t=>{this.handleMessage(t.data);}),this.eventSource.addEventListener("error",()=>{this.connected=!1,this.options.onStateChange?.(!1),this.cleanup(),this.scheduleReconnect();});}catch{this.scheduleReconnect();}}handleMessage(e){try{let t=JSON.parse(e);this.options.onEvent(t);}catch{}}cleanup(){this.eventSource&&(this.eventSource.close(),this.eventSource=null),this.reconnectTimer!==null&&(clearTimeout(this.reconnectTimer),this.reconnectTimer=null);}scheduleReconnect(){if(this.destroyed||this.reconnectAttempt>=this.maxReconnects)return;let e=m(this.reconnectAttempt,1e3,3e4);this.reconnectAttempt++,this.reconnectTimer=setTimeout(()=>{this.reconnectTimer=null,this.destroyed||this.openConnection();},e);}};var P="nexus_flags_",F="nexus_anon_id",g=class{memory=new Map;storageKey;localStorageAvailable;broadcastChannel=null;constructor(e){this.storageKey=`${P}${e}`,this.localStorageAvailable=this.testLocalStorage(),this.hydrate(),this.setupBroadcastChannel();}set(e,t){this.memory.set(e,t),this.persist(),this.broadcastUpdate(e,t);}get(e){return this.memory.get(e)}setAll(e){for(let[t,n]of Object.entries(e))this.memory.set(t,n);this.persist();}delete(e){this.memory.delete(e),this.persist();}getAll(){return Object.fromEntries(this.memory.entries())}clear(){if(this.memory.clear(),this.localStorageAvailable)try{window.localStorage.removeItem(this.storageKey);}catch{}this.broadcastChannel?.close();}getOrCreateAnonymousId(){if(typeof window>"u")return "anon-ssr-node";if(this.localStorageAvailable)try{let e=window.localStorage.getItem(F);return e||(e=`anon_${Math.random().toString(36).substring(2,11)}`,window.localStorage.setItem(F,e)),e}catch{}return `anon_${Math.random().toString(36).substring(2,11)}`}testLocalStorage(){try{if(typeof window>"u")return !1;let e="__nexus_test__";return window.localStorage.setItem(e,"1"),window.localStorage.removeItem(e),!0}catch{return false}}hydrate(){if(this.localStorageAvailable)try{let e=window.localStorage.getItem(this.storageKey);if(e){let t=JSON.parse(e);for(let[n,s]of Object.entries(t))this.memory.set(n,s);}}catch{}}persist(){if(this.localStorageAvailable)try{window.localStorage.setItem(this.storageKey,JSON.stringify(Object.fromEntries(this.memory.entries())));}catch{}}setupBroadcastChannel(){try{typeof BroadcastChannel<"u"&&(this.broadcastChannel=new BroadcastChannel(`nexus_flags_${this.storageKey}`),this.broadcastChannel.onmessage=e=>{e.data?.key&&e.data?.result&&this.memory.set(e.data.key,e.data.result);});}catch{}}broadcastUpdate(e,t){try{this.broadcastChannel?.postMessage({key:e,result:t});}catch{}}};var b=class{apiKey;baseUrl;timeoutMs;user;storage;listeners=new Map;sseManager;flagDefinitions=new Map;constructor(e={}){this.apiKey=T(e.apiKey),this.baseUrl=w(e.baseUrl),this.timeoutMs=e.timeoutMs??3e3,this.storage=new g(this.apiKey.substring(0,16)),this.user=e.user??{id:this.storage.getOrCreateAnonymousId()},e.bootstrap&&this.storage.setAll(e.bootstrap),e.realtime!==false&&this.initRealtimeSync(),this.refreshFlags();}isEnabled(e,t=false){let n=this.storage.get(e);return n!==void 0?n.enabled:t}getVariant(e,t,n){let s=this.storage.get(e);if(!s?.enabled||!s.variants)return n;let a=s.variants[t];return a!==void 0?a:n}async identify(e){this.user={...this.user,...e},await this.refreshFlags();}reset(){this.user={id:this.storage.getOrCreateAnonymousId()},this.refreshFlags();}onFlagChange(e,t){return this.listeners.has(e)||this.listeners.set(e,new Set),this.listeners.get(e).add(t),()=>this.listeners.get(e)?.delete(t)}destroy(){this.sseManager?.disconnect(),this.listeners.clear(),this.storage.clear();}initRealtimeSync(){this.sseManager=new f({url:`${this.baseUrl}/api/v1/flags/stream`,apiKey:this.apiKey,onEvent:e=>{if(e.type==="FLAG_UPDATE"&&e.data){let t=this.flagDefinitions.get(e.key),n=t?v(t,this.user):e.data;this.storage.set(e.key,n),this.listeners.get(e.key)?.forEach(s=>s(n));}else e.type==="FLAG_DELETE"&&(this.storage.delete(e.key),this.flagDefinitions.delete(e.key));}}),this.sseManager.connect();}async refreshFlags(){try{let e=await x({url:`${this.baseUrl}/api/v1/flags/eval`,method:"GET",headers:{Authorization:`Bearer ${this.apiKey}`,"X-Nexus-User-Id":this.user.id??"anon","X-Nexus-Country":this.user.country??""},timeoutMs:this.timeoutMs,maxRetries:2});for(let[t,n]of Object.entries(e.data)){this.flagDefinitions.set(t,n);let s=v(n,this.user);this.storage.set(t,s),this.listeners.get(t)?.forEach(a=>a(s));}}catch{}}};
8
- exports.FlagStorage=g;exports.NexusFlagsClient=b;exports.SSEManager=f;exports.computeRolloutBucket=N;exports.evaluateFlag=v;exports.murmur3=C;return exports;})({});
7
+ 6. NUXT_PUBLIC_NEXUS_API_KEY (Nuxt)`);return e.trim()}function x(r,e="https://api.nexus.dev"){return (r||p("NEXT_PUBLIC_NEXUS_URL")||T("VITE_NEXUS_URL")||e).replace(/\/$/,"")}function F(r,e=0){let t=e>>>0,n=3432918353,s=461845907,a=0,l=Math.floor(r.length/4)*4;for(;a<l;){let o=r.charCodeAt(a)&255|(r.charCodeAt(a+1)&255)<<8|(r.charCodeAt(a+2)&255)<<16|(r.charCodeAt(a+3)&255)<<24;o=Math.imul(o,n),o=o<<15|o>>>17,o=Math.imul(o,s),t^=o,t=t<<13|t>>>19,t=Math.imul(t,5)+3864292196>>>0,a+=4;}let i=0,h=r.length&3;return h>=3&&(i^=(r.charCodeAt(a+2)&255)<<16),h>=2&&(i^=(r.charCodeAt(a+1)&255)<<8),h>=1&&(i^=r.charCodeAt(a)&255,i=Math.imul(i,n),i=i<<15|i>>>17,i=Math.imul(i,s),t^=i),t^=r.length,t^=t>>>16,t=Math.imul(t,2246822507),t^=t>>>13,t=Math.imul(t,3266489909),t^=t>>>16,t>>>0}function C(r,e){let t=`${r}:${e}`;return F(t)%100}function R(r,e){let t=r.replace(/^v/,"").split(".").map(Number),n=e.replace(/^v/,"").split(".").map(Number);for(let s=0;s<3;s++){let a=(t[s]??0)-(n[s]??0);if(a!==0)return a}return 0}function L(r,e){let t=e.split("."),n=r;for(let s of t){if(n==null||typeof n!="object")return;n=n[s];}return n}function K(r,e){let t=L(e,r.attribute),n=r.values;switch(r.operator){case "EQUALS":return t===n[0];case "NOT_EQUALS":return t!==n[0];case "IN":return n.includes(t);case "NOT_IN":return !n.includes(t);case "CONTAINS":return typeof t=="string"&&t.includes(String(n[0]));case "NOT_CONTAINS":return typeof t=="string"&&!t.includes(String(n[0]));case "STARTS_WITH":return typeof t=="string"&&t.startsWith(String(n[0]));case "ENDS_WITH":return typeof t=="string"&&t.endsWith(String(n[0]));case "GREATER_THAN":return typeof t=="number"&&t>Number(n[0]);case "LESS_THAN":return typeof t=="number"&&t<Number(n[0]);case "SEMVER_GTE":return typeof t=="string"&&typeof n[0]=="string"&&R(t,String(n[0]))>=0;case "SEMVER_LTE":return typeof t=="string"&&typeof n[0]=="string"&&R(t,String(n[0]))<=0;default:return false}}function y(r,e){if(!r.isEnabled)return {key:r.key,enabled:false,variants:{},reason:"KILL_SWITCH",version:r.version};if(r.targetingRules.length>0){if(!r.targetingRules.every(n=>K(n,e)))return {key:r.key,enabled:false,variants:{},reason:"FALLBACK",version:r.version};if(r.rolloutPercentage>=100)return {key:r.key,enabled:true,variants:r.variants,reason:"TARGETING_MATCH",version:r.version}}if(r.rolloutPercentage>0){let t=e.id??"anon";return C(t,r.key)<r.rolloutPercentage?{key:r.key,enabled:true,variants:r.variants,reason:r.targetingRules.length>0?"TARGETING_MATCH":"ROLLOUT_MATCH",version:r.version}:{key:r.key,enabled:false,variants:{},reason:"FALLBACK",version:r.version}}return {key:r.key,enabled:false,variants:{},reason:"KILL_SWITCH",version:r.version}}var g=class{options;eventSource=null;connected=false;destroyed=false;reconnectAttempt=0;reconnectTimer=null;maxReconnects;constructor(e){this.options=e,this.maxReconnects=e.maxReconnects??1/0;}get isConnected(){return this.connected}connect(){this.destroyed||this.connected||this.eventSource||this.openConnection();}disconnect(){this.destroyed=true,this.cleanup();}openConnection(){if(!this.destroyed)try{let e=new URL(this.options.url);e.searchParams.set("apiKey",this.options.apiKey),this.eventSource=new EventSource(e.toString()),this.eventSource.addEventListener("open",()=>{this.connected=!0,this.reconnectAttempt=0,this.options.onStateChange?.(!0);}),this.eventSource.addEventListener("message",t=>{this.handleMessage(t.data);}),this.eventSource.addEventListener("flag_update",t=>{this.handleMessage(t.data);}),this.eventSource.addEventListener("FLAG_UPDATE",t=>{this.handleMessage(t.data);}),this.eventSource.addEventListener("FLAG_DELETE",t=>{this.handleMessage(t.data);}),this.eventSource.addEventListener("error",()=>{this.connected=!1,this.options.onStateChange?.(!1),this.cleanup(),this.scheduleReconnect();});}catch{this.scheduleReconnect();}}handleMessage(e){try{let t=JSON.parse(e);this.options.onEvent(t);}catch{}}cleanup(){this.eventSource&&(this.eventSource.close(),this.eventSource=null),this.reconnectTimer!==null&&(clearTimeout(this.reconnectTimer),this.reconnectTimer=null);}scheduleReconnect(){if(this.destroyed||this.reconnectAttempt>=this.maxReconnects)return;let e=v(this.reconnectAttempt,1e3,3e4);this.reconnectAttempt++,this.reconnectTimer=setTimeout(()=>{this.reconnectTimer=null,this.destroyed||this.openConnection();},e);}};var O="nexus_flags_",N="nexus_anon_id",f=class{memory=new Map;storageKey;localStorageAvailable;broadcastChannel=null;constructor(e){this.storageKey=`${O}${e}`,this.localStorageAvailable=this.testLocalStorage(),this.hydrate(),this.setupBroadcastChannel();}set(e,t){this.memory.set(e,t),this.persist(),this.broadcastUpdate(e,t);}get(e){return this.memory.get(e)}setAll(e){for(let[t,n]of Object.entries(e))this.memory.set(t,n);this.persist();}delete(e){this.memory.delete(e),this.persist();}getAll(){return Object.fromEntries(this.memory.entries())}clear(){if(this.memory.clear(),this.localStorageAvailable)try{window.localStorage.removeItem(this.storageKey);}catch{}this.broadcastChannel?.close();}getOrCreateAnonymousId(){if(typeof window>"u")return "anon-ssr-node";if(this.localStorageAvailable)try{let e=window.localStorage.getItem(N);return e||(e=`anon_${Math.random().toString(36).substring(2,11)}`,window.localStorage.setItem(N,e)),e}catch{}return `anon_${Math.random().toString(36).substring(2,11)}`}testLocalStorage(){try{if(typeof window>"u")return !1;let e="__nexus_test__";return window.localStorage.setItem(e,"1"),window.localStorage.removeItem(e),!0}catch{return false}}hydrate(){if(this.localStorageAvailable)try{let e=window.localStorage.getItem(this.storageKey);if(e){let t=JSON.parse(e);for(let[n,s]of Object.entries(t))this.memory.set(n,s);}}catch{}}persist(){if(this.localStorageAvailable)try{window.localStorage.setItem(this.storageKey,JSON.stringify(Object.fromEntries(this.memory.entries())));}catch{}}setupBroadcastChannel(){try{typeof BroadcastChannel<"u"&&(this.broadcastChannel=new BroadcastChannel(`nexus_flags_${this.storageKey}`),this.broadcastChannel.onmessage=e=>{e.data?.key&&e.data?.result&&this.memory.set(e.data.key,e.data.result);});}catch{}}broadcastUpdate(e,t){try{this.broadcastChannel?.postMessage({key:e,result:t});}catch{}}};var b=class{apiKey;baseUrl;timeoutMs;user;storage;listeners=new Map;sseManager;flagDefinitions=new Map;constructor(e={}){if(this.apiKey=w(e.apiKey),this.baseUrl=x(e.baseUrl),this.timeoutMs=e.timeoutMs??3e3,this.storage=new f(this.apiKey.substring(0,16)),this.user=e.user??{id:this.storage.getOrCreateAnonymousId()},e.bootstrap){let t={};for(let[n,s]of Object.entries(e.bootstrap))if(typeof s=="boolean")t[n]={key:n,enabled:s,variants:{},reason:s?"DEFAULT_ENABLED":"KILL_SWITCH",version:1};else if(s&&typeof s=="object"){let a=s,l=typeof a.enabled=="boolean"?a.enabled:typeof a.isEnabled=="boolean"?a.isEnabled:true;t[n]={key:a.key??n,enabled:l,variants:a.variants??{},reason:l?"DEFAULT_ENABLED":"KILL_SWITCH",version:typeof a.version=="number"?a.version:1};}this.storage.setAll(t);}e.realtime!==false&&this.initRealtimeSync(),this.refreshFlags();}isEnabled(e,t=false){let n=this.storage.get(e);return n!==void 0?n.enabled:t}getVariant(e,t,n){let s=this.storage.get(e);if(!s?.enabled||!s.variants)return n;let a=s.variants[t];return a!==void 0?a:n}async identify(e){this.user={...this.user,...e},await this.refreshFlags();}reset(){this.user={id:this.storage.getOrCreateAnonymousId()},this.refreshFlags();}onFlagChange(e,t){return this.listeners.has(e)||this.listeners.set(e,new Set),this.listeners.get(e).add(t),()=>this.listeners.get(e)?.delete(t)}destroy(){this.sseManager?.disconnect(),this.listeners.clear(),this.storage.clear();}initRealtimeSync(){this.sseManager=new g({url:`${this.baseUrl}/api/v1/flags/stream`,apiKey:this.apiKey,onEvent:e=>{if(e.type==="FLAG_UPDATE"&&e.data){let t=this.flagDefinitions.get(e.key),n=t?y(t,this.user):e.data;this.storage.set(e.key,n),this.listeners.get(e.key)?.forEach(s=>s(n));}else e.type==="FLAG_DELETE"&&(this.storage.delete(e.key),this.flagDefinitions.delete(e.key));}}),this.sseManager.connect();}async refreshFlags(){try{let e=await A({url:`${this.baseUrl}/api/v1/flags/eval`,method:"GET",headers:{Authorization:`Bearer ${this.apiKey}`,"X-Nexus-User-Id":this.user.id??"anon","X-Nexus-Country":this.user.country??""},timeoutMs:this.timeoutMs,maxRetries:2}),t=e.data,n=t&&typeof t=="object"&&"data"in t?t.data:e.data;if(n&&typeof n=="object")for(let[s,a]of Object.entries(n)){this.flagDefinitions.set(s,a);let l=y(a,this.user);this.storage.set(s,l),this.listeners.get(s)?.forEach(i=>i(l));}}catch{}}};
8
+ exports.FlagStorage=f;exports.NexusFlagsClient=b;exports.SSEManager=g;exports.computeRolloutBucket=C;exports.evaluateFlag=y;exports.murmur3=F;return exports;})({});
package/dist/index.mjs CHANGED
@@ -1 +1 @@
1
- import {computeBackoffMs,resolveApiKey,resolveBaseUrl,fetchWithRetry}from'@nexussdk/core';function p(r,e=0){let t=e>>>0,s=3432918353,n=461845907,a=0,f=Math.floor(r.length/4)*4;for(;a<f;){let o=r.charCodeAt(a)&255|(r.charCodeAt(a+1)&255)<<8|(r.charCodeAt(a+2)&255)<<16|(r.charCodeAt(a+3)&255)<<24;o=Math.imul(o,s),o=o<<15|o>>>17,o=Math.imul(o,n),t^=o,t=t<<13|t>>>19,t=Math.imul(t,5)+3864292196>>>0,a+=4;}let i=0,h=r.length&3;return h>=3&&(i^=(r.charCodeAt(a+2)&255)<<16),h>=2&&(i^=(r.charCodeAt(a+1)&255)<<8),h>=1&&(i^=r.charCodeAt(a)&255,i=Math.imul(i,s),i=i<<15|i>>>17,i=Math.imul(i,n),t^=i),t^=r.length,t^=t>>>16,t=Math.imul(t,2246822507),t^=t>>>13,t=Math.imul(t,3266489909),t^=t>>>16,t>>>0}function v(r,e){let t=`${r}:${e}`;return p(t)%100}function g(r,e){let t=r.replace(/^v/,"").split(".").map(Number),s=e.replace(/^v/,"").split(".").map(Number);for(let n=0;n<3;n++){let a=(t[n]??0)-(s[n]??0);if(a!==0)return a}return 0}function y(r,e){let t=e.split("."),s=r;for(let n of t){if(s==null||typeof s!="object")return;s=s[n];}return s}function b(r,e){let t=y(e,r.attribute),s=r.values;switch(r.operator){case "EQUALS":return t===s[0];case "NOT_EQUALS":return t!==s[0];case "IN":return s.includes(t);case "NOT_IN":return !s.includes(t);case "CONTAINS":return typeof t=="string"&&t.includes(String(s[0]));case "NOT_CONTAINS":return typeof t=="string"&&!t.includes(String(s[0]));case "STARTS_WITH":return typeof t=="string"&&t.startsWith(String(s[0]));case "ENDS_WITH":return typeof t=="string"&&t.endsWith(String(s[0]));case "GREATER_THAN":return typeof t=="number"&&t>Number(s[0]);case "LESS_THAN":return typeof t=="number"&&t<Number(s[0]);case "SEMVER_GTE":return typeof t=="string"&&typeof s[0]=="string"&&g(t,String(s[0]))>=0;case "SEMVER_LTE":return typeof t=="string"&&typeof s[0]=="string"&&g(t,String(s[0]))<=0;default:return false}}function u(r,e){if(!r.isEnabled)return {key:r.key,enabled:false,variants:{},reason:"KILL_SWITCH",version:r.version};if(r.targetingRules.length>0){if(!r.targetingRules.every(s=>b(s,e)))return {key:r.key,enabled:false,variants:{},reason:"FALLBACK",version:r.version};if(r.rolloutPercentage>=100)return {key:r.key,enabled:true,variants:r.variants,reason:"TARGETING_MATCH",version:r.version}}if(r.rolloutPercentage>0){let t=e.id??"anon";return v(t,r.key)<r.rolloutPercentage?{key:r.key,enabled:true,variants:r.variants,reason:r.targetingRules.length>0?"TARGETING_MATCH":"ROLLOUT_MATCH",version:r.version}:{key:r.key,enabled:false,variants:{},reason:"FALLBACK",version:r.version}}return {key:r.key,enabled:false,variants:{},reason:"KILL_SWITCH",version:r.version}}var l=class{options;eventSource=null;connected=false;destroyed=false;reconnectAttempt=0;reconnectTimer=null;maxReconnects;constructor(e){this.options=e,this.maxReconnects=e.maxReconnects??1/0;}get isConnected(){return this.connected}connect(){this.destroyed||this.connected||this.eventSource||this.openConnection();}disconnect(){this.destroyed=true,this.cleanup();}openConnection(){if(!this.destroyed)try{let e=new URL(this.options.url);e.searchParams.set("apiKey",this.options.apiKey),this.eventSource=new EventSource(e.toString()),this.eventSource.addEventListener("open",()=>{this.connected=!0,this.reconnectAttempt=0,this.options.onStateChange?.(!0);}),this.eventSource.addEventListener("message",t=>{this.handleMessage(t.data);}),this.eventSource.addEventListener("flag_update",t=>{this.handleMessage(t.data);}),this.eventSource.addEventListener("error",()=>{this.connected=!1,this.options.onStateChange?.(!1),this.cleanup(),this.scheduleReconnect();});}catch{this.scheduleReconnect();}}handleMessage(e){try{let t=JSON.parse(e);this.options.onEvent(t);}catch{}}cleanup(){this.eventSource&&(this.eventSource.close(),this.eventSource=null),this.reconnectTimer!==null&&(clearTimeout(this.reconnectTimer),this.reconnectTimer=null);}scheduleReconnect(){if(this.destroyed||this.reconnectAttempt>=this.maxReconnects)return;let e=computeBackoffMs(this.reconnectAttempt,1e3,3e4);this.reconnectAttempt++,this.reconnectTimer=setTimeout(()=>{this.reconnectTimer=null,this.destroyed||this.openConnection();},e);}};var E="nexus_flags_",m="nexus_anon_id",c=class{memory=new Map;storageKey;localStorageAvailable;broadcastChannel=null;constructor(e){this.storageKey=`${E}${e}`,this.localStorageAvailable=this.testLocalStorage(),this.hydrate(),this.setupBroadcastChannel();}set(e,t){this.memory.set(e,t),this.persist(),this.broadcastUpdate(e,t);}get(e){return this.memory.get(e)}setAll(e){for(let[t,s]of Object.entries(e))this.memory.set(t,s);this.persist();}delete(e){this.memory.delete(e),this.persist();}getAll(){return Object.fromEntries(this.memory.entries())}clear(){if(this.memory.clear(),this.localStorageAvailable)try{window.localStorage.removeItem(this.storageKey);}catch{}this.broadcastChannel?.close();}getOrCreateAnonymousId(){if(typeof window>"u")return "anon-ssr-node";if(this.localStorageAvailable)try{let e=window.localStorage.getItem(m);return e||(e=`anon_${Math.random().toString(36).substring(2,11)}`,window.localStorage.setItem(m,e)),e}catch{}return `anon_${Math.random().toString(36).substring(2,11)}`}testLocalStorage(){try{if(typeof window>"u")return !1;let e="__nexus_test__";return window.localStorage.setItem(e,"1"),window.localStorage.removeItem(e),!0}catch{return false}}hydrate(){if(this.localStorageAvailable)try{let e=window.localStorage.getItem(this.storageKey);if(e){let t=JSON.parse(e);for(let[s,n]of Object.entries(t))this.memory.set(s,n);}}catch{}}persist(){if(this.localStorageAvailable)try{window.localStorage.setItem(this.storageKey,JSON.stringify(Object.fromEntries(this.memory.entries())));}catch{}}setupBroadcastChannel(){try{typeof BroadcastChannel<"u"&&(this.broadcastChannel=new BroadcastChannel(`nexus_flags_${this.storageKey}`),this.broadcastChannel.onmessage=e=>{e.data?.key&&e.data?.result&&this.memory.set(e.data.key,e.data.result);});}catch{}}broadcastUpdate(e,t){try{this.broadcastChannel?.postMessage({key:e,result:t});}catch{}}};var d=class{apiKey;baseUrl;timeoutMs;user;storage;listeners=new Map;sseManager;flagDefinitions=new Map;constructor(e={}){this.apiKey=resolveApiKey(e.apiKey),this.baseUrl=resolveBaseUrl(e.baseUrl),this.timeoutMs=e.timeoutMs??3e3,this.storage=new c(this.apiKey.substring(0,16)),this.user=e.user??{id:this.storage.getOrCreateAnonymousId()},e.bootstrap&&this.storage.setAll(e.bootstrap),e.realtime!==false&&this.initRealtimeSync(),this.refreshFlags();}isEnabled(e,t=false){let s=this.storage.get(e);return s!==void 0?s.enabled:t}getVariant(e,t,s){let n=this.storage.get(e);if(!n?.enabled||!n.variants)return s;let a=n.variants[t];return a!==void 0?a:s}async identify(e){this.user={...this.user,...e},await this.refreshFlags();}reset(){this.user={id:this.storage.getOrCreateAnonymousId()},this.refreshFlags();}onFlagChange(e,t){return this.listeners.has(e)||this.listeners.set(e,new Set),this.listeners.get(e).add(t),()=>this.listeners.get(e)?.delete(t)}destroy(){this.sseManager?.disconnect(),this.listeners.clear(),this.storage.clear();}initRealtimeSync(){this.sseManager=new l({url:`${this.baseUrl}/api/v1/flags/stream`,apiKey:this.apiKey,onEvent:e=>{if(e.type==="FLAG_UPDATE"&&e.data){let t=this.flagDefinitions.get(e.key),s=t?u(t,this.user):e.data;this.storage.set(e.key,s),this.listeners.get(e.key)?.forEach(n=>n(s));}else e.type==="FLAG_DELETE"&&(this.storage.delete(e.key),this.flagDefinitions.delete(e.key));}}),this.sseManager.connect();}async refreshFlags(){try{let e=await fetchWithRetry({url:`${this.baseUrl}/api/v1/flags/eval`,method:"GET",headers:{Authorization:`Bearer ${this.apiKey}`,"X-Nexus-User-Id":this.user.id??"anon","X-Nexus-Country":this.user.country??""},timeoutMs:this.timeoutMs,maxRetries:2});for(let[t,s]of Object.entries(e.data)){this.flagDefinitions.set(t,s);let n=u(s,this.user);this.storage.set(t,n),this.listeners.get(t)?.forEach(a=>a(n));}}catch{}}};export{c as FlagStorage,d as NexusFlagsClient,l as SSEManager,v as computeRolloutBucket,u as evaluateFlag,p as murmur3};
1
+ import {computeBackoffMs,resolveApiKey,resolveBaseUrl,fetchWithRetry}from'@nexussdk/core';function v(n,t=0){let e=t>>>0,s=3432918353,r=461845907,a=0,l=Math.floor(n.length/4)*4;for(;a<l;){let o=n.charCodeAt(a)&255|(n.charCodeAt(a+1)&255)<<8|(n.charCodeAt(a+2)&255)<<16|(n.charCodeAt(a+3)&255)<<24;o=Math.imul(o,s),o=o<<15|o>>>17,o=Math.imul(o,r),e^=o,e=e<<13|e>>>19,e=Math.imul(e,5)+3864292196>>>0,a+=4;}let i=0,g=n.length&3;return g>=3&&(i^=(n.charCodeAt(a+2)&255)<<16),g>=2&&(i^=(n.charCodeAt(a+1)&255)<<8),g>=1&&(i^=n.charCodeAt(a)&255,i=Math.imul(i,s),i=i<<15|i>>>17,i=Math.imul(i,r),e^=i),e^=n.length,e^=e>>>16,e=Math.imul(e,2246822507),e^=e>>>13,e=Math.imul(e,3266489909),e^=e>>>16,e>>>0}function f(n,t){let e=`${n}:${t}`;return v(e)%100}function p(n,t){let e=n.replace(/^v/,"").split(".").map(Number),s=t.replace(/^v/,"").split(".").map(Number);for(let r=0;r<3;r++){let a=(e[r]??0)-(s[r]??0);if(a!==0)return a}return 0}function y(n,t){let e=t.split("."),s=n;for(let r of e){if(s==null||typeof s!="object")return;s=s[r];}return s}function b(n,t){let e=y(t,n.attribute),s=n.values;switch(n.operator){case "EQUALS":return e===s[0];case "NOT_EQUALS":return e!==s[0];case "IN":return s.includes(e);case "NOT_IN":return !s.includes(e);case "CONTAINS":return typeof e=="string"&&e.includes(String(s[0]));case "NOT_CONTAINS":return typeof e=="string"&&!e.includes(String(s[0]));case "STARTS_WITH":return typeof e=="string"&&e.startsWith(String(s[0]));case "ENDS_WITH":return typeof e=="string"&&e.endsWith(String(s[0]));case "GREATER_THAN":return typeof e=="number"&&e>Number(s[0]);case "LESS_THAN":return typeof e=="number"&&e<Number(s[0]);case "SEMVER_GTE":return typeof e=="string"&&typeof s[0]=="string"&&p(e,String(s[0]))>=0;case "SEMVER_LTE":return typeof e=="string"&&typeof s[0]=="string"&&p(e,String(s[0]))<=0;default:return false}}function d(n,t){if(!n.isEnabled)return {key:n.key,enabled:false,variants:{},reason:"KILL_SWITCH",version:n.version};if(n.targetingRules.length>0){if(!n.targetingRules.every(s=>b(s,t)))return {key:n.key,enabled:false,variants:{},reason:"FALLBACK",version:n.version};if(n.rolloutPercentage>=100)return {key:n.key,enabled:true,variants:n.variants,reason:"TARGETING_MATCH",version:n.version}}if(n.rolloutPercentage>0){let e=t.id??"anon";return f(e,n.key)<n.rolloutPercentage?{key:n.key,enabled:true,variants:n.variants,reason:n.targetingRules.length>0?"TARGETING_MATCH":"ROLLOUT_MATCH",version:n.version}:{key:n.key,enabled:false,variants:{},reason:"FALLBACK",version:n.version}}return {key:n.key,enabled:false,variants:{},reason:"KILL_SWITCH",version:n.version}}var c=class{options;eventSource=null;connected=false;destroyed=false;reconnectAttempt=0;reconnectTimer=null;maxReconnects;constructor(t){this.options=t,this.maxReconnects=t.maxReconnects??1/0;}get isConnected(){return this.connected}connect(){this.destroyed||this.connected||this.eventSource||this.openConnection();}disconnect(){this.destroyed=true,this.cleanup();}openConnection(){if(!this.destroyed)try{let t=new URL(this.options.url);t.searchParams.set("apiKey",this.options.apiKey),this.eventSource=new EventSource(t.toString()),this.eventSource.addEventListener("open",()=>{this.connected=!0,this.reconnectAttempt=0,this.options.onStateChange?.(!0);}),this.eventSource.addEventListener("message",e=>{this.handleMessage(e.data);}),this.eventSource.addEventListener("flag_update",e=>{this.handleMessage(e.data);}),this.eventSource.addEventListener("FLAG_UPDATE",e=>{this.handleMessage(e.data);}),this.eventSource.addEventListener("FLAG_DELETE",e=>{this.handleMessage(e.data);}),this.eventSource.addEventListener("error",()=>{this.connected=!1,this.options.onStateChange?.(!1),this.cleanup(),this.scheduleReconnect();});}catch{this.scheduleReconnect();}}handleMessage(t){try{let e=JSON.parse(t);this.options.onEvent(e);}catch{}}cleanup(){this.eventSource&&(this.eventSource.close(),this.eventSource=null),this.reconnectTimer!==null&&(clearTimeout(this.reconnectTimer),this.reconnectTimer=null);}scheduleReconnect(){if(this.destroyed||this.reconnectAttempt>=this.maxReconnects)return;let t=computeBackoffMs(this.reconnectAttempt,1e3,3e4);this.reconnectAttempt++,this.reconnectTimer=setTimeout(()=>{this.reconnectTimer=null,this.destroyed||this.openConnection();},t);}};var S="nexus_flags_",m="nexus_anon_id",u=class{memory=new Map;storageKey;localStorageAvailable;broadcastChannel=null;constructor(t){this.storageKey=`${S}${t}`,this.localStorageAvailable=this.testLocalStorage(),this.hydrate(),this.setupBroadcastChannel();}set(t,e){this.memory.set(t,e),this.persist(),this.broadcastUpdate(t,e);}get(t){return this.memory.get(t)}setAll(t){for(let[e,s]of Object.entries(t))this.memory.set(e,s);this.persist();}delete(t){this.memory.delete(t),this.persist();}getAll(){return Object.fromEntries(this.memory.entries())}clear(){if(this.memory.clear(),this.localStorageAvailable)try{window.localStorage.removeItem(this.storageKey);}catch{}this.broadcastChannel?.close();}getOrCreateAnonymousId(){if(typeof window>"u")return "anon-ssr-node";if(this.localStorageAvailable)try{let t=window.localStorage.getItem(m);return t||(t=`anon_${Math.random().toString(36).substring(2,11)}`,window.localStorage.setItem(m,t)),t}catch{}return `anon_${Math.random().toString(36).substring(2,11)}`}testLocalStorage(){try{if(typeof window>"u")return !1;let t="__nexus_test__";return window.localStorage.setItem(t,"1"),window.localStorage.removeItem(t),!0}catch{return false}}hydrate(){if(this.localStorageAvailable)try{let t=window.localStorage.getItem(this.storageKey);if(t){let e=JSON.parse(t);for(let[s,r]of Object.entries(e))this.memory.set(s,r);}}catch{}}persist(){if(this.localStorageAvailable)try{window.localStorage.setItem(this.storageKey,JSON.stringify(Object.fromEntries(this.memory.entries())));}catch{}}setupBroadcastChannel(){try{typeof BroadcastChannel<"u"&&(this.broadcastChannel=new BroadcastChannel(`nexus_flags_${this.storageKey}`),this.broadcastChannel.onmessage=t=>{t.data?.key&&t.data?.result&&this.memory.set(t.data.key,t.data.result);});}catch{}}broadcastUpdate(t,e){try{this.broadcastChannel?.postMessage({key:t,result:e});}catch{}}};var h=class{apiKey;baseUrl;timeoutMs;user;storage;listeners=new Map;sseManager;flagDefinitions=new Map;constructor(t={}){if(this.apiKey=resolveApiKey(t.apiKey),this.baseUrl=resolveBaseUrl(t.baseUrl),this.timeoutMs=t.timeoutMs??3e3,this.storage=new u(this.apiKey.substring(0,16)),this.user=t.user??{id:this.storage.getOrCreateAnonymousId()},t.bootstrap){let e={};for(let[s,r]of Object.entries(t.bootstrap))if(typeof r=="boolean")e[s]={key:s,enabled:r,variants:{},reason:r?"DEFAULT_ENABLED":"KILL_SWITCH",version:1};else if(r&&typeof r=="object"){let a=r,l=typeof a.enabled=="boolean"?a.enabled:typeof a.isEnabled=="boolean"?a.isEnabled:true;e[s]={key:a.key??s,enabled:l,variants:a.variants??{},reason:l?"DEFAULT_ENABLED":"KILL_SWITCH",version:typeof a.version=="number"?a.version:1};}this.storage.setAll(e);}t.realtime!==false&&this.initRealtimeSync(),this.refreshFlags();}isEnabled(t,e=false){let s=this.storage.get(t);return s!==void 0?s.enabled:e}getVariant(t,e,s){let r=this.storage.get(t);if(!r?.enabled||!r.variants)return s;let a=r.variants[e];return a!==void 0?a:s}async identify(t){this.user={...this.user,...t},await this.refreshFlags();}reset(){this.user={id:this.storage.getOrCreateAnonymousId()},this.refreshFlags();}onFlagChange(t,e){return this.listeners.has(t)||this.listeners.set(t,new Set),this.listeners.get(t).add(e),()=>this.listeners.get(t)?.delete(e)}destroy(){this.sseManager?.disconnect(),this.listeners.clear(),this.storage.clear();}initRealtimeSync(){this.sseManager=new c({url:`${this.baseUrl}/api/v1/flags/stream`,apiKey:this.apiKey,onEvent:t=>{if(t.type==="FLAG_UPDATE"&&t.data){let e=this.flagDefinitions.get(t.key),s=e?d(e,this.user):t.data;this.storage.set(t.key,s),this.listeners.get(t.key)?.forEach(r=>r(s));}else t.type==="FLAG_DELETE"&&(this.storage.delete(t.key),this.flagDefinitions.delete(t.key));}}),this.sseManager.connect();}async refreshFlags(){try{let t=await fetchWithRetry({url:`${this.baseUrl}/api/v1/flags/eval`,method:"GET",headers:{Authorization:`Bearer ${this.apiKey}`,"X-Nexus-User-Id":this.user.id??"anon","X-Nexus-Country":this.user.country??""},timeoutMs:this.timeoutMs,maxRetries:2}),e=t.data,s=e&&typeof e=="object"&&"data"in e?e.data:t.data;if(s&&typeof s=="object")for(let[r,a]of Object.entries(s)){this.flagDefinitions.set(r,a);let l=d(a,this.user);this.storage.set(r,l),this.listeners.get(r)?.forEach(i=>i(l));}}catch{}}};export{u as FlagStorage,h as NexusFlagsClient,c as SSEManager,f as computeRolloutBucket,d as evaluateFlag,v as murmur3};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nexussdk/flags",
3
- "version": "0.0.3",
3
+ "version": "0.0.4",
4
4
  "private": false,
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -21,19 +21,40 @@
21
21
  "directory": "packages/sdk-flags"
22
22
  },
23
23
  "homepage": "https://github.com/Huynhdung295/NexusSDK#readme",
24
+ "bugs": {
25
+ "url": "https://github.com/Huynhdung295/NexusSDK/issues"
26
+ },
24
27
  "keywords": [
25
28
  "nexus",
26
29
  "nexussdk",
27
30
  "feature-flags",
31
+ "flags",
32
+ "feature-toggles",
28
33
  "remote-config",
29
34
  "abac",
30
35
  "sse",
31
- "murmur3"
36
+ "murmur3",
37
+ "canary-release",
38
+ "multivariate-testing",
39
+ "offline-first",
40
+ "self-hosted",
41
+ "local-dev-server",
42
+ "zero-dependencies",
43
+ "react",
44
+ "vue",
45
+ "angular",
46
+ "svelte",
47
+ "nextjs",
48
+ "nuxt",
49
+ "edge-runtime"
32
50
  ],
33
51
  "files": [
34
52
  "dist",
35
53
  "README.md"
36
54
  ],
55
+ "bin": {
56
+ "nexus-flags-dev": "./dist/dev-server/cli.mjs"
57
+ },
37
58
  "main": "./dist/index.cjs",
38
59
  "module": "./dist/index.mjs",
39
60
  "types": "./dist/index.d.ts",
@@ -42,13 +63,17 @@
42
63
  "types": "./dist/index.d.ts",
43
64
  "import": "./dist/index.mjs",
44
65
  "require": "./dist/index.cjs"
66
+ },
67
+ "./dev-server": {
68
+ "import": "./dist/dev-server/cli.mjs"
45
69
  }
46
70
  },
47
71
  "dependencies": {
48
- "@nexussdk/contracts": "0.0.3",
49
- "@nexussdk/core": "0.0.3"
72
+ "@nexussdk/core": "0.0.4",
73
+ "@nexussdk/contracts": "0.0.4"
50
74
  },
51
75
  "devDependencies": {
76
+ "@types/node": "^22.13.4",
52
77
  "tsup": "^8.0.2",
53
78
  "typescript": "^5.4.5",
54
79
  "rimraf": "^5.0.5"
@@ -56,6 +81,7 @@
56
81
  "scripts": {
57
82
  "build": "tsup",
58
83
  "dev": "tsup --watch",
84
+ "flags-server": "node dist/dev-server/cli.mjs",
59
85
  "lint": "tsc --noEmit",
60
86
  "clean": "rimraf dist"
61
87
  }