@nexussdk/sdk 0.0.3 → 0.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +40 -2
- package/dist/index.cjs +2 -1
- package/dist/index.d.mts +41 -45
- package/dist/index.d.ts +41 -45
- package/dist/index.global.js +4 -3
- package/dist/index.mjs +2 -1
- package/dist/react.cjs +2 -1
- package/dist/react.d.mts +111 -76
- package/dist/react.d.ts +111 -76
- package/dist/react.mjs +2 -1
- package/dist/vue.cjs +2 -0
- package/dist/vue.d.mts +234 -0
- package/dist/vue.d.ts +234 -0
- package/dist/vue.mjs +2 -0
- package/package.json +32 -10
package/dist/react.d.mts
CHANGED
|
@@ -1,23 +1,34 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { UserContext, FlagEvaluationResult } from '@nexussdk/contracts';
|
|
2
|
+
import { NexusFlagsClient, NexusFlagsOptions } from '@nexussdk/flags';
|
|
3
|
+
import { NexusTrackerClient, NexusTrackerOptions } from '@nexussdk/tracker';
|
|
4
|
+
import { UserContext, SamplingConfig, TransportPlugin, PerformanceVitalsOptions, SeverityLevel, Breadcrumb, NexusErrorInfo, FlagEvaluationResult } from '@nexussdk/contracts';
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* @fileoverview Unified Nexus singleton facade wrapping Flags and Tracker clients.
|
|
8
8
|
* @module @nexus/sdk/nexus
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
+
/**
|
|
12
|
+
* Dev server routing configuration.
|
|
13
|
+
*/
|
|
14
|
+
interface NexusDevServerOptions {
|
|
15
|
+
/** Enable local dev server ingestion mode. Auto-enabled in development if true. */
|
|
16
|
+
enabled?: boolean;
|
|
17
|
+
/** Port the dev server is listening on. Defaults to 4567. */
|
|
18
|
+
port?: number;
|
|
19
|
+
/** Host the dev server is bound to. Defaults to 'localhost'. */
|
|
20
|
+
host?: string;
|
|
21
|
+
}
|
|
11
22
|
/**
|
|
12
23
|
* Unified initialization options for the Nexus SDK umbrella.
|
|
13
24
|
*
|
|
14
25
|
* @example
|
|
15
26
|
* Nexus.init({
|
|
16
27
|
* apiKey: 'pk_live_...',
|
|
17
|
-
* baseUrl: 'http://localhost:8080',
|
|
18
28
|
* user: { id: 'usr_12345', country: 'VN' },
|
|
19
29
|
* environment: 'production',
|
|
20
30
|
* autoCapture: true,
|
|
31
|
+
* devServer: { enabled: process.env.NODE_ENV === 'development' },
|
|
21
32
|
* });
|
|
22
33
|
*/
|
|
23
34
|
interface NexusInitOptions {
|
|
@@ -33,6 +44,14 @@ interface NexusInitOptions {
|
|
|
33
44
|
tags?: Record<string, string>;
|
|
34
45
|
/** Toggle automated global error capture. Defaults to true. */
|
|
35
46
|
autoCapture?: boolean;
|
|
47
|
+
/** Client-side rate limiting and deduplication sampling options. */
|
|
48
|
+
sampling?: SamplingConfig;
|
|
49
|
+
/** Pluggable transport adapter ('fetch', 'console', 'localStorage', custom fn). */
|
|
50
|
+
transport?: TransportPlugin;
|
|
51
|
+
/** Web Vitals performance observer options, or true for default observation. */
|
|
52
|
+
vitals?: boolean | PerformanceVitalsOptions;
|
|
53
|
+
/** Local dev server auto-routing configuration. */
|
|
54
|
+
devServer?: NexusDevServerOptions;
|
|
36
55
|
/** Additional flags-specific options. */
|
|
37
56
|
flags?: Partial<NexusFlagsOptions>;
|
|
38
57
|
/** Additional tracker-specific options. */
|
|
@@ -59,6 +78,7 @@ interface NexusInitOptions {
|
|
|
59
78
|
*/
|
|
60
79
|
declare class Nexus {
|
|
61
80
|
private static instance;
|
|
81
|
+
private static vitalsCleanup;
|
|
62
82
|
/** The underlying feature flags client instance. */
|
|
63
83
|
readonly flags: NexusFlagsClient;
|
|
64
84
|
/** The underlying error tracker client instance. */
|
|
@@ -71,9 +91,6 @@ declare class Nexus {
|
|
|
71
91
|
*
|
|
72
92
|
* @param options - SDK configuration options.
|
|
73
93
|
* @returns The initialized Nexus singleton instance.
|
|
74
|
-
*
|
|
75
|
-
* @example
|
|
76
|
-
* const nexus = Nexus.init({ apiKey: 'pk_live_...' });
|
|
77
94
|
*/
|
|
78
95
|
static init(options?: NexusInitOptions): Nexus;
|
|
79
96
|
/**
|
|
@@ -81,73 +98,52 @@ declare class Nexus {
|
|
|
81
98
|
*
|
|
82
99
|
* @returns The active Nexus instance.
|
|
83
100
|
* @throws {Error} If `Nexus.init()` has not been called yet.
|
|
84
|
-
*
|
|
85
|
-
* @example
|
|
86
|
-
* const nexus = Nexus.getInstance();
|
|
87
|
-
* nexus.flags.isEnabled('checkout_v2');
|
|
88
101
|
*/
|
|
89
102
|
static getInstance(): Nexus;
|
|
90
103
|
/**
|
|
91
104
|
* Convenience method: Check if a feature flag is enabled.
|
|
92
|
-
*
|
|
93
|
-
* @param key - Flag identifier.
|
|
94
|
-
* @param defaultValue - Fallback if flag is missing.
|
|
95
|
-
* @returns Boolean enabled state.
|
|
96
|
-
*
|
|
97
|
-
* @example
|
|
98
|
-
* if (Nexus.isEnabled('checkout_redesign')) { ... }
|
|
99
105
|
*/
|
|
100
106
|
static isEnabled(key: string, defaultValue?: boolean): boolean;
|
|
101
107
|
/**
|
|
102
108
|
* Convenience method: Get a flag variant value.
|
|
103
|
-
*
|
|
104
|
-
* @param key - Flag identifier.
|
|
105
|
-
* @param variantKey - Variant property name.
|
|
106
|
-
* @param defaultValue - Fallback value.
|
|
107
|
-
* @returns Variant value cast to type T.
|
|
108
|
-
*
|
|
109
|
-
* @example
|
|
110
|
-
* const rate = Nexus.getVariant<number>('promo_banner_v2', 'discount_rate', 10);
|
|
111
109
|
*/
|
|
112
110
|
static getVariant<T = unknown>(key: string, variantKey: string, defaultValue?: T): T;
|
|
113
111
|
/**
|
|
114
112
|
* Convenience method: Capture an error manually.
|
|
115
|
-
*
|
|
116
|
-
* @param error - Error instance, string, or unknown value.
|
|
117
|
-
* @param extra - Optional metadata tags.
|
|
118
|
-
*
|
|
119
|
-
* @example
|
|
120
|
-
* Nexus.captureError(new TypeError('Cannot read properties of null'));
|
|
121
113
|
*/
|
|
122
114
|
static captureError(error: unknown, extra?: Record<string, unknown>): void;
|
|
115
|
+
/**
|
|
116
|
+
* Convenience method: Capture an informational or warning message event.
|
|
117
|
+
*/
|
|
118
|
+
static captureMessage(message: string, level?: SeverityLevel, extra?: Record<string, unknown>): void;
|
|
119
|
+
/**
|
|
120
|
+
* Convenience method: Add a breadcrumb manually.
|
|
121
|
+
*/
|
|
122
|
+
static addBreadcrumb(breadcrumb: Breadcrumb): void;
|
|
123
|
+
/**
|
|
124
|
+
* Convenience method: Set extra contextual metadata.
|
|
125
|
+
*/
|
|
126
|
+
static setExtra(key: string, value: unknown): void;
|
|
127
|
+
/**
|
|
128
|
+
* Convenience method: Attach Web Vitals observer to tracker.
|
|
129
|
+
*/
|
|
130
|
+
static attachWebVitals(options?: PerformanceVitalsOptions): () => void;
|
|
123
131
|
/**
|
|
124
132
|
* Convenience method: Update user context for both flags and tracker.
|
|
125
|
-
*
|
|
126
|
-
* @param user - New user context (merged with existing).
|
|
127
|
-
* @returns Promise resolving after flags refresh.
|
|
128
|
-
*
|
|
129
|
-
* @example
|
|
130
|
-
* await Nexus.identify({ id: 'usr_12345', country: 'VN' });
|
|
131
133
|
*/
|
|
132
134
|
static identify(user: UserContext): Promise<void>;
|
|
133
135
|
/**
|
|
134
136
|
* Resets user context to anonymous state (e.g. on logout).
|
|
135
|
-
*
|
|
136
|
-
* @example
|
|
137
|
-
* Nexus.reset(); // called on user logout
|
|
138
137
|
*/
|
|
139
138
|
static reset(): void;
|
|
140
139
|
/**
|
|
141
140
|
* Gracefully tears down both clients, closing SSE connections and flushing pending events.
|
|
142
|
-
*
|
|
143
|
-
* @example
|
|
144
|
-
* await Nexus.destroy();
|
|
145
141
|
*/
|
|
146
142
|
static destroy(): Promise<void>;
|
|
147
143
|
}
|
|
148
144
|
|
|
149
145
|
/**
|
|
150
|
-
* @fileoverview React Context Provider and
|
|
146
|
+
* @fileoverview React Context Provider, hooks, and Error Boundary for the Nexus SDK.
|
|
151
147
|
* @module @nexus/sdk/react
|
|
152
148
|
*/
|
|
153
149
|
|
|
@@ -156,12 +152,13 @@ interface NexusContextValue {
|
|
|
156
152
|
tracker: NexusTrackerClient;
|
|
157
153
|
nexus: Nexus;
|
|
158
154
|
}
|
|
155
|
+
declare const NexusContext: React.Context<NexusContextValue | null>;
|
|
159
156
|
/**
|
|
160
157
|
* Props for the NexusProvider component.
|
|
161
158
|
*/
|
|
162
159
|
interface NexusProviderProps extends NexusInitOptions {
|
|
163
160
|
/** Child components that will have access to Nexus context. */
|
|
164
|
-
children?: React.ReactNode
|
|
161
|
+
children?: React.ReactNode;
|
|
165
162
|
}
|
|
166
163
|
/**
|
|
167
164
|
* React Context Provider that initializes the Nexus SDK and makes it available
|
|
@@ -194,11 +191,6 @@ declare function NexusProvider({ children, ...initOptions }: NexusProviderProps)
|
|
|
194
191
|
*
|
|
195
192
|
* @returns Object with `flags`, `tracker`, and `nexus` properties.
|
|
196
193
|
* @throws {Error} If called outside of a `NexusProvider`.
|
|
197
|
-
*
|
|
198
|
-
* @example
|
|
199
|
-
* const { flags, tracker } = useNexus();
|
|
200
|
-
* const enabled = flags.isEnabled('checkout_v2');
|
|
201
|
-
* tracker.captureError(new Error('something failed'));
|
|
202
194
|
*/
|
|
203
195
|
declare function useNexus(): NexusContextValue;
|
|
204
196
|
/**
|
|
@@ -209,14 +201,6 @@ interface UseFlagResult {
|
|
|
209
201
|
enabled: boolean;
|
|
210
202
|
/**
|
|
211
203
|
* Retrieves a specific variant value from this flag.
|
|
212
|
-
*
|
|
213
|
-
* @param variantKey - Property name inside the variants object.
|
|
214
|
-
* @param defaultValue - Fallback if variant is not found.
|
|
215
|
-
* @returns Typed variant value.
|
|
216
|
-
*
|
|
217
|
-
* @example
|
|
218
|
-
* const { enabled, getVariant } = useFlag('promo_banner_v2');
|
|
219
|
-
* const rate = getVariant<number>('discount_rate', 10);
|
|
220
204
|
*/
|
|
221
205
|
getVariant: <T = unknown>(variantKey: string, defaultValue?: T) => T;
|
|
222
206
|
/** Full evaluation result including reason and version. */
|
|
@@ -224,26 +208,77 @@ interface UseFlagResult {
|
|
|
224
208
|
}
|
|
225
209
|
/**
|
|
226
210
|
* React hook for subscribing to a feature flag's real-time state.
|
|
211
|
+
*/
|
|
212
|
+
declare function useFlag(key: string, defaultEnabled?: boolean): UseFlagResult;
|
|
213
|
+
interface NexusGuardProps {
|
|
214
|
+
/** Component tree to wrap with error boundary protection. */
|
|
215
|
+
children?: React.ReactNode;
|
|
216
|
+
/**
|
|
217
|
+
* Custom fallback UI to display on crash.
|
|
218
|
+
* Can be a static ReactNode or a render function receiving `NexusErrorInfo`.
|
|
219
|
+
*/
|
|
220
|
+
fallback?: React.ReactNode | ((info: NexusErrorInfo) => React.ReactNode);
|
|
221
|
+
/** Callback fired immediately when an error is caught. */
|
|
222
|
+
onError?: (error: Error, info: NexusErrorInfo) => void;
|
|
223
|
+
/** Automatically resets the error boundary on browser navigation / popstate. */
|
|
224
|
+
resetOnNavigation?: boolean;
|
|
225
|
+
/** Array of values that will trigger an automatic reset when changed. */
|
|
226
|
+
resetKeys?: unknown[];
|
|
227
|
+
/** Custom tags attached to errors captured by this guard. */
|
|
228
|
+
tags?: Record<string, string>;
|
|
229
|
+
/** Optional custom tracker instance. Defaults to context or singleton. */
|
|
230
|
+
tracker?: NexusTrackerClient;
|
|
231
|
+
}
|
|
232
|
+
interface NexusGuardState {
|
|
233
|
+
hasError: boolean;
|
|
234
|
+
errorInfo: NexusErrorInfo | null;
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* React Error Boundary component powered by Nexus telemetry.
|
|
238
|
+
* Prevents whole-app white-screen crashes, sends crash telemetry with component stack traces,
|
|
239
|
+
* and allows customized graceful degradation.
|
|
227
240
|
*
|
|
228
|
-
*
|
|
229
|
-
*
|
|
230
|
-
*
|
|
231
|
-
*
|
|
232
|
-
*
|
|
233
|
-
*
|
|
241
|
+
* @example
|
|
242
|
+
* // Global app guard with custom render fallback
|
|
243
|
+
* <NexusGuard fallback={({ error, errorId, reset }) => (
|
|
244
|
+
* <div className="error-card">
|
|
245
|
+
* <h2>Something went wrong</h2>
|
|
246
|
+
* <p>Support Reference: {errorId}</p>
|
|
247
|
+
* <button onClick={reset}>Try Again</button>
|
|
248
|
+
* </div>
|
|
249
|
+
* )}>
|
|
250
|
+
* <App />
|
|
251
|
+
* </NexusGuard>
|
|
234
252
|
*
|
|
235
253
|
* @example
|
|
236
|
-
*
|
|
237
|
-
*
|
|
238
|
-
*
|
|
254
|
+
* // Isolated widget guard (e.g. sidebar or checkout card)
|
|
255
|
+
* <NexusGuard fallback={<p>Sidebar temporarily unavailable.</p>}>
|
|
256
|
+
* <Sidebar />
|
|
257
|
+
* </NexusGuard>
|
|
258
|
+
*/
|
|
259
|
+
declare class NexusGuard extends React.Component<NexusGuardProps, NexusGuardState> {
|
|
260
|
+
static contextType: React.Context<NexusContextValue | null>;
|
|
261
|
+
context: React.ContextType<typeof NexusContext>;
|
|
262
|
+
constructor(props: NexusGuardProps);
|
|
263
|
+
static getDerivedStateFromError(error: Error): Partial<NexusGuardState>;
|
|
264
|
+
componentDidCatch(error: Error, reactInfo: React.ErrorInfo): void;
|
|
265
|
+
componentDidMount(): void;
|
|
266
|
+
componentDidUpdate(prevProps: NexusGuardProps): void;
|
|
267
|
+
componentWillUnmount(): void;
|
|
268
|
+
private handleNavigation;
|
|
269
|
+
/** Resets the error boundary state back to healthy rendering. */
|
|
270
|
+
reset: () => void;
|
|
271
|
+
render(): React.ReactNode;
|
|
272
|
+
}
|
|
273
|
+
/**
|
|
274
|
+
* Higher-Order Component (HOC) variant of NexusGuard.
|
|
275
|
+
* Wraps any component with an isolated Error Boundary.
|
|
239
276
|
*
|
|
240
|
-
*
|
|
241
|
-
*
|
|
242
|
-
*
|
|
243
|
-
*
|
|
244
|
-
* );
|
|
245
|
-
* }
|
|
277
|
+
* @example
|
|
278
|
+
* const SafeSidebar = withNexusGuard(Sidebar, {
|
|
279
|
+
* fallback: <div>Sidebar error</div>,
|
|
280
|
+
* });
|
|
246
281
|
*/
|
|
247
|
-
declare function
|
|
282
|
+
declare function withNexusGuard<P extends object>(Component: React.ComponentType<P>, options?: Omit<NexusGuardProps, 'children'>): React.FC<P>;
|
|
248
283
|
|
|
249
|
-
export { NexusProvider, type NexusProviderProps, type UseFlagResult, useFlag, useNexus };
|
|
284
|
+
export { NexusContext, type NexusContextValue, NexusGuard, type NexusGuardProps, type NexusGuardState, NexusProvider, type NexusProviderProps, type UseFlagResult, useFlag, useNexus, withNexusGuard };
|
package/dist/react.d.ts
CHANGED
|
@@ -1,23 +1,34 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { UserContext, FlagEvaluationResult } from '@nexussdk/contracts';
|
|
2
|
+
import { NexusFlagsClient, NexusFlagsOptions } from '@nexussdk/flags';
|
|
3
|
+
import { NexusTrackerClient, NexusTrackerOptions } from '@nexussdk/tracker';
|
|
4
|
+
import { UserContext, SamplingConfig, TransportPlugin, PerformanceVitalsOptions, SeverityLevel, Breadcrumb, NexusErrorInfo, FlagEvaluationResult } from '@nexussdk/contracts';
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* @fileoverview Unified Nexus singleton facade wrapping Flags and Tracker clients.
|
|
8
8
|
* @module @nexus/sdk/nexus
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
+
/**
|
|
12
|
+
* Dev server routing configuration.
|
|
13
|
+
*/
|
|
14
|
+
interface NexusDevServerOptions {
|
|
15
|
+
/** Enable local dev server ingestion mode. Auto-enabled in development if true. */
|
|
16
|
+
enabled?: boolean;
|
|
17
|
+
/** Port the dev server is listening on. Defaults to 4567. */
|
|
18
|
+
port?: number;
|
|
19
|
+
/** Host the dev server is bound to. Defaults to 'localhost'. */
|
|
20
|
+
host?: string;
|
|
21
|
+
}
|
|
11
22
|
/**
|
|
12
23
|
* Unified initialization options for the Nexus SDK umbrella.
|
|
13
24
|
*
|
|
14
25
|
* @example
|
|
15
26
|
* Nexus.init({
|
|
16
27
|
* apiKey: 'pk_live_...',
|
|
17
|
-
* baseUrl: 'http://localhost:8080',
|
|
18
28
|
* user: { id: 'usr_12345', country: 'VN' },
|
|
19
29
|
* environment: 'production',
|
|
20
30
|
* autoCapture: true,
|
|
31
|
+
* devServer: { enabled: process.env.NODE_ENV === 'development' },
|
|
21
32
|
* });
|
|
22
33
|
*/
|
|
23
34
|
interface NexusInitOptions {
|
|
@@ -33,6 +44,14 @@ interface NexusInitOptions {
|
|
|
33
44
|
tags?: Record<string, string>;
|
|
34
45
|
/** Toggle automated global error capture. Defaults to true. */
|
|
35
46
|
autoCapture?: boolean;
|
|
47
|
+
/** Client-side rate limiting and deduplication sampling options. */
|
|
48
|
+
sampling?: SamplingConfig;
|
|
49
|
+
/** Pluggable transport adapter ('fetch', 'console', 'localStorage', custom fn). */
|
|
50
|
+
transport?: TransportPlugin;
|
|
51
|
+
/** Web Vitals performance observer options, or true for default observation. */
|
|
52
|
+
vitals?: boolean | PerformanceVitalsOptions;
|
|
53
|
+
/** Local dev server auto-routing configuration. */
|
|
54
|
+
devServer?: NexusDevServerOptions;
|
|
36
55
|
/** Additional flags-specific options. */
|
|
37
56
|
flags?: Partial<NexusFlagsOptions>;
|
|
38
57
|
/** Additional tracker-specific options. */
|
|
@@ -59,6 +78,7 @@ interface NexusInitOptions {
|
|
|
59
78
|
*/
|
|
60
79
|
declare class Nexus {
|
|
61
80
|
private static instance;
|
|
81
|
+
private static vitalsCleanup;
|
|
62
82
|
/** The underlying feature flags client instance. */
|
|
63
83
|
readonly flags: NexusFlagsClient;
|
|
64
84
|
/** The underlying error tracker client instance. */
|
|
@@ -71,9 +91,6 @@ declare class Nexus {
|
|
|
71
91
|
*
|
|
72
92
|
* @param options - SDK configuration options.
|
|
73
93
|
* @returns The initialized Nexus singleton instance.
|
|
74
|
-
*
|
|
75
|
-
* @example
|
|
76
|
-
* const nexus = Nexus.init({ apiKey: 'pk_live_...' });
|
|
77
94
|
*/
|
|
78
95
|
static init(options?: NexusInitOptions): Nexus;
|
|
79
96
|
/**
|
|
@@ -81,73 +98,52 @@ declare class Nexus {
|
|
|
81
98
|
*
|
|
82
99
|
* @returns The active Nexus instance.
|
|
83
100
|
* @throws {Error} If `Nexus.init()` has not been called yet.
|
|
84
|
-
*
|
|
85
|
-
* @example
|
|
86
|
-
* const nexus = Nexus.getInstance();
|
|
87
|
-
* nexus.flags.isEnabled('checkout_v2');
|
|
88
101
|
*/
|
|
89
102
|
static getInstance(): Nexus;
|
|
90
103
|
/**
|
|
91
104
|
* Convenience method: Check if a feature flag is enabled.
|
|
92
|
-
*
|
|
93
|
-
* @param key - Flag identifier.
|
|
94
|
-
* @param defaultValue - Fallback if flag is missing.
|
|
95
|
-
* @returns Boolean enabled state.
|
|
96
|
-
*
|
|
97
|
-
* @example
|
|
98
|
-
* if (Nexus.isEnabled('checkout_redesign')) { ... }
|
|
99
105
|
*/
|
|
100
106
|
static isEnabled(key: string, defaultValue?: boolean): boolean;
|
|
101
107
|
/**
|
|
102
108
|
* Convenience method: Get a flag variant value.
|
|
103
|
-
*
|
|
104
|
-
* @param key - Flag identifier.
|
|
105
|
-
* @param variantKey - Variant property name.
|
|
106
|
-
* @param defaultValue - Fallback value.
|
|
107
|
-
* @returns Variant value cast to type T.
|
|
108
|
-
*
|
|
109
|
-
* @example
|
|
110
|
-
* const rate = Nexus.getVariant<number>('promo_banner_v2', 'discount_rate', 10);
|
|
111
109
|
*/
|
|
112
110
|
static getVariant<T = unknown>(key: string, variantKey: string, defaultValue?: T): T;
|
|
113
111
|
/**
|
|
114
112
|
* Convenience method: Capture an error manually.
|
|
115
|
-
*
|
|
116
|
-
* @param error - Error instance, string, or unknown value.
|
|
117
|
-
* @param extra - Optional metadata tags.
|
|
118
|
-
*
|
|
119
|
-
* @example
|
|
120
|
-
* Nexus.captureError(new TypeError('Cannot read properties of null'));
|
|
121
113
|
*/
|
|
122
114
|
static captureError(error: unknown, extra?: Record<string, unknown>): void;
|
|
115
|
+
/**
|
|
116
|
+
* Convenience method: Capture an informational or warning message event.
|
|
117
|
+
*/
|
|
118
|
+
static captureMessage(message: string, level?: SeverityLevel, extra?: Record<string, unknown>): void;
|
|
119
|
+
/**
|
|
120
|
+
* Convenience method: Add a breadcrumb manually.
|
|
121
|
+
*/
|
|
122
|
+
static addBreadcrumb(breadcrumb: Breadcrumb): void;
|
|
123
|
+
/**
|
|
124
|
+
* Convenience method: Set extra contextual metadata.
|
|
125
|
+
*/
|
|
126
|
+
static setExtra(key: string, value: unknown): void;
|
|
127
|
+
/**
|
|
128
|
+
* Convenience method: Attach Web Vitals observer to tracker.
|
|
129
|
+
*/
|
|
130
|
+
static attachWebVitals(options?: PerformanceVitalsOptions): () => void;
|
|
123
131
|
/**
|
|
124
132
|
* Convenience method: Update user context for both flags and tracker.
|
|
125
|
-
*
|
|
126
|
-
* @param user - New user context (merged with existing).
|
|
127
|
-
* @returns Promise resolving after flags refresh.
|
|
128
|
-
*
|
|
129
|
-
* @example
|
|
130
|
-
* await Nexus.identify({ id: 'usr_12345', country: 'VN' });
|
|
131
133
|
*/
|
|
132
134
|
static identify(user: UserContext): Promise<void>;
|
|
133
135
|
/**
|
|
134
136
|
* Resets user context to anonymous state (e.g. on logout).
|
|
135
|
-
*
|
|
136
|
-
* @example
|
|
137
|
-
* Nexus.reset(); // called on user logout
|
|
138
137
|
*/
|
|
139
138
|
static reset(): void;
|
|
140
139
|
/**
|
|
141
140
|
* Gracefully tears down both clients, closing SSE connections and flushing pending events.
|
|
142
|
-
*
|
|
143
|
-
* @example
|
|
144
|
-
* await Nexus.destroy();
|
|
145
141
|
*/
|
|
146
142
|
static destroy(): Promise<void>;
|
|
147
143
|
}
|
|
148
144
|
|
|
149
145
|
/**
|
|
150
|
-
* @fileoverview React Context Provider and
|
|
146
|
+
* @fileoverview React Context Provider, hooks, and Error Boundary for the Nexus SDK.
|
|
151
147
|
* @module @nexus/sdk/react
|
|
152
148
|
*/
|
|
153
149
|
|
|
@@ -156,12 +152,13 @@ interface NexusContextValue {
|
|
|
156
152
|
tracker: NexusTrackerClient;
|
|
157
153
|
nexus: Nexus;
|
|
158
154
|
}
|
|
155
|
+
declare const NexusContext: React.Context<NexusContextValue | null>;
|
|
159
156
|
/**
|
|
160
157
|
* Props for the NexusProvider component.
|
|
161
158
|
*/
|
|
162
159
|
interface NexusProviderProps extends NexusInitOptions {
|
|
163
160
|
/** Child components that will have access to Nexus context. */
|
|
164
|
-
children?: React.ReactNode
|
|
161
|
+
children?: React.ReactNode;
|
|
165
162
|
}
|
|
166
163
|
/**
|
|
167
164
|
* React Context Provider that initializes the Nexus SDK and makes it available
|
|
@@ -194,11 +191,6 @@ declare function NexusProvider({ children, ...initOptions }: NexusProviderProps)
|
|
|
194
191
|
*
|
|
195
192
|
* @returns Object with `flags`, `tracker`, and `nexus` properties.
|
|
196
193
|
* @throws {Error} If called outside of a `NexusProvider`.
|
|
197
|
-
*
|
|
198
|
-
* @example
|
|
199
|
-
* const { flags, tracker } = useNexus();
|
|
200
|
-
* const enabled = flags.isEnabled('checkout_v2');
|
|
201
|
-
* tracker.captureError(new Error('something failed'));
|
|
202
194
|
*/
|
|
203
195
|
declare function useNexus(): NexusContextValue;
|
|
204
196
|
/**
|
|
@@ -209,14 +201,6 @@ interface UseFlagResult {
|
|
|
209
201
|
enabled: boolean;
|
|
210
202
|
/**
|
|
211
203
|
* Retrieves a specific variant value from this flag.
|
|
212
|
-
*
|
|
213
|
-
* @param variantKey - Property name inside the variants object.
|
|
214
|
-
* @param defaultValue - Fallback if variant is not found.
|
|
215
|
-
* @returns Typed variant value.
|
|
216
|
-
*
|
|
217
|
-
* @example
|
|
218
|
-
* const { enabled, getVariant } = useFlag('promo_banner_v2');
|
|
219
|
-
* const rate = getVariant<number>('discount_rate', 10);
|
|
220
204
|
*/
|
|
221
205
|
getVariant: <T = unknown>(variantKey: string, defaultValue?: T) => T;
|
|
222
206
|
/** Full evaluation result including reason and version. */
|
|
@@ -224,26 +208,77 @@ interface UseFlagResult {
|
|
|
224
208
|
}
|
|
225
209
|
/**
|
|
226
210
|
* React hook for subscribing to a feature flag's real-time state.
|
|
211
|
+
*/
|
|
212
|
+
declare function useFlag(key: string, defaultEnabled?: boolean): UseFlagResult;
|
|
213
|
+
interface NexusGuardProps {
|
|
214
|
+
/** Component tree to wrap with error boundary protection. */
|
|
215
|
+
children?: React.ReactNode;
|
|
216
|
+
/**
|
|
217
|
+
* Custom fallback UI to display on crash.
|
|
218
|
+
* Can be a static ReactNode or a render function receiving `NexusErrorInfo`.
|
|
219
|
+
*/
|
|
220
|
+
fallback?: React.ReactNode | ((info: NexusErrorInfo) => React.ReactNode);
|
|
221
|
+
/** Callback fired immediately when an error is caught. */
|
|
222
|
+
onError?: (error: Error, info: NexusErrorInfo) => void;
|
|
223
|
+
/** Automatically resets the error boundary on browser navigation / popstate. */
|
|
224
|
+
resetOnNavigation?: boolean;
|
|
225
|
+
/** Array of values that will trigger an automatic reset when changed. */
|
|
226
|
+
resetKeys?: unknown[];
|
|
227
|
+
/** Custom tags attached to errors captured by this guard. */
|
|
228
|
+
tags?: Record<string, string>;
|
|
229
|
+
/** Optional custom tracker instance. Defaults to context or singleton. */
|
|
230
|
+
tracker?: NexusTrackerClient;
|
|
231
|
+
}
|
|
232
|
+
interface NexusGuardState {
|
|
233
|
+
hasError: boolean;
|
|
234
|
+
errorInfo: NexusErrorInfo | null;
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* React Error Boundary component powered by Nexus telemetry.
|
|
238
|
+
* Prevents whole-app white-screen crashes, sends crash telemetry with component stack traces,
|
|
239
|
+
* and allows customized graceful degradation.
|
|
227
240
|
*
|
|
228
|
-
*
|
|
229
|
-
*
|
|
230
|
-
*
|
|
231
|
-
*
|
|
232
|
-
*
|
|
233
|
-
*
|
|
241
|
+
* @example
|
|
242
|
+
* // Global app guard with custom render fallback
|
|
243
|
+
* <NexusGuard fallback={({ error, errorId, reset }) => (
|
|
244
|
+
* <div className="error-card">
|
|
245
|
+
* <h2>Something went wrong</h2>
|
|
246
|
+
* <p>Support Reference: {errorId}</p>
|
|
247
|
+
* <button onClick={reset}>Try Again</button>
|
|
248
|
+
* </div>
|
|
249
|
+
* )}>
|
|
250
|
+
* <App />
|
|
251
|
+
* </NexusGuard>
|
|
234
252
|
*
|
|
235
253
|
* @example
|
|
236
|
-
*
|
|
237
|
-
*
|
|
238
|
-
*
|
|
254
|
+
* // Isolated widget guard (e.g. sidebar or checkout card)
|
|
255
|
+
* <NexusGuard fallback={<p>Sidebar temporarily unavailable.</p>}>
|
|
256
|
+
* <Sidebar />
|
|
257
|
+
* </NexusGuard>
|
|
258
|
+
*/
|
|
259
|
+
declare class NexusGuard extends React.Component<NexusGuardProps, NexusGuardState> {
|
|
260
|
+
static contextType: React.Context<NexusContextValue | null>;
|
|
261
|
+
context: React.ContextType<typeof NexusContext>;
|
|
262
|
+
constructor(props: NexusGuardProps);
|
|
263
|
+
static getDerivedStateFromError(error: Error): Partial<NexusGuardState>;
|
|
264
|
+
componentDidCatch(error: Error, reactInfo: React.ErrorInfo): void;
|
|
265
|
+
componentDidMount(): void;
|
|
266
|
+
componentDidUpdate(prevProps: NexusGuardProps): void;
|
|
267
|
+
componentWillUnmount(): void;
|
|
268
|
+
private handleNavigation;
|
|
269
|
+
/** Resets the error boundary state back to healthy rendering. */
|
|
270
|
+
reset: () => void;
|
|
271
|
+
render(): React.ReactNode;
|
|
272
|
+
}
|
|
273
|
+
/**
|
|
274
|
+
* Higher-Order Component (HOC) variant of NexusGuard.
|
|
275
|
+
* Wraps any component with an isolated Error Boundary.
|
|
239
276
|
*
|
|
240
|
-
*
|
|
241
|
-
*
|
|
242
|
-
*
|
|
243
|
-
*
|
|
244
|
-
* );
|
|
245
|
-
* }
|
|
277
|
+
* @example
|
|
278
|
+
* const SafeSidebar = withNexusGuard(Sidebar, {
|
|
279
|
+
* fallback: <div>Sidebar error</div>,
|
|
280
|
+
* });
|
|
246
281
|
*/
|
|
247
|
-
declare function
|
|
282
|
+
declare function withNexusGuard<P extends object>(Component: React.ComponentType<P>, options?: Omit<NexusGuardProps, 'children'>): React.FC<P>;
|
|
248
283
|
|
|
249
|
-
export { NexusProvider, type NexusProviderProps, type UseFlagResult, useFlag, useNexus };
|
|
284
|
+
export { NexusContext, type NexusContextValue, NexusGuard, type NexusGuardProps, type NexusGuardState, NexusProvider, type NexusProviderProps, type UseFlagResult, useFlag, useNexus, withNexusGuard };
|
package/dist/react.mjs
CHANGED
|
@@ -1 +1,2 @@
|
|
|
1
|
-
import
|
|
1
|
+
import i,{createContext,useRef,useMemo,useEffect,useContext,useState}from'react';import {NexusFlagsClient}from'@nexussdk/flags';import {parseStackTrace,computeFingerprint,NexusTrackerClient,attachWebVitals}from'@nexussdk/tracker';var l=class t{static instance=null;static vitalsCleanup=null;flags;tracker;constructor(e={}){let{apiKey:r,baseUrl:n,user:s,environment:a,tags:o,autoCapture:u,sampling:m,transport:d,vitals:c,devServer:p,flags:y,tracker:C}=e;this.flags=new NexusFlagsClient({apiKey:r,baseUrl:n,user:s,...y});let N=n,R=d;if(p?.enabled){let f=p.port??4567;N=`http://${p.host??"localhost"}:${f}`;}if(this.tracker=new NexusTrackerClient({apiKey:r,baseUrl:N,environment:a,tags:o,autoCapture:u,sampling:m,transport:R,...C}),c){let f=typeof c=="object"?c:{};t.vitalsCleanup=attachWebVitals(this.tracker,f);}}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,n){return t.getInstance().flags.getVariant(e,r,n)}static captureError(e,r){t.getInstance().tracker.captureError(e,r);}static captureMessage(e,r="info",n){t.getInstance().tracker.captureMessage(e,r,n);}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);}};var g=createContext(null);function M({children:t,...e}){let r=useRef(null);r.current||(r.current=l.init(e));let n=useMemo(()=>({flags:r.current.flags,tracker:r.current.tracker,nexus:r.current}),[]);return useEffect(()=>()=>{l.destroy();},[]),i.createElement(g.Provider,{value:n},t)}function O(){let t=useContext(g);if(!t)throw new Error("[Nexus SDK] useNexus() must be called inside a <NexusProvider>.");return t}function W(t,e=false){let{flags:r}=O(),[n,s]=useState(()=>r.isEnabled(t)!==e?{key:t,enabled:r.isEnabled(t),variants:{},reason:"DEFAULT_ENABLED",version:0}:null);return useEffect(()=>{let a=r.isEnabled(t,e);return a!==(n?.enabled??e)&&s({key:t,enabled:a,variants:{},reason:"DEFAULT_ENABLED",version:0}),r.onFlagChange(t,u=>{s(u);})},[t]),useMemo(()=>({enabled:n?.enabled??r.isEnabled(t,e),getVariant:(a,o)=>r.getVariant(t,a,o),result:n}),[n,t,r,e])}var x=class extends i.Component{static contextType=g;constructor(e){super(e),this.state={hasError:false,errorInfo:null};}static getDerivedStateFromError(e){let r=parseStackTrace(e.stack),s=`NX-${computeFingerprint(e.name,e.message,r[0]).replace(/^fp_/,"").slice(0,8).toUpperCase()}`;return {hasError:true,errorInfo:{error:e,errorId:s,reset:()=>{}}}}componentDidCatch(e,r){let{onError:n,tags:s,tracker:a}=this.props,o=a;if(!o&&this.context?.tracker&&(o=this.context.tracker),!o)try{o=l.getInstance().tracker;}catch{}let u=parseStackTrace(e.stack),d=`NX-${computeFingerprint(e.name,e.message,u[0]).replace(/^fp_/,"").slice(0,8).toUpperCase()}`,c={error:e,errorId:d,componentStack:r.componentStack??void 0,reset:()=>this.reset()};if(this.setState({errorInfo:c}),o&&o.captureError(e,{tags:{...s,guardErrorId:d},componentStack:r.componentStack??void 0}),n)try{n(e,c);}catch(p){typeof console<"u"&&console.warn("[NexusGuard] onError handler threw:",p);}}componentDidMount(){this.props.resetOnNavigation&&typeof window<"u"&&window.addEventListener("popstate",this.handleNavigation);}componentDidUpdate(e){let{resetKeys:r}=this.props;this.state.hasError&&r&&e.resetKeys&&r.some((s,a)=>s!==e.resetKeys[a])&&this.reset();}componentWillUnmount(){this.props.resetOnNavigation&&typeof window<"u"&&window.removeEventListener("popstate",this.handleNavigation);}handleNavigation=()=>{this.state.hasError&&this.reset();};reset=()=>{this.setState({hasError:false,errorInfo:null});};render(){let{hasError:e,errorInfo:r}=this.state,{fallback:n,children:s}=this.props;return e&&r?typeof n=="function"?n(r):n||i.createElement("div",{role:"alert",style:{padding:"16px 20px",margin:"12px 0",borderRadius:"8px",backgroundColor:"#1f1315",border:"1px solid #7f1d1d",color:"#fca5a5",fontFamily:"-apple-system, BlinkMacSystemFont, Segoe UI, Roboto, sans-serif",fontSize:"14px",lineHeight:1.5}},i.createElement("div",{style:{display:"flex",alignItems:"center",justifyContent:"space-between",marginBottom:"8px"}},i.createElement("strong",{style:{color:"#ef4444"}},"Application Error Encountered"),i.createElement("span",{style:{fontSize:"12px",fontFamily:"monospace",background:"#450a0a",padding:"2px 8px",borderRadius:"4px"}},r.errorId)),i.createElement("p",{style:{margin:"0 0 12px 0",color:"#fecaca"}},r.error.message),i.createElement("button",{type:"button",onClick:()=>this.reset(),style:{background:"#b91c1c",border:"none",color:"#fff",padding:"6px 14px",borderRadius:"6px",fontSize:"13px",cursor:"pointer",fontWeight:500}},"Retry")):s}};function $(t,e={}){let r=s=>i.createElement(x,e,i.createElement(t,s)),n=t.displayName||t.name||"Component";return r.displayName=`withNexusGuard(${n})`,r}
|
|
2
|
+
export{g as NexusContext,x as NexusGuard,M as NexusProvider,W as useFlag,O as useNexus,$ as withNexusGuard};
|
package/dist/vue.cjs
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
'use strict';var vue=require('vue'),flags=require('@nexussdk/flags'),tracker=require('@nexussdk/tracker');var p=class e{static instance=null;static vitalsCleanup=null;flags;tracker;constructor(t={}){let{apiKey:n,baseUrl:r,user:o,environment:a,tags:s,autoCapture:i,sampling:f,transport:x,vitals:l,devServer:c,flags:g,tracker:C}=t;this.flags=new flags.NexusFlagsClient({apiKey:n,baseUrl:r,user:o,...g});let m=r,I=x;if(c?.enabled){let d=c.port??4567;m=`http://${c.host??"localhost"}:${d}`;}if(this.tracker=new tracker.NexusTrackerClient({apiKey:n,baseUrl:m,environment:a,tags:s,autoCapture:i,sampling:f,transport:I,...C}),l){let d=typeof l=="object"?l:{};e.vitalsCleanup=tracker.attachWebVitals(this.tracker,d);}}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,n=false){return e.getInstance().flags.isEnabled(t,n)}static getVariant(t,n,r){return e.getInstance().flags.getVariant(t,n,r)}static captureError(t,n){e.getInstance().tracker.captureError(t,n);}static captureMessage(t,n="info",r){e.getInstance().tracker.captureMessage(t,n,r);}static addBreadcrumb(t){e.getInstance().tracker.addBreadcrumb(t);}static setExtra(t,n){e.getInstance().tracker.setExtra(t,n);}static attachWebVitals(t){return tracker.attachWebVitals(e.getInstance().tracker,t)}static async identify(t){let n=e.getInstance();n.tracker.setUser(t),await n.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);}};var y=Symbol("NexusSDK"),H={install(e,t={}){let n=p.init(t),r={flags:n.flags,tracker:n.tracker,nexus:n};e.provide(y,r);let o=e.config.errorHandler;e.config.errorHandler=(a,s,i)=>{n.tracker.captureError(a,{componentStack:i,tags:{framework:"vue3",component:s?.$options?.name||"AnonymousComponent"}}),o&&o(a,s,i);},e.component("NexusGuard",R);}};function N(){let e=vue.inject(y);if(e)return e;let t=p.getInstance();return {flags:t.flags,tracker:t.tracker,nexus:t}}function A(e,t=false){let{flags:n}=N(),r=vue.ref(n.isEnabled(e,t)),o=vue.shallowRef(null),a=n.onFlagChange(e,s=>{r.value=s.enabled,o.value=s;});return vue.onUnmounted(()=>{a();}),{enabled:r,getVariant:(s,i)=>n.getVariant(e,s,i),result:o}}var R=vue.defineComponent({name:"NexusGuard",props:{onError:{type:Function,default:void 0},tags:{type:Object,default:()=>({})}},setup(e,{slots:t}){let n=vue.ref(false),r=vue.shallowRef(null),o=()=>{n.value=false,r.value=null;};return vue.onErrorCaptured((a,s,i)=>{let f=tracker.parseStackTrace(a.stack),l=`NX-${tracker.computeFingerprint(a.name,a.message,f[0]).replace(/^fp_/,"").slice(0,8).toUpperCase()}`,c={error:a,errorId:l,componentStack:i,reset:o};n.value=true,r.value=c;try{let{tracker:g}=N();g.captureError(a,{componentStack:i,tags:{...e.tags,guardErrorId:l,framework:"vue3",component:s?.$options?.name||"AnonymousComponent"}});}catch{}if(e.onError)try{e.onError(a,c);}catch{}return false}),()=>n.value&&r.value?t.fallback?t.fallback(r.value):vue.h("div",{role:"alert",style:{padding:"16px 20px",margin:"12px 0",borderRadius:"8px",backgroundColor:"#1f1315",border:"1px solid #7f1d1d",color:"#fca5a5",fontFamily:"-apple-system, BlinkMacSystemFont, Segoe UI, Roboto, sans-serif",fontSize:"14px"}},[vue.h("div",{style:{display:"flex",justifyContent:"space-between",marginBottom:"8px"}},[vue.h("strong",{style:{color:"#ef4444"}},"Vue Component Crash Protected"),vue.h("span",{style:{fontFamily:"monospace",fontSize:"12px",background:"#450a0a",padding:"2px 8px",borderRadius:"4px"}},r.value.errorId)]),vue.h("p",{style:{margin:"0 0 12px 0",color:"#fecaca"}},r.value.error.message),vue.h("button",{type:"button",onClick:o,style:{background:"#b91c1c",border:"none",color:"#fff",padding:"6px 14px",borderRadius:"6px",fontSize:"13px",cursor:"pointer",fontWeight:"500"}},"Retry Component")]):t.default?t.default():null}});
|
|
2
|
+
exports.NEXUS_KEY=y;exports.NexusGuardVue=R;exports.NexusPlugin=H;exports.useFlag=A;exports.useNexus=N;
|