@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 +55 -0
- package/dist/index.cjs +66 -0
- package/dist/index.d.cts +71 -0
- package/dist/index.d.ts +71 -0
- package/dist/index.js +61 -0
- package/package.json +33 -0
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;
|
package/dist/index.d.cts
ADDED
|
@@ -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.d.ts
ADDED
|
@@ -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
|
+
}
|