@nexussdk/sdk 0.0.1 → 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.
package/README.md ADDED
@@ -0,0 +1,162 @@
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
+ [![npm version](https://img.shields.io/npm/v/@nexussdk/sdk.svg)](https://www.npmjs.com/package/@nexussdk/sdk)
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](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
+ ## Vue 3 & Nuxt 4 Integration
110
+
111
+ Install `NexusPlugin` and use the `<NexusGuard>` error boundary component:
112
+
113
+ ```typescript
114
+ // main.ts
115
+ import { createApp } from 'vue';
116
+ import { NexusPlugin } from '@nexussdk/sdk/vue';
117
+ import App from './App.vue';
118
+
119
+ const app = createApp(App);
120
+ app.use(NexusPlugin, {
121
+ apiKey: 'pk_live_your_client_key',
122
+ environment: 'production',
123
+ autoCapture: true,
124
+ realtime: true,
125
+ });
126
+ app.mount('#app');
127
+ ```
128
+
129
+ ```vue
130
+ <!-- App.vue -->
131
+ <script setup lang="ts">
132
+ import { useFeatureFlag, NexusGuard } from '@nexussdk/sdk/vue';
133
+
134
+ const { isEnabled } = useFeatureFlag('new-checkout-flow', false);
135
+ </script>
136
+
137
+ <template>
138
+ <NexusGuard>
139
+ <button v-if="isEnabled">1-Click Express Checkout</button>
140
+ <button v-else>Standard Checkout</button>
141
+ </NexusGuard>
142
+ </template>
143
+ ```
144
+
145
+ ---
146
+
147
+ ## Architecture & Subpackages
148
+
149
+ `@nexussdk/sdk` bundles the following specialized modules for maximum modularity:
150
+
151
+ | Package | Role |
152
+ |---|---|
153
+ | [`@nexussdk/contracts`](https://www.npmjs.com/package/@nexussdk/contracts) | SSOT TypeScript types and RFC 7807 error models |
154
+ | [`@nexussdk/core`](https://www.npmjs.com/package/@nexussdk/core) | Ring buffer transport kernel and environment resolvers |
155
+ | [`@nexussdk/flags`](https://www.npmjs.com/package/@nexussdk/flags) | Standalone feature flag evaluator, local dev server (`nexus-flags-dev`) & SSE manager |
156
+ | [`@nexussdk/tracker`](https://www.npmjs.com/package/@nexussdk/tracker) | Standalone error tracker, local dev server (`nexus-dev`) & PII sanitizer |
157
+
158
+ ---
159
+
160
+ ## License
161
+
162
+ MIT ÂĐ [Nexus Platform](https://github.com/Huynhdung295/NexusSDK)
package/dist/index.cjs CHANGED
@@ -1,2 +1,2 @@
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;//# sourceMappingURL=index.cjs.map
2
- //# sourceMappingURL=index.cjs.map
1
+ 'use strict';var flags=require('@nexussdk/flags'),tracker=require('@nexussdk/tracker');var o=class t{static instance=null;static vitalsCleanup=null;flags;tracker;constructor(e={}){let{apiKey:r,baseUrl:s,user:u,environment:p,tags:g,autoCapture:x,sampling:d,transport:f,vitals:n,devServer:a,flags:v,tracker:m}=e;this.flags=new flags.NexusFlagsClient({apiKey:r,baseUrl:s,user:u,...v});let l=s,b=f;if(a?.enabled){let i=a.port??4567;l=`http://${a.host??"localhost"}:${i}`;}if(this.tracker=new tracker.NexusTrackerClient({apiKey:r,baseUrl:l,environment:p,tags:g,autoCapture:x,sampling:d,transport:b,...m}),n){let i=typeof n=="object"?n:{};t.vitalsCleanup=tracker.attachWebVitals(this.tracker,i);}}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,r=false){return t.getInstance().flags.isEnabled(e,r)}static getVariant(e,r,s){return t.getInstance().flags.getVariant(e,r,s)}static captureError(e,r){t.getInstance().tracker.captureError(e,r);}static captureMessage(e,r="info",s){t.getInstance().tracker.captureMessage(e,r,s);}static addBreadcrumb(e){t.getInstance().tracker.addBreadcrumb(e);}static setExtra(e,r){t.getInstance().tracker.setExtra(e,r);}static attachWebVitals(e){return tracker.attachWebVitals(t.getInstance().tracker,e)}static async identify(e){let r=t.getInstance();r.tracker.setUser(e),await r.flags.identify(e);}static reset(){let e=t.getInstance();e.tracker.setUser(null),e.flags.reset();}static async destroy(){t.vitalsCleanup&&(t.vitalsCleanup(),t.vitalsCleanup=null),t.instance&&(await t.instance.tracker.flush(),t.instance.tracker.destroy(),t.instance.flags.destroy(),t.instance=null);}};
2
+ Object.defineProperty(exports,"NexusFlagsClient",{enumerable:true,get:function(){return flags.NexusFlagsClient}});Object.defineProperty(exports,"NexusGuardCore",{enumerable:true,get:function(){return tracker.NexusGuardCore}});Object.defineProperty(exports,"NexusTrackerClient",{enumerable:true,get:function(){return tracker.NexusTrackerClient}});Object.defineProperty(exports,"attachWebVitals",{enumerable:true,get:function(){return tracker.attachWebVitals}});exports.Nexus=o;
package/dist/index.d.mts CHANGED
@@ -1,25 +1,36 @@
1
1
  import { NexusFlagsClient, NexusFlagsOptions } from '@nexussdk/flags';
2
2
  export { INexusFlagsClient, NexusFlagsClient, NexusFlagsOptions } from '@nexussdk/flags';
3
3
  import { NexusTrackerClient, NexusTrackerOptions } from '@nexussdk/tracker';
4
- export { INexusTrackerClient, NexusTrackerClient, NexusTrackerOptions } from '@nexussdk/tracker';
5
- import { UserContext } from '@nexussdk/contracts';
6
- export { ErrorEventPayload, FeatureFlag, FlagEvaluationResult, ProblemDetails, UserContext } from '@nexussdk/contracts';
4
+ export { INexusTrackerClient, NexusGuardCore, NexusGuardCoreOptions, NexusTrackerClient, NexusTrackerOptions, attachWebVitals } from '@nexussdk/tracker';
5
+ import { UserContext, SamplingConfig, TransportPlugin, PerformanceVitalsOptions, SeverityLevel, Breadcrumb } from '@nexussdk/contracts';
6
+ export { Breadcrumb, ErrorEventPayload, FeatureFlag, FlagEvaluationResult, NexusErrorInfo, PerformanceVitalEntry, PerformanceVitalsOptions, ProblemDetails, SamplingConfig, SeverityLevel, TransportPlugin, UserContext } from '@nexussdk/contracts';
7
7
 
8
8
  /**
9
9
  * @fileoverview Unified Nexus singleton facade wrapping Flags and Tracker clients.
10
10
  * @module @nexus/sdk/nexus
11
11
  */
12
12
 
13
+ /**
14
+ * Dev server routing configuration.
15
+ */
16
+ interface NexusDevServerOptions {
17
+ /** Enable local dev server ingestion mode. Auto-enabled in development if true. */
18
+ enabled?: boolean;
19
+ /** Port the dev server is listening on. Defaults to 4567. */
20
+ port?: number;
21
+ /** Host the dev server is bound to. Defaults to 'localhost'. */
22
+ host?: string;
23
+ }
13
24
  /**
14
25
  * Unified initialization options for the Nexus SDK umbrella.
15
26
  *
16
27
  * @example
17
28
  * Nexus.init({
18
29
  * apiKey: 'pk_live_...',
19
- * baseUrl: 'http://localhost:8080',
20
30
  * user: { id: 'usr_12345', country: 'VN' },
21
31
  * environment: 'production',
22
32
  * autoCapture: true,
33
+ * devServer: { enabled: process.env.NODE_ENV === 'development' },
23
34
  * });
24
35
  */
25
36
  interface NexusInitOptions {
@@ -35,6 +46,14 @@ interface NexusInitOptions {
35
46
  tags?: Record<string, string>;
36
47
  /** Toggle automated global error capture. Defaults to true. */
37
48
  autoCapture?: boolean;
49
+ /** Client-side rate limiting and deduplication sampling options. */
50
+ sampling?: SamplingConfig;
51
+ /** Pluggable transport adapter ('fetch', 'console', 'localStorage', custom fn). */
52
+ transport?: TransportPlugin;
53
+ /** Web Vitals performance observer options, or true for default observation. */
54
+ vitals?: boolean | PerformanceVitalsOptions;
55
+ /** Local dev server auto-routing configuration. */
56
+ devServer?: NexusDevServerOptions;
38
57
  /** Additional flags-specific options. */
39
58
  flags?: Partial<NexusFlagsOptions>;
40
59
  /** Additional tracker-specific options. */
@@ -61,6 +80,7 @@ interface NexusInitOptions {
61
80
  */
62
81
  declare class Nexus {
63
82
  private static instance;
83
+ private static vitalsCleanup;
64
84
  /** The underlying feature flags client instance. */
65
85
  readonly flags: NexusFlagsClient;
66
86
  /** The underlying error tracker client instance. */
@@ -73,9 +93,6 @@ declare class Nexus {
73
93
  *
74
94
  * @param options - SDK configuration options.
75
95
  * @returns The initialized Nexus singleton instance.
76
- *
77
- * @example
78
- * const nexus = Nexus.init({ apiKey: 'pk_live_...' });
79
96
  */
80
97
  static init(options?: NexusInitOptions): Nexus;
81
98
  /**
@@ -83,69 +100,48 @@ declare class Nexus {
83
100
  *
84
101
  * @returns The active Nexus instance.
85
102
  * @throws {Error} If `Nexus.init()` has not been called yet.
86
- *
87
- * @example
88
- * const nexus = Nexus.getInstance();
89
- * nexus.flags.isEnabled('checkout_v2');
90
103
  */
91
104
  static getInstance(): Nexus;
92
105
  /**
93
106
  * Convenience method: Check if a feature flag is enabled.
94
- *
95
- * @param key - Flag identifier.
96
- * @param defaultValue - Fallback if flag is missing.
97
- * @returns Boolean enabled state.
98
- *
99
- * @example
100
- * if (Nexus.isEnabled('checkout_redesign')) { ... }
101
107
  */
102
108
  static isEnabled(key: string, defaultValue?: boolean): boolean;
103
109
  /**
104
110
  * Convenience method: Get a flag variant value.
105
- *
106
- * @param key - Flag identifier.
107
- * @param variantKey - Variant property name.
108
- * @param defaultValue - Fallback value.
109
- * @returns Variant value cast to type T.
110
- *
111
- * @example
112
- * const rate = Nexus.getVariant<number>('promo_banner_v2', 'discount_rate', 10);
113
111
  */
114
112
  static getVariant<T = unknown>(key: string, variantKey: string, defaultValue?: T): T;
115
113
  /**
116
114
  * Convenience method: Capture an error manually.
117
- *
118
- * @param error - Error instance, string, or unknown value.
119
- * @param extra - Optional metadata tags.
120
- *
121
- * @example
122
- * Nexus.captureError(new TypeError('Cannot read properties of null'));
123
115
  */
124
116
  static captureError(error: unknown, extra?: Record<string, unknown>): void;
117
+ /**
118
+ * Convenience method: Capture an informational or warning message event.
119
+ */
120
+ static captureMessage(message: string, level?: SeverityLevel, extra?: Record<string, unknown>): void;
121
+ /**
122
+ * Convenience method: Add a breadcrumb manually.
123
+ */
124
+ static addBreadcrumb(breadcrumb: Breadcrumb): void;
125
+ /**
126
+ * Convenience method: Set extra contextual metadata.
127
+ */
128
+ static setExtra(key: string, value: unknown): void;
129
+ /**
130
+ * Convenience method: Attach Web Vitals observer to tracker.
131
+ */
132
+ static attachWebVitals(options?: PerformanceVitalsOptions): () => void;
125
133
  /**
126
134
  * Convenience method: Update user context for both flags and tracker.
127
- *
128
- * @param user - New user context (merged with existing).
129
- * @returns Promise resolving after flags refresh.
130
- *
131
- * @example
132
- * await Nexus.identify({ id: 'usr_12345', country: 'VN' });
133
135
  */
134
136
  static identify(user: UserContext): Promise<void>;
135
137
  /**
136
138
  * Resets user context to anonymous state (e.g. on logout).
137
- *
138
- * @example
139
- * Nexus.reset(); // called on user logout
140
139
  */
141
140
  static reset(): void;
142
141
  /**
143
142
  * Gracefully tears down both clients, closing SSE connections and flushing pending events.
144
- *
145
- * @example
146
- * await Nexus.destroy();
147
143
  */
148
144
  static destroy(): Promise<void>;
149
145
  }
150
146
 
151
- export { Nexus, type NexusInitOptions };
147
+ export { Nexus, type NexusDevServerOptions, type NexusInitOptions };
package/dist/index.d.ts CHANGED
@@ -1,25 +1,36 @@
1
1
  import { NexusFlagsClient, NexusFlagsOptions } from '@nexussdk/flags';
2
2
  export { INexusFlagsClient, NexusFlagsClient, NexusFlagsOptions } from '@nexussdk/flags';
3
3
  import { NexusTrackerClient, NexusTrackerOptions } from '@nexussdk/tracker';
4
- export { INexusTrackerClient, NexusTrackerClient, NexusTrackerOptions } from '@nexussdk/tracker';
5
- import { UserContext } from '@nexussdk/contracts';
6
- export { ErrorEventPayload, FeatureFlag, FlagEvaluationResult, ProblemDetails, UserContext } from '@nexussdk/contracts';
4
+ export { INexusTrackerClient, NexusGuardCore, NexusGuardCoreOptions, NexusTrackerClient, NexusTrackerOptions, attachWebVitals } from '@nexussdk/tracker';
5
+ import { UserContext, SamplingConfig, TransportPlugin, PerformanceVitalsOptions, SeverityLevel, Breadcrumb } from '@nexussdk/contracts';
6
+ export { Breadcrumb, ErrorEventPayload, FeatureFlag, FlagEvaluationResult, NexusErrorInfo, PerformanceVitalEntry, PerformanceVitalsOptions, ProblemDetails, SamplingConfig, SeverityLevel, TransportPlugin, UserContext } from '@nexussdk/contracts';
7
7
 
8
8
  /**
9
9
  * @fileoverview Unified Nexus singleton facade wrapping Flags and Tracker clients.
10
10
  * @module @nexus/sdk/nexus
11
11
  */
12
12
 
13
+ /**
14
+ * Dev server routing configuration.
15
+ */
16
+ interface NexusDevServerOptions {
17
+ /** Enable local dev server ingestion mode. Auto-enabled in development if true. */
18
+ enabled?: boolean;
19
+ /** Port the dev server is listening on. Defaults to 4567. */
20
+ port?: number;
21
+ /** Host the dev server is bound to. Defaults to 'localhost'. */
22
+ host?: string;
23
+ }
13
24
  /**
14
25
  * Unified initialization options for the Nexus SDK umbrella.
15
26
  *
16
27
  * @example
17
28
  * Nexus.init({
18
29
  * apiKey: 'pk_live_...',
19
- * baseUrl: 'http://localhost:8080',
20
30
  * user: { id: 'usr_12345', country: 'VN' },
21
31
  * environment: 'production',
22
32
  * autoCapture: true,
33
+ * devServer: { enabled: process.env.NODE_ENV === 'development' },
23
34
  * });
24
35
  */
25
36
  interface NexusInitOptions {
@@ -35,6 +46,14 @@ interface NexusInitOptions {
35
46
  tags?: Record<string, string>;
36
47
  /** Toggle automated global error capture. Defaults to true. */
37
48
  autoCapture?: boolean;
49
+ /** Client-side rate limiting and deduplication sampling options. */
50
+ sampling?: SamplingConfig;
51
+ /** Pluggable transport adapter ('fetch', 'console', 'localStorage', custom fn). */
52
+ transport?: TransportPlugin;
53
+ /** Web Vitals performance observer options, or true for default observation. */
54
+ vitals?: boolean | PerformanceVitalsOptions;
55
+ /** Local dev server auto-routing configuration. */
56
+ devServer?: NexusDevServerOptions;
38
57
  /** Additional flags-specific options. */
39
58
  flags?: Partial<NexusFlagsOptions>;
40
59
  /** Additional tracker-specific options. */
@@ -61,6 +80,7 @@ interface NexusInitOptions {
61
80
  */
62
81
  declare class Nexus {
63
82
  private static instance;
83
+ private static vitalsCleanup;
64
84
  /** The underlying feature flags client instance. */
65
85
  readonly flags: NexusFlagsClient;
66
86
  /** The underlying error tracker client instance. */
@@ -73,9 +93,6 @@ declare class Nexus {
73
93
  *
74
94
  * @param options - SDK configuration options.
75
95
  * @returns The initialized Nexus singleton instance.
76
- *
77
- * @example
78
- * const nexus = Nexus.init({ apiKey: 'pk_live_...' });
79
96
  */
80
97
  static init(options?: NexusInitOptions): Nexus;
81
98
  /**
@@ -83,69 +100,48 @@ declare class Nexus {
83
100
  *
84
101
  * @returns The active Nexus instance.
85
102
  * @throws {Error} If `Nexus.init()` has not been called yet.
86
- *
87
- * @example
88
- * const nexus = Nexus.getInstance();
89
- * nexus.flags.isEnabled('checkout_v2');
90
103
  */
91
104
  static getInstance(): Nexus;
92
105
  /**
93
106
  * Convenience method: Check if a feature flag is enabled.
94
- *
95
- * @param key - Flag identifier.
96
- * @param defaultValue - Fallback if flag is missing.
97
- * @returns Boolean enabled state.
98
- *
99
- * @example
100
- * if (Nexus.isEnabled('checkout_redesign')) { ... }
101
107
  */
102
108
  static isEnabled(key: string, defaultValue?: boolean): boolean;
103
109
  /**
104
110
  * Convenience method: Get a flag variant value.
105
- *
106
- * @param key - Flag identifier.
107
- * @param variantKey - Variant property name.
108
- * @param defaultValue - Fallback value.
109
- * @returns Variant value cast to type T.
110
- *
111
- * @example
112
- * const rate = Nexus.getVariant<number>('promo_banner_v2', 'discount_rate', 10);
113
111
  */
114
112
  static getVariant<T = unknown>(key: string, variantKey: string, defaultValue?: T): T;
115
113
  /**
116
114
  * Convenience method: Capture an error manually.
117
- *
118
- * @param error - Error instance, string, or unknown value.
119
- * @param extra - Optional metadata tags.
120
- *
121
- * @example
122
- * Nexus.captureError(new TypeError('Cannot read properties of null'));
123
115
  */
124
116
  static captureError(error: unknown, extra?: Record<string, unknown>): void;
117
+ /**
118
+ * Convenience method: Capture an informational or warning message event.
119
+ */
120
+ static captureMessage(message: string, level?: SeverityLevel, extra?: Record<string, unknown>): void;
121
+ /**
122
+ * Convenience method: Add a breadcrumb manually.
123
+ */
124
+ static addBreadcrumb(breadcrumb: Breadcrumb): void;
125
+ /**
126
+ * Convenience method: Set extra contextual metadata.
127
+ */
128
+ static setExtra(key: string, value: unknown): void;
129
+ /**
130
+ * Convenience method: Attach Web Vitals observer to tracker.
131
+ */
132
+ static attachWebVitals(options?: PerformanceVitalsOptions): () => void;
125
133
  /**
126
134
  * Convenience method: Update user context for both flags and tracker.
127
- *
128
- * @param user - New user context (merged with existing).
129
- * @returns Promise resolving after flags refresh.
130
- *
131
- * @example
132
- * await Nexus.identify({ id: 'usr_12345', country: 'VN' });
133
135
  */
134
136
  static identify(user: UserContext): Promise<void>;
135
137
  /**
136
138
  * Resets user context to anonymous state (e.g. on logout).
137
- *
138
- * @example
139
- * Nexus.reset(); // called on user logout
140
139
  */
141
140
  static reset(): void;
142
141
  /**
143
142
  * Gracefully tears down both clients, closing SSE connections and flushing pending events.
144
- *
145
- * @example
146
- * await Nexus.destroy();
147
143
  */
148
144
  static destroy(): Promise<void>;
149
145
  }
150
146
 
151
- export { Nexus, type NexusInitOptions };
147
+ export { Nexus, type NexusDevServerOptions, type NexusInitOptions };
@@ -1,9 +1,9 @@
1
- var Nexus=(function(exports){'use strict';function f(e,t=1e3,r=3e4){let n=t*Math.pow(2,e),s=Math.min(r,n);return Math.random()*s}function T(e,t){return new Promise((r,n)=>{if(t?.aborted){n(new DOMException("Aborted","AbortError"));return}let s=setTimeout(r,e);t?.addEventListener("abort",()=>{clearTimeout(s),n(new DOMException("Aborted","AbortError"));});})}function O(e){return e>=500||e===429}async function x(e){let{url:t,method:r="GET",headers:n={},body:s,timeoutMs:a=5e3,maxRetries:i=3,retryBaseMs:o=1e3,retryMaxMs:l=3e4,signal:c}=e,d=new Error("Request failed");for(let p=0;p<=i;p++){if(c?.aborted)throw new DOMException("Request aborted by caller.","AbortError");let b=new AbortController,k=setTimeout(()=>b.abort(),a);c?.addEventListener("abort",()=>b.abort());try{let h={method:r,headers:{"Content-Type":"application/json",...n},signal:b.signal};s!==void 0&&(h.body=typeof s=="string"?s:JSON.stringify(s));let u=await fetch(t,h);if(clearTimeout(k),u.ok)return {data:await u.json(),status:u.status,headers:u.headers};if(O(u.status)&&p<i){d=new Error(`HTTP ${u.status}: ${u.statusText}`);let P=f(p,o,l);await T(P,c);continue}let K=await u.text().catch(()=>"");throw new Error(`HTTP ${u.status}: ${K}`)}catch(h){if(clearTimeout(k),h instanceof DOMException&&h.name==="AbortError")throw h;if(d=h instanceof Error?h:new Error(String(h)),p<i){let u=f(p,o,l);await T(u,c);}}}throw d}var C=class{capacity;buffer;head=0;count=0;constructor(e){if(e<1)throw new RangeError(`RingBuffer capacity must be >= 1, got ${e}`);this.capacity=e,this.buffer=new Array(e).fill(void 0);}push(e){this.buffer[this.head]=e,this.head=(this.head+1)%this.capacity,this.count<this.capacity&&this.count++;}toArray(){if(this.count===0)return [];let e=[];if(this.count<this.capacity)for(let t=0;t<this.count;t++)e.push(this.buffer[t]);else for(let t=0;t<this.capacity;t++)e.push(this.buffer[(this.head+t)%this.capacity]);return e}get size(){return this.count}get maxCapacity(){return this.capacity}get isFull(){return this.count===this.capacity}clear(){this.buffer.fill(void 0),this.head=0,this.count=0;}peek(){if(this.count===0)return;let e=(this.head-1+this.capacity)%this.capacity;return this.buffer[e]}};function g(e){try{let t=globalThis.process;if(typeof t<"u"&&t&&t.env)return t.env[e]??void 0}catch{}}function _(e){try{let t=typeof globalThis<"u"&&globalThis.__import_meta__;if(t&&t.env)return t.env[e]??void 0;let r=new Function("try { return import.meta; } catch(e) { return undefined; }")();if(r&&r.env)return r.env[e]??void 0}catch{}}function $(){try{if(typeof window<"u"&&window.__NEXUS_API_KEY__)return window.__NEXUS_API_KEY__}catch{}}function m(e){let t=e||$()||g("NEXUS_API_KEY")||g("NEXT_PUBLIC_NEXUS_API_KEY")||_("VITE_NEXUS_API_KEY")||g("NUXT_PUBLIC_NEXUS_API_KEY");if(!t||t.trim()==="")throw new Error(`[Nexus SDK] No API key found. Please provide one via:
1
+ var Nexus=(function(exports){'use strict';function f(e,t=1e3,r=3e4){let n=t*Math.pow(2,e),s=Math.min(r,n);return Math.random()*s}function L(e,t){return new Promise((r,n)=>{if(t?.aborted){n(new DOMException("Aborted","AbortError"));return}let s=setTimeout(r,e);t?.addEventListener("abort",()=>{clearTimeout(s),n(new DOMException("Aborted","AbortError"));});})}function W(e){return e>=500||e===429}async function M(e){let{url:t,method:r="GET",headers:n={},body:s,timeoutMs:a=5e3,maxRetries:o=3,retryBaseMs:c=1e3,retryMaxMs:u=3e4,signal:i}=e,l=new Error("Request failed");for(let h=0;h<=o;h++){if(i?.aborted)throw new DOMException("Request aborted by caller.","AbortError");let m=new AbortController,b=setTimeout(()=>m.abort(),a);i?.addEventListener("abort",()=>m.abort());try{let p={method:r,headers:{"Content-Type":"application/json",...n},signal:m.signal};s!==void 0&&(p.body=typeof s=="string"?s:JSON.stringify(s));let d=await fetch(t,p);if(clearTimeout(b),d.ok)return {data:await d.json(),status:d.status,headers:d.headers};if(W(d.status)&&h<o){l=new Error(`HTTP ${d.status}: ${d.statusText}`);let I=f(h,c,u);await L(I,i);continue}let y=await d.text().catch(()=>"");throw new Error(`HTTP ${d.status}: ${y}`)}catch(p){if(clearTimeout(b),p instanceof DOMException&&p.name==="AbortError")throw p;if(l=p instanceof Error?p:new Error(String(p)),h<o){let d=f(h,c,u);await L(d,i);}}}throw l}var P=class{capacity;buffer;head=0;count=0;constructor(e){if(e<1)throw new RangeError(`RingBuffer capacity must be >= 1, got ${e}`);this.capacity=e,this.buffer=new Array(e).fill(void 0);}push(e){this.buffer[this.head]=e,this.head=(this.head+1)%this.capacity,this.count<this.capacity&&this.count++;}toArray(){if(this.count===0)return [];let e=[];if(this.count<this.capacity)for(let t=0;t<this.count;t++)e.push(this.buffer[t]);else for(let t=0;t<this.capacity;t++)e.push(this.buffer[(this.head+t)%this.capacity]);return e}get size(){return this.count}get maxCapacity(){return this.capacity}get isFull(){return this.count===this.capacity}clear(){this.buffer.fill(void 0),this.head=0,this.count=0;}peek(){if(this.count===0)return;let e=(this.head-1+this.capacity)%this.capacity;return this.buffer[e]}};function E(e){try{let t=globalThis.process;if(typeof t<"u"&&t&&t.env)return t.env[e]??void 0}catch{}}function O(e){try{let t=typeof globalThis<"u"&&globalThis.__import_meta__;if(t&&t.env)return t.env[e]??void 0;let r=new Function("try { return import.meta; } catch(e) { return undefined; }")();if(r&&r.env)return r.env[e]??void 0}catch{}}function X(){try{if(typeof window<"u"&&window.__NEXUS_API_KEY__)return window.__NEXUS_API_KEY__}catch{}}function w(e){let t=e||X()||E("NEXUS_API_KEY")||E("NEXT_PUBLIC_NEXUS_API_KEY")||O("VITE_NEXUS_API_KEY")||E("NUXT_PUBLIC_NEXUS_API_KEY");if(!t||t.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 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;})({});//# sourceMappingURL=index.global.js.map
9
- //# sourceMappingURL=index.global.js.map
7
+ 6. NUXT_PUBLIC_NEXUS_API_KEY (Nuxt)`);return t.trim()}function S(e,t="https://api.nexus.dev"){return (e||E("NEXT_PUBLIC_NEXUS_URL")||O("VITE_NEXUS_URL")||t).replace(/\/$/,"")}function x(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(o=>x(o,t,r+1,n));return t.delete(e),a}let s={};for(let a of Object.keys(e)){let o=e[a];s[a]=x(o,t,r+1,n);}return t.delete(e),s}function T(e,t=8){let r=x(e,new WeakSet,0,t);try{return JSON.stringify(r)}catch{return JSON.stringify({error:"[SerializationFailed]"})}}function V(e,t=0){let r=t>>>0,n=3432918353,s=461845907,a=0,o=Math.floor(e.length/4)*4;for(;a<o;){let i=e.charCodeAt(a)&255|(e.charCodeAt(a+1)&255)<<8|(e.charCodeAt(a+2)&255)<<16|(e.charCodeAt(a+3)&255)<<24;i=Math.imul(i,n),i=i<<15|i>>>17,i=Math.imul(i,s),r^=i,r=r<<13|r>>>19,r=Math.imul(r,5)+3864292196>>>0,a+=4;}let c=0,u=e.length&3;return u>=3&&(c^=(e.charCodeAt(a+2)&255)<<16),u>=2&&(c^=(e.charCodeAt(a+1)&255)<<8),u>=1&&(c^=e.charCodeAt(a)&255,c=Math.imul(c,n),c=c<<15|c>>>17,c=Math.imul(c,s),r^=c),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 G(e,t){let r=`${e}:${t}`;return V(r)%100}function R(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 z(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 Y(e,t){let r=z(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"&&R(r,String(n[0]))>=0;case "SEMVER_LTE":return typeof r=="string"&&typeof n[0]=="string"&&R(r,String(n[0]))<=0;default:return false}}function U(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=>Y(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 G(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("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=f(this.reconnectAttempt,1e3,3e4);this.reconnectAttempt++,this.reconnectTimer=setTimeout(()=>{this.reconnectTimer=null,this.destroyed||this.openConnection();},e);}},Z="nexus_flags_",F="nexus_anon_id",q=class{memory=new Map;storageKey;localStorageAvailable;broadcastChannel=null;constructor(e){this.storageKey=`${Z}${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(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[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{}}},N=class{apiKey;baseUrl;timeoutMs;user;storage;listeners=new Map;sseManager;flagDefinitions=new Map;constructor(e={}){if(this.apiKey=w(e.apiKey),this.baseUrl=S(e.baseUrl),this.timeoutMs=e.timeoutMs??3e3,this.storage=new q(this.apiKey.substring(0,16)),this.user=e.user??{id:this.storage.getOrCreateAnonymousId()},e.bootstrap){let t={};for(let[r,n]of Object.entries(e.bootstrap))if(typeof n=="boolean")t[r]={key:r,enabled:n,variants:{},reason:n?"DEFAULT_ENABLED":"KILL_SWITCH",version:1};else if(n&&typeof n=="object"){let s=n,a=typeof s.enabled=="boolean"?s.enabled:typeof s.isEnabled=="boolean"?s.isEnabled:true;t[r]={key:s.key??r,enabled:a,variants:s.variants??{},reason:a?"DEFAULT_ENABLED":"KILL_SWITCH",version:typeof s.version=="number"?s.version:1};}this.storage.setAll(t);}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?U(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 M({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,r=t&&typeof t=="object"&&"data"in t?t.data:e.data;if(r&&typeof r=="object")for(let[n,s]of Object.entries(r)){this.flagDefinitions.set(n,s);let a=U(s,this.user);this.storage.set(n,a),this.listeners.get(n)?.forEach(o=>o(a));}}catch{}}};var Q=/^(password|passwd|token|secret|authorization|bearer|auth|credit_?card|cvv|cvc|ssn|api_?key|access_?token|refresh_?token)$/i,ee=/\b(?:\d[ -]*?){13,16}\b/g,te=/[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/g,K=/([?&](token|auth|key|secret|password|api_key|access_token|refresh_token)=)[^&]*/gi;function re(e){return e.replace(ee,"[CARD_REDACTED]").replace(te,"[EMAIL_REDACTED]").replace(K,"$1[REDACTED]")}function C(e,t=0){if(t>5)return "[MaxDepthExceeded]";if(e==null)return e;if(typeof e=="string")return re(e);if(typeof e!="object")return e;if(Array.isArray(e))return e.map(n=>C(n,t+1));let r={};for(let[n,s]of Object.entries(e))Q.test(n)?r[n]="[REDACTED]":r[n]=C(s,t+1);return r}function B(e){return e.replace(K,"$1[REDACTED]")}function H(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 o=s.match(/^(.+?)@(.+?):(\d+):(\d+)$/);o&&t.push({functionName:o[1]??"<anonymous>",fileName:o[2]??"<unknown>",lineNumber:parseInt(o[3]??"0",10),columnNumber:parseInt(o[4]??"0",10)});}return t}function j(e,t,r){let n=r?.fileName??"unknown",s=r?.lineNumber??0,a=`${e}:${t}:${n}:${s}`,o=5381;for(let c=0;c<a.length;c++)o=(o<<5)+o+a.charCodeAt(c)>>>0;return `fp_${o.toString(16).padStart(8,"0")}`}var ne=class{buffer;listenersAttached=false;clickHandler;popStateHandler;constructor(e){this.buffer=new P(e),this.clickHandler=this.handleClick.bind(this),this.popStateHandler=this.handleNavigation.bind(this);}push(e){let t=e.data?C(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 ${B(window.location.href)}`,level:"info",data:{url:B(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);};}},$="nexus_events",D=200,se=class{options;maxRetries;plugin;isPageHiding=false;constructor(e){this.options=e,this.maxRetries=e.maxRetries??2,this.plugin=e.plugin??"fetch",this.plugin==="fetch"&&(typeof document<"u"&&document.addEventListener("visibilitychange",()=>{document.visibilityState==="hidden"&&(this.isPageHiding=true);}),typeof window<"u"&&window.addEventListener("pagehide",()=>{this.isPageHiding=true;}));}send(e){if(typeof this.plugin=="function"){this.plugin(e);return}switch(this.plugin){case "console":this.sendConsole(e);return;case "localStorage":this.sendLocalStorage(e);return;default:this.sendFetch(e);}}async flush(e){if(typeof this.plugin=="function"){await this.plugin(e);return}switch(this.plugin){case "console":this.sendConsole(e);return;case "localStorage":this.sendLocalStorage(e);return;default:{let t=T(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);}}}sendFetch(e){let t=T(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);}sendConsole(e){let t=`[Nexus|${e.errorType}]`;console.group(t,e.errorMessage),console.warn("Fingerprint:",e.fingerprint),console.warn("Stack frames:",e.stackTrace.slice(0,3)),e.breadcrumbs.length>0&&console.warn("Last breadcrumbs:",e.breadcrumbs.slice(-5)),e.userContext&&console.warn("User:",e.userContext),console.groupEnd();}sendLocalStorage(e){if(!(typeof localStorage>"u"))try{let t=localStorage.getItem($),r=t?JSON.parse(t):[];r.push(e),r.length>D&&r.splice(0,r.length-D),localStorage.setItem($,T(r));}catch{}}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 ae(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 _=class{apiKey;environment;tags;extras;beforeSend;breadcrumbManager;transport;samplingRate;dedupeWindowMs;maxPerSession;sessionCaptureCount=0;userContext;dedupeMap=new Map;cleanupListeners;constructor(e={}){this.apiKey=w(e.apiKey);let t=S(e.baseUrl);this.environment=e.environment??"production",this.tags={...e.tags},this.extras={},this.beforeSend=e.beforeSend,this.samplingRate=Math.max(0,Math.min(1,e.sampling?.rate??1)),this.dedupeWindowMs=e.sampling?.dedupeWindow??1e4,this.maxPerSession=e.sampling?.maxPerSession??100,this.breadcrumbManager=new ne(Math.min(e.maxBreadcrumbs??20,50)),this.transport=new se({endpoint:`${t}/api/v1/telemetry/errors`,apiKey:this.apiKey,plugin:e.transport}),e.autoCapture!==false&&typeof window<"u"&&(this.breadcrumbManager.attachListeners(),this.cleanupListeners=ae(this));}captureError(e,t){if(this.sessionCaptureCount>=this.maxPerSession||this.samplingRate<1&&Math.random()>this.samplingRate)return;let r=this.normalizeError(e),n=H(r.stack),s=j(r.type,r.message,n[0]),a=this.dedupeMap.get(s);if(a){a.count+=1;return}let o={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()},c=C(o),u=this.beforeSend?this.beforeSend(c):c;if(!u)return;this.transport.send(u),this.sessionCaptureCount++;let i=setTimeout(()=>{let l=this.dedupeMap.get(s);if(l&&l.count>1){let h={...l.lastPayload,occurrenceCount:l.count,clientTimestamp:Date.now()};this.transport.send(h);}this.dedupeMap.delete(s);},this.dedupeWindowMs);this.dedupeMap.set(s,{timer:i,count:1,lastPayload:u});}captureMessage(e,t="info",r){let n=new Error(e);n.name="NexusMessage",this.captureError(n,{_level:t,...r});}addBreadcrumb(e){this.breadcrumbManager.push(e);}setUser(e){this.userContext=e??void 0;}setTag(e,t){this.tags[e]=t;}setExtra(e,t){this.extras[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}}},ie=class{_error=null;_errorInfo=null;_listeners=new Set;_tracker;_onError;_tags;_generateErrorId;constructor(e={}){this._tracker=e.tracker,this._onError=e.onError,this._tags=e.tags,this._generateErrorId=e.generateErrorId??(t=>{let r=H(t.stack);return `NX-${j(t.name,t.message,r[0]).replace(/^fp_/,"").slice(0,8).toUpperCase()}`});}get hasError(){return this._error!==null}get currentError(){return this._error}get errorInfo(){return this._errorInfo}capture(e,t){let r=e instanceof Error?e:new Error(String(e));this._error=r;let n=this._generateErrorId(r),s={error:r,errorId:n,reset:()=>this.recover(),componentStack:t?.componentStack};if(this._errorInfo=s,this._tracker)try{this._tracker.captureError(r,{tags:{...this._tags,...t?.tags,guardErrorId:n},componentStack:t?.componentStack});}catch(a){typeof console<"u"&&console.warn("[NexusGuardCore] Failed to forward error to tracker:",a);}if(this._onError)try{this._onError(r,s);}catch(a){typeof console<"u"&&console.warn("[NexusGuardCore] onError callback threw:",a);}return this._notify(),s}recover(){this._error!==null&&(this._error=null,this._errorInfo=null,this._notify());}reset(){this.recover();}subscribe(e){return this._listeners.add(e),e(this._errorInfo),()=>{this._listeners.delete(e);}}_notify(){for(let e of this._listeners)try{e(this._errorInfo);}catch(t){typeof console<"u"&&console.error("[NexusGuardCore] Subscriber threw error:",t);}}},oe={LCP:[2500,4e3],CLS:[.1,.25],FID:[100,300],TTFB:[800,1800],INP:[200,500],FCP:[1800,3e3]};function g(e,t){let[r,n]=oe[e]??[1/0,1/0];return t<=r?"good":t<=n?"needs-improvement":"poor"}function v(){return typeof performance>"u"?"navigate":performance.getEntriesByType("navigation")[0]?.type??"navigate"}function ce(e){if(typeof PerformanceObserver>"u")return ()=>{};let t=0,r=null;try{r=new PerformanceObserver(a=>{let o=a.getEntries(),c=o[o.length-1];c&&(t=c.startTime??0);}),r.observe({type:"largest-contentful-paint",buffered:!0});}catch{return ()=>{}}let n=()=>{t!==0&&e({name:"LCP",value:t,rating:g("LCP",t),delta:t,navigationType:v()});},s=()=>n();return document.addEventListener("visibilitychange",s,{once:true}),()=>{r?.disconnect(),document.removeEventListener("visibilitychange",s);}}function le(e){if(typeof PerformanceObserver>"u")return ()=>{};let t=0,r=null;try{r=new PerformanceObserver(a=>{for(let o of a.getEntries()){let c=o;c.hadRecentInput||(t+=c.value??0);}}),r.observe({type:"layout-shift",buffered:!0});}catch{return ()=>{}}let n=()=>{t!==0&&e({name:"CLS",value:t,rating:g("CLS",t),delta:t,navigationType:v()});},s=()=>n();return document.addEventListener("visibilitychange",s,{once:true}),()=>{r?.disconnect(),document.removeEventListener("visibilitychange",s);}}function ue(e){if(typeof PerformanceObserver>"u")return ()=>{};let t=null;try{t=new PerformanceObserver(r=>{let n=r.getEntries()[0];if(!n)return;let s=n.processingStart-n.startTime;e({name:"FID",value:s,rating:g("FID",s),delta:s,navigationType:v()}),t?.disconnect();}),t.observe({type:"first-input",buffered:!0});}catch{return ()=>{}}return ()=>t?.disconnect()}function de(e){if(typeof PerformanceObserver>"u")return ()=>{};try{let t=performance.getEntriesByType("navigation")[0];if(t){let r=t.responseStart-t.requestStart;e({name:"TTFB",value:r,rating:g("TTFB",r),delta:r,navigationType:t.type??"navigate"});}}catch{}return ()=>{}}function he(e){if(typeof PerformanceObserver>"u")return ()=>{};let t=0,r=null;try{r=new PerformanceObserver(a=>{for(let o of a.getEntries()){let c=o;c.duration>t&&(t=c.duration);}}),r.observe({type:"event",buffered:!0,durationThreshold:16});}catch{return ()=>{}}let n=()=>{t!==0&&e({name:"INP",value:t,rating:g("INP",t),delta:t,navigationType:v()});},s=()=>n();return document.addEventListener("visibilitychange",s,{once:true}),()=>{r?.disconnect(),document.removeEventListener("visibilitychange",s);}}function pe(e){if(typeof PerformanceObserver>"u")return ()=>{};let t=null;try{t=new PerformanceObserver(r=>{let n=r.getEntries().find(a=>a.name==="first-contentful-paint");if(!n)return;let s=n.startTime;e({name:"FCP",value:s,rating:g("FCP",s),delta:s,navigationType:v()}),t?.disconnect();}),t.observe({type:"paint",buffered:!0});}catch{return ()=>{}}return ()=>t?.disconnect()}function fe(e,t){if(typeof PerformanceObserver>"u")return ()=>{};let r=null;try{r=new PerformanceObserver(n=>{for(let s of n.getEntries())s.duration>=t&&e.addBreadcrumb({category:"custom",message:`Long task detected: ${Math.round(s.duration)}ms`,level:s.duration>=t*3?"error":"warning",data:{durationMs:Math.round(s.duration),startTime:Math.round(s.startTime)}});}),r.observe({type:"longtask",buffered:!0});}catch{return ()=>{}}return ()=>r?.disconnect()}var ge={LCP:ce,CLS:le,FID:ue,TTFB:de,INP:he,FCP:pe};function k(e,t={}){if(typeof window>"u")return ()=>{};let{captureAsEvents:r=false,poorRatingOnly:n=false,vitals:s=["LCP","CLS","FID","TTFB","INP","FCP"],longTaskThresholdMs:a=50}=t,o=i=>{n&&i.rating==="good"||(e.addBreadcrumb({category:"custom",message:`Web Vital ${i.name}: ${i.rating.toUpperCase()} (${Math.round(i.value)}${i.name==="CLS"?"":"ms"})`,level:i.rating==="poor"?"error":i.rating==="needs-improvement"?"warning":"info",data:{metric:i.name,value:i.value,rating:i.rating,navigationType:i.navigationType}}),r&&i.rating!=="good"&&e.captureMessage(`Performance degradation: ${i.name} is ${i.rating} (${Math.round(i.value)}${i.name==="CLS"?"":"ms"})`,i.rating==="poor"?"error":"warning",{metric:i.name,value:i.value,rating:i.rating,navigationType:i.navigationType}));},c=s.map(i=>{let l=ge[i];return l?l(o):()=>{}}),u=fe(e,a);return ()=>{c.forEach(i=>i()),u();}}var A=class e{static instance=null;static vitalsCleanup=null;flags;tracker;constructor(t={}){let{apiKey:r,baseUrl:n,user:s,environment:a,tags:o,autoCapture:c,sampling:u,transport:i,vitals:l,devServer:h,flags:m,tracker:b}=t;this.flags=new N({apiKey:r,baseUrl:n,user:s,...m});let p=n,d=i;if(h?.enabled){let y=h.port??4567;p=`http://${h.host??"localhost"}:${y}`;}if(this.tracker=new _({apiKey:r,baseUrl:p,environment:a,tags:o,autoCapture:c,sampling:u,transport:d,...b}),l){let y=typeof l=="object"?l:{};e.vitalsCleanup=k(this.tracker,y);}}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 captureMessage(t,r="info",n){e.getInstance().tracker.captureMessage(t,r,n);}static addBreadcrumb(t){e.getInstance().tracker.addBreadcrumb(t);}static setExtra(t,r){e.getInstance().tracker.setExtra(t,r);}static attachWebVitals(t){return k(e.getInstance().tracker,t)}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.vitalsCleanup&&(e.vitalsCleanup(),e.vitalsCleanup=null),e.instance&&(await e.instance.tracker.flush(),e.instance.tracker.destroy(),e.instance.flags.destroy(),e.instance=null);}};
9
+ exports.Nexus=A;exports.NexusFlagsClient=N;exports.NexusGuardCore=ie;exports.NexusTrackerClient=_;exports.attachWebVitals=k;return exports;})({});
package/dist/index.mjs CHANGED
@@ -1,2 +1,2 @@
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};//# sourceMappingURL=index.mjs.map
2
- //# sourceMappingURL=index.mjs.map
1
+ import {NexusFlagsClient}from'@nexussdk/flags';export{NexusFlagsClient}from'@nexussdk/flags';import {NexusTrackerClient,attachWebVitals}from'@nexussdk/tracker';export{NexusGuardCore,NexusTrackerClient,attachWebVitals}from'@nexussdk/tracker';var o=class t{static instance=null;static vitalsCleanup=null;flags;tracker;constructor(e={}){let{apiKey:r,baseUrl:s,user:u,environment:p,tags:g,autoCapture:x,sampling:d,transport:f,vitals:n,devServer:a,flags:v,tracker:m}=e;this.flags=new NexusFlagsClient({apiKey:r,baseUrl:s,user:u,...v});let l=s,b=f;if(a?.enabled){let i=a.port??4567;l=`http://${a.host??"localhost"}:${i}`;}if(this.tracker=new NexusTrackerClient({apiKey:r,baseUrl:l,environment:p,tags:g,autoCapture:x,sampling:d,transport:b,...m}),n){let i=typeof n=="object"?n:{};t.vitalsCleanup=attachWebVitals(this.tracker,i);}}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,r=false){return t.getInstance().flags.isEnabled(e,r)}static getVariant(e,r,s){return t.getInstance().flags.getVariant(e,r,s)}static captureError(e,r){t.getInstance().tracker.captureError(e,r);}static captureMessage(e,r="info",s){t.getInstance().tracker.captureMessage(e,r,s);}static addBreadcrumb(e){t.getInstance().tracker.addBreadcrumb(e);}static setExtra(e,r){t.getInstance().tracker.setExtra(e,r);}static attachWebVitals(e){return attachWebVitals(t.getInstance().tracker,e)}static async identify(e){let r=t.getInstance();r.tracker.setUser(e),await r.flags.identify(e);}static reset(){let e=t.getInstance();e.tracker.setUser(null),e.flags.reset();}static async destroy(){t.vitalsCleanup&&(t.vitalsCleanup(),t.vitalsCleanup=null),t.instance&&(await t.instance.tracker.flush(),t.instance.tracker.destroy(),t.instance.flags.destroy(),t.instance=null);}};
2
+ export{o as Nexus};