@nexussdk/sdk 0.0.1 → 0.0.3
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/README.md +124 -0
- package/dist/index.cjs +1 -2
- package/dist/index.global.js +1 -2
- package/dist/index.mjs +1 -2
- package/dist/react.cjs +1 -2
- package/dist/react.mjs +1 -2
- package/package.json +36 -5
- package/.turbo/turbo-build.log +0 -45
- package/dist/index.cjs.map +0 -1
- package/dist/index.global.js.map +0 -1
- package/dist/index.mjs.map +0 -1
- package/dist/react.cjs.map +0 -1
- package/dist/react.mjs.map +0 -1
- package/src/index.ts +0 -27
- package/src/nexus.ts +0 -208
- package/src/react.tsx +0 -204
- package/tsconfig.json +0 -10
- package/tsup.config.ts +0 -40
package/README.md
ADDED
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
# @nexussdk/sdk
|
|
2
|
+
|
|
3
|
+
The official umbrella client SDK for the **Nexus Platform** — high-performance feature flags, error telemetry, and React hooks with zero runtime bloat (<8KB gzipped).
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/@nexussdk/sdk)
|
|
6
|
+
[](https://opensource.org/licenses/MIT)
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## Features
|
|
11
|
+
|
|
12
|
+
- 🚩 **Feature Flags & Remote Config**: Deterministic Murmur3 hashing, ABAC evaluation rules, and real-time SSE streaming updates.
|
|
13
|
+
- ⚡ **Error Telemetry & Monitoring**: Automatic uncaught exception catching, unhandled promise rejection tracking, and regex-based PII scrubbing.
|
|
14
|
+
- ⚛️ **First-Class React 19 Support**: Idiomatic hooks (`useFlag`, `useNexus`) and `NexusProvider` context wrapper.
|
|
15
|
+
- 🪶 **Ultra Lightweight**: Zero heavy dependencies, tree-shakeable, and sub-8KB gzipped footprint.
|
|
16
|
+
- 🔒 **Type Safe**: Strict TypeScript contracts and autocomplete out of the box.
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## Installation
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
# npm
|
|
24
|
+
npm install @nexussdk/sdk
|
|
25
|
+
|
|
26
|
+
# pnpm
|
|
27
|
+
pnpm add @nexussdk/sdk
|
|
28
|
+
|
|
29
|
+
# yarn
|
|
30
|
+
yarn add @nexussdk/sdk
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
## Quick Start (Vanilla / Node / Browser)
|
|
36
|
+
|
|
37
|
+
```typescript
|
|
38
|
+
import { createNexus } from '@nexussdk/sdk';
|
|
39
|
+
|
|
40
|
+
const nexus = createNexus({
|
|
41
|
+
clientKey: 'pk_live_your_client_key',
|
|
42
|
+
baseUrl: 'https://api.nexusplatform.io',
|
|
43
|
+
environment: 'production',
|
|
44
|
+
user: {
|
|
45
|
+
id: 'usr_123',
|
|
46
|
+
email: 'user@example.com',
|
|
47
|
+
role: 'premium',
|
|
48
|
+
},
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
// 1. Evaluate feature flag
|
|
52
|
+
const isNewCheckout = await nexus.flags.isEnabled('new-checkout-flow', false);
|
|
53
|
+
if (isNewCheckout) {
|
|
54
|
+
// Render new checkout
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// 2. Capture error with breadcrumbs
|
|
58
|
+
try {
|
|
59
|
+
// your app logic
|
|
60
|
+
} catch (error) {
|
|
61
|
+
nexus.tracker.captureException(error, {
|
|
62
|
+
tags: { module: 'checkout' },
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
---
|
|
68
|
+
|
|
69
|
+
## React Integration
|
|
70
|
+
|
|
71
|
+
Wrap your application in `NexusProvider`:
|
|
72
|
+
|
|
73
|
+
```tsx
|
|
74
|
+
import React from 'react';
|
|
75
|
+
import { NexusProvider, useFlag, useNexus } from '@nexussdk/sdk/react';
|
|
76
|
+
|
|
77
|
+
function App() {
|
|
78
|
+
return (
|
|
79
|
+
<NexusProvider
|
|
80
|
+
config={{
|
|
81
|
+
clientKey: 'pk_live_your_client_key',
|
|
82
|
+
baseUrl: 'https://api.nexusplatform.io',
|
|
83
|
+
}}
|
|
84
|
+
>
|
|
85
|
+
<CheckoutPage />
|
|
86
|
+
</NexusProvider>
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function CheckoutPage() {
|
|
91
|
+
const isV2Enabled = useFlag('v2-checkout-button', false);
|
|
92
|
+
const nexus = useNexus();
|
|
93
|
+
|
|
94
|
+
const handleCheckout = () => {
|
|
95
|
+
nexus.tracker.addBreadcrumb('Clicked checkout', 'ui');
|
|
96
|
+
// Proceed to checkout
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
return (
|
|
100
|
+
<button onClick={handleCheckout}>
|
|
101
|
+
{isV2Enabled ? 'Express Checkout' : 'Standard Checkout'}
|
|
102
|
+
</button>
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
---
|
|
108
|
+
|
|
109
|
+
## Architecture & Subpackages
|
|
110
|
+
|
|
111
|
+
`@nexussdk/sdk` bundles the following specialized modules for maximum modularity:
|
|
112
|
+
|
|
113
|
+
| Package | Role |
|
|
114
|
+
|---|---|
|
|
115
|
+
| [`@nexussdk/contracts`](https://www.npmjs.com/package/@nexussdk/contracts) | SSOT TypeScript types and RFC 7807 error models |
|
|
116
|
+
| [`@nexussdk/core`](https://www.npmjs.com/package/@nexussdk/core) | Ring buffer transport kernel and environment resolvers |
|
|
117
|
+
| [`@nexussdk/flags`](https://www.npmjs.com/package/@nexussdk/flags) | Standalone feature flag evaluator and SSE manager |
|
|
118
|
+
| [`@nexussdk/tracker`](https://www.npmjs.com/package/@nexussdk/tracker) | Standalone error tracker and PII sanitizer |
|
|
119
|
+
|
|
120
|
+
---
|
|
121
|
+
|
|
122
|
+
## License
|
|
123
|
+
|
|
124
|
+
MIT © [Nexus Platform](https://github.com/Huynhdung295/NexusSDK)
|
package/dist/index.cjs
CHANGED
|
@@ -1,2 +1 @@
|
|
|
1
|
-
'use strict';var flags=require('@nexussdk/flags'),tracker=require('@nexussdk/tracker');var n=class t{static instance=null;flags;tracker;constructor(e={}){let{apiKey:s,baseUrl:r,user:a,environment:i,tags:o,autoCapture:c,flags:l,tracker:u}=e;this.flags=new flags.NexusFlagsClient({apiKey:s,baseUrl:r,user:a,...l}),this.tracker=new tracker.NexusTrackerClient({apiKey:s,baseUrl:r,environment:i,tags:o,autoCapture:c,...u});}static init(e={}){return t.instance||(t.instance=new t(e)),t.instance}static getInstance(){if(!t.instance)throw new Error('[Nexus SDK] Not initialized. Call Nexus.init({ apiKey: "..." }) first.');return t.instance}static isEnabled(e,s=false){return t.getInstance().flags.isEnabled(e,s)}static getVariant(e,s,r){return t.getInstance().flags.getVariant(e,s,r)}static captureError(e,s){t.getInstance().tracker.captureError(e,s);}static async identify(e){let s=t.getInstance();s.tracker.setUser(e),await s.flags.identify(e);}static reset(){let e=t.getInstance();e.tracker.setUser(null),e.flags.reset();}static async destroy(){t.instance&&(await t.instance.tracker.flush(),t.instance.tracker.destroy(),t.instance.flags.destroy(),t.instance=null);}};Object.defineProperty(exports,"NexusFlagsClient",{enumerable:true,get:function(){return flags.NexusFlagsClient}});Object.defineProperty(exports,"NexusTrackerClient",{enumerable:true,get:function(){return tracker.NexusTrackerClient}});exports.Nexus=n
|
|
2
|
-
//# sourceMappingURL=index.cjs.map
|
|
1
|
+
'use strict';var flags=require('@nexussdk/flags'),tracker=require('@nexussdk/tracker');var n=class t{static instance=null;flags;tracker;constructor(e={}){let{apiKey:s,baseUrl:r,user:a,environment:i,tags:o,autoCapture:c,flags:l,tracker:u}=e;this.flags=new flags.NexusFlagsClient({apiKey:s,baseUrl:r,user:a,...l}),this.tracker=new tracker.NexusTrackerClient({apiKey:s,baseUrl:r,environment:i,tags:o,autoCapture:c,...u});}static init(e={}){return t.instance||(t.instance=new t(e)),t.instance}static getInstance(){if(!t.instance)throw new Error('[Nexus SDK] Not initialized. Call Nexus.init({ apiKey: "..." }) first.');return t.instance}static isEnabled(e,s=false){return t.getInstance().flags.isEnabled(e,s)}static getVariant(e,s,r){return t.getInstance().flags.getVariant(e,s,r)}static captureError(e,s){t.getInstance().tracker.captureError(e,s);}static async identify(e){let s=t.getInstance();s.tracker.setUser(e),await s.flags.identify(e);}static reset(){let e=t.getInstance();e.tracker.setUser(null),e.flags.reset();}static async destroy(){t.instance&&(await t.instance.tracker.flush(),t.instance.tracker.destroy(),t.instance.flags.destroy(),t.instance=null);}};Object.defineProperty(exports,"NexusFlagsClient",{enumerable:true,get:function(){return flags.NexusFlagsClient}});Object.defineProperty(exports,"NexusTrackerClient",{enumerable:true,get:function(){return tracker.NexusTrackerClient}});exports.Nexus=n;
|
package/dist/index.global.js
CHANGED
|
@@ -5,5 +5,4 @@ var Nexus=(function(exports){'use strict';function f(e,t=1e3,r=3e4){let n=t*Math
|
|
|
5
5
|
4. NEXT_PUBLIC_NEXUS_API_KEY (Next.js)
|
|
6
6
|
5. VITE_NEXUS_API_KEY (Vite)
|
|
7
7
|
6. NUXT_PUBLIC_NEXUS_API_KEY (Nuxt)`);return t.trim()}function y(e,t="https://api.nexus.dev"){return (e||g("NEXT_PUBLIC_NEXUS_URL")||_("VITE_NEXUS_URL")||t).replace(/\/$/,"")}function w(e,t,r,n){if(r>n)return "[MaxDepthExceeded]";if(e==null)return e;if(typeof e!="object"&&typeof e!="function")return typeof e=="bigint"||typeof e=="symbol"?e.toString():typeof e=="function"?"[Function]":e;if(e instanceof Error)return {name:e.name,message:e.message,stack:e.stack};if(t.has(e))return "[Circular]";if(t.add(e),Array.isArray(e)){let a=e.map(i=>w(i,t,r+1,n));return t.delete(e),a}let s={};for(let a of Object.keys(e)){let i=e[a];s[a]=w(i,t,r+1,n);}return t.delete(e),s}function E(e,t=8){let r=w(e,new WeakSet,0,t);try{return JSON.stringify(r)}catch{return JSON.stringify({error:"[SerializationFailed]"})}}function D(e,t=0){let r=t>>>0,n=3432918353,s=461845907,a=0,i=Math.floor(e.length/4)*4;for(;a<i;){let c=e.charCodeAt(a)&255|(e.charCodeAt(a+1)&255)<<8|(e.charCodeAt(a+2)&255)<<16|(e.charCodeAt(a+3)&255)<<24;c=Math.imul(c,n),c=c<<15|c>>>17,c=Math.imul(c,s),r^=c,r=r<<13|r>>>19,r=Math.imul(r,5)+3864292196>>>0,a+=4;}let o=0,l=e.length&3;return l>=3&&(o^=(e.charCodeAt(a+2)&255)<<16),l>=2&&(o^=(e.charCodeAt(a+1)&255)<<8),l>=1&&(o^=e.charCodeAt(a)&255,o=Math.imul(o,n),o=o<<15|o>>>17,o=Math.imul(o,s),r^=o),r^=e.length,r^=r>>>16,r=Math.imul(r,2246822507),r^=r>>>13,r=Math.imul(r,3266489909),r^=r>>>16,r>>>0}function B(e,t){let r=`${e}:${t}`;return D(r)%100}function M(e,t){let r=e.replace(/^v/,"").split(".").map(Number),n=t.replace(/^v/,"").split(".").map(Number);for(let s=0;s<3;s++){let a=(r[s]??0)-(n[s]??0);if(a!==0)return a}return 0}function F(e,t){let r=t.split("."),n=e;for(let s of r){if(n==null||typeof n!="object")return;n=n[s];}return n}function H(e,t){let r=F(t,e.attribute),n=e.values;switch(e.operator){case "EQUALS":return r===n[0];case "NOT_EQUALS":return r!==n[0];case "IN":return n.includes(r);case "NOT_IN":return !n.includes(r);case "CONTAINS":return typeof r=="string"&&r.includes(String(n[0]));case "NOT_CONTAINS":return typeof r=="string"&&!r.includes(String(n[0]));case "STARTS_WITH":return typeof r=="string"&&r.startsWith(String(n[0]));case "ENDS_WITH":return typeof r=="string"&&r.endsWith(String(n[0]));case "GREATER_THAN":return typeof r=="number"&&r>Number(n[0]);case "LESS_THAN":return typeof r=="number"&&r<Number(n[0]);case "SEMVER_GTE":return typeof r=="string"&&typeof n[0]=="string"&&M(r,String(n[0]))>=0;case "SEMVER_LTE":return typeof r=="string"&&typeof n[0]=="string"&&M(r,String(n[0]))<=0;default:return false}}function I(e,t){if(!e.isEnabled)return {key:e.key,enabled:false,variants:{},reason:"KILL_SWITCH",version:e.version};if(e.targetingRules.length>0){if(!e.targetingRules.every(r=>H(r,t)))return {key:e.key,enabled:false,variants:{},reason:"FALLBACK",version:e.version};if(e.rolloutPercentage>=100)return {key:e.key,enabled:true,variants:e.variants,reason:"TARGETING_MATCH",version:e.version}}if(e.rolloutPercentage>0){let r=t.id??"anon";return B(r,e.key)<e.rolloutPercentage?{key:e.key,enabled:true,variants:e.variants,reason:e.targetingRules.length>0?"TARGETING_MATCH":"ROLLOUT_MATCH",version:e.version}:{key:e.key,enabled:false,variants:{},reason:"FALLBACK",version:e.version}}return {key:e.key,enabled:false,variants:{},reason:"KILL_SWITCH",version:e.version}}var j=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=f(this.reconnectAttempt,1e3,3e4);this.reconnectAttempt++,this.reconnectTimer=setTimeout(()=>{this.reconnectTimer=null,this.destroyed||this.openConnection();},e);}},X="nexus_flags_",R="nexus_anon_id",W=class{memory=new Map;storageKey;localStorageAvailable;broadcastChannel=null;constructor(e){this.storageKey=`${X}${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,r]of Object.entries(e))this.memory.set(t,r);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(R);return e||(e=`anon_${Math.random().toString(36).substring(2,11)}`,window.localStorage.setItem(R,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[r,n]of Object.entries(t))this.memory.set(r,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{}}},S=class{apiKey;baseUrl;timeoutMs;user;storage;listeners=new Map;sseManager;flagDefinitions=new Map;constructor(e={}){this.apiKey=m(e.apiKey),this.baseUrl=y(e.baseUrl),this.timeoutMs=e.timeoutMs??3e3,this.storage=new W(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 r=this.storage.get(e);return r!==void 0?r.enabled:t}getVariant(e,t,r){let n=this.storage.get(e);if(!n?.enabled||!n.variants)return r;let s=n.variants[t];return s!==void 0?s:r}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 j({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),r=t?I(t,this.user):e.data;this.storage.set(e.key,r),this.listeners.get(e.key)?.forEach(n=>n(r));}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,r]of Object.entries(e.data)){this.flagDefinitions.set(t,r);let n=I(r,this.user);this.storage.set(t,n),this.listeners.get(t)?.forEach(s=>s(n));}}catch{}}};var z=/^(password|passwd|token|secret|authorization|bearer|auth|credit_?card|cvv|cvc|ssn|api_?key|access_?token|refresh_?token)$/i,Y=/\b(?:\d[ -]*?){13,16}\b/g,G=/[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/g,U=/([?&](token|auth|key|secret|password|api_key|access_token|refresh_token)=)[^&]*/gi;function V(e){return e.replace(Y,"[CARD_REDACTED]").replace(G,"[EMAIL_REDACTED]").replace(U,"$1[REDACTED]")}function v(e,t=0){if(t>5)return "[MaxDepthExceeded]";if(e==null)return e;if(typeof e=="string")return V(e);if(typeof e!="object")return e;if(Array.isArray(e))return e.map(n=>v(n,t+1));let r={};for(let[n,s]of Object.entries(e))z.test(n)?r[n]="[REDACTED]":r[n]=v(s,t+1);return r}function L(e){return e.replace(U,"$1[REDACTED]")}function J(e){if(!e)return [];let t=[],r=e.split(`
|
|
8
|
-
`);for(let n of r){let s=n.trim(),a=s.match(/^at\s+(.+?)\s+\((.+?):(\d+):(\d+)\)$/)||s.match(/^at\s+(.+?):(\d+):(\d+)$/)||s.match(/^at\s+\((.+?):(\d+):(\d+)\)$/);if(a){a.length===5?t.push({functionName:a[1]??"<anonymous>",fileName:a[2]??"<unknown>",lineNumber:parseInt(a[3]??"0",10),columnNumber:parseInt(a[4]??"0",10)}):a.length===4&&t.push({functionName:"<anonymous>",fileName:a[1]??"<unknown>",lineNumber:parseInt(a[2]??"0",10),columnNumber:parseInt(a[3]??"0",10)});continue}let i=s.match(/^(.+?)@(.+?):(\d+):(\d+)$/);i&&t.push({functionName:i[1]??"<anonymous>",fileName:i[2]??"<unknown>",lineNumber:parseInt(i[3]??"0",10),columnNumber:parseInt(i[4]??"0",10)});}return t}function Z(e,t,r){let n=r?.fileName??"unknown",s=r?.lineNumber??0,a=`${e}:${t}:${n}:${s}`,i=5381;for(let o=0;o<a.length;o++)i=(i<<5)+i+a.charCodeAt(o)>>>0;return `fp_${i.toString(16).padStart(8,"0")}`}var q=class{buffer;listenersAttached=false;clickHandler;popStateHandler;constructor(e){this.buffer=new C(e),this.clickHandler=this.handleClick.bind(this),this.popStateHandler=this.handleNavigation.bind(this);}push(e){let t=e.data?v(e.data):void 0;this.buffer.push({...e,data:t,timestamp:Date.now()});}getAll(){return this.buffer.toArray()}clear(){this.buffer.clear();}attachListeners(){this.listenersAttached||typeof window>"u"||(this.listenersAttached=true,document.addEventListener("click",this.clickHandler,{capture:true,passive:true}),window.addEventListener("popstate",this.popStateHandler,{passive:true}),this.interceptConsoleError());}detachListeners(){!this.listenersAttached||typeof window>"u"||(document.removeEventListener("click",this.clickHandler,{capture:true}),window.removeEventListener("popstate",this.popStateHandler),this.listenersAttached=false);}handleClick(e){let t=e.target;if(!t||t instanceof HTMLInputElement&&(t.type==="password"||t.hasAttribute("data-nexus-mask")))return;let r=this.describeElement(t);this.push({category:"ui.click",message:`Clicked ${r}`,level:"info",data:{elementTag:t.tagName.toLowerCase(),elementId:t.id||void 0,elementClass:t.className||void 0}});}handleNavigation(){this.push({category:"navigation",message:`Navigated to ${L(window.location.href)}`,level:"info",data:{url:L(window.location.href)}});}describeElement(e){let t=[e.tagName.toLowerCase()];return e.id&&t.push(`#${e.id}`),e.getAttribute("aria-label")&&t.push(`[aria-label="${e.getAttribute("aria-label")}"]`),t.join("")}interceptConsoleError(){let e=console.error.bind(console);console.error=(...t)=>{this.push({category:"console",message:t.map(String).join(" ").substring(0,500),level:"error"}),e(...t);};}},Q=class{options;maxRetries;isPageHiding=false;constructor(e){this.options=e,this.maxRetries=e.maxRetries??2,typeof document<"u"&&document.addEventListener("visibilitychange",()=>{document.visibilityState==="hidden"&&(this.isPageHiding=true);}),typeof window<"u"&&window.addEventListener("pagehide",()=>{this.isPageHiding=true;});}send(e){let t=E(e);if(this.isPageHiding&&typeof navigator<"u"&&typeof navigator.sendBeacon=="function"){let r=new Blob([t],{type:"application/json"});navigator.sendBeacon(this.options.endpoint,r);return}this.sendWithRetry(t,0);}async flush(e){let t=E(e);if(typeof navigator<"u"&&typeof navigator.sendBeacon=="function"){let r=new Blob([t],{type:"application/json"});navigator.sendBeacon(this.options.endpoint,r);return}await this.sendWithRetry(t,0);}async sendWithRetry(e,t){try{let r=await fetch(this.options.endpoint,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${this.options.apiKey}`},body:e,keepalive:!0});if(r.ok)return;if(r.status>=500&&t<this.maxRetries){let n=f(t,1e3,3e4);await new Promise(s=>setTimeout(s,n)),await this.sendWithRetry(e,t+1);}}catch{if(t<this.maxRetries){let r=f(t,1e3,3e4);await new Promise(n=>setTimeout(n,r)),await this.sendWithRetry(e,t+1);}}}};function ee(e){if(typeof window>"u")return ()=>{};let t=n=>{let s=n.error instanceof Error?n.error:new Error(n.message);e.captureError(s);},r=n=>{e.captureError(n.reason);};return window.addEventListener("error",t),window.addEventListener("unhandledrejection",r),()=>{window.removeEventListener("error",t),window.removeEventListener("unhandledrejection",r);}}var A=class{apiKey;environment;tags;beforeSend;breadcrumbManager;transport;userContext;dedupeMap=new Map;cleanupListeners;constructor(e={}){this.apiKey=m(e.apiKey);let t=y(e.baseUrl);this.environment=e.environment??"production",this.tags={...e.tags},this.beforeSend=e.beforeSend,this.breadcrumbManager=new q(Math.min(e.maxBreadcrumbs??20,50)),this.transport=new Q({endpoint:`${t}/api/v1/telemetry/errors`,apiKey:this.apiKey}),e.autoCapture!==false&&typeof window<"u"&&(this.breadcrumbManager.attachListeners(),this.cleanupListeners=ee(this));}captureError(e,t){let r=this.normalizeError(e),n=J(r.stack),s=Z(r.type,r.message,n[0]),a=this.dedupeMap.get(s);if(a){a.count+=1;return}let i={fingerprint:s,errorType:r.type,errorMessage:r.message,stackTrace:n,breadcrumbs:this.breadcrumbManager.getAll(),userContext:this.userContext,deviceContext:this.getDeviceContext(),tags:{environment:this.environment,...this.tags,...t},occurrenceCount:1,clientTimestamp:Date.now()},o=v(i),l=this.beforeSend?this.beforeSend(o):o;if(!l)return;this.transport.send(l);let c=setTimeout(()=>{let d=this.dedupeMap.get(s);if(d&&d.count>1){let p={...d.lastPayload,occurrenceCount:d.count,clientTimestamp:Date.now()};this.transport.send(p);}this.dedupeMap.delete(s);},1e4);this.dedupeMap.set(s,{timer:c,count:1,lastPayload:l});}addBreadcrumb(e){this.breadcrumbManager.push(e);}setUser(e){this.userContext=e??void 0;}setTag(e,t){this.tags[e]=t;}async flush(){for(let[e,t]of this.dedupeMap.entries()){if(clearTimeout(t.timer),t.count>0){let r={...t.lastPayload,occurrenceCount:t.count,clientTimestamp:Date.now()};await this.transport.flush(r);}this.dedupeMap.delete(e);}}destroy(){this.cleanupListeners?.(),this.breadcrumbManager.detachListeners(),this.breadcrumbManager.clear(),this.dedupeMap.forEach(e=>clearTimeout(e.timer)),this.dedupeMap.clear();}normalizeError(e){return e instanceof Error?{type:e.name||"Error",message:e.message,stack:e.stack}:typeof e=="string"?{type:"UnhandledException",message:e}:{type:"NonErrorRejection",message:String(e)}}getDeviceContext(){let e=typeof window<"u"&&typeof navigator<"u";return {userAgent:e?navigator.userAgent:"Node/SSR",currentUrl:e?window.location.href:"",viewport:e?`${window.innerWidth}x${window.innerHeight}`:void 0,timezone:Intl?.DateTimeFormat()?.resolvedOptions()?.timeZone,networkStatus:e&&"connection"in navigator?navigator.connection?.effectiveType??"unknown":void 0}}};var N=class e{static instance=null;flags;tracker;constructor(t={}){let{apiKey:r,baseUrl:n,user:s,environment:a,tags:i,autoCapture:o,flags:l,tracker:c}=t;this.flags=new S({apiKey:r,baseUrl:n,user:s,...l}),this.tracker=new A({apiKey:r,baseUrl:n,environment:a,tags:i,autoCapture:o,...c});}static init(t={}){return e.instance||(e.instance=new e(t)),e.instance}static getInstance(){if(!e.instance)throw new Error('[Nexus SDK] Not initialized. Call Nexus.init({ apiKey: "..." }) first.');return e.instance}static isEnabled(t,r=false){return e.getInstance().flags.isEnabled(t,r)}static getVariant(t,r,n){return e.getInstance().flags.getVariant(t,r,n)}static captureError(t,r){e.getInstance().tracker.captureError(t,r);}static async identify(t){let r=e.getInstance();r.tracker.setUser(t),await r.flags.identify(t);}static reset(){let t=e.getInstance();t.tracker.setUser(null),t.flags.reset();}static async destroy(){e.instance&&(await e.instance.tracker.flush(),e.instance.tracker.destroy(),e.instance.flags.destroy(),e.instance=null);}};exports.Nexus=N;exports.NexusFlagsClient=S;exports.NexusTrackerClient=A;return exports;})({})
|
|
9
|
-
//# sourceMappingURL=index.global.js.map
|
|
8
|
+
`);for(let n of r){let s=n.trim(),a=s.match(/^at\s+(.+?)\s+\((.+?):(\d+):(\d+)\)$/)||s.match(/^at\s+(.+?):(\d+):(\d+)$/)||s.match(/^at\s+\((.+?):(\d+):(\d+)\)$/);if(a){a.length===5?t.push({functionName:a[1]??"<anonymous>",fileName:a[2]??"<unknown>",lineNumber:parseInt(a[3]??"0",10),columnNumber:parseInt(a[4]??"0",10)}):a.length===4&&t.push({functionName:"<anonymous>",fileName:a[1]??"<unknown>",lineNumber:parseInt(a[2]??"0",10),columnNumber:parseInt(a[3]??"0",10)});continue}let i=s.match(/^(.+?)@(.+?):(\d+):(\d+)$/);i&&t.push({functionName:i[1]??"<anonymous>",fileName:i[2]??"<unknown>",lineNumber:parseInt(i[3]??"0",10),columnNumber:parseInt(i[4]??"0",10)});}return t}function Z(e,t,r){let n=r?.fileName??"unknown",s=r?.lineNumber??0,a=`${e}:${t}:${n}:${s}`,i=5381;for(let o=0;o<a.length;o++)i=(i<<5)+i+a.charCodeAt(o)>>>0;return `fp_${i.toString(16).padStart(8,"0")}`}var q=class{buffer;listenersAttached=false;clickHandler;popStateHandler;constructor(e){this.buffer=new C(e),this.clickHandler=this.handleClick.bind(this),this.popStateHandler=this.handleNavigation.bind(this);}push(e){let t=e.data?v(e.data):void 0;this.buffer.push({...e,data:t,timestamp:Date.now()});}getAll(){return this.buffer.toArray()}clear(){this.buffer.clear();}attachListeners(){this.listenersAttached||typeof window>"u"||(this.listenersAttached=true,document.addEventListener("click",this.clickHandler,{capture:true,passive:true}),window.addEventListener("popstate",this.popStateHandler,{passive:true}),this.interceptConsoleError());}detachListeners(){!this.listenersAttached||typeof window>"u"||(document.removeEventListener("click",this.clickHandler,{capture:true}),window.removeEventListener("popstate",this.popStateHandler),this.listenersAttached=false);}handleClick(e){let t=e.target;if(!t||t instanceof HTMLInputElement&&(t.type==="password"||t.hasAttribute("data-nexus-mask")))return;let r=this.describeElement(t);this.push({category:"ui.click",message:`Clicked ${r}`,level:"info",data:{elementTag:t.tagName.toLowerCase(),elementId:t.id||void 0,elementClass:t.className||void 0}});}handleNavigation(){this.push({category:"navigation",message:`Navigated to ${L(window.location.href)}`,level:"info",data:{url:L(window.location.href)}});}describeElement(e){let t=[e.tagName.toLowerCase()];return e.id&&t.push(`#${e.id}`),e.getAttribute("aria-label")&&t.push(`[aria-label="${e.getAttribute("aria-label")}"]`),t.join("")}interceptConsoleError(){let e=console.error.bind(console);console.error=(...t)=>{this.push({category:"console",message:t.map(String).join(" ").substring(0,500),level:"error"}),e(...t);};}},Q=class{options;maxRetries;isPageHiding=false;constructor(e){this.options=e,this.maxRetries=e.maxRetries??2,typeof document<"u"&&document.addEventListener("visibilitychange",()=>{document.visibilityState==="hidden"&&(this.isPageHiding=true);}),typeof window<"u"&&window.addEventListener("pagehide",()=>{this.isPageHiding=true;});}send(e){let t=E(e);if(this.isPageHiding&&typeof navigator<"u"&&typeof navigator.sendBeacon=="function"){let r=new Blob([t],{type:"application/json"});navigator.sendBeacon(this.options.endpoint,r);return}this.sendWithRetry(t,0);}async flush(e){let t=E(e);if(typeof navigator<"u"&&typeof navigator.sendBeacon=="function"){let r=new Blob([t],{type:"application/json"});navigator.sendBeacon(this.options.endpoint,r);return}await this.sendWithRetry(t,0);}async sendWithRetry(e,t){try{let r=await fetch(this.options.endpoint,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${this.options.apiKey}`},body:e,keepalive:!0});if(r.ok)return;if(r.status>=500&&t<this.maxRetries){let n=f(t,1e3,3e4);await new Promise(s=>setTimeout(s,n)),await this.sendWithRetry(e,t+1);}}catch{if(t<this.maxRetries){let r=f(t,1e3,3e4);await new Promise(n=>setTimeout(n,r)),await this.sendWithRetry(e,t+1);}}}};function ee(e){if(typeof window>"u")return ()=>{};let t=n=>{let s=n.error instanceof Error?n.error:new Error(n.message);e.captureError(s);},r=n=>{e.captureError(n.reason);};return window.addEventListener("error",t),window.addEventListener("unhandledrejection",r),()=>{window.removeEventListener("error",t),window.removeEventListener("unhandledrejection",r);}}var A=class{apiKey;environment;tags;beforeSend;breadcrumbManager;transport;userContext;dedupeMap=new Map;cleanupListeners;constructor(e={}){this.apiKey=m(e.apiKey);let t=y(e.baseUrl);this.environment=e.environment??"production",this.tags={...e.tags},this.beforeSend=e.beforeSend,this.breadcrumbManager=new q(Math.min(e.maxBreadcrumbs??20,50)),this.transport=new Q({endpoint:`${t}/api/v1/telemetry/errors`,apiKey:this.apiKey}),e.autoCapture!==false&&typeof window<"u"&&(this.breadcrumbManager.attachListeners(),this.cleanupListeners=ee(this));}captureError(e,t){let r=this.normalizeError(e),n=J(r.stack),s=Z(r.type,r.message,n[0]),a=this.dedupeMap.get(s);if(a){a.count+=1;return}let i={fingerprint:s,errorType:r.type,errorMessage:r.message,stackTrace:n,breadcrumbs:this.breadcrumbManager.getAll(),userContext:this.userContext,deviceContext:this.getDeviceContext(),tags:{environment:this.environment,...this.tags,...t},occurrenceCount:1,clientTimestamp:Date.now()},o=v(i),l=this.beforeSend?this.beforeSend(o):o;if(!l)return;this.transport.send(l);let c=setTimeout(()=>{let d=this.dedupeMap.get(s);if(d&&d.count>1){let p={...d.lastPayload,occurrenceCount:d.count,clientTimestamp:Date.now()};this.transport.send(p);}this.dedupeMap.delete(s);},1e4);this.dedupeMap.set(s,{timer:c,count:1,lastPayload:l});}addBreadcrumb(e){this.breadcrumbManager.push(e);}setUser(e){this.userContext=e??void 0;}setTag(e,t){this.tags[e]=t;}async flush(){for(let[e,t]of this.dedupeMap.entries()){if(clearTimeout(t.timer),t.count>0){let r={...t.lastPayload,occurrenceCount:t.count,clientTimestamp:Date.now()};await this.transport.flush(r);}this.dedupeMap.delete(e);}}destroy(){this.cleanupListeners?.(),this.breadcrumbManager.detachListeners(),this.breadcrumbManager.clear(),this.dedupeMap.forEach(e=>clearTimeout(e.timer)),this.dedupeMap.clear();}normalizeError(e){return e instanceof Error?{type:e.name||"Error",message:e.message,stack:e.stack}:typeof e=="string"?{type:"UnhandledException",message:e}:{type:"NonErrorRejection",message:String(e)}}getDeviceContext(){let e=typeof window<"u"&&typeof navigator<"u";return {userAgent:e?navigator.userAgent:"Node/SSR",currentUrl:e?window.location.href:"",viewport:e?`${window.innerWidth}x${window.innerHeight}`:void 0,timezone:Intl?.DateTimeFormat()?.resolvedOptions()?.timeZone,networkStatus:e&&"connection"in navigator?navigator.connection?.effectiveType??"unknown":void 0}}};var N=class e{static instance=null;flags;tracker;constructor(t={}){let{apiKey:r,baseUrl:n,user:s,environment:a,tags:i,autoCapture:o,flags:l,tracker:c}=t;this.flags=new S({apiKey:r,baseUrl:n,user:s,...l}),this.tracker=new A({apiKey:r,baseUrl:n,environment:a,tags:i,autoCapture:o,...c});}static init(t={}){return e.instance||(e.instance=new e(t)),e.instance}static getInstance(){if(!e.instance)throw new Error('[Nexus SDK] Not initialized. Call Nexus.init({ apiKey: "..." }) first.');return e.instance}static isEnabled(t,r=false){return e.getInstance().flags.isEnabled(t,r)}static getVariant(t,r,n){return e.getInstance().flags.getVariant(t,r,n)}static captureError(t,r){e.getInstance().tracker.captureError(t,r);}static async identify(t){let r=e.getInstance();r.tracker.setUser(t),await r.flags.identify(t);}static reset(){let t=e.getInstance();t.tracker.setUser(null),t.flags.reset();}static async destroy(){e.instance&&(await e.instance.tracker.flush(),e.instance.tracker.destroy(),e.instance.flags.destroy(),e.instance=null);}};exports.Nexus=N;exports.NexusFlagsClient=S;exports.NexusTrackerClient=A;return exports;})({});
|
package/dist/index.mjs
CHANGED
|
@@ -1,2 +1 @@
|
|
|
1
|
-
import {NexusFlagsClient}from'@nexussdk/flags';export{NexusFlagsClient}from'@nexussdk/flags';import {NexusTrackerClient}from'@nexussdk/tracker';export{NexusTrackerClient}from'@nexussdk/tracker';var n=class t{static instance=null;flags;tracker;constructor(e={}){let{apiKey:s,baseUrl:r,user:a,environment:i,tags:o,autoCapture:c,flags:l,tracker:u}=e;this.flags=new NexusFlagsClient({apiKey:s,baseUrl:r,user:a,...l}),this.tracker=new NexusTrackerClient({apiKey:s,baseUrl:r,environment:i,tags:o,autoCapture:c,...u});}static init(e={}){return t.instance||(t.instance=new t(e)),t.instance}static getInstance(){if(!t.instance)throw new Error('[Nexus SDK] Not initialized. Call Nexus.init({ apiKey: "..." }) first.');return t.instance}static isEnabled(e,s=false){return t.getInstance().flags.isEnabled(e,s)}static getVariant(e,s,r){return t.getInstance().flags.getVariant(e,s,r)}static captureError(e,s){t.getInstance().tracker.captureError(e,s);}static async identify(e){let s=t.getInstance();s.tracker.setUser(e),await s.flags.identify(e);}static reset(){let e=t.getInstance();e.tracker.setUser(null),e.flags.reset();}static async destroy(){t.instance&&(await t.instance.tracker.flush(),t.instance.tracker.destroy(),t.instance.flags.destroy(),t.instance=null);}};export{n as Nexus}
|
|
2
|
-
//# sourceMappingURL=index.mjs.map
|
|
1
|
+
import {NexusFlagsClient}from'@nexussdk/flags';export{NexusFlagsClient}from'@nexussdk/flags';import {NexusTrackerClient}from'@nexussdk/tracker';export{NexusTrackerClient}from'@nexussdk/tracker';var n=class t{static instance=null;flags;tracker;constructor(e={}){let{apiKey:s,baseUrl:r,user:a,environment:i,tags:o,autoCapture:c,flags:l,tracker:u}=e;this.flags=new NexusFlagsClient({apiKey:s,baseUrl:r,user:a,...l}),this.tracker=new NexusTrackerClient({apiKey:s,baseUrl:r,environment:i,tags:o,autoCapture:c,...u});}static init(e={}){return t.instance||(t.instance=new t(e)),t.instance}static getInstance(){if(!t.instance)throw new Error('[Nexus SDK] Not initialized. Call Nexus.init({ apiKey: "..." }) first.');return t.instance}static isEnabled(e,s=false){return t.getInstance().flags.isEnabled(e,s)}static getVariant(e,s,r){return t.getInstance().flags.getVariant(e,s,r)}static captureError(e,s){t.getInstance().tracker.captureError(e,s);}static async identify(e){let s=t.getInstance();s.tracker.setUser(e),await s.flags.identify(e);}static reset(){let e=t.getInstance();e.tracker.setUser(null),e.flags.reset();}static async destroy(){t.instance&&(await t.instance.tracker.flush(),t.instance.tracker.destroy(),t.instance.flags.destroy(),t.instance=null);}};export{n as Nexus};
|
package/dist/react.cjs
CHANGED
|
@@ -1,2 +1 @@
|
|
|
1
|
-
'use strict';var N=require('react'),flags=require('@nexussdk/flags'),tracker=require('@nexussdk/tracker');function _interopDefault(e){return e&&e.__esModule?e:{default:e}}var N__default=/*#__PURE__*/_interopDefault(N);var a=class e{static instance=null;flags;tracker;constructor(n={}){let{apiKey:t,baseUrl:s,user:i,environment:r,tags:u,autoCapture:o,flags:p,tracker:g}=n;this.flags=new flags.NexusFlagsClient({apiKey:t,baseUrl:s,user:i,...p}),this.tracker=new tracker.NexusTrackerClient({apiKey:t,baseUrl:s,environment:r,tags:u,autoCapture:o,...g});}static init(n={}){return e.instance||(e.instance=new e(n)),e.instance}static getInstance(){if(!e.instance)throw new Error('[Nexus SDK] Not initialized. Call Nexus.init({ apiKey: "..." }) first.');return e.instance}static isEnabled(n,t=false){return e.getInstance().flags.isEnabled(n,t)}static getVariant(n,t,s){return e.getInstance().flags.getVariant(n,t,s)}static captureError(n,t){e.getInstance().tracker.captureError(n,t);}static async identify(n){let t=e.getInstance();t.tracker.setUser(n),await t.flags.identify(n);}static reset(){let n=e.getInstance();n.tracker.setUser(null),n.flags.reset();}static async destroy(){e.instance&&(await e.instance.tracker.flush(),e.instance.tracker.destroy(),e.instance.flags.destroy(),e.instance=null);}};var x=N.createContext(null);function y({children:e,...n}){let t=N.useRef(null);t.current||(t.current=a.init(n));let s=N.useMemo(()=>({flags:t.current.flags,tracker:t.current.tracker,nexus:t.current}),[]);return N.useEffect(()=>()=>{a.destroy();},[]),N__default.default.createElement(x.Provider,{value:s},e)}function C(){let e=N.useContext(x);if(!e)throw new Error("[Nexus SDK] useNexus() must be called inside a <NexusProvider>.");return e}function I(e,n=false){let{flags:t}=C(),[s,i]=N.useState(()=>t.isEnabled(e)!==n?{key:e,enabled:t.isEnabled(e),variants:{},reason:"DEFAULT_ENABLED",version:0}:null);return N.useEffect(()=>{let r=t.isEnabled(e,n);return r!==(s?.enabled??n)&&i({key:e,enabled:r,variants:{},reason:"DEFAULT_ENABLED",version:0}),t.onFlagChange(e,o=>{i(o);})},[e]),N.useMemo(()=>({enabled:s?.enabled??t.isEnabled(e,n),getVariant:(r,u)=>t.getVariant(e,r,u),result:s}),[s,e,t,n])}exports.NexusProvider=y;exports.useFlag=I;exports.useNexus=C
|
|
2
|
-
//# sourceMappingURL=react.cjs.map
|
|
1
|
+
'use strict';var N=require('react'),flags=require('@nexussdk/flags'),tracker=require('@nexussdk/tracker');function _interopDefault(e){return e&&e.__esModule?e:{default:e}}var N__default=/*#__PURE__*/_interopDefault(N);var a=class e{static instance=null;flags;tracker;constructor(n={}){let{apiKey:t,baseUrl:s,user:i,environment:r,tags:u,autoCapture:o,flags:p,tracker:g}=n;this.flags=new flags.NexusFlagsClient({apiKey:t,baseUrl:s,user:i,...p}),this.tracker=new tracker.NexusTrackerClient({apiKey:t,baseUrl:s,environment:r,tags:u,autoCapture:o,...g});}static init(n={}){return e.instance||(e.instance=new e(n)),e.instance}static getInstance(){if(!e.instance)throw new Error('[Nexus SDK] Not initialized. Call Nexus.init({ apiKey: "..." }) first.');return e.instance}static isEnabled(n,t=false){return e.getInstance().flags.isEnabled(n,t)}static getVariant(n,t,s){return e.getInstance().flags.getVariant(n,t,s)}static captureError(n,t){e.getInstance().tracker.captureError(n,t);}static async identify(n){let t=e.getInstance();t.tracker.setUser(n),await t.flags.identify(n);}static reset(){let n=e.getInstance();n.tracker.setUser(null),n.flags.reset();}static async destroy(){e.instance&&(await e.instance.tracker.flush(),e.instance.tracker.destroy(),e.instance.flags.destroy(),e.instance=null);}};var x=N.createContext(null);function y({children:e,...n}){let t=N.useRef(null);t.current||(t.current=a.init(n));let s=N.useMemo(()=>({flags:t.current.flags,tracker:t.current.tracker,nexus:t.current}),[]);return N.useEffect(()=>()=>{a.destroy();},[]),N__default.default.createElement(x.Provider,{value:s},e)}function C(){let e=N.useContext(x);if(!e)throw new Error("[Nexus SDK] useNexus() must be called inside a <NexusProvider>.");return e}function I(e,n=false){let{flags:t}=C(),[s,i]=N.useState(()=>t.isEnabled(e)!==n?{key:e,enabled:t.isEnabled(e),variants:{},reason:"DEFAULT_ENABLED",version:0}:null);return N.useEffect(()=>{let r=t.isEnabled(e,n);return r!==(s?.enabled??n)&&i({key:e,enabled:r,variants:{},reason:"DEFAULT_ENABLED",version:0}),t.onFlagChange(e,o=>{i(o);})},[e]),N.useMemo(()=>({enabled:s?.enabled??t.isEnabled(e,n),getVariant:(r,u)=>t.getVariant(e,r,u),result:s}),[s,e,t,n])}exports.NexusProvider=y;exports.useFlag=I;exports.useNexus=C;
|
package/dist/react.mjs
CHANGED
|
@@ -1,2 +1 @@
|
|
|
1
|
-
import N,{createContext,useRef,useMemo,useEffect,useContext,useState}from'react';import {NexusFlagsClient}from'@nexussdk/flags';import {NexusTrackerClient}from'@nexussdk/tracker';var a=class e{static instance=null;flags;tracker;constructor(n={}){let{apiKey:t,baseUrl:s,user:i,environment:r,tags:u,autoCapture:o,flags:p,tracker:g}=n;this.flags=new NexusFlagsClient({apiKey:t,baseUrl:s,user:i,...p}),this.tracker=new NexusTrackerClient({apiKey:t,baseUrl:s,environment:r,tags:u,autoCapture:o,...g});}static init(n={}){return e.instance||(e.instance=new e(n)),e.instance}static getInstance(){if(!e.instance)throw new Error('[Nexus SDK] Not initialized. Call Nexus.init({ apiKey: "..." }) first.');return e.instance}static isEnabled(n,t=false){return e.getInstance().flags.isEnabled(n,t)}static getVariant(n,t,s){return e.getInstance().flags.getVariant(n,t,s)}static captureError(n,t){e.getInstance().tracker.captureError(n,t);}static async identify(n){let t=e.getInstance();t.tracker.setUser(n),await t.flags.identify(n);}static reset(){let n=e.getInstance();n.tracker.setUser(null),n.flags.reset();}static async destroy(){e.instance&&(await e.instance.tracker.flush(),e.instance.tracker.destroy(),e.instance.flags.destroy(),e.instance=null);}};var x=createContext(null);function y({children:e,...n}){let t=useRef(null);t.current||(t.current=a.init(n));let s=useMemo(()=>({flags:t.current.flags,tracker:t.current.tracker,nexus:t.current}),[]);return useEffect(()=>()=>{a.destroy();},[]),N.createElement(x.Provider,{value:s},e)}function C(){let e=useContext(x);if(!e)throw new Error("[Nexus SDK] useNexus() must be called inside a <NexusProvider>.");return e}function I(e,n=false){let{flags:t}=C(),[s,i]=useState(()=>t.isEnabled(e)!==n?{key:e,enabled:t.isEnabled(e),variants:{},reason:"DEFAULT_ENABLED",version:0}:null);return useEffect(()=>{let r=t.isEnabled(e,n);return r!==(s?.enabled??n)&&i({key:e,enabled:r,variants:{},reason:"DEFAULT_ENABLED",version:0}),t.onFlagChange(e,o=>{i(o);})},[e]),useMemo(()=>({enabled:s?.enabled??t.isEnabled(e,n),getVariant:(r,u)=>t.getVariant(e,r,u),result:s}),[s,e,t,n])}export{y as NexusProvider,I as useFlag,C as useNexus}
|
|
2
|
-
//# sourceMappingURL=react.mjs.map
|
|
1
|
+
import N,{createContext,useRef,useMemo,useEffect,useContext,useState}from'react';import {NexusFlagsClient}from'@nexussdk/flags';import {NexusTrackerClient}from'@nexussdk/tracker';var a=class e{static instance=null;flags;tracker;constructor(n={}){let{apiKey:t,baseUrl:s,user:i,environment:r,tags:u,autoCapture:o,flags:p,tracker:g}=n;this.flags=new NexusFlagsClient({apiKey:t,baseUrl:s,user:i,...p}),this.tracker=new NexusTrackerClient({apiKey:t,baseUrl:s,environment:r,tags:u,autoCapture:o,...g});}static init(n={}){return e.instance||(e.instance=new e(n)),e.instance}static getInstance(){if(!e.instance)throw new Error('[Nexus SDK] Not initialized. Call Nexus.init({ apiKey: "..." }) first.');return e.instance}static isEnabled(n,t=false){return e.getInstance().flags.isEnabled(n,t)}static getVariant(n,t,s){return e.getInstance().flags.getVariant(n,t,s)}static captureError(n,t){e.getInstance().tracker.captureError(n,t);}static async identify(n){let t=e.getInstance();t.tracker.setUser(n),await t.flags.identify(n);}static reset(){let n=e.getInstance();n.tracker.setUser(null),n.flags.reset();}static async destroy(){e.instance&&(await e.instance.tracker.flush(),e.instance.tracker.destroy(),e.instance.flags.destroy(),e.instance=null);}};var x=createContext(null);function y({children:e,...n}){let t=useRef(null);t.current||(t.current=a.init(n));let s=useMemo(()=>({flags:t.current.flags,tracker:t.current.tracker,nexus:t.current}),[]);return useEffect(()=>()=>{a.destroy();},[]),N.createElement(x.Provider,{value:s},e)}function C(){let e=useContext(x);if(!e)throw new Error("[Nexus SDK] useNexus() must be called inside a <NexusProvider>.");return e}function I(e,n=false){let{flags:t}=C(),[s,i]=useState(()=>t.isEnabled(e)!==n?{key:e,enabled:t.isEnabled(e),variants:{},reason:"DEFAULT_ENABLED",version:0}:null);return useEffect(()=>{let r=t.isEnabled(e,n);return r!==(s?.enabled??n)&&i({key:e,enabled:r,variants:{},reason:"DEFAULT_ENABLED",version:0}),t.onFlagChange(e,o=>{i(o);})},[e]),useMemo(()=>({enabled:s?.enabled??t.isEnabled(e,n),getVariant:(r,u)=>t.getVariant(e,r,u),result:s}),[s,e,t,n])}export{y as NexusProvider,I as useFlag,C as useNexus};
|
package/package.json
CHANGED
|
@@ -1,11 +1,42 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nexussdk/sdk",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.3",
|
|
4
4
|
"private": false,
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
7
7
|
},
|
|
8
8
|
"description": "Unified umbrella SDK for Nexus Platform — feature flags + error tracking + React hooks",
|
|
9
|
+
"license": "MIT",
|
|
10
|
+
"author": "Nexus",
|
|
11
|
+
"contributors": [
|
|
12
|
+
{
|
|
13
|
+
"name": "Hồ Huỳnh Dũng",
|
|
14
|
+
"email": "hohuynhdung@gmail.com",
|
|
15
|
+
"url": "https://github.com/Huynhdung295"
|
|
16
|
+
}
|
|
17
|
+
],
|
|
18
|
+
"repository": {
|
|
19
|
+
"type": "git",
|
|
20
|
+
"url": "https://github.com/Huynhdung295/NexusSDK.git",
|
|
21
|
+
"directory": "packages/sdk"
|
|
22
|
+
},
|
|
23
|
+
"homepage": "https://github.com/Huynhdung295/NexusSDK#readme",
|
|
24
|
+
"keywords": [
|
|
25
|
+
"nexus",
|
|
26
|
+
"nexussdk",
|
|
27
|
+
"feature-flags",
|
|
28
|
+
"feature-management",
|
|
29
|
+
"telemetry",
|
|
30
|
+
"error-tracking",
|
|
31
|
+
"error-monitoring",
|
|
32
|
+
"react",
|
|
33
|
+
"nextjs",
|
|
34
|
+
"sdk"
|
|
35
|
+
],
|
|
36
|
+
"files": [
|
|
37
|
+
"dist",
|
|
38
|
+
"README.md"
|
|
39
|
+
],
|
|
9
40
|
"main": "./dist/index.cjs",
|
|
10
41
|
"module": "./dist/index.mjs",
|
|
11
42
|
"types": "./dist/index.d.ts",
|
|
@@ -22,10 +53,10 @@
|
|
|
22
53
|
}
|
|
23
54
|
},
|
|
24
55
|
"dependencies": {
|
|
25
|
-
"@nexussdk/
|
|
26
|
-
"@nexussdk/
|
|
27
|
-
"@nexussdk/
|
|
28
|
-
"@nexussdk/
|
|
56
|
+
"@nexussdk/contracts": "0.0.3",
|
|
57
|
+
"@nexussdk/core": "0.0.3",
|
|
58
|
+
"@nexussdk/flags": "0.0.3",
|
|
59
|
+
"@nexussdk/tracker": "0.0.3"
|
|
29
60
|
},
|
|
30
61
|
"peerDependencies": {
|
|
31
62
|
"react": ">=18.0.0"
|
package/.turbo/turbo-build.log
DELETED
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
> @nexussdk/sdk@0.0.1 build /home/runner/work/NexusSDK/NexusSDK/packages/sdk
|
|
3
|
-
> tsup
|
|
4
|
-
|
|
5
|
-
[34mCLI[39m Building entry: {"react":"src/react.tsx"}
|
|
6
|
-
[34mCLI[39m Using tsconfig: tsconfig.json
|
|
7
|
-
[34mCLI[39m tsup v8.5.1
|
|
8
|
-
[34mCLI[39m Using tsup config: /home/runner/work/NexusSDK/NexusSDK/packages/sdk/tsup.config.ts
|
|
9
|
-
[34mCLI[39m Building entry: src/index.ts
|
|
10
|
-
[34mCLI[39m Using tsconfig: tsconfig.json
|
|
11
|
-
[34mCLI[39m tsup v8.5.1
|
|
12
|
-
[34mCLI[39m Using tsup config: /home/runner/work/NexusSDK/NexusSDK/packages/sdk/tsup.config.ts
|
|
13
|
-
[34mCLI[39m Target: es2022
|
|
14
|
-
[34mESM[39m Build start
|
|
15
|
-
[34mCJS[39m Build start
|
|
16
|
-
[34mCLI[39m Target: es2022
|
|
17
|
-
[34mCLI[39m Cleaning output folder
|
|
18
|
-
[34mESM[39m Build start
|
|
19
|
-
[34mCJS[39m Build start
|
|
20
|
-
[34mIIFE[39m Build start
|
|
21
|
-
dist/react.cjs (1:0): Module level directives cause errors when bundled, "use client" in "dist/react.cjs" was ignored.
|
|
22
|
-
dist/react.mjs (1:0): Module level directives cause errors when bundled, "use client" in "dist/react.mjs" was ignored.
|
|
23
|
-
[32mCJS[39m [1mdist/index.cjs [22m[32m1.45 KB[39m
|
|
24
|
-
[32mCJS[39m [1mdist/index.cjs.map [22m[32m8.39 KB[39m
|
|
25
|
-
[32mCJS[39m ⚡️ Build success in 473ms
|
|
26
|
-
[32mCJS[39m [1mdist/react.cjs [22m[32m2.26 KB[39m
|
|
27
|
-
[32mCJS[39m [1mdist/react.cjs.map [22m[32m16.56 KB[39m
|
|
28
|
-
[32mCJS[39m ⚡️ Build success in 483ms
|
|
29
|
-
[32mESM[39m [1mdist/react.mjs [22m[32m2.17 KB[39m
|
|
30
|
-
[32mESM[39m [1mdist/react.mjs.map [22m[32m16.56 KB[39m
|
|
31
|
-
[32mESM[39m ⚡️ Build success in 484ms
|
|
32
|
-
[32mIIFE[39m [1mdist/index.global.js [22m[32m19.65 KB[39m
|
|
33
|
-
[32mIIFE[39m [1mdist/index.global.js.map [22m[32m116.63 KB[39m
|
|
34
|
-
[32mIIFE[39m ⚡️ Build success in 475ms
|
|
35
|
-
[32mESM[39m [1mdist/index.mjs [22m[32m1.31 KB[39m
|
|
36
|
-
[32mESM[39m [1mdist/index.mjs.map [22m[32m8.39 KB[39m
|
|
37
|
-
[32mESM[39m ⚡️ Build success in 475ms
|
|
38
|
-
[34mDTS[39m Build start
|
|
39
|
-
[34mDTS[39m Build start
|
|
40
|
-
[32mDTS[39m ⚡️ Build success in 1445ms
|
|
41
|
-
[32mDTS[39m [1mdist/index.d.mts [22m[32m5.12 KB[39m
|
|
42
|
-
[32mDTS[39m [1mdist/index.d.ts [22m[32m5.12 KB[39m
|
|
43
|
-
[32mDTS[39m ⚡️ Build success in 1567ms
|
|
44
|
-
[32mDTS[39m [1mdist/react.d.mts [22m[32m8.12 KB[39m
|
|
45
|
-
[32mDTS[39m [1mdist/react.d.ts [22m[32m8.12 KB[39m
|
package/dist/index.cjs.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/nexus.ts"],"names":["Nexus","_Nexus","options","apiKey","baseUrl","user","environment","tags","autoCapture","flagsOpts","trackerOpts","NexusFlagsClient","NexusTrackerClient","key","defaultValue","variantKey","error","extra","instance"],"mappings":"uFA6DO,IAAMA,CAAAA,CAAN,MAAMC,CAAM,CACjB,OAAe,QAAA,CAAyB,IAAA,CAGxB,KAAA,CAEA,OAAA,CAER,WAAA,CAAYC,CAAAA,CAA4B,EAAC,CAAG,CAClD,GAAM,CAAE,MAAA,CAAAC,CAAAA,CAAQ,OAAA,CAAAC,EAAS,IAAA,CAAAC,CAAAA,CAAM,WAAA,CAAAC,CAAAA,CAAa,IAAA,CAAAC,CAAAA,CAAM,YAAAC,CAAAA,CAAa,KAAA,CAAOC,CAAAA,CAAW,OAAA,CAASC,CAAY,CAAA,CAAIR,EAE1G,IAAA,CAAK,KAAA,CAAQ,IAAIS,sBAAAA,CAAiB,CAChC,MAAA,CAAAR,CAAAA,CACA,OAAA,CAAAC,CAAAA,CACA,IAAA,CAAAC,CAAAA,CACA,GAAGI,CACL,CAAC,EAED,IAAA,CAAK,OAAA,CAAU,IAAIG,0BAAAA,CAAmB,CACpC,MAAA,CAAAT,EACA,OAAA,CAAAC,CAAAA,CACA,WAAA,CAAAE,CAAAA,CACA,IAAA,CAAAC,CAAAA,CACA,YAAAC,CAAAA,CACA,GAAGE,CACL,CAAC,EACH,CAaA,OAAc,IAAA,CAAKR,CAAAA,CAA4B,EAAC,CAAU,CACxD,OAAKD,CAAAA,CAAM,WACTA,CAAAA,CAAM,QAAA,CAAW,IAAIA,CAAAA,CAAMC,CAAO,CAAA,CAAA,CAE7BD,EAAM,QACf,CAYA,OAAc,WAAA,EAAqB,CACjC,GAAI,CAACA,CAAAA,CAAM,QAAA,CACT,MAAM,IAAI,KAAA,CAAM,wEAAwE,CAAA,CAE1F,OAAOA,CAAAA,CAAM,QACf,CAYA,OAAc,SAAA,CAAUY,CAAAA,CAAaC,EAAe,KAAA,CAAgB,CAClE,OAAOb,CAAAA,CAAM,WAAA,EAAY,CAAE,MAAM,SAAA,CAAUY,CAAAA,CAAKC,CAAY,CAC9D,CAaA,OAAc,WAAwBD,CAAAA,CAAaE,CAAAA,CAAoBD,CAAAA,CAAqB,CAC1F,OAAOb,CAAAA,CAAM,WAAA,EAAY,CAAE,KAAA,CAAM,UAAA,CAAcY,CAAAA,CAAKE,CAAAA,CAAYD,CAAY,CAC9E,CAWA,OAAc,YAAA,CAAaE,CAAAA,CAAgBC,CAAAA,CAAuC,CAChFhB,CAAAA,CAAM,aAAY,CAAE,OAAA,CAAQ,YAAA,CAAae,CAAAA,CAAOC,CAAK,EACvD,CAWA,aAAoB,QAAA,CAASZ,CAAAA,CAAkC,CAC7D,IAAMa,CAAAA,CAAWjB,CAAAA,CAAM,WAAA,EAAY,CACnCiB,CAAAA,CAAS,OAAA,CAAQ,OAAA,CAAQb,CAAI,CAAA,CAC7B,MAAMa,CAAAA,CAAS,KAAA,CAAM,QAAA,CAASb,CAAI,EACpC,CAQA,OAAc,KAAA,EAAc,CAC1B,IAAMa,CAAAA,CAAWjB,CAAAA,CAAM,WAAA,GACvBiB,CAAAA,CAAS,OAAA,CAAQ,OAAA,CAAQ,IAAI,CAAA,CAC7BA,CAAAA,CAAS,KAAA,CAAM,KAAA,GACjB,CAQA,aAAoB,OAAA,EAAyB,CACvCjB,CAAAA,CAAM,WACR,MAAMA,CAAAA,CAAM,QAAA,CAAS,OAAA,CAAQ,KAAA,EAAM,CACnCA,CAAAA,CAAM,QAAA,CAAS,OAAA,CAAQ,OAAA,EAAQ,CAC/BA,CAAAA,CAAM,QAAA,CAAS,KAAA,CAAM,SAAQ,CAC7BA,CAAAA,CAAM,QAAA,CAAW,IAAA,EAErB,CACF","file":"index.cjs","sourcesContent":["/**\n * @fileoverview Unified Nexus singleton facade wrapping Flags and Tracker clients.\n * @module @nexus/sdk/nexus\n */\n\nimport { NexusFlagsClient } from '@nexussdk/flags';\nimport type { NexusFlagsOptions } from '@nexussdk/flags';\nimport { NexusTrackerClient } from '@nexussdk/tracker';\nimport type { NexusTrackerOptions } from '@nexussdk/tracker';\nimport type { UserContext } from '@nexussdk/contracts';\n\n/**\n * Unified initialization options for the Nexus SDK umbrella.\n *\n * @example\n * Nexus.init({\n * apiKey: 'pk_live_...',\n * baseUrl: 'http://localhost:8080',\n * user: { id: 'usr_12345', country: 'VN' },\n * environment: 'production',\n * autoCapture: true,\n * });\n */\nexport interface NexusInitOptions {\n /** Public API key. Resolved from env if omitted. */\n apiKey?: string;\n /** Base URL for all API calls. */\n baseUrl?: string;\n /** Initial user context for flag targeting and error attribution. */\n user?: UserContext;\n /** Target environment for telemetry routing. Defaults to 'production'. */\n environment?: string;\n /** Global tags attached to all telemetry events. */\n tags?: Record<string, string>;\n /** Toggle automated global error capture. Defaults to true. */\n autoCapture?: boolean;\n /** Additional flags-specific options. */\n flags?: Partial<NexusFlagsOptions>;\n /** Additional tracker-specific options. */\n tracker?: Partial<NexusTrackerOptions>;\n}\n\n/**\n * The Nexus singleton class — the primary unified entry point for the SDK.\n *\n * Provides access to both the feature flags client and the error tracker client.\n * Initialize once, then use throughout your application.\n *\n * @example\n * // Initialize (call once at app startup)\n * Nexus.init({ apiKey: 'pk_live_...' });\n *\n * // Feature flags\n * const showBanner = Nexus.isEnabled('promo_banner_v2', false);\n *\n * // Error tracking\n * Nexus.captureError(new Error('Something went wrong'));\n *\n * // Update user context\n * await Nexus.identify({ id: 'usr_12345', country: 'VN' });\n */\nexport class Nexus {\n private static instance: Nexus | null = null;\n\n /** The underlying feature flags client instance. */\n public readonly flags: NexusFlagsClient;\n /** The underlying error tracker client instance. */\n public readonly tracker: NexusTrackerClient;\n\n private constructor(options: NexusInitOptions = {}) {\n const { apiKey, baseUrl, user, environment, tags, autoCapture, flags: flagsOpts, tracker: trackerOpts } = options;\n\n this.flags = new NexusFlagsClient({\n apiKey,\n baseUrl,\n user,\n ...flagsOpts,\n });\n\n this.tracker = new NexusTrackerClient({\n apiKey,\n baseUrl,\n environment,\n tags,\n autoCapture,\n ...trackerOpts,\n });\n }\n\n /**\n * Initializes the Nexus SDK singleton.\n * Must be called before any other SDK methods.\n * Safe to call multiple times — returns existing instance after first init.\n *\n * @param options - SDK configuration options.\n * @returns The initialized Nexus singleton instance.\n *\n * @example\n * const nexus = Nexus.init({ apiKey: 'pk_live_...' });\n */\n public static init(options: NexusInitOptions = {}): Nexus {\n if (!Nexus.instance) {\n Nexus.instance = new Nexus(options);\n }\n return Nexus.instance;\n }\n\n /**\n * Returns the current Nexus singleton instance.\n *\n * @returns The active Nexus instance.\n * @throws {Error} If `Nexus.init()` has not been called yet.\n *\n * @example\n * const nexus = Nexus.getInstance();\n * nexus.flags.isEnabled('checkout_v2');\n */\n public static getInstance(): Nexus {\n if (!Nexus.instance) {\n throw new Error('[Nexus SDK] Not initialized. Call Nexus.init({ apiKey: \"...\" }) first.');\n }\n return Nexus.instance;\n }\n\n /**\n * Convenience method: Check if a feature flag is enabled.\n *\n * @param key - Flag identifier.\n * @param defaultValue - Fallback if flag is missing.\n * @returns Boolean enabled state.\n *\n * @example\n * if (Nexus.isEnabled('checkout_redesign')) { ... }\n */\n public static isEnabled(key: string, defaultValue = false): boolean {\n return Nexus.getInstance().flags.isEnabled(key, defaultValue);\n }\n\n /**\n * Convenience method: Get a flag variant value.\n *\n * @param key - Flag identifier.\n * @param variantKey - Variant property name.\n * @param defaultValue - Fallback value.\n * @returns Variant value cast to type T.\n *\n * @example\n * const rate = Nexus.getVariant<number>('promo_banner_v2', 'discount_rate', 10);\n */\n public static getVariant<T = unknown>(key: string, variantKey: string, defaultValue?: T): T {\n return Nexus.getInstance().flags.getVariant<T>(key, variantKey, defaultValue);\n }\n\n /**\n * Convenience method: Capture an error manually.\n *\n * @param error - Error instance, string, or unknown value.\n * @param extra - Optional metadata tags.\n *\n * @example\n * Nexus.captureError(new TypeError('Cannot read properties of null'));\n */\n public static captureError(error: unknown, extra?: Record<string, unknown>): void {\n Nexus.getInstance().tracker.captureError(error, extra);\n }\n\n /**\n * Convenience method: Update user context for both flags and tracker.\n *\n * @param user - New user context (merged with existing).\n * @returns Promise resolving after flags refresh.\n *\n * @example\n * await Nexus.identify({ id: 'usr_12345', country: 'VN' });\n */\n public static async identify(user: UserContext): Promise<void> {\n const instance = Nexus.getInstance();\n instance.tracker.setUser(user);\n await instance.flags.identify(user);\n }\n\n /**\n * Resets user context to anonymous state (e.g. on logout).\n *\n * @example\n * Nexus.reset(); // called on user logout\n */\n public static reset(): void {\n const instance = Nexus.getInstance();\n instance.tracker.setUser(null);\n instance.flags.reset();\n }\n\n /**\n * Gracefully tears down both clients, closing SSE connections and flushing pending events.\n *\n * @example\n * await Nexus.destroy();\n */\n public static async destroy(): Promise<void> {\n if (Nexus.instance) {\n await Nexus.instance.tracker.flush();\n Nexus.instance.tracker.destroy();\n Nexus.instance.flags.destroy();\n Nexus.instance = null;\n }\n }\n}\n"]}
|