@aforoai/storefront-widgets 1.0.3 → 1.0.5
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/CHANGELOG.md +22 -0
- package/dist/index.cjs +80 -33
- package/dist/index.d.cts +18 -5
- package/dist/index.d.ts +18 -5
- package/dist/index.mjs +80 -33
- package/dist/loader.js +1 -1
- package/dist/loader.mjs +1 -1
- package/dist/sri.json +11 -11
- package/dist/vanilla/index.cjs +80 -33
- package/dist/vanilla/index.mjs +80 -33
- package/dist/vue/index.cjs +80 -33
- package/dist/vue/index.mjs +80 -33
- package/dist/widgets/checkout-flow.js +5 -5
- package/dist/widgets/invoice-list.js +6 -6
- package/dist/widgets/payment-method.js +5 -5
- package/dist/widgets/pricing-card.js +8 -8
- package/dist/widgets/subscribe-button.js +8 -8
- package/dist/widgets/subscription-manager.js +5 -5
- package/dist/widgets/upgrade-cancel.js +6 -6
- package/dist/widgets/usage-meter.js +5 -5
- package/loader.sri.txt +1 -1
- package/package.json +1 -1
package/dist/loader.mjs
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
var T="1.0.
|
|
1
|
+
var T="1.0.5";var y=new WeakMap,c=new Map,f={},A=false;function h(){A||typeof window!="undefined"&&(window.addEventListener("message",e=>{let t=e.data;!t||typeof t!="object"||typeof t.type!="string"||t.type.startsWith("aforo.")&&$(t);}),A=true);}function $(e){let t=c.get(e.type);if(t)for(let o of t)try{o(e);}catch(i){}let n=c.get("*");if(n)for(let o of n)try{o(e);}catch(i){}}var p=new Map;function N(e,t){p.set(e,t);}function w(e){return typeof e=="object"&&e!==null&&typeof e.tagName=="string"}var b={version:T,configure(e){f={...f,...e};},getConfig(){return {...f}},mount(e,t){var r,l,u,s,d;if(!w(e)||y.has(e))return false;let n=t.widget,o=p.get(n);if(!o)return typeof console!="undefined"&&console.warn(`[aforo:embed] No mount handler registered for widget "${n}". Loader bundle may not have finished loading.`),false;let i={widget:n,tenantSlug:(l=(r=t.tenantSlug)!=null?r:f.tenantSlug)!=null?l:"",embedKey:(s=(u=t.embedKey)!=null?u:f.embedKey)!=null?s:"",bridgeToken:(d=t.bridgeToken)!=null?d:f.bridgeToken,layout:t.layout,themeOverrides:t.themeOverrides,config:t.config};if(!i.tenantSlug||!i.embedKey)return typeof console!="undefined"&&console.error(`[aforo:embed] Widget "${n}" requires tenantSlug + embedKey. Provide via AforoEmbed.configure() or data-* attributes.`),false;h();let a=o(e,i);return y.set(e,{element:e,widget:n,teardown:a}),true},unmount(e){if(!w(e))return false;let t=y.get(e);if(!t)return false;try{t.teardown();}catch(n){}return y.delete(e),true},on(e,t){h();let n=c.get(e);return n||(n=new Set,c.set(e,n)),n.add(t),()=>{let o=c.get(e);o&&(o.delete(t),o.size===0&&c.delete(e));}},_registerWidget:N,_registeredWidgetsForTesting(){return Array.from(p.keys())},_resetForTesting(){p.clear(),c.clear(),f={};}};function k(e){if(!e)return {};try{let t=JSON.parse(e);if(t&&typeof t=="object"&&!Array.isArray(t)){let n={},o=["primary","primaryContrast","text","textMuted","bg","border","radius","fontFamily"],i=t;for(let a of o){let r=i[a];typeof r=="string"&&r.length>0&&(n[a]=r);}return n}}catch(t){}return {}}var U=new Set(["pricing-card","subscribe-button","checkout-flow","subscription-manager","invoice-list","usage-meter","payment-method","upgrade-cancel"]),B="https://embed.aforo.ai/v1/widgets",g="[aforo:loader]";function D(){let e=typeof document!="undefined"?document.currentScript:null;if(e&&e.tagName==="SCRIPT")return e;if(typeof document!="undefined"){let t=document.getElementsByTagName("script");for(let n=t.length-1;n>=0;n--){let o=t[n];if(!o)continue;let i=o.getAttribute("src")||"";if(/(\/v1)?\/loader(\.umd)?\.js$/.test(i))return o}}return null}function G(e){let t=e==null?void 0:e.getAttribute("src");if(!t)return null;try{let n=typeof location!="undefined"?location.href:void 0,o=new URL(t,n),i=o.pathname.replace(/\/loader(\.umd)?\.js$/,"");return `${o.origin}${i}/widgets`}catch(n){return null}}function v(e,t){let n=e.getAttribute(t);if(n==null||n.trim()==="")return;let o=Number(n);return Number.isFinite(o)?o:void 0}function j(e,t){let n=e.getAttribute(t);if(n==null)return;let o=n.trim().toLowerCase();if(o==="true")return true;if(o==="false")return false}function V(e){let t=(e.getAttribute("data-aforo-widget")||"").trim(),n=e.getAttribute("data-tenant-slug")||e.getAttribute("data-tenant")||"",o=e.getAttribute("data-embed-key")||"",i=e.getAttribute("data-bridge-token")||void 0,a=e.getAttribute("data-layout")||void 0,r=k(e.getAttribute("data-theme-overrides")),l=e.getAttribute("data-offering-id")||void 0,u=e.getAttribute("data-subscription-id")||void 0,s=e.getAttribute("data-metric")||void 0,d=e.getAttribute("data-featured-offering-id")||void 0,m=e.getAttribute("data-mode")||void 0,M=e.getAttribute("data-return-url")||void 0,S=e.getAttribute("data-default-status")||void 0,F=e.getAttribute("data-render-mode")||void 0,_=e.getAttribute("data-locale")||void 0,C=v(e,"data-page-size"),O=v(e,"data-poll-interval-ms"),I=e.getAttribute("data-theme")||void 0,R=e.getAttribute("data-cart-type")||void 0,W=e.getAttribute("data-target-id")||void 0,H=e.getAttribute("data-cta-text")||void 0,x=e.getAttribute("data-cta-url")||void 0,P=v(e,"data-max-plans"),K=j(e,"data-show-features");return {widget:t,tenantSlug:n,embedKey:o,bridgeToken:i,layout:a,themeOverrides:r,config:{offeringId:l,subscriptionId:u,metricName:s,featuredOfferingId:d,mode:m,returnUrl:M,defaultStatus:S,renderMode:F,locale:_,pageSize:C,pollIntervalMs:O,theme:I,cartType:R,targetId:W,ctaText:H,ctaUrl:x,maxPlans:P,showFeatures:K}}}function z(e,t,n){return new Promise((o,i)=>{if(typeof document=="undefined")return i(new Error("no document"));let a=document.querySelector(`script[data-aforo-bundle="${e}"]`);if(a){if(a.dataset.aforoLoaded==="1")return o();a.addEventListener("load",()=>o()),a.addEventListener("error",()=>i(new Error(`bundle load failed: ${e}`)));return}let r=document.createElement("script");r.src=`${t}/${e}.js`,r.async=true,r.crossOrigin="anonymous",r.dataset.aforoBundle=e,n&&r.setAttribute("nonce",n),r.addEventListener("load",()=>{r.dataset.aforoLoaded="1",o();}),r.addEventListener("error",()=>i(new Error(`bundle load failed: ${e}`))),document.head.appendChild(r);})}function L(e){if(!e.__aforoEmbedReadyFired){e.__aforoEmbedReadyFired=true;try{typeof e.aforoEmbedReady=="function"&&e.aforoEmbedReady();}catch(t){typeof console!="undefined"&&console.error(`${g} aforoEmbedReady callback threw`,t);}if(typeof document!="undefined"&&typeof CustomEvent!="undefined")try{document.dispatchEvent(new CustomEvent("aforoEmbedReady"));}catch(t){}}}async function E(e){if(typeof window=="undefined"||typeof document=="undefined")return;let t=window;if(window.location.protocol!=="https:"&&window.location.hostname!=="localhost"){typeof console!="undefined"&&console.error(`${g} Refusing to mount Aforo widgets on a non-HTTPS page. Embed Plugin requires HTTPS (FR-TIER-12). Current origin: ${window.location.origin}`);return}if(t.__aforoEmbedLoaderInitialized){typeof console!="undefined"&&console.warn(`${g} Loader already initialised on this page \u2014 skipping duplicate run.`);return}t.__aforoEmbedLoaderInitialized=true,t.aforoEmbed||(t.aforoEmbed=b);let n=D(),o=(n==null?void 0:n.getAttribute("nonce"))||null,i=(e==null?void 0:e.bundleBaseUrl)||G(n)||B,a=Array.from(document.querySelectorAll("[data-aforo-widget]"));if(a.length===0){L(t);return}let r=new Map;for(let u of a){let s=(u.getAttribute("data-aforo-widget")||"").trim();if(!U.has(s)){typeof console!="undefined"&&console.warn(`${g} Unknown widget id "${s}" \u2014 skipping`);continue}let d=r.get(s);d||(d=[],r.set(s,d)),d.push(u);}let l=Array.from(r.entries()).map(async([u,s])=>{try{await z(u,i,o);for(let d of s){let m=V(d);if(!m.widget||!m.tenantSlug||!m.embedKey){typeof console!="undefined"&&console.error(`${g} Placeholder missing required attributes (data-tenant-slug + data-embed-key). Skipping element.`,d);continue}b.mount(d,m);}}catch(d){typeof console!="undefined"&&console.error(`${g} Failed to load bundle for "${u}"`,d);}});await Promise.all(l).catch(()=>{}),L(t);}typeof document!="undefined"&&document.readyState!=="loading"?Promise.resolve().then(()=>{E();}):typeof document!="undefined"&&document.addEventListener("DOMContentLoaded",()=>{E();});b._loader={bootstrap:E};
|
|
2
2
|
export{b as AforoEmbed,E as bootstrap};//# sourceMappingURL=loader.mjs.map
|
|
3
3
|
//# sourceMappingURL=loader.mjs.map
|
package/dist/sri.json
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "1.0.
|
|
3
|
-
"generatedAt": "2026-08-
|
|
2
|
+
"version": "1.0.5",
|
|
3
|
+
"generatedAt": "2026-08-26T07:52:12.266Z",
|
|
4
4
|
"bundles": {
|
|
5
|
-
"loader.js": "sha384-
|
|
6
|
-
"widgets/checkout-flow.js": "sha384-
|
|
7
|
-
"widgets/invoice-list.js": "sha384-
|
|
8
|
-
"widgets/payment-method.js": "sha384-
|
|
9
|
-
"widgets/pricing-card.js": "sha384-
|
|
10
|
-
"widgets/subscribe-button.js": "sha384-
|
|
11
|
-
"widgets/subscription-manager.js": "sha384-
|
|
12
|
-
"widgets/upgrade-cancel.js": "sha384-
|
|
13
|
-
"widgets/usage-meter.js": "sha384-
|
|
5
|
+
"loader.js": "sha384-dSFfjDiOhFwuhOq6QaJlGLJq05Fl3yau3wPc5kuYnO+OmN/MKp91+AwOpG0zFAJ7",
|
|
6
|
+
"widgets/checkout-flow.js": "sha384-ABsG5M7QXhYJUmB81gURADHt98yRZaJpMxw5qDXiUs7LHa34LGXgfGEzSZrMC2Lr",
|
|
7
|
+
"widgets/invoice-list.js": "sha384-Dk5I2atCFalMIClYl6NkhZo3rrQNsfxq64y+geG+Lm3vZ1rZSg3xeVfY4tVMl0mD",
|
|
8
|
+
"widgets/payment-method.js": "sha384-xkM3m6gLJBhs0aK/A+/tcEQIWjz5Fj/h5dl4X/aEXmu6Dc0fVfxIV2ZQ1NDKDfsa",
|
|
9
|
+
"widgets/pricing-card.js": "sha384-F0ZraASZJCoL/E3v2tCQAAilNm6BndFRYsH5CFAYlWizibZyrNCVddq/pZ4ZQeP6",
|
|
10
|
+
"widgets/subscribe-button.js": "sha384-85KTFUAYOsDkedU1SKInEn0V0iAMUX4hokvn7ve69Q2Ouh9dvJoPY9OEYuWsDvfF",
|
|
11
|
+
"widgets/subscription-manager.js": "sha384-3BNZFvMphX9jAsmzrE09tnPBIKbXsxLhVSv1KynXrJK217OmFekbdHLzkPtGphcw",
|
|
12
|
+
"widgets/upgrade-cancel.js": "sha384-ZvFGSCqU3+MoAeXeytEbYzpDZInRDP0YJ9AUl6yoMdwQ4p4W7stLLgdM3l3dNIxZ",
|
|
13
|
+
"widgets/usage-meter.js": "sha384-2g3qNlSZMkw6sTHKsXbsupaWQbdQzE+4U8jbFlRUaV0qpMKeptV4po98z0PpSM61"
|
|
14
14
|
}
|
|
15
15
|
}
|
package/dist/vanilla/index.cjs
CHANGED
|
@@ -27,7 +27,7 @@ var React7__namespace = /*#__PURE__*/_interopNamespace(React7);
|
|
|
27
27
|
// src/vanilla/index.ts
|
|
28
28
|
|
|
29
29
|
// src/core/version.ts
|
|
30
|
-
var VERSION = "1.0.
|
|
30
|
+
var VERSION = "1.0.5";
|
|
31
31
|
|
|
32
32
|
// src/core/AforoEmbed.ts
|
|
33
33
|
var mountedElements = /* @__PURE__ */ new WeakMap();
|
|
@@ -547,7 +547,8 @@ var _BridgeClient = class _BridgeClient {
|
|
|
547
547
|
throw await this.parseError(resp);
|
|
548
548
|
}
|
|
549
549
|
const body = await resp.json();
|
|
550
|
-
|
|
550
|
+
const data = body?.data ?? body;
|
|
551
|
+
if (!data || !data.sessionJwt || typeof data.expiresAt !== "number") {
|
|
551
552
|
throw new BridgeClientError(
|
|
552
553
|
"Malformed bridge exchange response",
|
|
553
554
|
500,
|
|
@@ -556,9 +557,9 @@ var _BridgeClient = class _BridgeClient {
|
|
|
556
557
|
);
|
|
557
558
|
}
|
|
558
559
|
return {
|
|
559
|
-
sessionJwt:
|
|
560
|
-
expiresAt:
|
|
561
|
-
customerId:
|
|
560
|
+
sessionJwt: data.sessionJwt,
|
|
561
|
+
expiresAt: data.expiresAt,
|
|
562
|
+
customerId: data.customerId ?? null
|
|
562
563
|
};
|
|
563
564
|
}
|
|
564
565
|
/**
|
|
@@ -599,10 +600,23 @@ var _BridgeClient = class _BridgeClient {
|
|
|
599
600
|
/**
|
|
600
601
|
* Headless config — the PricingCard widget's primary read path.
|
|
601
602
|
*
|
|
602
|
-
*
|
|
603
|
-
*
|
|
604
|
-
*
|
|
605
|
-
*
|
|
603
|
+
* Hits `GET /api/v1/portal/embed/tenant-config/{slug}` — the SAME
|
|
604
|
+
* embed-key-authenticated endpoint {@link getTenantBrandKit} uses, now
|
|
605
|
+
* extended server-side with `offerings` + `embeddableWidget`. This was
|
|
606
|
+
* previously wired to `GET /api/v1/portal/headless/config`, an unrelated
|
|
607
|
+
* T6 Headless-tier endpoint that requires `X-Storefront-Key`/
|
|
608
|
+
* `X-Tenant-Id` auth — headers the embed key flow never sends. Every
|
|
609
|
+
* embed-tier fetch against that URL failed 400 "Missing tenant
|
|
610
|
+
* identification" by construction; this endpoint is scoped to the same
|
|
611
|
+
* `EmbedKeyAuthFilter` gate as the rest of the embed plane, so the
|
|
612
|
+
* `Authorization: Bearer <embed_key>` header `buildHeaders()` already
|
|
613
|
+
* sends is sufficient.
|
|
614
|
+
*
|
|
615
|
+
* The backend response is still a flat record (`tenantSlug,
|
|
616
|
+
* primaryColor, secondaryColor, logoUrl, fontFamily, offerings,
|
|
617
|
+
* embeddableWidget`) — kept flat so `getTenantBrandKit()`'s existing
|
|
618
|
+
* consumers (the ThemeReader cascade) are unaffected. This method nests
|
|
619
|
+
* the branding fields into the shape `HeadlessConfigResponse` expects.
|
|
606
620
|
*
|
|
607
621
|
* Pattern #18 fail-soft — returns `null` on transport failure, 404, or
|
|
608
622
|
* malformed JSON. Widgets fall through to their empty / error state and
|
|
@@ -617,7 +631,7 @@ var _BridgeClient = class _BridgeClient {
|
|
|
617
631
|
}
|
|
618
632
|
const target = slug || this.tenantSlug;
|
|
619
633
|
if (!target) return null;
|
|
620
|
-
const url = `${this.baseUrl}/api/v1/portal/
|
|
634
|
+
const url = `${this.baseUrl}/api/v1/portal/embed/tenant-config/${encodeURIComponent(target)}`;
|
|
621
635
|
let resp;
|
|
622
636
|
try {
|
|
623
637
|
resp = await this.send(url, {
|
|
@@ -630,7 +644,19 @@ var _BridgeClient = class _BridgeClient {
|
|
|
630
644
|
if (!resp.ok) return null;
|
|
631
645
|
try {
|
|
632
646
|
const body = await resp.json();
|
|
633
|
-
|
|
647
|
+
if (!body || typeof body !== "object") return null;
|
|
648
|
+
const data = body.data ?? body;
|
|
649
|
+
return {
|
|
650
|
+
tenantSlug: data.tenantSlug,
|
|
651
|
+
branding: {
|
|
652
|
+
primaryColor: data.primaryColor ?? null,
|
|
653
|
+
secondaryColor: data.secondaryColor ?? null,
|
|
654
|
+
logoUrl: data.logoUrl ?? null,
|
|
655
|
+
fontFamily: data.fontFamily ?? null
|
|
656
|
+
},
|
|
657
|
+
offerings: Array.isArray(data.offerings) ? data.offerings : [],
|
|
658
|
+
embeddableWidget: data.embeddableWidget ?? null
|
|
659
|
+
};
|
|
634
660
|
} catch {
|
|
635
661
|
return null;
|
|
636
662
|
}
|
|
@@ -774,7 +800,8 @@ var _BridgeClient = class _BridgeClient {
|
|
|
774
800
|
false
|
|
775
801
|
);
|
|
776
802
|
}
|
|
777
|
-
|
|
803
|
+
const data = body?.data ?? body ?? {};
|
|
804
|
+
if (!data.checkoutUrl || !data.checkoutSessionId || !data.expiresAt) {
|
|
778
805
|
throw new BridgeClientError(
|
|
779
806
|
"Malformed checkout-initiate response (missing required fields)",
|
|
780
807
|
500,
|
|
@@ -783,9 +810,9 @@ var _BridgeClient = class _BridgeClient {
|
|
|
783
810
|
);
|
|
784
811
|
}
|
|
785
812
|
return {
|
|
786
|
-
checkoutUrl:
|
|
787
|
-
checkoutSessionId:
|
|
788
|
-
expiresAt:
|
|
813
|
+
checkoutUrl: data.checkoutUrl,
|
|
814
|
+
checkoutSessionId: data.checkoutSessionId,
|
|
815
|
+
expiresAt: data.expiresAt
|
|
789
816
|
};
|
|
790
817
|
}
|
|
791
818
|
/* ────────────────────────────────────────────────────────────────────
|
|
@@ -1593,13 +1620,16 @@ var _BridgeClient = class _BridgeClient {
|
|
|
1593
1620
|
if (!resp.ok) return emptyPage;
|
|
1594
1621
|
try {
|
|
1595
1622
|
const body = await resp.json();
|
|
1596
|
-
if (!body || typeof body !== "object")
|
|
1623
|
+
if (!body || typeof body !== "object") {
|
|
1624
|
+
return emptyPage;
|
|
1625
|
+
}
|
|
1626
|
+
const data = body.data ?? body;
|
|
1597
1627
|
return {
|
|
1598
|
-
content: Array.isArray(
|
|
1599
|
-
totalElements: typeof
|
|
1600
|
-
totalPages: typeof
|
|
1601
|
-
page: typeof
|
|
1602
|
-
size: typeof
|
|
1628
|
+
content: Array.isArray(data.content) ? data.content : [],
|
|
1629
|
+
totalElements: typeof data.totalElements === "number" && Number.isFinite(data.totalElements) ? data.totalElements : 0,
|
|
1630
|
+
totalPages: typeof data.totalPages === "number" && Number.isFinite(data.totalPages) ? data.totalPages : 0,
|
|
1631
|
+
page: typeof data.page === "number" && Number.isFinite(data.page) ? data.page : filter.page ?? 0,
|
|
1632
|
+
size: typeof data.size === "number" && Number.isFinite(data.size) ? data.size : filter.size ?? 10
|
|
1603
1633
|
};
|
|
1604
1634
|
} catch {
|
|
1605
1635
|
return emptyPage;
|
|
@@ -3103,6 +3133,20 @@ function safeFormatCurrency(priceCents, currency, locale, intlOverride) {
|
|
|
3103
3133
|
return `${currency} ${amount.toFixed(2)}`;
|
|
3104
3134
|
}
|
|
3105
3135
|
}
|
|
3136
|
+
function resolveDisplayPrice(offering, locale, intlOverride) {
|
|
3137
|
+
const flat = safeFormatCurrency(offering.priceCents, offering.currency, locale, intlOverride);
|
|
3138
|
+
if (flat !== "\u2014") return { text: flat, unitSuffix: null };
|
|
3139
|
+
const perUnitPlan = offering.ratePlans?.find(
|
|
3140
|
+
(rp) => rp.pricingModel === "PER_UNIT" && rp.perUnitPriceCents != null
|
|
3141
|
+
);
|
|
3142
|
+
if (perUnitPlan) {
|
|
3143
|
+
const unitPrice = safeFormatCurrency(perUnitPlan.perUnitPriceCents, offering.currency, locale, intlOverride);
|
|
3144
|
+
if (unitPrice !== "\u2014") {
|
|
3145
|
+
return { text: unitPrice, unitSuffix: perUnitPlan.unitLabel || "unit" };
|
|
3146
|
+
}
|
|
3147
|
+
}
|
|
3148
|
+
return { text: "\u2014", unitSuffix: null };
|
|
3149
|
+
}
|
|
3106
3150
|
function localizedName(o) {
|
|
3107
3151
|
return o.localizedDisplayName || o.name || "(unnamed plan)";
|
|
3108
3152
|
}
|
|
@@ -3506,12 +3550,7 @@ function PlanCard({
|
|
|
3506
3550
|
onCtaClick,
|
|
3507
3551
|
intlOverride
|
|
3508
3552
|
}) {
|
|
3509
|
-
const price =
|
|
3510
|
-
offering.priceCents,
|
|
3511
|
-
offering.currency,
|
|
3512
|
-
locale,
|
|
3513
|
-
intlOverride
|
|
3514
|
-
);
|
|
3553
|
+
const { text: price, unitSuffix } = resolveDisplayPrice(offering, locale, intlOverride);
|
|
3515
3554
|
const isContactSales = price === "\u2014";
|
|
3516
3555
|
const planName = localizedName(offering);
|
|
3517
3556
|
const description = localizedDescription(offering);
|
|
@@ -3532,7 +3571,7 @@ function PlanCard({
|
|
|
3532
3571
|
"div",
|
|
3533
3572
|
{
|
|
3534
3573
|
role: "group",
|
|
3535
|
-
"aria-label": `${planName}, ${price} per ${offering.billingCycle}`,
|
|
3574
|
+
"aria-label": `${planName}, ${price} per ${unitSuffix ?? offering.billingCycle}`,
|
|
3536
3575
|
className: `aforo-w-pc-card${featured ? " aforo-w-pc-card-featured" : ""}`,
|
|
3537
3576
|
style: cardStyle2,
|
|
3538
3577
|
children: [
|
|
@@ -3597,14 +3636,14 @@ function PlanCard({
|
|
|
3597
3636
|
children: price
|
|
3598
3637
|
}
|
|
3599
3638
|
),
|
|
3600
|
-
!isContactSales && offering.billingCycle ? /* @__PURE__ */ jsxRuntime.jsxs(
|
|
3639
|
+
!isContactSales && (unitSuffix || offering.billingCycle) ? /* @__PURE__ */ jsxRuntime.jsxs(
|
|
3601
3640
|
"span",
|
|
3602
3641
|
{
|
|
3603
3642
|
className: "aforo-w-pc-cycle",
|
|
3604
3643
|
style: { fontSize: 13, color: tokens.textMuted },
|
|
3605
3644
|
children: [
|
|
3606
3645
|
"/ ",
|
|
3607
|
-
humanCycle(offering.billingCycle)
|
|
3646
|
+
unitSuffix ?? humanCycle(offering.billingCycle)
|
|
3608
3647
|
]
|
|
3609
3648
|
}
|
|
3610
3649
|
) : null
|
|
@@ -3762,6 +3801,7 @@ function TableLayout({
|
|
|
3762
3801
|
/* @__PURE__ */ jsxRuntime.jsx("th", { scope: "col", style: { ...headerCellStyle, color: tokens.textMuted, fontSize: 12 }, children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "aforo-w-pc-sr-only", style: srOnlyStyle, children: "Feature" }) }),
|
|
3763
3802
|
offerings.map((o) => {
|
|
3764
3803
|
const featured = offeringIsFeatured(o, featuredOverride);
|
|
3804
|
+
const { text: tablePrice, unitSuffix: tableUnitSuffix } = resolveDisplayPrice(o, locale, intlOverride);
|
|
3765
3805
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
3766
3806
|
"th",
|
|
3767
3807
|
{
|
|
@@ -3791,13 +3831,20 @@ function TableLayout({
|
|
|
3791
3831
|
children: "Most popular"
|
|
3792
3832
|
}
|
|
3793
3833
|
) : null,
|
|
3794
|
-
/* @__PURE__ */ jsxRuntime.
|
|
3834
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: { fontSize: 22, fontWeight: 700 }, children: [
|
|
3835
|
+
tablePrice,
|
|
3836
|
+
tableUnitSuffix ? /* @__PURE__ */ jsxRuntime.jsxs("span", { style: { fontSize: 12, fontWeight: 400, color: tokens.textMuted }, children: [
|
|
3837
|
+
" ",
|
|
3838
|
+
"/ ",
|
|
3839
|
+
tableUnitSuffix
|
|
3840
|
+
] }) : null
|
|
3841
|
+
] }),
|
|
3795
3842
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3796
3843
|
"button",
|
|
3797
3844
|
{
|
|
3798
3845
|
type: "button",
|
|
3799
3846
|
onClick: () => onCtaClick(o),
|
|
3800
|
-
"aria-label": `${ctaLabel(o, ctaTextProp,
|
|
3847
|
+
"aria-label": `${ctaLabel(o, ctaTextProp, tablePrice === "\u2014")} for ${localizedName(o)}`,
|
|
3801
3848
|
style: {
|
|
3802
3849
|
inlineSize: "100%",
|
|
3803
3850
|
padding: "8px 12px",
|
|
@@ -3810,7 +3857,7 @@ function TableLayout({
|
|
|
3810
3857
|
cursor: "pointer",
|
|
3811
3858
|
fontFamily: tokens.fontFamily
|
|
3812
3859
|
},
|
|
3813
|
-
children: ctaLabel(o, ctaTextProp,
|
|
3860
|
+
children: ctaLabel(o, ctaTextProp, tablePrice === "\u2014")
|
|
3814
3861
|
}
|
|
3815
3862
|
)
|
|
3816
3863
|
] })
|
package/dist/vanilla/index.mjs
CHANGED
|
@@ -5,7 +5,7 @@ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
|
5
5
|
// src/vanilla/index.ts
|
|
6
6
|
|
|
7
7
|
// src/core/version.ts
|
|
8
|
-
var VERSION = "1.0.
|
|
8
|
+
var VERSION = "1.0.5";
|
|
9
9
|
|
|
10
10
|
// src/core/AforoEmbed.ts
|
|
11
11
|
var mountedElements = /* @__PURE__ */ new WeakMap();
|
|
@@ -525,7 +525,8 @@ var _BridgeClient = class _BridgeClient {
|
|
|
525
525
|
throw await this.parseError(resp);
|
|
526
526
|
}
|
|
527
527
|
const body = await resp.json();
|
|
528
|
-
|
|
528
|
+
const data = body?.data ?? body;
|
|
529
|
+
if (!data || !data.sessionJwt || typeof data.expiresAt !== "number") {
|
|
529
530
|
throw new BridgeClientError(
|
|
530
531
|
"Malformed bridge exchange response",
|
|
531
532
|
500,
|
|
@@ -534,9 +535,9 @@ var _BridgeClient = class _BridgeClient {
|
|
|
534
535
|
);
|
|
535
536
|
}
|
|
536
537
|
return {
|
|
537
|
-
sessionJwt:
|
|
538
|
-
expiresAt:
|
|
539
|
-
customerId:
|
|
538
|
+
sessionJwt: data.sessionJwt,
|
|
539
|
+
expiresAt: data.expiresAt,
|
|
540
|
+
customerId: data.customerId ?? null
|
|
540
541
|
};
|
|
541
542
|
}
|
|
542
543
|
/**
|
|
@@ -577,10 +578,23 @@ var _BridgeClient = class _BridgeClient {
|
|
|
577
578
|
/**
|
|
578
579
|
* Headless config — the PricingCard widget's primary read path.
|
|
579
580
|
*
|
|
580
|
-
*
|
|
581
|
-
*
|
|
582
|
-
*
|
|
583
|
-
*
|
|
581
|
+
* Hits `GET /api/v1/portal/embed/tenant-config/{slug}` — the SAME
|
|
582
|
+
* embed-key-authenticated endpoint {@link getTenantBrandKit} uses, now
|
|
583
|
+
* extended server-side with `offerings` + `embeddableWidget`. This was
|
|
584
|
+
* previously wired to `GET /api/v1/portal/headless/config`, an unrelated
|
|
585
|
+
* T6 Headless-tier endpoint that requires `X-Storefront-Key`/
|
|
586
|
+
* `X-Tenant-Id` auth — headers the embed key flow never sends. Every
|
|
587
|
+
* embed-tier fetch against that URL failed 400 "Missing tenant
|
|
588
|
+
* identification" by construction; this endpoint is scoped to the same
|
|
589
|
+
* `EmbedKeyAuthFilter` gate as the rest of the embed plane, so the
|
|
590
|
+
* `Authorization: Bearer <embed_key>` header `buildHeaders()` already
|
|
591
|
+
* sends is sufficient.
|
|
592
|
+
*
|
|
593
|
+
* The backend response is still a flat record (`tenantSlug,
|
|
594
|
+
* primaryColor, secondaryColor, logoUrl, fontFamily, offerings,
|
|
595
|
+
* embeddableWidget`) — kept flat so `getTenantBrandKit()`'s existing
|
|
596
|
+
* consumers (the ThemeReader cascade) are unaffected. This method nests
|
|
597
|
+
* the branding fields into the shape `HeadlessConfigResponse` expects.
|
|
584
598
|
*
|
|
585
599
|
* Pattern #18 fail-soft — returns `null` on transport failure, 404, or
|
|
586
600
|
* malformed JSON. Widgets fall through to their empty / error state and
|
|
@@ -595,7 +609,7 @@ var _BridgeClient = class _BridgeClient {
|
|
|
595
609
|
}
|
|
596
610
|
const target = slug || this.tenantSlug;
|
|
597
611
|
if (!target) return null;
|
|
598
|
-
const url = `${this.baseUrl}/api/v1/portal/
|
|
612
|
+
const url = `${this.baseUrl}/api/v1/portal/embed/tenant-config/${encodeURIComponent(target)}`;
|
|
599
613
|
let resp;
|
|
600
614
|
try {
|
|
601
615
|
resp = await this.send(url, {
|
|
@@ -608,7 +622,19 @@ var _BridgeClient = class _BridgeClient {
|
|
|
608
622
|
if (!resp.ok) return null;
|
|
609
623
|
try {
|
|
610
624
|
const body = await resp.json();
|
|
611
|
-
|
|
625
|
+
if (!body || typeof body !== "object") return null;
|
|
626
|
+
const data = body.data ?? body;
|
|
627
|
+
return {
|
|
628
|
+
tenantSlug: data.tenantSlug,
|
|
629
|
+
branding: {
|
|
630
|
+
primaryColor: data.primaryColor ?? null,
|
|
631
|
+
secondaryColor: data.secondaryColor ?? null,
|
|
632
|
+
logoUrl: data.logoUrl ?? null,
|
|
633
|
+
fontFamily: data.fontFamily ?? null
|
|
634
|
+
},
|
|
635
|
+
offerings: Array.isArray(data.offerings) ? data.offerings : [],
|
|
636
|
+
embeddableWidget: data.embeddableWidget ?? null
|
|
637
|
+
};
|
|
612
638
|
} catch {
|
|
613
639
|
return null;
|
|
614
640
|
}
|
|
@@ -752,7 +778,8 @@ var _BridgeClient = class _BridgeClient {
|
|
|
752
778
|
false
|
|
753
779
|
);
|
|
754
780
|
}
|
|
755
|
-
|
|
781
|
+
const data = body?.data ?? body ?? {};
|
|
782
|
+
if (!data.checkoutUrl || !data.checkoutSessionId || !data.expiresAt) {
|
|
756
783
|
throw new BridgeClientError(
|
|
757
784
|
"Malformed checkout-initiate response (missing required fields)",
|
|
758
785
|
500,
|
|
@@ -761,9 +788,9 @@ var _BridgeClient = class _BridgeClient {
|
|
|
761
788
|
);
|
|
762
789
|
}
|
|
763
790
|
return {
|
|
764
|
-
checkoutUrl:
|
|
765
|
-
checkoutSessionId:
|
|
766
|
-
expiresAt:
|
|
791
|
+
checkoutUrl: data.checkoutUrl,
|
|
792
|
+
checkoutSessionId: data.checkoutSessionId,
|
|
793
|
+
expiresAt: data.expiresAt
|
|
767
794
|
};
|
|
768
795
|
}
|
|
769
796
|
/* ────────────────────────────────────────────────────────────────────
|
|
@@ -1571,13 +1598,16 @@ var _BridgeClient = class _BridgeClient {
|
|
|
1571
1598
|
if (!resp.ok) return emptyPage;
|
|
1572
1599
|
try {
|
|
1573
1600
|
const body = await resp.json();
|
|
1574
|
-
if (!body || typeof body !== "object")
|
|
1601
|
+
if (!body || typeof body !== "object") {
|
|
1602
|
+
return emptyPage;
|
|
1603
|
+
}
|
|
1604
|
+
const data = body.data ?? body;
|
|
1575
1605
|
return {
|
|
1576
|
-
content: Array.isArray(
|
|
1577
|
-
totalElements: typeof
|
|
1578
|
-
totalPages: typeof
|
|
1579
|
-
page: typeof
|
|
1580
|
-
size: typeof
|
|
1606
|
+
content: Array.isArray(data.content) ? data.content : [],
|
|
1607
|
+
totalElements: typeof data.totalElements === "number" && Number.isFinite(data.totalElements) ? data.totalElements : 0,
|
|
1608
|
+
totalPages: typeof data.totalPages === "number" && Number.isFinite(data.totalPages) ? data.totalPages : 0,
|
|
1609
|
+
page: typeof data.page === "number" && Number.isFinite(data.page) ? data.page : filter.page ?? 0,
|
|
1610
|
+
size: typeof data.size === "number" && Number.isFinite(data.size) ? data.size : filter.size ?? 10
|
|
1581
1611
|
};
|
|
1582
1612
|
} catch {
|
|
1583
1613
|
return emptyPage;
|
|
@@ -3081,6 +3111,20 @@ function safeFormatCurrency(priceCents, currency, locale, intlOverride) {
|
|
|
3081
3111
|
return `${currency} ${amount.toFixed(2)}`;
|
|
3082
3112
|
}
|
|
3083
3113
|
}
|
|
3114
|
+
function resolveDisplayPrice(offering, locale, intlOverride) {
|
|
3115
|
+
const flat = safeFormatCurrency(offering.priceCents, offering.currency, locale, intlOverride);
|
|
3116
|
+
if (flat !== "\u2014") return { text: flat, unitSuffix: null };
|
|
3117
|
+
const perUnitPlan = offering.ratePlans?.find(
|
|
3118
|
+
(rp) => rp.pricingModel === "PER_UNIT" && rp.perUnitPriceCents != null
|
|
3119
|
+
);
|
|
3120
|
+
if (perUnitPlan) {
|
|
3121
|
+
const unitPrice = safeFormatCurrency(perUnitPlan.perUnitPriceCents, offering.currency, locale, intlOverride);
|
|
3122
|
+
if (unitPrice !== "\u2014") {
|
|
3123
|
+
return { text: unitPrice, unitSuffix: perUnitPlan.unitLabel || "unit" };
|
|
3124
|
+
}
|
|
3125
|
+
}
|
|
3126
|
+
return { text: "\u2014", unitSuffix: null };
|
|
3127
|
+
}
|
|
3084
3128
|
function localizedName(o) {
|
|
3085
3129
|
return o.localizedDisplayName || o.name || "(unnamed plan)";
|
|
3086
3130
|
}
|
|
@@ -3484,12 +3528,7 @@ function PlanCard({
|
|
|
3484
3528
|
onCtaClick,
|
|
3485
3529
|
intlOverride
|
|
3486
3530
|
}) {
|
|
3487
|
-
const price =
|
|
3488
|
-
offering.priceCents,
|
|
3489
|
-
offering.currency,
|
|
3490
|
-
locale,
|
|
3491
|
-
intlOverride
|
|
3492
|
-
);
|
|
3531
|
+
const { text: price, unitSuffix } = resolveDisplayPrice(offering, locale, intlOverride);
|
|
3493
3532
|
const isContactSales = price === "\u2014";
|
|
3494
3533
|
const planName = localizedName(offering);
|
|
3495
3534
|
const description = localizedDescription(offering);
|
|
@@ -3510,7 +3549,7 @@ function PlanCard({
|
|
|
3510
3549
|
"div",
|
|
3511
3550
|
{
|
|
3512
3551
|
role: "group",
|
|
3513
|
-
"aria-label": `${planName}, ${price} per ${offering.billingCycle}`,
|
|
3552
|
+
"aria-label": `${planName}, ${price} per ${unitSuffix ?? offering.billingCycle}`,
|
|
3514
3553
|
className: `aforo-w-pc-card${featured ? " aforo-w-pc-card-featured" : ""}`,
|
|
3515
3554
|
style: cardStyle2,
|
|
3516
3555
|
children: [
|
|
@@ -3575,14 +3614,14 @@ function PlanCard({
|
|
|
3575
3614
|
children: price
|
|
3576
3615
|
}
|
|
3577
3616
|
),
|
|
3578
|
-
!isContactSales && offering.billingCycle ? /* @__PURE__ */ jsxs(
|
|
3617
|
+
!isContactSales && (unitSuffix || offering.billingCycle) ? /* @__PURE__ */ jsxs(
|
|
3579
3618
|
"span",
|
|
3580
3619
|
{
|
|
3581
3620
|
className: "aforo-w-pc-cycle",
|
|
3582
3621
|
style: { fontSize: 13, color: tokens.textMuted },
|
|
3583
3622
|
children: [
|
|
3584
3623
|
"/ ",
|
|
3585
|
-
humanCycle(offering.billingCycle)
|
|
3624
|
+
unitSuffix ?? humanCycle(offering.billingCycle)
|
|
3586
3625
|
]
|
|
3587
3626
|
}
|
|
3588
3627
|
) : null
|
|
@@ -3740,6 +3779,7 @@ function TableLayout({
|
|
|
3740
3779
|
/* @__PURE__ */ jsx("th", { scope: "col", style: { ...headerCellStyle, color: tokens.textMuted, fontSize: 12 }, children: /* @__PURE__ */ jsx("span", { className: "aforo-w-pc-sr-only", style: srOnlyStyle, children: "Feature" }) }),
|
|
3741
3780
|
offerings.map((o) => {
|
|
3742
3781
|
const featured = offeringIsFeatured(o, featuredOverride);
|
|
3782
|
+
const { text: tablePrice, unitSuffix: tableUnitSuffix } = resolveDisplayPrice(o, locale, intlOverride);
|
|
3743
3783
|
return /* @__PURE__ */ jsx(
|
|
3744
3784
|
"th",
|
|
3745
3785
|
{
|
|
@@ -3769,13 +3809,20 @@ function TableLayout({
|
|
|
3769
3809
|
children: "Most popular"
|
|
3770
3810
|
}
|
|
3771
3811
|
) : null,
|
|
3772
|
-
/* @__PURE__ */
|
|
3812
|
+
/* @__PURE__ */ jsxs("div", { style: { fontSize: 22, fontWeight: 700 }, children: [
|
|
3813
|
+
tablePrice,
|
|
3814
|
+
tableUnitSuffix ? /* @__PURE__ */ jsxs("span", { style: { fontSize: 12, fontWeight: 400, color: tokens.textMuted }, children: [
|
|
3815
|
+
" ",
|
|
3816
|
+
"/ ",
|
|
3817
|
+
tableUnitSuffix
|
|
3818
|
+
] }) : null
|
|
3819
|
+
] }),
|
|
3773
3820
|
/* @__PURE__ */ jsx(
|
|
3774
3821
|
"button",
|
|
3775
3822
|
{
|
|
3776
3823
|
type: "button",
|
|
3777
3824
|
onClick: () => onCtaClick(o),
|
|
3778
|
-
"aria-label": `${ctaLabel(o, ctaTextProp,
|
|
3825
|
+
"aria-label": `${ctaLabel(o, ctaTextProp, tablePrice === "\u2014")} for ${localizedName(o)}`,
|
|
3779
3826
|
style: {
|
|
3780
3827
|
inlineSize: "100%",
|
|
3781
3828
|
padding: "8px 12px",
|
|
@@ -3788,7 +3835,7 @@ function TableLayout({
|
|
|
3788
3835
|
cursor: "pointer",
|
|
3789
3836
|
fontFamily: tokens.fontFamily
|
|
3790
3837
|
},
|
|
3791
|
-
children: ctaLabel(o, ctaTextProp,
|
|
3838
|
+
children: ctaLabel(o, ctaTextProp, tablePrice === "\u2014")
|
|
3792
3839
|
}
|
|
3793
3840
|
)
|
|
3794
3841
|
] })
|