@adviverse/react 1.0.0

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,55 @@
1
+ # @adviverse/react
2
+
3
+ React components for [Adviverse](https://adviverse.com) ad slots. One import — no `dangerouslySetInnerHTML`, no `document.currentScript` workarounds, no manual script loading.
4
+
5
+ ```bash
6
+ npm i @adviverse/react
7
+ ```
8
+
9
+ ## Quick start
10
+
11
+ ```tsx
12
+ import { AdSlot } from '@adviverse/react';
13
+
14
+ export function Sidebar() {
15
+ return <AdSlot tag="tk_80f77ad8d23bdbd927" sizes="300x600" style={{ width: 300, height: 600 }} />;
16
+ }
17
+ ```
18
+
19
+ Size the slot with `style`/`className` (or pass `sizes`) — the ad matches the box. That's the whole integration. The SDK loads once, lazily; slots tear down on unmount.
20
+
21
+ ## Next.js (App Router)
22
+
23
+ Refill slots on client-side navigation with `<AdRefresh>`:
24
+
25
+ ```tsx
26
+ 'use client';
27
+ import { usePathname } from 'next/navigation';
28
+ import { AdRefresh } from '@adviverse/react';
29
+
30
+ // render once in app/layout.tsx
31
+ export function Ads() { return <AdRefresh routeKey={usePathname()} />; }
32
+ ```
33
+
34
+ ## API
35
+
36
+ | Export | Purpose |
37
+ | --- | --- |
38
+ | `<AdSlot tag sizes? style? />` | A single ad slot. `sizes`: `"300x250"`, a list `"160x600,300x600"`, or `"fluid"`. |
39
+ | `<AdRefresh routeKey />` | Refill slots when `routeKey` changes (SPA route change). Idempotent. |
40
+ | `<AdScript endpoint? />` | Load the SDK once, explicitly (optional — `AdSlot` loads it lazily). |
41
+ | `loadSdk(endpoint?)` | Promise that resolves when the SDK is ready. |
42
+
43
+ ## Content Security Policy
44
+
45
+ If you run a CSP, allow the Adviverse hosts:
46
+
47
+ ```
48
+ script-src https://serve.adviverse.com
49
+ img-src https://cdn.adviverse.com https://serve.adviverse.com data:
50
+ connect-src https://serve.adviverse.com
51
+ ```
52
+
53
+ ## Debugging
54
+
55
+ Add `?adviverse_debug=1` to the URL (or set `window.ADVIVERSE_DEBUG = true`) to console-log each request, the resolved size, and any no-fill reason. Use the reserved tag `tk_test` to render a labeled test ad that always fills — verify your integration before a campaign is live.
package/dist/index.cjs ADDED
@@ -0,0 +1,66 @@
1
+ 'use strict';
2
+
3
+ var react = require('react');
4
+ var jsxRuntime = require('react/jsx-runtime');
5
+
6
+ var DEFAULT_ENDPOINT = "https://serve.adviverse.com";
7
+ var loadPromise = null;
8
+ function loadSdk(endpoint) {
9
+ if (typeof window === "undefined" || typeof document === "undefined") return Promise.resolve();
10
+ if (window.adviverse && window.adviverse.render) return Promise.resolve();
11
+ if (loadPromise) return loadPromise;
12
+ const base = (endpoint || window.ADVIVERSE_ENDPOINT || DEFAULT_ENDPOINT).replace(/\/$/, "");
13
+ loadPromise = new Promise((resolve, reject) => {
14
+ const existing = document.querySelector("script[data-adviverse-sdk]");
15
+ if (existing) {
16
+ if (window.adviverse && window.adviverse.render) return resolve();
17
+ existing.addEventListener("load", () => resolve());
18
+ existing.addEventListener("error", () => reject(new Error("adviverse sdk failed to load")));
19
+ return;
20
+ }
21
+ const s = document.createElement("script");
22
+ s.async = true;
23
+ s.src = base + "/sdk.js";
24
+ s.crossOrigin = "anonymous";
25
+ s.setAttribute("data-adviverse-sdk", "1");
26
+ s.onload = () => resolve();
27
+ s.onerror = () => reject(new Error("adviverse sdk failed to load"));
28
+ document.head.appendChild(s);
29
+ });
30
+ return loadPromise;
31
+ }
32
+ function AdSlot({ tag, sizes, endpoint, ...rest }) {
33
+ const ref = react.useRef(null);
34
+ react.useEffect(() => {
35
+ let dead = false;
36
+ const el = ref.current;
37
+ loadSdk(endpoint).then(() => {
38
+ if (dead || !el) return;
39
+ window.adviverse?.render?.(el, { tag, sizes });
40
+ }).catch(() => {
41
+ });
42
+ return () => {
43
+ dead = true;
44
+ if (el) window.adviverse?.destroy?.(el);
45
+ };
46
+ }, [tag, sizes, endpoint]);
47
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { ref, "data-tag": tag, "data-sizes": sizes, ...rest });
48
+ }
49
+ function AdScript({ endpoint }) {
50
+ react.useEffect(() => {
51
+ loadSdk(endpoint).catch(() => {
52
+ });
53
+ }, [endpoint]);
54
+ return null;
55
+ }
56
+ function AdRefresh({ routeKey }) {
57
+ react.useEffect(() => {
58
+ window.adviverse?.refresh?.();
59
+ }, [routeKey]);
60
+ return null;
61
+ }
62
+
63
+ exports.AdRefresh = AdRefresh;
64
+ exports.AdScript = AdScript;
65
+ exports.AdSlot = AdSlot;
66
+ exports.loadSdk = loadSdk;
@@ -0,0 +1,71 @@
1
+ import * as react from 'react';
2
+ import { HTMLAttributes, CSSProperties } from 'react';
3
+
4
+ /**
5
+ * @adviverse/react — drop-in React components for Adviverse ad slots.
6
+ *
7
+ * import { AdSlot } from '@adviverse/react';
8
+ * <AdSlot tag="tk_…" sizes="300x250" style={{ width: 300, height: 250 }} />
9
+ *
10
+ * No dangerouslySetInnerHTML, no document.currentScript workarounds, no manual
11
+ * script loading. The SDK is loaded once (lazily), each slot fills the box you
12
+ * size, and slots are torn down on unmount.
13
+ */
14
+ type AdviverseGlobal = {
15
+ render?: (el: HTMLElement, opts: {
16
+ tag: string;
17
+ sizes?: string;
18
+ }) => Promise<unknown>;
19
+ destroy?: (el: HTMLElement) => void;
20
+ refresh?: (root?: HTMLElement | Document) => Promise<{
21
+ filled: string[];
22
+ nofill: string[];
23
+ skipped: string[];
24
+ }>;
25
+ ready?: (cb: () => void) => void;
26
+ version?: string;
27
+ };
28
+ declare global {
29
+ interface Window {
30
+ adviverse?: AdviverseGlobal;
31
+ ADVIVERSE_ENDPOINT?: string;
32
+ ADVIVERSE_DEBUG?: boolean;
33
+ }
34
+ }
35
+ /** Load the Adviverse SDK once (idempotent). Resolves when it's ready. */
36
+ declare function loadSdk(endpoint?: string): Promise<void>;
37
+ interface AdSlotProps extends Omit<HTMLAttributes<HTMLDivElement>, 'children'> {
38
+ /** Placement tag key, e.g. "tk_80f77ad8d23bdbd927". */
39
+ tag: string;
40
+ /**
41
+ * Explicit size(s): "300x250", a comma list "160x600,300x600", or "fluid"
42
+ * (fill the container width, server picks the height). Omit to size from the
43
+ * element's own CSS box.
44
+ */
45
+ sizes?: string;
46
+ /** Size the box here (or via className) — the ad matches it. */
47
+ style?: CSSProperties;
48
+ endpoint?: string;
49
+ }
50
+ /**
51
+ * A single ad slot. Size it with `style`/`className` (or pass `sizes`); the SDK
52
+ * fills the box and tears down on unmount.
53
+ */
54
+ declare function AdSlot({ tag, sizes, endpoint, ...rest }: AdSlotProps): react.JSX.Element;
55
+ /**
56
+ * Loads the SDK once, explicitly (optional — <AdSlot> loads it lazily too). Handy
57
+ * in a root layout if you prefer the loader mounted up front.
58
+ */
59
+ declare function AdScript({ endpoint }: {
60
+ endpoint?: string;
61
+ }): null;
62
+ /**
63
+ * Refill ad slots after a client-side route change (SPA). Pass a value that
64
+ * changes per route — e.g. Next.js App Router: `<AdRefresh routeKey={usePathname()} />`.
65
+ * Idempotent: already-filled slots are skipped and the popunder never re-arms.
66
+ */
67
+ declare function AdRefresh({ routeKey }: {
68
+ routeKey?: string | number;
69
+ }): null;
70
+
71
+ export { AdRefresh, AdScript, AdSlot, type AdSlotProps, loadSdk };
@@ -0,0 +1,71 @@
1
+ import * as react from 'react';
2
+ import { HTMLAttributes, CSSProperties } from 'react';
3
+
4
+ /**
5
+ * @adviverse/react — drop-in React components for Adviverse ad slots.
6
+ *
7
+ * import { AdSlot } from '@adviverse/react';
8
+ * <AdSlot tag="tk_…" sizes="300x250" style={{ width: 300, height: 250 }} />
9
+ *
10
+ * No dangerouslySetInnerHTML, no document.currentScript workarounds, no manual
11
+ * script loading. The SDK is loaded once (lazily), each slot fills the box you
12
+ * size, and slots are torn down on unmount.
13
+ */
14
+ type AdviverseGlobal = {
15
+ render?: (el: HTMLElement, opts: {
16
+ tag: string;
17
+ sizes?: string;
18
+ }) => Promise<unknown>;
19
+ destroy?: (el: HTMLElement) => void;
20
+ refresh?: (root?: HTMLElement | Document) => Promise<{
21
+ filled: string[];
22
+ nofill: string[];
23
+ skipped: string[];
24
+ }>;
25
+ ready?: (cb: () => void) => void;
26
+ version?: string;
27
+ };
28
+ declare global {
29
+ interface Window {
30
+ adviverse?: AdviverseGlobal;
31
+ ADVIVERSE_ENDPOINT?: string;
32
+ ADVIVERSE_DEBUG?: boolean;
33
+ }
34
+ }
35
+ /** Load the Adviverse SDK once (idempotent). Resolves when it's ready. */
36
+ declare function loadSdk(endpoint?: string): Promise<void>;
37
+ interface AdSlotProps extends Omit<HTMLAttributes<HTMLDivElement>, 'children'> {
38
+ /** Placement tag key, e.g. "tk_80f77ad8d23bdbd927". */
39
+ tag: string;
40
+ /**
41
+ * Explicit size(s): "300x250", a comma list "160x600,300x600", or "fluid"
42
+ * (fill the container width, server picks the height). Omit to size from the
43
+ * element's own CSS box.
44
+ */
45
+ sizes?: string;
46
+ /** Size the box here (or via className) — the ad matches it. */
47
+ style?: CSSProperties;
48
+ endpoint?: string;
49
+ }
50
+ /**
51
+ * A single ad slot. Size it with `style`/`className` (or pass `sizes`); the SDK
52
+ * fills the box and tears down on unmount.
53
+ */
54
+ declare function AdSlot({ tag, sizes, endpoint, ...rest }: AdSlotProps): react.JSX.Element;
55
+ /**
56
+ * Loads the SDK once, explicitly (optional — <AdSlot> loads it lazily too). Handy
57
+ * in a root layout if you prefer the loader mounted up front.
58
+ */
59
+ declare function AdScript({ endpoint }: {
60
+ endpoint?: string;
61
+ }): null;
62
+ /**
63
+ * Refill ad slots after a client-side route change (SPA). Pass a value that
64
+ * changes per route — e.g. Next.js App Router: `<AdRefresh routeKey={usePathname()} />`.
65
+ * Idempotent: already-filled slots are skipped and the popunder never re-arms.
66
+ */
67
+ declare function AdRefresh({ routeKey }: {
68
+ routeKey?: string | number;
69
+ }): null;
70
+
71
+ export { AdRefresh, AdScript, AdSlot, type AdSlotProps, loadSdk };
package/dist/index.js ADDED
@@ -0,0 +1,61 @@
1
+ import { useRef, useEffect } from 'react';
2
+ import { jsx } from 'react/jsx-runtime';
3
+
4
+ var DEFAULT_ENDPOINT = "https://serve.adviverse.com";
5
+ var loadPromise = null;
6
+ function loadSdk(endpoint) {
7
+ if (typeof window === "undefined" || typeof document === "undefined") return Promise.resolve();
8
+ if (window.adviverse && window.adviverse.render) return Promise.resolve();
9
+ if (loadPromise) return loadPromise;
10
+ const base = (endpoint || window.ADVIVERSE_ENDPOINT || DEFAULT_ENDPOINT).replace(/\/$/, "");
11
+ loadPromise = new Promise((resolve, reject) => {
12
+ const existing = document.querySelector("script[data-adviverse-sdk]");
13
+ if (existing) {
14
+ if (window.adviverse && window.adviverse.render) return resolve();
15
+ existing.addEventListener("load", () => resolve());
16
+ existing.addEventListener("error", () => reject(new Error("adviverse sdk failed to load")));
17
+ return;
18
+ }
19
+ const s = document.createElement("script");
20
+ s.async = true;
21
+ s.src = base + "/sdk.js";
22
+ s.crossOrigin = "anonymous";
23
+ s.setAttribute("data-adviverse-sdk", "1");
24
+ s.onload = () => resolve();
25
+ s.onerror = () => reject(new Error("adviverse sdk failed to load"));
26
+ document.head.appendChild(s);
27
+ });
28
+ return loadPromise;
29
+ }
30
+ function AdSlot({ tag, sizes, endpoint, ...rest }) {
31
+ const ref = useRef(null);
32
+ useEffect(() => {
33
+ let dead = false;
34
+ const el = ref.current;
35
+ loadSdk(endpoint).then(() => {
36
+ if (dead || !el) return;
37
+ window.adviverse?.render?.(el, { tag, sizes });
38
+ }).catch(() => {
39
+ });
40
+ return () => {
41
+ dead = true;
42
+ if (el) window.adviverse?.destroy?.(el);
43
+ };
44
+ }, [tag, sizes, endpoint]);
45
+ return /* @__PURE__ */ jsx("div", { ref, "data-tag": tag, "data-sizes": sizes, ...rest });
46
+ }
47
+ function AdScript({ endpoint }) {
48
+ useEffect(() => {
49
+ loadSdk(endpoint).catch(() => {
50
+ });
51
+ }, [endpoint]);
52
+ return null;
53
+ }
54
+ function AdRefresh({ routeKey }) {
55
+ useEffect(() => {
56
+ window.adviverse?.refresh?.();
57
+ }, [routeKey]);
58
+ return null;
59
+ }
60
+
61
+ export { AdRefresh, AdScript, AdSlot, loadSdk };
package/package.json ADDED
@@ -0,0 +1,33 @@
1
+ {
2
+ "name": "@adviverse/react",
3
+ "version": "1.0.0",
4
+ "description": "React components for Adviverse ad slots — <AdSlot />, <AdScript />, <AdRefresh />. One import, no dangerouslySetInnerHTML, no currentScript workarounds.",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "main": "./dist/index.cjs",
8
+ "module": "./dist/index.js",
9
+ "types": "./dist/index.d.ts",
10
+ "exports": {
11
+ ".": {
12
+ "types": "./dist/index.d.ts",
13
+ "import": "./dist/index.js",
14
+ "require": "./dist/index.cjs"
15
+ }
16
+ },
17
+ "sideEffects": false,
18
+ "files": ["dist", "README.md"],
19
+ "scripts": {
20
+ "typecheck": "tsc --noEmit",
21
+ "build": "tsup"
22
+ },
23
+ "keywords": ["adviverse", "ads", "react", "nextjs", "adslot"],
24
+ "peerDependencies": {
25
+ "react": ">=17"
26
+ },
27
+ "devDependencies": {
28
+ "@types/react": "^18.3.0",
29
+ "react": "^18.3.0",
30
+ "tsup": "^8.3.0",
31
+ "typescript": "^5.6.2"
32
+ }
33
+ }