@vylth/nexus-react 1.0.0 → 1.0.1

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,110 @@
1
+ # @vylth/nexus-react
2
+
3
+ Nexus SSO SDK for React — add authentication to any VYLTH app in minutes.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm install @vylth/nexus-react
9
+ ```
10
+
11
+ ## Quick Start
12
+
13
+ ### 1. Wrap with NexusProvider
14
+
15
+ ```tsx
16
+ import { BrowserRouter } from 'react-router-dom';
17
+ import { NexusProvider } from '@vylth/nexus-react';
18
+
19
+ ReactDOM.createRoot(document.getElementById('root')!).render(
20
+ <BrowserRouter>
21
+ <NexusProvider
22
+ nexusUrl="https://accounts.vylth.com"
23
+ clientId="YOUR_CLIENT_ID"
24
+ >
25
+ <App />
26
+ </NexusProvider>
27
+ </BrowserRouter>
28
+ );
29
+ ```
30
+
31
+ ### 2. Add Callback Route
32
+
33
+ ```tsx
34
+ import { NexusCallback } from '@vylth/nexus-react';
35
+
36
+ <Route path="/auth/callback" element={
37
+ <NexusCallback
38
+ onSuccess={() => window.location.replace('/')}
39
+ onError={() => window.location.replace('/login')}
40
+ />
41
+ } />
42
+ ```
43
+
44
+ The callback route must match the redirect URI registered in Nexus.
45
+
46
+ ### 3. Use Auth in Components
47
+
48
+ ```tsx
49
+ import { useNexus } from '@vylth/nexus-react';
50
+
51
+ function MyComponent() {
52
+ const { user, isAuthenticated, isLoading, login, logout } = useNexus();
53
+
54
+ if (isLoading) return <Spinner />;
55
+ if (!isAuthenticated) { login(); return null; }
56
+
57
+ return (
58
+ <div>
59
+ <p>Welcome, {user.username}</p>
60
+ <p>Role: {user.globalRole}</p>
61
+ <button onClick={logout}>Logout</button>
62
+ </div>
63
+ );
64
+ }
65
+ ```
66
+
67
+ ### 4. Login Button
68
+
69
+ ```tsx
70
+ import { NexusLoginButton } from '@vylth/nexus-react';
71
+
72
+ <NexusLoginButton />
73
+ ```
74
+
75
+ ## Exports
76
+
77
+ | Export | Type | Description |
78
+ |--------|------|-------------|
79
+ | `NexusProvider` | Component | Context provider — wrap your app |
80
+ | `NexusCallback` | Component | OAuth callback handler |
81
+ | `NexusLoginButton` | Component | Pre-styled login button |
82
+ | `useNexus` | Hook | Access auth state and user info |
83
+
84
+ ## User Fields
85
+
86
+ ```typescript
87
+ interface NexusUser {
88
+ id: string;
89
+ email: string;
90
+ username?: string;
91
+ firstName: string;
92
+ lastName: string;
93
+ avatar?: string;
94
+ globalRole: string; // citizen, pioneer, navigator, commander, executor
95
+ emailVerified: boolean;
96
+ affiliateCode?: string;
97
+ appRoles?: Record<string, string>;
98
+ twoFactorVerified?: boolean;
99
+ }
100
+ ```
101
+
102
+ ## Prerequisites
103
+
104
+ 1. Register your app in Nexus (accounts.vylth.com → Admin → Manage Apps)
105
+ 2. Save the `client_id` from registration
106
+ 3. Set your redirect URI (e.g., `https://yourapp.vylth.com/auth/callback`)
107
+
108
+ ## Full Docs
109
+
110
+ See the complete integration guide at [accounts.vylth.com/docs](https://accounts.vylth.com/docs)
package/dist/index.js CHANGED
@@ -285,8 +285,10 @@ var import_jsx_runtime2 = require("react/jsx-runtime");
285
285
  function NexusCallback({ onSuccess, onError }) {
286
286
  const config = (0, import_react2.useContext)(NexusConfigContext);
287
287
  const [error, setError] = (0, import_react2.useState)("");
288
+ const processed = (0, import_react2.useRef)(false);
288
289
  (0, import_react2.useEffect)(() => {
289
- if (!config) return;
290
+ if (!config || processed.current) return;
291
+ processed.current = true;
290
292
  const params = new URLSearchParams(window.location.search);
291
293
  const code = params.get("code");
292
294
  const state = params.get("state");
@@ -339,11 +341,29 @@ function NexusCallback({ onSuccess, onError }) {
339
341
  )
340
342
  ] });
341
343
  }
342
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { display: "flex", alignItems: "center", justifyContent: "center", minHeight: "100vh", background: "#0a0a0a", color: "#fff" }, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { textAlign: "center" }, children: [
343
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { width: "32px", height: "32px", border: "3px solid rgba(255,255,255,0.1)", borderTopColor: "#d4a574", borderRadius: "50%", animation: "nexus-spin 0.8s linear infinite", margin: "0 auto 16px" } }),
344
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { style: { fontFamily: "monospace", fontSize: "14px", opacity: 0.6 }, children: "Authenticating with Nexus..." }),
344
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "center", minHeight: "100vh", background: "#0a0a0a", color: "#fff", gap: "20px" }, children: [
345
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { width: "48px", height: "48px", animation: "nexus-spin 2s linear infinite" }, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("svg", { viewBox: "0 0 64 64", fill: "none", xmlns: "http://www.w3.org/2000/svg", style: { width: "100%", height: "100%" }, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("g", { stroke: "rgba(255,255,255,0.5)", strokeWidth: "2", strokeLinecap: "round", children: [
346
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("circle", { cx: "32", cy: "32", r: "4", fill: "rgba(255,255,255,0.7)" }),
347
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("circle", { cx: "32", cy: "12", r: "3", fill: "rgba(255,255,255,0.5)" }),
348
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("line", { x1: "32", y1: "15", x2: "32", y2: "28", opacity: "0.4" }),
349
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("circle", { cx: "32", cy: "52", r: "3", fill: "rgba(255,255,255,0.5)" }),
350
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("line", { x1: "32", y1: "36", x2: "32", y2: "49", opacity: "0.4" }),
351
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("circle", { cx: "12", cy: "32", r: "3", fill: "rgba(255,255,255,0.5)" }),
352
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("line", { x1: "15", y1: "32", x2: "28", y2: "32", opacity: "0.4" }),
353
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("circle", { cx: "52", cy: "32", r: "3", fill: "rgba(255,255,255,0.5)" }),
354
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("line", { x1: "36", y1: "32", x2: "49", y2: "32", opacity: "0.4" }),
355
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("circle", { cx: "18", cy: "18", r: "2.5", fill: "rgba(255,255,255,0.35)" }),
356
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("line", { x1: "20", y1: "20", x2: "29", y2: "29", opacity: "0.25" }),
357
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("circle", { cx: "46", cy: "18", r: "2.5", fill: "rgba(255,255,255,0.35)" }),
358
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("line", { x1: "44", y1: "20", x2: "35", y2: "29", opacity: "0.25" }),
359
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("circle", { cx: "18", cy: "46", r: "2.5", fill: "rgba(255,255,255,0.35)" }),
360
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("line", { x1: "20", y1: "44", x2: "29", y2: "35", opacity: "0.25" }),
361
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("circle", { cx: "46", cy: "46", r: "2.5", fill: "rgba(255,255,255,0.35)" }),
362
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("line", { x1: "44", y1: "44", x2: "35", y2: "35", opacity: "0.25" })
363
+ ] }) }) }),
364
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { style: { fontFamily: "monospace", fontSize: "14px", opacity: 0.5 }, children: "Authenticating with Nexus..." }),
345
365
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("style", { children: `@keyframes nexus-spin { to { transform: rotate(360deg) } }` })
346
- ] }) });
366
+ ] });
347
367
  }
348
368
 
349
369
  // src/NexusLoginButton.tsx
package/dist/index.mjs CHANGED
@@ -243,13 +243,15 @@ function NexusProvider({
243
243
  var NexusConfigContext = createContext(null);
244
244
 
245
245
  // src/NexusCallback.tsx
246
- import { useContext, useEffect as useEffect2, useState as useState2 } from "react";
246
+ import { useContext, useEffect as useEffect2, useRef, useState as useState2 } from "react";
247
247
  import { jsx as jsx2, jsxs } from "react/jsx-runtime";
248
248
  function NexusCallback({ onSuccess, onError }) {
249
249
  const config = useContext(NexusConfigContext);
250
250
  const [error, setError] = useState2("");
251
+ const processed = useRef(false);
251
252
  useEffect2(() => {
252
- if (!config) return;
253
+ if (!config || processed.current) return;
254
+ processed.current = true;
253
255
  const params = new URLSearchParams(window.location.search);
254
256
  const code = params.get("code");
255
257
  const state = params.get("state");
@@ -302,11 +304,29 @@ function NexusCallback({ onSuccess, onError }) {
302
304
  )
303
305
  ] });
304
306
  }
305
- return /* @__PURE__ */ jsx2("div", { style: { display: "flex", alignItems: "center", justifyContent: "center", minHeight: "100vh", background: "#0a0a0a", color: "#fff" }, children: /* @__PURE__ */ jsxs("div", { style: { textAlign: "center" }, children: [
306
- /* @__PURE__ */ jsx2("div", { style: { width: "32px", height: "32px", border: "3px solid rgba(255,255,255,0.1)", borderTopColor: "#d4a574", borderRadius: "50%", animation: "nexus-spin 0.8s linear infinite", margin: "0 auto 16px" } }),
307
- /* @__PURE__ */ jsx2("p", { style: { fontFamily: "monospace", fontSize: "14px", opacity: 0.6 }, children: "Authenticating with Nexus..." }),
307
+ return /* @__PURE__ */ jsxs("div", { style: { display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "center", minHeight: "100vh", background: "#0a0a0a", color: "#fff", gap: "20px" }, children: [
308
+ /* @__PURE__ */ jsx2("div", { style: { width: "48px", height: "48px", animation: "nexus-spin 2s linear infinite" }, children: /* @__PURE__ */ jsx2("svg", { viewBox: "0 0 64 64", fill: "none", xmlns: "http://www.w3.org/2000/svg", style: { width: "100%", height: "100%" }, children: /* @__PURE__ */ jsxs("g", { stroke: "rgba(255,255,255,0.5)", strokeWidth: "2", strokeLinecap: "round", children: [
309
+ /* @__PURE__ */ jsx2("circle", { cx: "32", cy: "32", r: "4", fill: "rgba(255,255,255,0.7)" }),
310
+ /* @__PURE__ */ jsx2("circle", { cx: "32", cy: "12", r: "3", fill: "rgba(255,255,255,0.5)" }),
311
+ /* @__PURE__ */ jsx2("line", { x1: "32", y1: "15", x2: "32", y2: "28", opacity: "0.4" }),
312
+ /* @__PURE__ */ jsx2("circle", { cx: "32", cy: "52", r: "3", fill: "rgba(255,255,255,0.5)" }),
313
+ /* @__PURE__ */ jsx2("line", { x1: "32", y1: "36", x2: "32", y2: "49", opacity: "0.4" }),
314
+ /* @__PURE__ */ jsx2("circle", { cx: "12", cy: "32", r: "3", fill: "rgba(255,255,255,0.5)" }),
315
+ /* @__PURE__ */ jsx2("line", { x1: "15", y1: "32", x2: "28", y2: "32", opacity: "0.4" }),
316
+ /* @__PURE__ */ jsx2("circle", { cx: "52", cy: "32", r: "3", fill: "rgba(255,255,255,0.5)" }),
317
+ /* @__PURE__ */ jsx2("line", { x1: "36", y1: "32", x2: "49", y2: "32", opacity: "0.4" }),
318
+ /* @__PURE__ */ jsx2("circle", { cx: "18", cy: "18", r: "2.5", fill: "rgba(255,255,255,0.35)" }),
319
+ /* @__PURE__ */ jsx2("line", { x1: "20", y1: "20", x2: "29", y2: "29", opacity: "0.25" }),
320
+ /* @__PURE__ */ jsx2("circle", { cx: "46", cy: "18", r: "2.5", fill: "rgba(255,255,255,0.35)" }),
321
+ /* @__PURE__ */ jsx2("line", { x1: "44", y1: "20", x2: "35", y2: "29", opacity: "0.25" }),
322
+ /* @__PURE__ */ jsx2("circle", { cx: "18", cy: "46", r: "2.5", fill: "rgba(255,255,255,0.35)" }),
323
+ /* @__PURE__ */ jsx2("line", { x1: "20", y1: "44", x2: "29", y2: "35", opacity: "0.25" }),
324
+ /* @__PURE__ */ jsx2("circle", { cx: "46", cy: "46", r: "2.5", fill: "rgba(255,255,255,0.35)" }),
325
+ /* @__PURE__ */ jsx2("line", { x1: "44", y1: "44", x2: "35", y2: "35", opacity: "0.25" })
326
+ ] }) }) }),
327
+ /* @__PURE__ */ jsx2("p", { style: { fontFamily: "monospace", fontSize: "14px", opacity: 0.5 }, children: "Authenticating with Nexus..." }),
308
328
  /* @__PURE__ */ jsx2("style", { children: `@keyframes nexus-spin { to { transform: rotate(360deg) } }` })
309
- ] }) });
329
+ ] });
310
330
  }
311
331
 
312
332
  // src/NexusLoginButton.tsx
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vylth/nexus-react",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
4
  "description": "Nexus SSO SDK for React — Sign in with Nexus",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",