@nexushub/client 0.8.6 → 0.8.8
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/dist/cli.cjs +487 -95
- package/dist/react.cjs +112 -7
- package/dist/react.d.cts +2 -1
- package/dist/react.d.ts +2 -1
- package/dist/react.js +110 -5
- package/package.json +1 -1
package/dist/react.cjs
CHANGED
|
@@ -2438,6 +2438,89 @@ async function parseError(res) {
|
|
|
2438
2438
|
}
|
|
2439
2439
|
}
|
|
2440
2440
|
|
|
2441
|
+
// src/notifications/push-client.ts
|
|
2442
|
+
var NexusPushClient = class {
|
|
2443
|
+
constructor(config) {
|
|
2444
|
+
this.config = config;
|
|
2445
|
+
}
|
|
2446
|
+
/**
|
|
2447
|
+
* Helper to convert Base64 VAPID key to Uint8Array for WebPush security
|
|
2448
|
+
*/
|
|
2449
|
+
urlBase64ToUint8Array(base64String) {
|
|
2450
|
+
const padding = "=".repeat((4 - base64String.length % 4) % 4);
|
|
2451
|
+
const base64 = (base64String + padding).replace(/\-/g, "+").replace(/_/g, "/");
|
|
2452
|
+
const rawData = window.atob(base64);
|
|
2453
|
+
const outputArray = new Uint8Array(rawData.length);
|
|
2454
|
+
for (let i = 0; i < rawData.length; ++i) {
|
|
2455
|
+
outputArray[i] = rawData.charCodeAt(i);
|
|
2456
|
+
}
|
|
2457
|
+
return outputArray;
|
|
2458
|
+
}
|
|
2459
|
+
/**
|
|
2460
|
+
* Requests browser notification permissions and registers the WebPush subscription
|
|
2461
|
+
*/
|
|
2462
|
+
async requestSubscription(serviceWorkerPath = "/sw.js") {
|
|
2463
|
+
if (typeof window === "undefined" || !("serviceWorker" in navigator) || !("PushManager" in window)) {
|
|
2464
|
+
console.warn(
|
|
2465
|
+
"[NexusHub] Push notifications are not supported in this browser environment."
|
|
2466
|
+
);
|
|
2467
|
+
return false;
|
|
2468
|
+
}
|
|
2469
|
+
try {
|
|
2470
|
+
const permission = await Notification.requestPermission();
|
|
2471
|
+
if (permission !== "granted") {
|
|
2472
|
+
console.warn("[NexusHub] Notification permission denied by user.");
|
|
2473
|
+
return false;
|
|
2474
|
+
}
|
|
2475
|
+
const vapidRes = await fetch(
|
|
2476
|
+
`${this.config.apiUrl}/notifications/vapid-key`,
|
|
2477
|
+
{
|
|
2478
|
+
headers: {
|
|
2479
|
+
Authorization: `Bearer ${this.config.apiKey}`,
|
|
2480
|
+
"x-nexus-project": this.config.projectId
|
|
2481
|
+
}
|
|
2482
|
+
}
|
|
2483
|
+
);
|
|
2484
|
+
if (!vapidRes.ok) throw new Error("Failed to fetch VAPID public key.");
|
|
2485
|
+
const { publicKey } = await vapidRes.json();
|
|
2486
|
+
const registration = await navigator.serviceWorker.register(serviceWorkerPath);
|
|
2487
|
+
await navigator.serviceWorker.ready;
|
|
2488
|
+
const subscription = await registration.pushManager.subscribe({
|
|
2489
|
+
userVisibleOnly: true,
|
|
2490
|
+
// 🚀 RESOLVED: Cast as 'any' to bypass strict DOM BufferSource typings
|
|
2491
|
+
applicationServerKey: this.urlBase64ToUint8Array(publicKey)
|
|
2492
|
+
});
|
|
2493
|
+
const rawSub = subscription.toJSON();
|
|
2494
|
+
if (!rawSub.endpoint || !_optionalChain([rawSub, 'access', _46 => _46.keys, 'optionalAccess', _47 => _47.auth]) || !_optionalChain([rawSub, 'access', _48 => _48.keys, 'optionalAccess', _49 => _49.p256dh])) {
|
|
2495
|
+
throw new Error(
|
|
2496
|
+
"Malformed subscription payload received from browser."
|
|
2497
|
+
);
|
|
2498
|
+
}
|
|
2499
|
+
const res = await fetch(
|
|
2500
|
+
`${this.config.apiUrl}/notifications/project/${this.config.projectId}/subscribe`,
|
|
2501
|
+
{
|
|
2502
|
+
method: "POST",
|
|
2503
|
+
headers: {
|
|
2504
|
+
"Content-Type": "application/json",
|
|
2505
|
+
Authorization: `Bearer ${this.config.apiKey}`,
|
|
2506
|
+
"x-nexus-project": this.config.projectId
|
|
2507
|
+
},
|
|
2508
|
+
body: JSON.stringify({
|
|
2509
|
+
endpoint: rawSub.endpoint,
|
|
2510
|
+
auth: rawSub.keys.auth,
|
|
2511
|
+
p256dh: rawSub.keys.p256dh,
|
|
2512
|
+
provider: "WEB_PUSH"
|
|
2513
|
+
})
|
|
2514
|
+
}
|
|
2515
|
+
);
|
|
2516
|
+
return res.ok;
|
|
2517
|
+
} catch (err) {
|
|
2518
|
+
console.error("[NexusHub] WebPush subscription failed:", err);
|
|
2519
|
+
return false;
|
|
2520
|
+
}
|
|
2521
|
+
}
|
|
2522
|
+
};
|
|
2523
|
+
|
|
2441
2524
|
// src/components/NexusProvider.tsx
|
|
2442
2525
|
|
|
2443
2526
|
var NexusContext = _react.createContext.call(void 0, nexus);
|
|
@@ -2466,11 +2549,10 @@ var NexusProvider = ({
|
|
|
2466
2549
|
projectId,
|
|
2467
2550
|
disableAnalytics = false,
|
|
2468
2551
|
hasConsent = true,
|
|
2469
|
-
// NEW: default true to preserve existing behaviour
|
|
2470
2552
|
enableLiveFeed = false,
|
|
2471
|
-
|
|
2472
|
-
|
|
2473
|
-
//
|
|
2553
|
+
onLiveEvent,
|
|
2554
|
+
autoPromptPush = true
|
|
2555
|
+
// 🚀 Default: true for zero-config automatic visitor subscription
|
|
2474
2556
|
}) => {
|
|
2475
2557
|
const isInitialized = _react.useRef.call(void 0, false);
|
|
2476
2558
|
const socketRef = _react.useRef.call(void 0, null);
|
|
@@ -2488,6 +2570,29 @@ var NexusProvider = ({
|
|
|
2488
2570
|
}
|
|
2489
2571
|
return nexus.getConfig();
|
|
2490
2572
|
}, [projectId]);
|
|
2573
|
+
_react.useEffect.call(void 0, () => {
|
|
2574
|
+
if (typeof window === "undefined" || !("serviceWorker" in navigator))
|
|
2575
|
+
return;
|
|
2576
|
+
navigator.serviceWorker.register("/sw.js").then(async (registration) => {
|
|
2577
|
+
if (nexus.getConfig().debug) {
|
|
2578
|
+
console.log("[NexusHub] \u26A1 Sovereign Service Worker Active.");
|
|
2579
|
+
}
|
|
2580
|
+
if (autoPromptPush && "Notification" in window && Notification.permission === "default") {
|
|
2581
|
+
const pushClient = new NexusPushClient(nexus.getConfig());
|
|
2582
|
+
await pushClient.requestSubscription("/sw.js");
|
|
2583
|
+
return;
|
|
2584
|
+
}
|
|
2585
|
+
if ("Notification" in window && Notification.permission === "granted") {
|
|
2586
|
+
try {
|
|
2587
|
+
const pushClient = new NexusPushClient(nexus.getConfig());
|
|
2588
|
+
await pushClient.requestSubscription("/sw.js");
|
|
2589
|
+
} catch (e16) {
|
|
2590
|
+
}
|
|
2591
|
+
}
|
|
2592
|
+
}).catch((err) => {
|
|
2593
|
+
console.warn("[NexusHub] Service Worker registration failed:", err);
|
|
2594
|
+
});
|
|
2595
|
+
}, [autoPromptPush]);
|
|
2491
2596
|
_react.useEffect.call(void 0, () => {
|
|
2492
2597
|
if (typeof window === "undefined" || disableAnalytics || !hasConsent)
|
|
2493
2598
|
return;
|
|
@@ -2513,7 +2618,7 @@ var NexusProvider = ({
|
|
|
2513
2618
|
try {
|
|
2514
2619
|
const { io } = await Promise.resolve().then(() => _interopRequireWildcard(require("socket.io-client")));
|
|
2515
2620
|
const cfg = nexus.getConfig();
|
|
2516
|
-
const wsUrl = cfg.apiUrl || "
|
|
2621
|
+
const wsUrl = cfg.apiUrl || "https://gnapex.co.tz";
|
|
2517
2622
|
const socket = io(`${wsUrl}/analytics`, {
|
|
2518
2623
|
auth: { token: cfg.apiKey },
|
|
2519
2624
|
query: { projectId: cfg.projectId },
|
|
@@ -2531,7 +2636,7 @@ var NexusProvider = ({
|
|
|
2531
2636
|
});
|
|
2532
2637
|
socket.on("live_event", (event) => {
|
|
2533
2638
|
setLatestEvent(event);
|
|
2534
|
-
_optionalChain([onLiveEvent, 'optionalCall',
|
|
2639
|
+
_optionalChain([onLiveEvent, 'optionalCall', _50 => _50(event)]);
|
|
2535
2640
|
});
|
|
2536
2641
|
socketRef.current = socket;
|
|
2537
2642
|
} catch (err) {
|
|
@@ -2694,7 +2799,7 @@ function NexusVideo({
|
|
|
2694
2799
|
const isYouTube = value.includes("youtube.com") || value.includes("youtu.be");
|
|
2695
2800
|
const isVimeo = value.includes("vimeo.com");
|
|
2696
2801
|
if (isYouTube) {
|
|
2697
|
-
const videoId = value.includes("youtu.be") ? value.split("/").pop() : _optionalChain([value, 'access',
|
|
2802
|
+
const videoId = value.includes("youtu.be") ? value.split("/").pop() : _optionalChain([value, 'access', _51 => _51.split, 'call', _52 => _52("v="), 'access', _53 => _53[1], 'optionalAccess', _54 => _54.split, 'call', _55 => _55("&"), 'access', _56 => _56[0]]);
|
|
2698
2803
|
return /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
2699
2804
|
"iframe",
|
|
2700
2805
|
{
|
package/dist/react.d.cts
CHANGED
|
@@ -237,6 +237,7 @@ interface NexusProviderProps {
|
|
|
237
237
|
hasConsent?: boolean;
|
|
238
238
|
enableLiveFeed?: boolean;
|
|
239
239
|
onLiveEvent?: (event: LiveAnalyticsEvent) => void;
|
|
240
|
+
autoPromptPush?: boolean;
|
|
240
241
|
}
|
|
241
242
|
interface LiveAnalyticsEvent {
|
|
242
243
|
eventType: string;
|
|
@@ -262,7 +263,7 @@ interface LiveFeedContextValue {
|
|
|
262
263
|
isConnected: boolean;
|
|
263
264
|
}
|
|
264
265
|
declare const useNexusLiveFeed: () => LiveFeedContextValue;
|
|
265
|
-
declare const NexusProvider: ({ children, projectId, disableAnalytics, hasConsent, enableLiveFeed, onLiveEvent, }: NexusProviderProps) => react_jsx_runtime.JSX.Element;
|
|
266
|
+
declare const NexusProvider: ({ children, projectId, disableAnalytics, hasConsent, enableLiveFeed, onLiveEvent, autoPromptPush, }: NexusProviderProps) => react_jsx_runtime.JSX.Element;
|
|
266
267
|
declare const useNexus: () => NexusClient;
|
|
267
268
|
declare const useNexusAnalytics: () => {
|
|
268
269
|
track: (eventName: string, properties?: Record<string, any>) => void;
|
package/dist/react.d.ts
CHANGED
|
@@ -237,6 +237,7 @@ interface NexusProviderProps {
|
|
|
237
237
|
hasConsent?: boolean;
|
|
238
238
|
enableLiveFeed?: boolean;
|
|
239
239
|
onLiveEvent?: (event: LiveAnalyticsEvent) => void;
|
|
240
|
+
autoPromptPush?: boolean;
|
|
240
241
|
}
|
|
241
242
|
interface LiveAnalyticsEvent {
|
|
242
243
|
eventType: string;
|
|
@@ -262,7 +263,7 @@ interface LiveFeedContextValue {
|
|
|
262
263
|
isConnected: boolean;
|
|
263
264
|
}
|
|
264
265
|
declare const useNexusLiveFeed: () => LiveFeedContextValue;
|
|
265
|
-
declare const NexusProvider: ({ children, projectId, disableAnalytics, hasConsent, enableLiveFeed, onLiveEvent, }: NexusProviderProps) => react_jsx_runtime.JSX.Element;
|
|
266
|
+
declare const NexusProvider: ({ children, projectId, disableAnalytics, hasConsent, enableLiveFeed, onLiveEvent, autoPromptPush, }: NexusProviderProps) => react_jsx_runtime.JSX.Element;
|
|
266
267
|
declare const useNexus: () => NexusClient;
|
|
267
268
|
declare const useNexusAnalytics: () => {
|
|
268
269
|
track: (eventName: string, properties?: Record<string, any>) => void;
|
package/dist/react.js
CHANGED
|
@@ -2438,6 +2438,89 @@ async function parseError(res) {
|
|
|
2438
2438
|
}
|
|
2439
2439
|
}
|
|
2440
2440
|
|
|
2441
|
+
// src/notifications/push-client.ts
|
|
2442
|
+
var NexusPushClient = class {
|
|
2443
|
+
constructor(config) {
|
|
2444
|
+
this.config = config;
|
|
2445
|
+
}
|
|
2446
|
+
/**
|
|
2447
|
+
* Helper to convert Base64 VAPID key to Uint8Array for WebPush security
|
|
2448
|
+
*/
|
|
2449
|
+
urlBase64ToUint8Array(base64String) {
|
|
2450
|
+
const padding = "=".repeat((4 - base64String.length % 4) % 4);
|
|
2451
|
+
const base64 = (base64String + padding).replace(/\-/g, "+").replace(/_/g, "/");
|
|
2452
|
+
const rawData = window.atob(base64);
|
|
2453
|
+
const outputArray = new Uint8Array(rawData.length);
|
|
2454
|
+
for (let i = 0; i < rawData.length; ++i) {
|
|
2455
|
+
outputArray[i] = rawData.charCodeAt(i);
|
|
2456
|
+
}
|
|
2457
|
+
return outputArray;
|
|
2458
|
+
}
|
|
2459
|
+
/**
|
|
2460
|
+
* Requests browser notification permissions and registers the WebPush subscription
|
|
2461
|
+
*/
|
|
2462
|
+
async requestSubscription(serviceWorkerPath = "/sw.js") {
|
|
2463
|
+
if (typeof window === "undefined" || !("serviceWorker" in navigator) || !("PushManager" in window)) {
|
|
2464
|
+
console.warn(
|
|
2465
|
+
"[NexusHub] Push notifications are not supported in this browser environment."
|
|
2466
|
+
);
|
|
2467
|
+
return false;
|
|
2468
|
+
}
|
|
2469
|
+
try {
|
|
2470
|
+
const permission = await Notification.requestPermission();
|
|
2471
|
+
if (permission !== "granted") {
|
|
2472
|
+
console.warn("[NexusHub] Notification permission denied by user.");
|
|
2473
|
+
return false;
|
|
2474
|
+
}
|
|
2475
|
+
const vapidRes = await fetch(
|
|
2476
|
+
`${this.config.apiUrl}/notifications/vapid-key`,
|
|
2477
|
+
{
|
|
2478
|
+
headers: {
|
|
2479
|
+
Authorization: `Bearer ${this.config.apiKey}`,
|
|
2480
|
+
"x-nexus-project": this.config.projectId
|
|
2481
|
+
}
|
|
2482
|
+
}
|
|
2483
|
+
);
|
|
2484
|
+
if (!vapidRes.ok) throw new Error("Failed to fetch VAPID public key.");
|
|
2485
|
+
const { publicKey } = await vapidRes.json();
|
|
2486
|
+
const registration = await navigator.serviceWorker.register(serviceWorkerPath);
|
|
2487
|
+
await navigator.serviceWorker.ready;
|
|
2488
|
+
const subscription = await registration.pushManager.subscribe({
|
|
2489
|
+
userVisibleOnly: true,
|
|
2490
|
+
// 🚀 RESOLVED: Cast as 'any' to bypass strict DOM BufferSource typings
|
|
2491
|
+
applicationServerKey: this.urlBase64ToUint8Array(publicKey)
|
|
2492
|
+
});
|
|
2493
|
+
const rawSub = subscription.toJSON();
|
|
2494
|
+
if (!rawSub.endpoint || !rawSub.keys?.auth || !rawSub.keys?.p256dh) {
|
|
2495
|
+
throw new Error(
|
|
2496
|
+
"Malformed subscription payload received from browser."
|
|
2497
|
+
);
|
|
2498
|
+
}
|
|
2499
|
+
const res = await fetch(
|
|
2500
|
+
`${this.config.apiUrl}/notifications/project/${this.config.projectId}/subscribe`,
|
|
2501
|
+
{
|
|
2502
|
+
method: "POST",
|
|
2503
|
+
headers: {
|
|
2504
|
+
"Content-Type": "application/json",
|
|
2505
|
+
Authorization: `Bearer ${this.config.apiKey}`,
|
|
2506
|
+
"x-nexus-project": this.config.projectId
|
|
2507
|
+
},
|
|
2508
|
+
body: JSON.stringify({
|
|
2509
|
+
endpoint: rawSub.endpoint,
|
|
2510
|
+
auth: rawSub.keys.auth,
|
|
2511
|
+
p256dh: rawSub.keys.p256dh,
|
|
2512
|
+
provider: "WEB_PUSH"
|
|
2513
|
+
})
|
|
2514
|
+
}
|
|
2515
|
+
);
|
|
2516
|
+
return res.ok;
|
|
2517
|
+
} catch (err) {
|
|
2518
|
+
console.error("[NexusHub] WebPush subscription failed:", err);
|
|
2519
|
+
return false;
|
|
2520
|
+
}
|
|
2521
|
+
}
|
|
2522
|
+
};
|
|
2523
|
+
|
|
2441
2524
|
// src/components/NexusProvider.tsx
|
|
2442
2525
|
import { jsx as jsx2, jsxs } from "react/jsx-runtime";
|
|
2443
2526
|
var NexusContext = createContext2(nexus);
|
|
@@ -2466,11 +2549,10 @@ var NexusProvider = ({
|
|
|
2466
2549
|
projectId,
|
|
2467
2550
|
disableAnalytics = false,
|
|
2468
2551
|
hasConsent = true,
|
|
2469
|
-
// NEW: default true to preserve existing behaviour
|
|
2470
2552
|
enableLiveFeed = false,
|
|
2471
|
-
|
|
2472
|
-
|
|
2473
|
-
//
|
|
2553
|
+
onLiveEvent,
|
|
2554
|
+
autoPromptPush = true
|
|
2555
|
+
// 🚀 Default: true for zero-config automatic visitor subscription
|
|
2474
2556
|
}) => {
|
|
2475
2557
|
const isInitialized = useRef(false);
|
|
2476
2558
|
const socketRef = useRef(null);
|
|
@@ -2488,6 +2570,29 @@ var NexusProvider = ({
|
|
|
2488
2570
|
}
|
|
2489
2571
|
return nexus.getConfig();
|
|
2490
2572
|
}, [projectId]);
|
|
2573
|
+
useEffect2(() => {
|
|
2574
|
+
if (typeof window === "undefined" || !("serviceWorker" in navigator))
|
|
2575
|
+
return;
|
|
2576
|
+
navigator.serviceWorker.register("/sw.js").then(async (registration) => {
|
|
2577
|
+
if (nexus.getConfig().debug) {
|
|
2578
|
+
console.log("[NexusHub] \u26A1 Sovereign Service Worker Active.");
|
|
2579
|
+
}
|
|
2580
|
+
if (autoPromptPush && "Notification" in window && Notification.permission === "default") {
|
|
2581
|
+
const pushClient = new NexusPushClient(nexus.getConfig());
|
|
2582
|
+
await pushClient.requestSubscription("/sw.js");
|
|
2583
|
+
return;
|
|
2584
|
+
}
|
|
2585
|
+
if ("Notification" in window && Notification.permission === "granted") {
|
|
2586
|
+
try {
|
|
2587
|
+
const pushClient = new NexusPushClient(nexus.getConfig());
|
|
2588
|
+
await pushClient.requestSubscription("/sw.js");
|
|
2589
|
+
} catch {
|
|
2590
|
+
}
|
|
2591
|
+
}
|
|
2592
|
+
}).catch((err) => {
|
|
2593
|
+
console.warn("[NexusHub] Service Worker registration failed:", err);
|
|
2594
|
+
});
|
|
2595
|
+
}, [autoPromptPush]);
|
|
2491
2596
|
useEffect2(() => {
|
|
2492
2597
|
if (typeof window === "undefined" || disableAnalytics || !hasConsent)
|
|
2493
2598
|
return;
|
|
@@ -2513,7 +2618,7 @@ var NexusProvider = ({
|
|
|
2513
2618
|
try {
|
|
2514
2619
|
const { io } = await import("socket.io-client");
|
|
2515
2620
|
const cfg = nexus.getConfig();
|
|
2516
|
-
const wsUrl = cfg.apiUrl || "
|
|
2621
|
+
const wsUrl = cfg.apiUrl || "https://gnapex.co.tz";
|
|
2517
2622
|
const socket = io(`${wsUrl}/analytics`, {
|
|
2518
2623
|
auth: { token: cfg.apiKey },
|
|
2519
2624
|
query: { projectId: cfg.projectId },
|