@contentcredits/sdk 2.1.0 → 2.3.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/dist/content-credits.cjs.js +175 -42
- package/dist/content-credits.cjs.js.map +1 -1
- package/dist/content-credits.d.ts +118 -0
- package/dist/content-credits.esm.js +175 -42
- package/dist/content-credits.esm.js.map +1 -1
- package/dist/content-credits.umd.min.js +1 -1
- package/dist/content-credits.umd.min.js.map +1 -1
- package/dist/index.d.ts +46 -0
- package/dist/paywall/index.d.ts +3 -0
- package/dist/types/index.d.ts +94 -1
- package/package.json +1 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
function resolveConfig(raw) {
|
|
4
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
|
|
4
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
|
|
5
5
|
if (!raw.apiKey || typeof raw.apiKey !== 'string' || raw.apiKey.trim() === '') {
|
|
6
6
|
throw new Error('[ContentCredits] apiKey is required. Get yours from the Content Credits admin panel.');
|
|
7
7
|
}
|
|
@@ -10,7 +10,7 @@ function resolveConfig(raw) {
|
|
|
10
10
|
try {
|
|
11
11
|
hostName = new URL(articleUrl).hostname;
|
|
12
12
|
}
|
|
13
|
-
catch (
|
|
13
|
+
catch (_m) {
|
|
14
14
|
throw new Error(`[ContentCredits] Invalid articleUrl: "${articleUrl}"`);
|
|
15
15
|
}
|
|
16
16
|
return {
|
|
@@ -23,13 +23,23 @@ function resolveConfig(raw) {
|
|
|
23
23
|
enableComments: (_d = raw.enableComments) !== null && _d !== void 0 ? _d : true,
|
|
24
24
|
extensionId: (_e = raw.extensionId) !== null && _e !== void 0 ? _e : "ljehdpabbhgccmanhjdfacjnaigpgcml",
|
|
25
25
|
debug: (_f = raw.debug) !== null && _f !== void 0 ? _f : false,
|
|
26
|
+
headless: (_g = raw.headless) !== null && _g !== void 0 ? _g : false,
|
|
26
27
|
apiBaseUrl: "https://api.contentcredits.com",
|
|
27
28
|
accountsUrl: "https://accounts.contentcredits.com",
|
|
28
29
|
paywallTemplate: raw.paywallTemplate,
|
|
29
30
|
onAccessGranted: raw.onAccessGranted,
|
|
31
|
+
onStateChange: raw.onStateChange,
|
|
32
|
+
onReady: raw.onReady,
|
|
33
|
+
onLoginRequired: raw.onLoginRequired,
|
|
34
|
+
onPurchaseRequired: raw.onPurchaseRequired,
|
|
35
|
+
onInsufficientCredits: raw.onInsufficientCredits,
|
|
36
|
+
onPurchased: raw.onPurchased,
|
|
37
|
+
onUserLogin: raw.onUserLogin,
|
|
38
|
+
onUserLogout: raw.onUserLogout,
|
|
39
|
+
onError: raw.onError,
|
|
30
40
|
theme: {
|
|
31
|
-
primaryColor: (
|
|
32
|
-
fontFamily: (
|
|
41
|
+
primaryColor: (_j = (_h = raw.theme) === null || _h === void 0 ? void 0 : _h.primaryColor) !== null && _j !== void 0 ? _j : '#44C678',
|
|
42
|
+
fontFamily: (_l = (_k = raw.theme) === null || _k === void 0 ? void 0 : _k.fontFamily) !== null && _l !== void 0 ? _l : "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif",
|
|
33
43
|
},
|
|
34
44
|
};
|
|
35
45
|
}
|
|
@@ -1625,8 +1635,10 @@ function createPaywall(config, creditsApi, state, emitter, existingGate) {
|
|
|
1625
1635
|
function handleAccessGranted(creditsSpent = 0, balance = 0) {
|
|
1626
1636
|
var _a;
|
|
1627
1637
|
state.set({ hasAccess: true, isLoaded: true, isLoading: false });
|
|
1628
|
-
|
|
1629
|
-
|
|
1638
|
+
if (!config.headless) {
|
|
1639
|
+
gate.reveal();
|
|
1640
|
+
renderer.render('granted', { onLogin: doLogin, onPurchase: doPurchase, onBuyMoreCredits: doBuyMoreCredits });
|
|
1641
|
+
}
|
|
1630
1642
|
emitter.emit('paywall:hidden', {});
|
|
1631
1643
|
emitter.emit('article:purchased', { creditsSpent, remainingBalance: balance });
|
|
1632
1644
|
(_a = config.onAccessGranted) === null || _a === void 0 ? void 0 : _a.call(config);
|
|
@@ -1643,7 +1655,8 @@ function createPaywall(config, creditsApi, state, emitter, existingGate) {
|
|
|
1643
1655
|
window.location.href = authUrl;
|
|
1644
1656
|
return;
|
|
1645
1657
|
}
|
|
1646
|
-
|
|
1658
|
+
if (!config.headless)
|
|
1659
|
+
renderer.render('loading', { onLogin: doLogin, onPurchase: doPurchase, onBuyMoreCredits: doBuyMoreCredits });
|
|
1647
1660
|
const token = await openAuthPopup(authUrl);
|
|
1648
1661
|
if (token) {
|
|
1649
1662
|
state.set({ isLoggedIn: true });
|
|
@@ -1651,12 +1664,13 @@ function createPaywall(config, creditsApi, state, emitter, existingGate) {
|
|
|
1651
1664
|
}
|
|
1652
1665
|
else {
|
|
1653
1666
|
// Popup closed without login
|
|
1654
|
-
|
|
1667
|
+
if (!config.headless)
|
|
1668
|
+
renderer.render('login', { onLogin: doLogin, onPurchase: doPurchase, onBuyMoreCredits: doBuyMoreCredits });
|
|
1655
1669
|
}
|
|
1656
1670
|
}
|
|
1657
1671
|
// ── Purchase ──────────────────────────────────────────────────────────────
|
|
1658
1672
|
async function doPurchase() {
|
|
1659
|
-
var _a, _b, _c;
|
|
1673
|
+
var _a, _b, _c, _d, _e;
|
|
1660
1674
|
if (!tokenStorage.has()) {
|
|
1661
1675
|
await doLogin();
|
|
1662
1676
|
return;
|
|
@@ -1670,7 +1684,8 @@ function createPaywall(config, creditsApi, state, emitter, existingGate) {
|
|
|
1670
1684
|
});
|
|
1671
1685
|
return;
|
|
1672
1686
|
}
|
|
1673
|
-
|
|
1687
|
+
if (!config.headless)
|
|
1688
|
+
renderer.render('loading', { onLogin: doLogin, onPurchase: doPurchase, onBuyMoreCredits: doBuyMoreCredits });
|
|
1674
1689
|
state.set({ isLoading: true });
|
|
1675
1690
|
try {
|
|
1676
1691
|
const result = await creditsApi.purchaseArticle({
|
|
@@ -1684,7 +1699,8 @@ function createPaywall(config, creditsApi, state, emitter, existingGate) {
|
|
|
1684
1699
|
}
|
|
1685
1700
|
else {
|
|
1686
1701
|
state.set({ isLoading: false });
|
|
1687
|
-
|
|
1702
|
+
if (!config.headless)
|
|
1703
|
+
renderer.render('purchase', { onLogin: doLogin, onPurchase: doPurchase, onBuyMoreCredits: doBuyMoreCredits });
|
|
1688
1704
|
emitter.emit('error', { message: (_a = result.message) !== null && _a !== void 0 ? _a : 'Purchase failed' });
|
|
1689
1705
|
}
|
|
1690
1706
|
}
|
|
@@ -1692,17 +1708,24 @@ function createPaywall(config, creditsApi, state, emitter, existingGate) {
|
|
|
1692
1708
|
state.set({ isLoading: false });
|
|
1693
1709
|
if (err instanceof ApiError && err.status === 402) {
|
|
1694
1710
|
// Insufficient credits
|
|
1695
|
-
|
|
1711
|
+
if (!config.headless) {
|
|
1712
|
+
renderer.render('insufficient', { onLogin: doLogin, onPurchase: doPurchase, onBuyMoreCredits: doBuyMoreCredits }, {
|
|
1713
|
+
requiredCredits: state.get().requiredCredits,
|
|
1714
|
+
creditBalance: state.get().creditBalance,
|
|
1715
|
+
});
|
|
1716
|
+
}
|
|
1717
|
+
const required = (_b = state.get().requiredCredits) !== null && _b !== void 0 ? _b : 0;
|
|
1718
|
+
const available = (_c = state.get().creditBalance) !== null && _c !== void 0 ? _c : 0;
|
|
1719
|
+
(_d = config.onInsufficientCredits) === null || _d === void 0 ? void 0 : _d.call(config, { required, available });
|
|
1720
|
+
emitter.emit('credits:insufficient', { required, available });
|
|
1721
|
+
}
|
|
1722
|
+
else {
|
|
1723
|
+
if (!config.headless)
|
|
1724
|
+
renderer.render('purchase', { onLogin: doLogin, onPurchase: doPurchase, onBuyMoreCredits: doBuyMoreCredits });
|
|
1725
|
+
(_e = config.onPurchaseRequired) === null || _e === void 0 ? void 0 : _e.call(config, {
|
|
1696
1726
|
requiredCredits: state.get().requiredCredits,
|
|
1697
1727
|
creditBalance: state.get().creditBalance,
|
|
1698
1728
|
});
|
|
1699
|
-
emitter.emit('credits:insufficient', {
|
|
1700
|
-
required: (_b = state.get().requiredCredits) !== null && _b !== void 0 ? _b : 0,
|
|
1701
|
-
available: (_c = state.get().creditBalance) !== null && _c !== void 0 ? _c : 0,
|
|
1702
|
-
});
|
|
1703
|
-
}
|
|
1704
|
-
else {
|
|
1705
|
-
renderer.render('purchase', { onLogin: doLogin, onPurchase: doPurchase, onBuyMoreCredits: doBuyMoreCredits });
|
|
1706
1729
|
emitter.emit('error', { message: 'Purchase failed', error: err });
|
|
1707
1730
|
}
|
|
1708
1731
|
}
|
|
@@ -1712,16 +1735,21 @@ function createPaywall(config, creditsApi, state, emitter, existingGate) {
|
|
|
1712
1735
|
}
|
|
1713
1736
|
// ── Access Check ──────────────────────────────────────────────────────────
|
|
1714
1737
|
async function checkAccess() {
|
|
1738
|
+
var _a, _b, _c;
|
|
1715
1739
|
state.set({ isLoading: true });
|
|
1716
|
-
|
|
1740
|
+
if (!config.headless)
|
|
1741
|
+
renderer.render('checking', { onLogin: doLogin, onPurchase: doPurchase, onBuyMoreCredits: doBuyMoreCredits });
|
|
1717
1742
|
if (extensionAvailable) {
|
|
1718
1743
|
bridge.requestAuthorization(config.apiKey, config.hostName);
|
|
1719
1744
|
return; // response handled in onAuthorizationResponse
|
|
1720
1745
|
}
|
|
1721
1746
|
if (!tokenStorage.has()) {
|
|
1722
1747
|
state.set({ isLoading: false, isLoaded: true });
|
|
1723
|
-
|
|
1724
|
-
|
|
1748
|
+
if (!config.headless) {
|
|
1749
|
+
gate.hide();
|
|
1750
|
+
renderer.render('login', { onLogin: doLogin, onPurchase: doPurchase, onBuyMoreCredits: doBuyMoreCredits });
|
|
1751
|
+
}
|
|
1752
|
+
(_a = config.onLoginRequired) === null || _a === void 0 ? void 0 : _a.call(config);
|
|
1725
1753
|
emitter.emit('paywall:shown', {});
|
|
1726
1754
|
return;
|
|
1727
1755
|
}
|
|
@@ -1741,15 +1769,24 @@ function createPaywall(config, creditsApi, state, emitter, existingGate) {
|
|
|
1741
1769
|
handleAccessGranted(0, 0);
|
|
1742
1770
|
}
|
|
1743
1771
|
else {
|
|
1744
|
-
|
|
1745
|
-
|
|
1772
|
+
if (!config.headless) {
|
|
1773
|
+
gate.hide();
|
|
1774
|
+
renderer.render('purchase', { onLogin: doLogin, onPurchase: doPurchase, onBuyMoreCredits: doBuyMoreCredits });
|
|
1775
|
+
}
|
|
1776
|
+
(_b = config.onPurchaseRequired) === null || _b === void 0 ? void 0 : _b.call(config, {
|
|
1777
|
+
requiredCredits: state.get().requiredCredits,
|
|
1778
|
+
creditBalance: state.get().creditBalance,
|
|
1779
|
+
});
|
|
1746
1780
|
emitter.emit('paywall:shown', {});
|
|
1747
1781
|
}
|
|
1748
1782
|
}
|
|
1749
1783
|
catch (err) {
|
|
1750
1784
|
state.set({ isLoading: false, isLoaded: true });
|
|
1751
|
-
|
|
1752
|
-
|
|
1785
|
+
if (!config.headless) {
|
|
1786
|
+
gate.hide();
|
|
1787
|
+
renderer.render('login', { onLogin: doLogin, onPurchase: doPurchase, onBuyMoreCredits: doBuyMoreCredits });
|
|
1788
|
+
}
|
|
1789
|
+
(_c = config.onLoginRequired) === null || _c === void 0 ? void 0 : _c.call(config);
|
|
1753
1790
|
if (!(err instanceof ApiError && err.status === 401)) {
|
|
1754
1791
|
emitter.emit('error', { message: 'Access check failed', error: err });
|
|
1755
1792
|
}
|
|
@@ -1768,7 +1805,7 @@ function createPaywall(config, creditsApi, state, emitter, existingGate) {
|
|
|
1768
1805
|
if (extensionAvailable) {
|
|
1769
1806
|
bridge.attach();
|
|
1770
1807
|
bridge.onAuthorizationResponse(data => {
|
|
1771
|
-
var _a, _b, _c;
|
|
1808
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
1772
1809
|
state.set({
|
|
1773
1810
|
isLoggedIn: data.isAuthenticated,
|
|
1774
1811
|
hasAccess: data.doesHaveAccess,
|
|
@@ -1778,18 +1815,27 @@ function createPaywall(config, creditsApi, state, emitter, existingGate) {
|
|
|
1778
1815
|
requiredCredits: (_b = data.requiredCredits) !== null && _b !== void 0 ? _b : null,
|
|
1779
1816
|
});
|
|
1780
1817
|
if (!data.isAuthenticated) {
|
|
1781
|
-
|
|
1782
|
-
|
|
1818
|
+
if (!config.headless) {
|
|
1819
|
+
gate.hide();
|
|
1820
|
+
renderer.render('login', { onLogin: doLogin, onPurchase: doPurchase, onBuyMoreCredits: doBuyMoreCredits });
|
|
1821
|
+
}
|
|
1822
|
+
(_c = config.onLoginRequired) === null || _c === void 0 ? void 0 : _c.call(config);
|
|
1783
1823
|
emitter.emit('paywall:shown', {});
|
|
1784
1824
|
}
|
|
1785
1825
|
else if (data.doesHaveAccess) {
|
|
1786
|
-
handleAccessGranted(0, (
|
|
1826
|
+
handleAccessGranted(0, (_d = data.creditBalance) !== null && _d !== void 0 ? _d : 0);
|
|
1787
1827
|
}
|
|
1788
1828
|
else {
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1829
|
+
if (!config.headless) {
|
|
1830
|
+
gate.hide();
|
|
1831
|
+
renderer.render('purchase', { onLogin: doLogin, onPurchase: doPurchase, onBuyMoreCredits: doBuyMoreCredits }, {
|
|
1832
|
+
requiredCredits: data.requiredCredits,
|
|
1833
|
+
creditBalance: data.creditBalance,
|
|
1834
|
+
});
|
|
1835
|
+
}
|
|
1836
|
+
(_e = config.onPurchaseRequired) === null || _e === void 0 ? void 0 : _e.call(config, {
|
|
1837
|
+
requiredCredits: (_f = data.requiredCredits) !== null && _f !== void 0 ? _f : null,
|
|
1838
|
+
creditBalance: (_g = data.creditBalance) !== null && _g !== void 0 ? _g : null,
|
|
1793
1839
|
});
|
|
1794
1840
|
emitter.emit('paywall:shown', {});
|
|
1795
1841
|
}
|
|
@@ -1810,10 +1856,19 @@ function createPaywall(config, creditsApi, state, emitter, existingGate) {
|
|
|
1810
1856
|
}
|
|
1811
1857
|
function destroy() {
|
|
1812
1858
|
bridge.detach();
|
|
1813
|
-
|
|
1814
|
-
|
|
1859
|
+
if (!config.headless) {
|
|
1860
|
+
renderer.destroy();
|
|
1861
|
+
gate.reveal();
|
|
1862
|
+
}
|
|
1815
1863
|
}
|
|
1816
|
-
return {
|
|
1864
|
+
return {
|
|
1865
|
+
init,
|
|
1866
|
+
checkAccess,
|
|
1867
|
+
destroy,
|
|
1868
|
+
login: doLogin,
|
|
1869
|
+
purchase: doPurchase,
|
|
1870
|
+
buyMoreCredits: doBuyMoreCredits,
|
|
1871
|
+
};
|
|
1817
1872
|
}
|
|
1818
1873
|
|
|
1819
1874
|
const POSITION_KEY = 'cc-widget-pos';
|
|
@@ -2675,21 +2730,42 @@ class ContentCredits {
|
|
|
2675
2730
|
async _start() {
|
|
2676
2731
|
// 1. Consume any token that arrived in the URL (mobile redirect flow)
|
|
2677
2732
|
consumeTokenFromUrl();
|
|
2678
|
-
// 2.
|
|
2733
|
+
// 2. Wire config-level callbacks so developers don't need separate on() calls.
|
|
2734
|
+
if (this.config.onStateChange) {
|
|
2735
|
+
this.state.subscribe(this.config.onStateChange);
|
|
2736
|
+
}
|
|
2737
|
+
if (this.config.onReady) {
|
|
2738
|
+
this.emitter.on('ready', ({ state }) => this.config.onReady(state));
|
|
2739
|
+
}
|
|
2740
|
+
if (this.config.onPurchased) {
|
|
2741
|
+
this.emitter.on('article:purchased', (payload) => this.config.onPurchased(payload));
|
|
2742
|
+
}
|
|
2743
|
+
if (this.config.onUserLogin) {
|
|
2744
|
+
this.emitter.on('auth:login', ({ user }) => this.config.onUserLogin(user));
|
|
2745
|
+
}
|
|
2746
|
+
if (this.config.onUserLogout) {
|
|
2747
|
+
this.emitter.on('auth:logout', () => this.config.onUserLogout());
|
|
2748
|
+
}
|
|
2749
|
+
if (this.config.onError) {
|
|
2750
|
+
this.emitter.on('error', (payload) => this.config.onError(payload));
|
|
2751
|
+
}
|
|
2752
|
+
// 3. Hide premium content immediately (synchronous) before any async work.
|
|
2679
2753
|
// This prevents the flash of full article content that would otherwise
|
|
2680
2754
|
// appear during the token-refresh and access-check network round-trips.
|
|
2755
|
+
// Skipped in headless mode — the host app owns all DOM manipulation.
|
|
2681
2756
|
const earlyGate = createGate({
|
|
2682
2757
|
selector: this.config.contentSelector,
|
|
2683
2758
|
teaserParagraphs: this.config.teaserParagraphs,
|
|
2684
2759
|
});
|
|
2685
|
-
|
|
2686
|
-
|
|
2760
|
+
if (!this.config.headless)
|
|
2761
|
+
earlyGate.hide();
|
|
2762
|
+
// 4. If no access token in memory/session, attempt a silent refresh.
|
|
2687
2763
|
// This runs on every new browser session (after the browser was closed)
|
|
2688
2764
|
// and silently re-authenticates the user using their stored refresh token.
|
|
2689
2765
|
if (!tokenStorage.has()) {
|
|
2690
2766
|
await tryRefreshSession(this.config.apiBaseUrl);
|
|
2691
2767
|
}
|
|
2692
|
-
// Pass the pre-created gate so createPaywall reuses the same instance
|
|
2768
|
+
// 5. Pass the pre-created gate so createPaywall reuses the same instance
|
|
2693
2769
|
// (and its hiddenNodes list) rather than creating a second one.
|
|
2694
2770
|
this.paywallModule = createPaywall(this.config, this.creditsApi, this.state, this.emitter, earlyGate);
|
|
2695
2771
|
if (this.config.enableComments) {
|
|
@@ -2700,6 +2776,63 @@ class ContentCredits {
|
|
|
2700
2776
|
this.emitter.emit('ready', { state: this.state.get() });
|
|
2701
2777
|
}
|
|
2702
2778
|
// ── Public API ────────────────────────────────────────────────────────────
|
|
2779
|
+
/**
|
|
2780
|
+
* Subscribe to state changes. The callback receives the full state snapshot
|
|
2781
|
+
* every time any field changes. Returns an unsubscribe function.
|
|
2782
|
+
*
|
|
2783
|
+
* Primarily useful in **headless mode** — lets you drive your own UI from
|
|
2784
|
+
* reactive state without polling `getState()`.
|
|
2785
|
+
*
|
|
2786
|
+
* @example
|
|
2787
|
+
* const unsubscribe = cc.subscribe((state) => {
|
|
2788
|
+
* if (state.hasAccess) showFullContent();
|
|
2789
|
+
* else showPaywall(state);
|
|
2790
|
+
* });
|
|
2791
|
+
*/
|
|
2792
|
+
subscribe(fn) {
|
|
2793
|
+
return this.state.subscribe(fn);
|
|
2794
|
+
}
|
|
2795
|
+
/**
|
|
2796
|
+
* Trigger the login flow programmatically.
|
|
2797
|
+
*
|
|
2798
|
+
* - Desktop: opens a popup window to the Content Credits auth page.
|
|
2799
|
+
* - Mobile: performs a full-page redirect.
|
|
2800
|
+
* - Extension: delegates to the browser extension.
|
|
2801
|
+
*
|
|
2802
|
+
* Primarily useful in **headless mode** where you render your own "Login"
|
|
2803
|
+
* button and call this from its `onClick` handler.
|
|
2804
|
+
*/
|
|
2805
|
+
async login() {
|
|
2806
|
+
var _a;
|
|
2807
|
+
await ((_a = this.paywallModule) === null || _a === void 0 ? void 0 : _a.login());
|
|
2808
|
+
}
|
|
2809
|
+
/**
|
|
2810
|
+
* Trigger the article purchase flow programmatically.
|
|
2811
|
+
*
|
|
2812
|
+
* Deducts the required credits from the user's balance and, on success,
|
|
2813
|
+
* updates `state.hasAccess` to `true` and emits `article:purchased`.
|
|
2814
|
+
*
|
|
2815
|
+
* If the user is not logged in, this automatically opens the login flow
|
|
2816
|
+
* first, then proceeds with the purchase.
|
|
2817
|
+
*
|
|
2818
|
+
* Primarily useful in **headless mode** where you render your own "Unlock"
|
|
2819
|
+
* button and call this from its `onClick` handler.
|
|
2820
|
+
*/
|
|
2821
|
+
async purchase() {
|
|
2822
|
+
var _a;
|
|
2823
|
+
await ((_a = this.paywallModule) === null || _a === void 0 ? void 0 : _a.purchase());
|
|
2824
|
+
}
|
|
2825
|
+
/**
|
|
2826
|
+
* Open the Content Credits dashboard in a new tab so the user can top up
|
|
2827
|
+
* their credit balance.
|
|
2828
|
+
*
|
|
2829
|
+
* Primarily useful in **headless mode** when `state.creditBalance` is lower
|
|
2830
|
+
* than `state.requiredCredits`.
|
|
2831
|
+
*/
|
|
2832
|
+
buyMoreCredits() {
|
|
2833
|
+
var _a;
|
|
2834
|
+
(_a = this.paywallModule) === null || _a === void 0 ? void 0 : _a.buyMoreCredits();
|
|
2835
|
+
}
|
|
2703
2836
|
/** Subscribe to an SDK event. Returns an unsubscribe function. */
|
|
2704
2837
|
on(event, handler) {
|
|
2705
2838
|
return this.emitter.on(event, handler);
|
|
@@ -2741,7 +2874,7 @@ class ContentCredits {
|
|
|
2741
2874
|
}
|
|
2742
2875
|
/** SDK version string. */
|
|
2743
2876
|
static get version() {
|
|
2744
|
-
return "2.
|
|
2877
|
+
return "2.3.0";
|
|
2745
2878
|
}
|
|
2746
2879
|
}
|
|
2747
2880
|
// ── Auto-init from script data attributes (CDN usage) ────────────────────────
|