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