@nebulr-group/bridge-svelte 0.4.0-beta.6 → 0.4.0-beta.7
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.
|
@@ -96,12 +96,28 @@
|
|
|
96
96
|
});
|
|
97
97
|
|
|
98
98
|
const warningLevel = $derived(snapshot?.warningLevel ?? null);
|
|
99
|
+
// TBP-275 — `metered` quotas bill overage instead of blocking. Prefer the
|
|
100
|
+
// server-authoritative `overcap` flag (handles limit === 0 pure-per-unit)
|
|
101
|
+
// over a UI-derived `used > limit`.
|
|
102
|
+
const isMetered = $derived(snapshot?.policy === 'metered');
|
|
99
103
|
const overCap = $derived(
|
|
100
|
-
snapshot
|
|
104
|
+
snapshot
|
|
105
|
+
? (snapshot.overcap ?? snapshot.used > snapshot.limit)
|
|
106
|
+
: false,
|
|
101
107
|
);
|
|
102
|
-
|
|
108
|
+
// Hard caps show only at the warning thresholds. Metered quotas also show
|
|
109
|
+
// once billing has engaged (overCap), even with no warningLevel — that's the
|
|
110
|
+
// live "you're now being billed for overage" state.
|
|
111
|
+
const visible = $derived(
|
|
112
|
+
snapshot !== undefined && (warningLevel !== null || (isMetered && overCap)),
|
|
113
|
+
);
|
|
114
|
+
// Metered never blocks, so it never renders as 'critical' — it's informational.
|
|
103
115
|
const severity = $derived<Severity>(
|
|
104
|
-
|
|
116
|
+
isMetered
|
|
117
|
+
? 'warn'
|
|
118
|
+
: warningLevel === 'critical' || overCap
|
|
119
|
+
? 'critical'
|
|
120
|
+
: 'warn',
|
|
105
121
|
);
|
|
106
122
|
const displayLabel = $derived(label ?? snapshot?.label ?? metric);
|
|
107
123
|
const percent = $derived(
|
|
@@ -109,13 +125,58 @@
|
|
|
109
125
|
? Math.min(100, Math.round((snapshot.used / snapshot.limit) * 100))
|
|
110
126
|
: 0,
|
|
111
127
|
);
|
|
128
|
+
// Meter bar is meaningless for pure per-unit metered (limit 0) — hide it there.
|
|
129
|
+
const showMeter = $derived(!!snapshot && snapshot.limit > 0);
|
|
130
|
+
|
|
131
|
+
/** Format an estimated cost like "$1.00" / "1.00 SEK". */
|
|
132
|
+
function formatCost(amount: number | undefined, currency: string | undefined): string {
|
|
133
|
+
if (amount === undefined) return '';
|
|
134
|
+
const cur = (currency ?? '').toUpperCase();
|
|
135
|
+
try {
|
|
136
|
+
return new Intl.NumberFormat(undefined, {
|
|
137
|
+
style: 'currency',
|
|
138
|
+
currency: cur || 'USD',
|
|
139
|
+
}).format(amount);
|
|
140
|
+
} catch {
|
|
141
|
+
// Unknown currency code → fall back to "<amount> <CUR>".
|
|
142
|
+
return `${amount.toFixed(2)}${cur ? ` ${cur}` : ''}`;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
112
145
|
|
|
113
146
|
function getCopy(
|
|
114
147
|
snap: QuotaSnapshot | undefined,
|
|
115
148
|
admin: boolean,
|
|
116
149
|
): { title: string; body: string; cta?: string } {
|
|
117
150
|
if (!snap) return { title: '', body: '' };
|
|
118
|
-
|
|
151
|
+
|
|
152
|
+
// TBP-275 — metered: live usage + projected cost, never a blocking message.
|
|
153
|
+
if (snap.policy === 'metered') {
|
|
154
|
+
const overUnits = snap.limit > 0 ? Math.max(0, snap.used - snap.limit) : snap.used;
|
|
155
|
+
const cost = formatCost(snap.overageEstimate, snap.currency);
|
|
156
|
+
const costSuffix = cost ? ` · ~${cost} estimated this period` : '';
|
|
157
|
+
if (snap.limit > 0) {
|
|
158
|
+
// included allotment + overage
|
|
159
|
+
if (overUnits > 0) {
|
|
160
|
+
return {
|
|
161
|
+
title: `${displayLabel} overage`,
|
|
162
|
+
body: `${overUnits.toLocaleString()} over your ${snap.limit.toLocaleString()} included${costSuffix}.`,
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
// approaching the included allotment (warningLevel drove visibility)
|
|
166
|
+
const unit = formatCost(snap.unitAmount, snap.currency);
|
|
167
|
+
return {
|
|
168
|
+
title: `${displayLabel} approaching included limit`,
|
|
169
|
+
body: `You've used ${snap.used.toLocaleString()} of ${snap.limit.toLocaleString()} included${unit ? ` — extra usage is billed at ${unit}/unit` : ''}.`,
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
// pure per-unit (limit 0) — billed from unit 1
|
|
173
|
+
return {
|
|
174
|
+
title: `${displayLabel} usage`,
|
|
175
|
+
body: `${snap.used.toLocaleString()} ${displayLabel}${costSuffix}.`,
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
const over = snap.overcap ?? snap.used > snap.limit;
|
|
119
180
|
const remaining = Math.max(0, snap.remaining);
|
|
120
181
|
if (over) {
|
|
121
182
|
return admin
|
|
@@ -178,9 +239,11 @@
|
|
|
178
239
|
<div class="bqb-content">
|
|
179
240
|
<strong class="bqb-title">{copy.title}</strong>
|
|
180
241
|
<span class="bqb-body">{copy.body}</span>
|
|
181
|
-
|
|
182
|
-
<div class="bqb-meter
|
|
183
|
-
|
|
242
|
+
{#if showMeter}
|
|
243
|
+
<div class="bqb-meter" aria-hidden="true">
|
|
244
|
+
<div class="bqb-meter-fill" style={`width: ${Math.min(100, percent)}%`}></div>
|
|
245
|
+
</div>
|
|
246
|
+
{/if}
|
|
184
247
|
</div>
|
|
185
248
|
{#if copy.cta && isBillingAdmin}
|
|
186
249
|
<button type="button" class="bqb-cta" onclick={handleAction}>{copy.cta}</button>
|
|
@@ -121,8 +121,13 @@
|
|
|
121
121
|
picking = true;
|
|
122
122
|
pickError = null;
|
|
123
123
|
try {
|
|
124
|
-
if (price.amount === 0) {
|
|
125
|
-
// Free plan — select directly
|
|
124
|
+
if (price.amount === 0 && !plan.hasCost) {
|
|
125
|
+
// Free plan — select directly. TBP-275: guard on `!plan.hasCost` so a
|
|
126
|
+
// $0-base plan that carries METERED pricing is NOT treated as free —
|
|
127
|
+
// it falls through to checkout/changePlan below, capturing a payment
|
|
128
|
+
// method so per-unit overage can be billed (US-C). Without this guard a
|
|
129
|
+
// metered $0-base plan would hit selectFreePlan, which the backend now
|
|
130
|
+
// rejects ("has a cost — use the checkout endpoint instead").
|
|
126
131
|
await getBridgeAuth().selectFreePlan(plan.key);
|
|
127
132
|
await loadSubscription();
|
|
128
133
|
onSelect?.({ plan, price });
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nebulr-group/bridge-svelte",
|
|
3
|
-
"version": "0.4.0-beta.
|
|
3
|
+
"version": "0.4.0-beta.7",
|
|
4
4
|
"description": "Bridge Svelte library, This library helps you to add bridge authentication and feature flags, and payments to your svelte application.",
|
|
5
5
|
"author": "Iman Pouya",
|
|
6
6
|
"license": "MIT",
|
|
@@ -67,7 +67,7 @@
|
|
|
67
67
|
}
|
|
68
68
|
},
|
|
69
69
|
"dependencies": {
|
|
70
|
-
"@nebulr-group/bridge-auth-core": "0.4.0-beta.
|
|
70
|
+
"@nebulr-group/bridge-auth-core": "0.4.0-beta.8"
|
|
71
71
|
},
|
|
72
72
|
"devDependencies": {
|
|
73
73
|
"@sveltejs/kit": "^2.16.0",
|