@contentcredits/sdk 2.1.0 → 2.2.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 +123 -34
- package/dist/content-credits.cjs.js.map +1 -1
- package/dist/content-credits.d.ts +60 -0
- package/dist/content-credits.esm.js +123 -34
- 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 +14 -0
- package/package.json +1 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
function resolveConfig(raw) {
|
|
2
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
|
|
2
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
|
|
3
3
|
if (!raw.apiKey || typeof raw.apiKey !== 'string' || raw.apiKey.trim() === '') {
|
|
4
4
|
throw new Error('[ContentCredits] apiKey is required. Get yours from the Content Credits admin panel.');
|
|
5
5
|
}
|
|
@@ -8,7 +8,7 @@ function resolveConfig(raw) {
|
|
|
8
8
|
try {
|
|
9
9
|
hostName = new URL(articleUrl).hostname;
|
|
10
10
|
}
|
|
11
|
-
catch (
|
|
11
|
+
catch (_m) {
|
|
12
12
|
throw new Error(`[ContentCredits] Invalid articleUrl: "${articleUrl}"`);
|
|
13
13
|
}
|
|
14
14
|
return {
|
|
@@ -21,13 +21,14 @@ function resolveConfig(raw) {
|
|
|
21
21
|
enableComments: (_d = raw.enableComments) !== null && _d !== void 0 ? _d : true,
|
|
22
22
|
extensionId: (_e = raw.extensionId) !== null && _e !== void 0 ? _e : "ljehdpabbhgccmanhjdfacjnaigpgcml",
|
|
23
23
|
debug: (_f = raw.debug) !== null && _f !== void 0 ? _f : false,
|
|
24
|
+
headless: (_g = raw.headless) !== null && _g !== void 0 ? _g : false,
|
|
24
25
|
apiBaseUrl: "https://api.contentcredits.com",
|
|
25
26
|
accountsUrl: "https://accounts.contentcredits.com",
|
|
26
27
|
paywallTemplate: raw.paywallTemplate,
|
|
27
28
|
onAccessGranted: raw.onAccessGranted,
|
|
28
29
|
theme: {
|
|
29
|
-
primaryColor: (
|
|
30
|
-
fontFamily: (
|
|
30
|
+
primaryColor: (_j = (_h = raw.theme) === null || _h === void 0 ? void 0 : _h.primaryColor) !== null && _j !== void 0 ? _j : '#44C678',
|
|
31
|
+
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",
|
|
31
32
|
},
|
|
32
33
|
};
|
|
33
34
|
}
|
|
@@ -1623,8 +1624,10 @@ function createPaywall(config, creditsApi, state, emitter, existingGate) {
|
|
|
1623
1624
|
function handleAccessGranted(creditsSpent = 0, balance = 0) {
|
|
1624
1625
|
var _a;
|
|
1625
1626
|
state.set({ hasAccess: true, isLoaded: true, isLoading: false });
|
|
1626
|
-
|
|
1627
|
-
|
|
1627
|
+
if (!config.headless) {
|
|
1628
|
+
gate.reveal();
|
|
1629
|
+
renderer.render('granted', { onLogin: doLogin, onPurchase: doPurchase, onBuyMoreCredits: doBuyMoreCredits });
|
|
1630
|
+
}
|
|
1628
1631
|
emitter.emit('paywall:hidden', {});
|
|
1629
1632
|
emitter.emit('article:purchased', { creditsSpent, remainingBalance: balance });
|
|
1630
1633
|
(_a = config.onAccessGranted) === null || _a === void 0 ? void 0 : _a.call(config);
|
|
@@ -1641,7 +1644,8 @@ function createPaywall(config, creditsApi, state, emitter, existingGate) {
|
|
|
1641
1644
|
window.location.href = authUrl;
|
|
1642
1645
|
return;
|
|
1643
1646
|
}
|
|
1644
|
-
|
|
1647
|
+
if (!config.headless)
|
|
1648
|
+
renderer.render('loading', { onLogin: doLogin, onPurchase: doPurchase, onBuyMoreCredits: doBuyMoreCredits });
|
|
1645
1649
|
const token = await openAuthPopup(authUrl);
|
|
1646
1650
|
if (token) {
|
|
1647
1651
|
state.set({ isLoggedIn: true });
|
|
@@ -1649,7 +1653,8 @@ function createPaywall(config, creditsApi, state, emitter, existingGate) {
|
|
|
1649
1653
|
}
|
|
1650
1654
|
else {
|
|
1651
1655
|
// Popup closed without login
|
|
1652
|
-
|
|
1656
|
+
if (!config.headless)
|
|
1657
|
+
renderer.render('login', { onLogin: doLogin, onPurchase: doPurchase, onBuyMoreCredits: doBuyMoreCredits });
|
|
1653
1658
|
}
|
|
1654
1659
|
}
|
|
1655
1660
|
// ── Purchase ──────────────────────────────────────────────────────────────
|
|
@@ -1668,7 +1673,8 @@ function createPaywall(config, creditsApi, state, emitter, existingGate) {
|
|
|
1668
1673
|
});
|
|
1669
1674
|
return;
|
|
1670
1675
|
}
|
|
1671
|
-
|
|
1676
|
+
if (!config.headless)
|
|
1677
|
+
renderer.render('loading', { onLogin: doLogin, onPurchase: doPurchase, onBuyMoreCredits: doBuyMoreCredits });
|
|
1672
1678
|
state.set({ isLoading: true });
|
|
1673
1679
|
try {
|
|
1674
1680
|
const result = await creditsApi.purchaseArticle({
|
|
@@ -1682,7 +1688,8 @@ function createPaywall(config, creditsApi, state, emitter, existingGate) {
|
|
|
1682
1688
|
}
|
|
1683
1689
|
else {
|
|
1684
1690
|
state.set({ isLoading: false });
|
|
1685
|
-
|
|
1691
|
+
if (!config.headless)
|
|
1692
|
+
renderer.render('purchase', { onLogin: doLogin, onPurchase: doPurchase, onBuyMoreCredits: doBuyMoreCredits });
|
|
1686
1693
|
emitter.emit('error', { message: (_a = result.message) !== null && _a !== void 0 ? _a : 'Purchase failed' });
|
|
1687
1694
|
}
|
|
1688
1695
|
}
|
|
@@ -1690,17 +1697,20 @@ function createPaywall(config, creditsApi, state, emitter, existingGate) {
|
|
|
1690
1697
|
state.set({ isLoading: false });
|
|
1691
1698
|
if (err instanceof ApiError && err.status === 402) {
|
|
1692
1699
|
// Insufficient credits
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1700
|
+
if (!config.headless) {
|
|
1701
|
+
renderer.render('insufficient', { onLogin: doLogin, onPurchase: doPurchase, onBuyMoreCredits: doBuyMoreCredits }, {
|
|
1702
|
+
requiredCredits: state.get().requiredCredits,
|
|
1703
|
+
creditBalance: state.get().creditBalance,
|
|
1704
|
+
});
|
|
1705
|
+
}
|
|
1697
1706
|
emitter.emit('credits:insufficient', {
|
|
1698
1707
|
required: (_b = state.get().requiredCredits) !== null && _b !== void 0 ? _b : 0,
|
|
1699
1708
|
available: (_c = state.get().creditBalance) !== null && _c !== void 0 ? _c : 0,
|
|
1700
1709
|
});
|
|
1701
1710
|
}
|
|
1702
1711
|
else {
|
|
1703
|
-
|
|
1712
|
+
if (!config.headless)
|
|
1713
|
+
renderer.render('purchase', { onLogin: doLogin, onPurchase: doPurchase, onBuyMoreCredits: doBuyMoreCredits });
|
|
1704
1714
|
emitter.emit('error', { message: 'Purchase failed', error: err });
|
|
1705
1715
|
}
|
|
1706
1716
|
}
|
|
@@ -1711,15 +1721,18 @@ function createPaywall(config, creditsApi, state, emitter, existingGate) {
|
|
|
1711
1721
|
// ── Access Check ──────────────────────────────────────────────────────────
|
|
1712
1722
|
async function checkAccess() {
|
|
1713
1723
|
state.set({ isLoading: true });
|
|
1714
|
-
|
|
1724
|
+
if (!config.headless)
|
|
1725
|
+
renderer.render('checking', { onLogin: doLogin, onPurchase: doPurchase, onBuyMoreCredits: doBuyMoreCredits });
|
|
1715
1726
|
if (extensionAvailable) {
|
|
1716
1727
|
bridge.requestAuthorization(config.apiKey, config.hostName);
|
|
1717
1728
|
return; // response handled in onAuthorizationResponse
|
|
1718
1729
|
}
|
|
1719
1730
|
if (!tokenStorage.has()) {
|
|
1720
1731
|
state.set({ isLoading: false, isLoaded: true });
|
|
1721
|
-
|
|
1722
|
-
|
|
1732
|
+
if (!config.headless) {
|
|
1733
|
+
gate.hide();
|
|
1734
|
+
renderer.render('login', { onLogin: doLogin, onPurchase: doPurchase, onBuyMoreCredits: doBuyMoreCredits });
|
|
1735
|
+
}
|
|
1723
1736
|
emitter.emit('paywall:shown', {});
|
|
1724
1737
|
return;
|
|
1725
1738
|
}
|
|
@@ -1739,15 +1752,19 @@ function createPaywall(config, creditsApi, state, emitter, existingGate) {
|
|
|
1739
1752
|
handleAccessGranted(0, 0);
|
|
1740
1753
|
}
|
|
1741
1754
|
else {
|
|
1742
|
-
|
|
1743
|
-
|
|
1755
|
+
if (!config.headless) {
|
|
1756
|
+
gate.hide();
|
|
1757
|
+
renderer.render('purchase', { onLogin: doLogin, onPurchase: doPurchase, onBuyMoreCredits: doBuyMoreCredits });
|
|
1758
|
+
}
|
|
1744
1759
|
emitter.emit('paywall:shown', {});
|
|
1745
1760
|
}
|
|
1746
1761
|
}
|
|
1747
1762
|
catch (err) {
|
|
1748
1763
|
state.set({ isLoading: false, isLoaded: true });
|
|
1749
|
-
|
|
1750
|
-
|
|
1764
|
+
if (!config.headless) {
|
|
1765
|
+
gate.hide();
|
|
1766
|
+
renderer.render('login', { onLogin: doLogin, onPurchase: doPurchase, onBuyMoreCredits: doBuyMoreCredits });
|
|
1767
|
+
}
|
|
1751
1768
|
if (!(err instanceof ApiError && err.status === 401)) {
|
|
1752
1769
|
emitter.emit('error', { message: 'Access check failed', error: err });
|
|
1753
1770
|
}
|
|
@@ -1776,19 +1793,23 @@ function createPaywall(config, creditsApi, state, emitter, existingGate) {
|
|
|
1776
1793
|
requiredCredits: (_b = data.requiredCredits) !== null && _b !== void 0 ? _b : null,
|
|
1777
1794
|
});
|
|
1778
1795
|
if (!data.isAuthenticated) {
|
|
1779
|
-
|
|
1780
|
-
|
|
1796
|
+
if (!config.headless) {
|
|
1797
|
+
gate.hide();
|
|
1798
|
+
renderer.render('login', { onLogin: doLogin, onPurchase: doPurchase, onBuyMoreCredits: doBuyMoreCredits });
|
|
1799
|
+
}
|
|
1781
1800
|
emitter.emit('paywall:shown', {});
|
|
1782
1801
|
}
|
|
1783
1802
|
else if (data.doesHaveAccess) {
|
|
1784
1803
|
handleAccessGranted(0, (_c = data.creditBalance) !== null && _c !== void 0 ? _c : 0);
|
|
1785
1804
|
}
|
|
1786
1805
|
else {
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1806
|
+
if (!config.headless) {
|
|
1807
|
+
gate.hide();
|
|
1808
|
+
renderer.render('purchase', { onLogin: doLogin, onPurchase: doPurchase, onBuyMoreCredits: doBuyMoreCredits }, {
|
|
1809
|
+
requiredCredits: data.requiredCredits,
|
|
1810
|
+
creditBalance: data.creditBalance,
|
|
1811
|
+
});
|
|
1812
|
+
}
|
|
1792
1813
|
emitter.emit('paywall:shown', {});
|
|
1793
1814
|
}
|
|
1794
1815
|
});
|
|
@@ -1808,10 +1829,19 @@ function createPaywall(config, creditsApi, state, emitter, existingGate) {
|
|
|
1808
1829
|
}
|
|
1809
1830
|
function destroy() {
|
|
1810
1831
|
bridge.detach();
|
|
1811
|
-
|
|
1812
|
-
|
|
1832
|
+
if (!config.headless) {
|
|
1833
|
+
renderer.destroy();
|
|
1834
|
+
gate.reveal();
|
|
1835
|
+
}
|
|
1813
1836
|
}
|
|
1814
|
-
return {
|
|
1837
|
+
return {
|
|
1838
|
+
init,
|
|
1839
|
+
checkAccess,
|
|
1840
|
+
destroy,
|
|
1841
|
+
login: doLogin,
|
|
1842
|
+
purchase: doPurchase,
|
|
1843
|
+
buyMoreCredits: doBuyMoreCredits,
|
|
1844
|
+
};
|
|
1815
1845
|
}
|
|
1816
1846
|
|
|
1817
1847
|
const POSITION_KEY = 'cc-widget-pos';
|
|
@@ -2676,11 +2706,13 @@ class ContentCredits {
|
|
|
2676
2706
|
// 2. Hide premium content immediately (synchronous) before any async work.
|
|
2677
2707
|
// This prevents the flash of full article content that would otherwise
|
|
2678
2708
|
// appear during the token-refresh and access-check network round-trips.
|
|
2709
|
+
// Skipped in headless mode — the host app owns all DOM manipulation.
|
|
2679
2710
|
const earlyGate = createGate({
|
|
2680
2711
|
selector: this.config.contentSelector,
|
|
2681
2712
|
teaserParagraphs: this.config.teaserParagraphs,
|
|
2682
2713
|
});
|
|
2683
|
-
|
|
2714
|
+
if (!this.config.headless)
|
|
2715
|
+
earlyGate.hide();
|
|
2684
2716
|
// 3. If no access token in memory/session, attempt a silent refresh.
|
|
2685
2717
|
// This runs on every new browser session (after the browser was closed)
|
|
2686
2718
|
// and silently re-authenticates the user using their stored refresh token.
|
|
@@ -2698,6 +2730,63 @@ class ContentCredits {
|
|
|
2698
2730
|
this.emitter.emit('ready', { state: this.state.get() });
|
|
2699
2731
|
}
|
|
2700
2732
|
// ── Public API ────────────────────────────────────────────────────────────
|
|
2733
|
+
/**
|
|
2734
|
+
* Subscribe to state changes. The callback receives the full state snapshot
|
|
2735
|
+
* every time any field changes. Returns an unsubscribe function.
|
|
2736
|
+
*
|
|
2737
|
+
* Primarily useful in **headless mode** — lets you drive your own UI from
|
|
2738
|
+
* reactive state without polling `getState()`.
|
|
2739
|
+
*
|
|
2740
|
+
* @example
|
|
2741
|
+
* const unsubscribe = cc.subscribe((state) => {
|
|
2742
|
+
* if (state.hasAccess) showFullContent();
|
|
2743
|
+
* else showPaywall(state);
|
|
2744
|
+
* });
|
|
2745
|
+
*/
|
|
2746
|
+
subscribe(fn) {
|
|
2747
|
+
return this.state.subscribe(fn);
|
|
2748
|
+
}
|
|
2749
|
+
/**
|
|
2750
|
+
* Trigger the login flow programmatically.
|
|
2751
|
+
*
|
|
2752
|
+
* - Desktop: opens a popup window to the Content Credits auth page.
|
|
2753
|
+
* - Mobile: performs a full-page redirect.
|
|
2754
|
+
* - Extension: delegates to the browser extension.
|
|
2755
|
+
*
|
|
2756
|
+
* Primarily useful in **headless mode** where you render your own "Login"
|
|
2757
|
+
* button and call this from its `onClick` handler.
|
|
2758
|
+
*/
|
|
2759
|
+
async login() {
|
|
2760
|
+
var _a;
|
|
2761
|
+
await ((_a = this.paywallModule) === null || _a === void 0 ? void 0 : _a.login());
|
|
2762
|
+
}
|
|
2763
|
+
/**
|
|
2764
|
+
* Trigger the article purchase flow programmatically.
|
|
2765
|
+
*
|
|
2766
|
+
* Deducts the required credits from the user's balance and, on success,
|
|
2767
|
+
* updates `state.hasAccess` to `true` and emits `article:purchased`.
|
|
2768
|
+
*
|
|
2769
|
+
* If the user is not logged in, this automatically opens the login flow
|
|
2770
|
+
* first, then proceeds with the purchase.
|
|
2771
|
+
*
|
|
2772
|
+
* Primarily useful in **headless mode** where you render your own "Unlock"
|
|
2773
|
+
* button and call this from its `onClick` handler.
|
|
2774
|
+
*/
|
|
2775
|
+
async purchase() {
|
|
2776
|
+
var _a;
|
|
2777
|
+
await ((_a = this.paywallModule) === null || _a === void 0 ? void 0 : _a.purchase());
|
|
2778
|
+
}
|
|
2779
|
+
/**
|
|
2780
|
+
* Open the Content Credits dashboard in a new tab so the user can top up
|
|
2781
|
+
* their credit balance.
|
|
2782
|
+
*
|
|
2783
|
+
* Primarily useful in **headless mode** when `state.creditBalance` is lower
|
|
2784
|
+
* than `state.requiredCredits`.
|
|
2785
|
+
*/
|
|
2786
|
+
buyMoreCredits() {
|
|
2787
|
+
var _a;
|
|
2788
|
+
(_a = this.paywallModule) === null || _a === void 0 ? void 0 : _a.buyMoreCredits();
|
|
2789
|
+
}
|
|
2701
2790
|
/** Subscribe to an SDK event. Returns an unsubscribe function. */
|
|
2702
2791
|
on(event, handler) {
|
|
2703
2792
|
return this.emitter.on(event, handler);
|
|
@@ -2739,7 +2828,7 @@ class ContentCredits {
|
|
|
2739
2828
|
}
|
|
2740
2829
|
/** SDK version string. */
|
|
2741
2830
|
static get version() {
|
|
2742
|
-
return "2.
|
|
2831
|
+
return "2.2.0";
|
|
2743
2832
|
}
|
|
2744
2833
|
}
|
|
2745
2834
|
// ── Auto-init from script data attributes (CDN usage) ────────────────────────
|