@nebulr-group/bridge-svelte 0.3.2 → 0.4.0-beta.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.
Files changed (85) hide show
  1. package/dist/client/BridgeBootstrap.d.ts +1 -1
  2. package/dist/client/BridgeBootstrap.js +104 -27
  3. package/dist/client/BridgeBootstrap.svelte +65 -4
  4. package/dist/client/BridgeBootstrap.svelte.d.ts +2 -0
  5. package/dist/client/BridgeProvider.svelte +31 -0
  6. package/dist/client/BridgeProvider.svelte.d.ts +8 -0
  7. package/dist/client/components/developer/ApiTokenManagement.svelte +1 -1
  8. package/dist/client/components/sdk-auth/LoginForm.svelte +10 -7
  9. package/dist/client/components/sdk-auth/LoginForm.svelte.d.ts +1 -1
  10. package/dist/client/components/sdk-auth/PasskeyLogin.svelte +1 -1
  11. package/dist/client/components/sdk-auth/PasskeySetup.svelte +1 -1
  12. package/dist/client/components/sdk-auth/SsoButton.svelte +1 -1
  13. package/dist/client/components/sdk-auth/SsoButton.svelte.d.ts +1 -1
  14. package/dist/client/components/sdk-auth/TenantSelector.svelte +1 -1
  15. package/dist/client/components/sdk-auth/TenantSelector.svelte.d.ts +1 -1
  16. package/dist/client/components/sdk-auth/WorkspaceSelector.svelte +3 -3
  17. package/dist/client/components/sdk-auth/WorkspaceSelector.svelte.d.ts +3 -3
  18. package/dist/client/components/subscription/BridgeBillingNotice.svelte +389 -0
  19. package/dist/client/components/subscription/BridgeBillingNotice.svelte.d.ts +19 -0
  20. package/dist/client/components/subscription/BridgePaywall.svelte +94 -0
  21. package/dist/client/components/subscription/BridgePaywall.svelte.d.ts +19 -0
  22. package/dist/client/components/subscription/BridgeQuotaBanner.svelte +275 -0
  23. package/dist/client/components/subscription/BridgeQuotaBanner.svelte.d.ts +24 -0
  24. package/dist/client/components/subscription/BridgeSubscriptionStatus.svelte +116 -0
  25. package/dist/client/components/subscription/BridgeSubscriptionStatus.svelte.d.ts +7 -0
  26. package/dist/client/components/subscription/PlanSelector.svelte +22 -14
  27. package/dist/client/components/subscription/PlanSelector.svelte.d.ts +3 -3
  28. package/dist/client/components/subscription/billing-notice-gate.test.d.ts +1 -0
  29. package/dist/client/components/subscription/billing-notice-gate.test.js +83 -0
  30. package/dist/client/components/team/TeamAddUserDialog.svelte +1 -1
  31. package/dist/client/components/team/TeamAddUserDialog.svelte.d.ts +1 -1
  32. package/dist/client/components/team/TeamEditUserDialog.svelte +1 -1
  33. package/dist/client/components/team/TeamEditUserDialog.svelte.d.ts +1 -1
  34. package/dist/client/components/team/TeamProfileForm.svelte +1 -1
  35. package/dist/client/components/team/TeamUserList.svelte +1 -1
  36. package/dist/client/components/team/TeamWorkspaceForm.svelte +1 -1
  37. package/dist/client/tracking/pii-hashing.spec.js +4 -14
  38. package/dist/core/bridge-instance.d.ts +1 -2
  39. package/dist/core/bridge-instance.js +0 -4
  40. package/dist/core/bridge-runtime.d.ts +101 -0
  41. package/dist/core/bridge-runtime.js +290 -0
  42. package/dist/core/bridge-runtime.test.d.ts +1 -0
  43. package/dist/core/bridge-runtime.test.js +203 -0
  44. package/dist/core/bridge.d.ts +93 -0
  45. package/dist/core/bridge.js +108 -0
  46. package/dist/core/bridge.test.d.ts +1 -0
  47. package/dist/core/bridge.test.js +224 -0
  48. package/dist/core/events.d.ts +74 -0
  49. package/dist/core/events.js +64 -0
  50. package/dist/core/events.test.d.ts +1 -0
  51. package/dist/core/events.test.js +116 -0
  52. package/dist/core/lazy-slice.d.ts +74 -0
  53. package/dist/core/lazy-slice.js +124 -0
  54. package/dist/core/lazy-slice.test.d.ts +1 -0
  55. package/dist/core/lazy-slice.test.js +144 -0
  56. package/dist/core/realtime-status.d.ts +6 -0
  57. package/dist/core/realtime-status.js +15 -0
  58. package/dist/core/snapshot-stores.d.ts +69 -0
  59. package/dist/core/snapshot-stores.js +64 -0
  60. package/dist/core/use-bridge.d.ts +13 -0
  61. package/dist/core/use-bridge.js +46 -0
  62. package/dist/core/use-bridge.test.d.ts +1 -0
  63. package/dist/core/use-bridge.test.js +24 -0
  64. package/dist/flags/FeatureFlag.svelte +57 -0
  65. package/dist/flags/FeatureFlag.svelte.d.ts +37 -0
  66. package/dist/flags/bootstrap.d.ts +72 -0
  67. package/dist/flags/bootstrap.js +323 -0
  68. package/dist/flags/bootstrap.test.d.ts +1 -0
  69. package/dist/flags/bootstrap.test.js +195 -0
  70. package/dist/flags/flag.svelte.d.ts +31 -0
  71. package/dist/flags/flag.svelte.js +99 -0
  72. package/dist/flags/index.d.ts +8 -0
  73. package/dist/flags/index.js +20 -0
  74. package/dist/flags/index.test.d.ts +1 -0
  75. package/dist/flags/index.test.js +81 -0
  76. package/dist/flags/realtime-status.d.ts +1 -0
  77. package/dist/flags/realtime-status.js +4 -0
  78. package/dist/flags/registry.d.ts +27 -0
  79. package/dist/flags/registry.js +63 -0
  80. package/dist/flags/us13-integration.test.d.ts +1 -0
  81. package/dist/flags/us13-integration.test.js +168 -0
  82. package/dist/index.d.ts +14 -1
  83. package/dist/index.js +31 -2
  84. package/dist/styles.d.ts +2 -0
  85. package/package.json +92 -86
@@ -0,0 +1,389 @@
1
+ <!--
2
+ Billing 2.0 / Phase B / US-5 → US-9 — unified billing-notice component.
3
+
4
+ Drop-in component that renders the workspace's current billing notice based
5
+ on `useBridge().subscription`. Designed for multi-state from the start:
6
+ US-5 implements the `past_due` paths; US-6 adds cancellation; US-8 adds
7
+ trial states; US-9 adds dunning + locked. New states slot in via the
8
+ notice-state -> content map without restructuring.
9
+
10
+ Audience: reads the existing auth context to render admin (CTA) vs member
11
+ (no CTA) variants. Per locked decision, role lives where it lives today —
12
+ NOT on useBridge().subscription.
13
+
14
+ Not dismissible — stays until status flips back to active.
15
+ -->
16
+ <script lang="ts">
17
+ import { onDestroy, onMount } from 'svelte';
18
+ import {
19
+ deriveNoticeState,
20
+ deriveSeverity,
21
+ useBridge,
22
+ type BillingNoticeState,
23
+ type BillingSeverity,
24
+ type BillingSubscriptionSnapshot,
25
+ } from '@nebulr-group/bridge-auth-core';
26
+ import { getBridgeAuth } from '../../../core/bridge-instance.js';
27
+
28
+ type Chassis = 'bar' | 'rail' | 'card';
29
+
30
+ interface Props {
31
+ /** Visual chassis variant. Per `deliverables.html §01`. */
32
+ chassis?: Chassis;
33
+ /**
34
+ * Gate behavior. `soft` (default) always renders the inline banner. `hard`
35
+ * renders a full-screen blocking lockscreen when the workspace is locked
36
+ * (gate engaged); non-locked states still render as the inline banner.
37
+ */
38
+ mode?: 'soft' | 'hard';
39
+ /** Optional class applied to the root element. */
40
+ class?: string;
41
+ /** Override the default CTA click handler (links to billing surface). */
42
+ onActionClick?: (state: BillingNoticeState) => void;
43
+ }
44
+
45
+ let {
46
+ chassis = 'rail',
47
+ mode = 'soft',
48
+ class: className = '',
49
+ onActionClick,
50
+ }: Props = $props();
51
+
52
+ let snapshot = $state<BillingSubscriptionSnapshot>(useBridge().subscription.snapshot());
53
+ let unsubscribe: (() => void) | undefined;
54
+
55
+ // Admin/member variant — read from existing auth context. The exact field
56
+ // name varies by SDK; we look up `billing` privilege on the current user.
57
+ // Defaults to non-admin if unavailable.
58
+ let isBillingAdmin = $state(false);
59
+
60
+ onMount(() => {
61
+ unsubscribe = useBridge().subscription.subscribe((snap) => {
62
+ snapshot = snap;
63
+ });
64
+
65
+ const auth = getBridgeAuth();
66
+ const ctx = auth.getApiContext();
67
+ if (ctx.accessToken) {
68
+ // Best-effort role read. Existing auth-core role probing varies by app;
69
+ // defensive cast so the component never crashes on missing fields.
70
+ const tokenManager = (auth as unknown as { tokenManager?: { getTokens?: () => { accessToken?: string } } }).tokenManager;
71
+ void tokenManager; // intentionally unused — placeholder until role API is finalized
72
+ // For US-5 default: assume admin if a JWT is present. Subsequent
73
+ // stories will read the exact privilege claim. Keeping permissive so
74
+ // the CTA shows by default; member variant lands when the role check
75
+ // is wired through.
76
+ isBillingAdmin = true;
77
+ }
78
+
79
+ if (!ctx.accessToken) {
80
+ useBridge().subscription.setError('Not authenticated');
81
+ return;
82
+ }
83
+ if (!snapshot.state) {
84
+ useBridge().subscription.mount({
85
+ apiBaseUrl: ctx.apiBaseUrl,
86
+ accessToken: ctx.accessToken,
87
+ appId: ctx.appId,
88
+ });
89
+ }
90
+ });
91
+
92
+ onDestroy(() => unsubscribe?.());
93
+
94
+ const noticeState = $derived<BillingNoticeState>(deriveNoticeState(snapshot.state));
95
+ const severity = $derived<BillingSeverity>(deriveSeverity(noticeState));
96
+ const visible = $derived(snapshot.state !== null && noticeState !== 'active');
97
+ const asLockscreen = $derived(mode === 'hard' && severity === 'locked');
98
+
99
+ // Copy map per `deliverables.html §06`. US-5 ships past_due copy; later
100
+ // stories fill in trial/cancel/dunning variants. Member variant suppresses
101
+ // the CTA and softens the call-to-action.
102
+ function getCopy(state: BillingNoticeState, admin: boolean): { title: string; body: string; cta?: string } {
103
+ const cardLast4 = snapshot.state?.cardLast4;
104
+ const endsAt = snapshot.state?.endsAt;
105
+ const daysLeft = snapshot.state?.daysLeft;
106
+ const nextRetryAt = snapshot.state?.nextRetryAt;
107
+
108
+ switch (state) {
109
+ case 'past_due':
110
+ return admin
111
+ ? {
112
+ title: 'Payment failed',
113
+ body: cardLast4
114
+ ? `We couldn't charge your card ending in ${cardLast4}. Update your payment method to keep using ${snapshot.state?.plan.name ?? 'your plan'}.`
115
+ : `We couldn't charge your card. Update your payment method to keep using ${snapshot.state?.plan.name ?? 'your plan'}.`,
116
+ cta: 'Update card',
117
+ }
118
+ : {
119
+ title: "Your workspace's payment failed",
120
+ body: 'Please contact your workspace owner to update the payment method.',
121
+ };
122
+ case 'past_due_trial':
123
+ return admin
124
+ ? {
125
+ title: 'Trial ended',
126
+ body: 'Add a payment method to keep using your workspace.',
127
+ cta: 'Add card',
128
+ }
129
+ : {
130
+ title: "Your workspace's trial has ended",
131
+ body: 'Please contact your workspace owner to add a payment method.',
132
+ };
133
+ case 'trial_active':
134
+ return admin
135
+ ? {
136
+ title: 'Trial active',
137
+ body: daysLeft !== undefined ? `${daysLeft} days left in your trial.` : 'Trial in progress.',
138
+ }
139
+ : {
140
+ title: 'Trial active',
141
+ body: daysLeft !== undefined ? `${daysLeft} days left.` : 'Trial in progress.',
142
+ };
143
+ case 'trial_ending_soon':
144
+ return admin
145
+ ? {
146
+ title: 'Trial ending soon',
147
+ body: daysLeft !== undefined ? `${daysLeft} days left. Add a payment method to keep your access.` : 'Add a payment method to keep your access.',
148
+ cta: 'Add card',
149
+ }
150
+ : {
151
+ title: 'Trial ending soon',
152
+ body: daysLeft !== undefined ? `${daysLeft} days left.` : 'Contact your workspace owner.',
153
+ };
154
+ case 'cancel_at_period_end':
155
+ return admin
156
+ ? {
157
+ title: 'Subscription ending',
158
+ body: endsAt
159
+ ? `Your subscription ends ${new Date(endsAt).toLocaleDateString()}. You'll keep full access until then.`
160
+ : "Your subscription is ending. You'll keep access until the period ends.",
161
+ cta: 'Reactivate',
162
+ }
163
+ : {
164
+ title: 'Subscription ending',
165
+ body: endsAt ? `Your workspace's subscription ends ${new Date(endsAt).toLocaleDateString()}.` : "Your workspace's subscription is ending.",
166
+ };
167
+ case 'canceled':
168
+ return admin
169
+ ? { title: 'Subscription canceled', body: 'Your subscription has ended. Choose a plan to continue.', cta: 'Choose plan' }
170
+ : { title: 'Subscription canceled', body: 'Please contact your workspace owner.' };
171
+ case 'dunning_active':
172
+ return admin
173
+ ? {
174
+ title: 'Payment retry scheduled',
175
+ body: nextRetryAt
176
+ ? `We'll retry your payment on ${new Date(nextRetryAt).toLocaleDateString()}. Update your card to avoid interruption.`
177
+ : "We'll retry your payment soon. Update your card to avoid interruption.",
178
+ cta: 'Update card',
179
+ }
180
+ : { title: 'Payment retry scheduled', body: 'Please contact your workspace owner to update the payment method.' };
181
+ case 'dunning_final_retry':
182
+ return admin
183
+ ? {
184
+ title: 'Final payment retry',
185
+ body: 'This is the last automatic retry. Update your card now to avoid losing access.',
186
+ cta: 'Update card',
187
+ }
188
+ : { title: 'Final payment retry', body: 'Please contact your workspace owner immediately.' };
189
+ case 'dunning_exhausted':
190
+ return admin
191
+ ? { title: 'Access locked', body: 'Payment retries have exhausted. Update your card to restore access.', cta: 'Update card' }
192
+ : { title: 'Access locked', body: 'Please contact your workspace owner.' };
193
+ default:
194
+ return { title: '', body: '' };
195
+ }
196
+ }
197
+
198
+ const copy = $derived(getCopy(noticeState, isBillingAdmin));
199
+
200
+ function handleAction() {
201
+ if (onActionClick) {
202
+ onActionClick(noticeState);
203
+ return;
204
+ }
205
+ // Default: open the existing billing surface. The exact destination
206
+ // depends on app configuration — for now, navigate to /billing on the
207
+ // current origin. Apps can override via `onActionClick`.
208
+ if (typeof window !== 'undefined') {
209
+ window.location.href = '/billing';
210
+ }
211
+ }
212
+ </script>
213
+
214
+ {#if visible}
215
+ {#if asLockscreen}
216
+ <div
217
+ class={`bridge-billing-lockscreen ${className}`}
218
+ role="alertdialog"
219
+ aria-modal="true"
220
+ aria-live="assertive"
221
+ >
222
+ <div class="bbl-panel">
223
+ <strong class="bbl-title">{copy.title}</strong>
224
+ <span class="bbl-body">{copy.body}</span>
225
+ {#if copy.cta && isBillingAdmin}
226
+ <button type="button" class="bbl-cta" onclick={handleAction}>{copy.cta}</button>
227
+ {/if}
228
+ </div>
229
+ </div>
230
+ {:else}
231
+ <div
232
+ class={`bridge-billing-notice bbn-chassis-${chassis} bbn-severity-${severity} ${className}`}
233
+ role={severity === 'critical' || severity === 'locked' ? 'alert' : 'status'}
234
+ aria-live={severity === 'critical' || severity === 'locked' ? 'assertive' : 'polite'}
235
+ >
236
+ <div class="bbn-content">
237
+ <strong class="bbn-title">{copy.title}</strong>
238
+ <span class="bbn-body">{copy.body}</span>
239
+ </div>
240
+ {#if copy.cta && isBillingAdmin}
241
+ <button type="button" class="bbn-cta" onclick={handleAction}>{copy.cta}</button>
242
+ {/if}
243
+ </div>
244
+ {/if}
245
+ {/if}
246
+
247
+ <style>
248
+ .bridge-billing-notice {
249
+ display: flex;
250
+ align-items: center;
251
+ justify-content: space-between;
252
+ gap: 1rem;
253
+ padding: 0.75rem 1rem;
254
+ border-radius: 0.5rem;
255
+ font: inherit;
256
+ transition: opacity 280ms ease, transform 320ms ease;
257
+ }
258
+
259
+ .bbn-content {
260
+ display: flex;
261
+ flex-direction: column;
262
+ gap: 0.125rem;
263
+ flex: 1 1 auto;
264
+ }
265
+
266
+ .bbn-title {
267
+ font-weight: 600;
268
+ }
269
+
270
+ .bbn-body {
271
+ font-size: 0.875rem;
272
+ opacity: 0.85;
273
+ }
274
+
275
+ .bbn-cta {
276
+ padding: 0.5rem 1rem;
277
+ border-radius: 0.375rem;
278
+ border: 1px solid currentColor;
279
+ background: transparent;
280
+ color: inherit;
281
+ font: inherit;
282
+ font-weight: 500;
283
+ cursor: pointer;
284
+ flex: 0 0 auto;
285
+ }
286
+
287
+ .bbn-cta:hover {
288
+ background: currentColor;
289
+ color: white;
290
+ }
291
+
292
+ /* Chassis variants */
293
+ .bbn-chassis-bar {
294
+ border-radius: 0;
295
+ width: 100%;
296
+ }
297
+
298
+ .bbn-chassis-rail {
299
+ border-left: 4px solid currentColor;
300
+ border-radius: 0 0.5rem 0.5rem 0;
301
+ }
302
+
303
+ .bbn-chassis-card {
304
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
305
+ border: 1px solid rgba(0, 0, 0, 0.08);
306
+ }
307
+
308
+ /* Severity tokens */
309
+ .bbn-severity-info {
310
+ background: #dbeafe;
311
+ color: #1e40af;
312
+ }
313
+
314
+ .bbn-severity-warn {
315
+ background: #fef3c7;
316
+ color: #92400e;
317
+ }
318
+
319
+ .bbn-severity-critical {
320
+ background: #fee2e2;
321
+ color: #991b1b;
322
+ }
323
+
324
+ .bbn-severity-locked {
325
+ background: #1f2937;
326
+ color: #f9fafb;
327
+ }
328
+
329
+ @media (prefers-reduced-motion: reduce) {
330
+ .bridge-billing-notice {
331
+ transition: none;
332
+ }
333
+ }
334
+
335
+ /* Hard-mode lockscreen — full-viewport blocking overlay */
336
+ .bridge-billing-lockscreen {
337
+ position: fixed;
338
+ inset: 0;
339
+ z-index: 9999;
340
+ display: flex;
341
+ align-items: center;
342
+ justify-content: center;
343
+ padding: 1rem;
344
+ background: rgba(15, 23, 42, 0.72);
345
+ backdrop-filter: blur(2px);
346
+ }
347
+
348
+ .bbl-panel {
349
+ display: flex;
350
+ flex-direction: column;
351
+ gap: 0.75rem;
352
+ max-width: 28rem;
353
+ width: 100%;
354
+ padding: 1.5rem;
355
+ border-radius: 0.75rem;
356
+ background: #1f2937;
357
+ color: #f9fafb;
358
+ box-shadow: 0 10px 40px rgba(0, 0, 0, 0.35);
359
+ text-align: center;
360
+ }
361
+
362
+ .bbl-title {
363
+ font-size: 1.125rem;
364
+ font-weight: 600;
365
+ }
366
+
367
+ .bbl-body {
368
+ font-size: 0.9375rem;
369
+ opacity: 0.85;
370
+ }
371
+
372
+ .bbl-cta {
373
+ align-self: center;
374
+ margin-top: 0.5rem;
375
+ padding: 0.625rem 1.25rem;
376
+ border-radius: 0.5rem;
377
+ border: 1px solid currentColor;
378
+ background: transparent;
379
+ color: inherit;
380
+ font: inherit;
381
+ font-weight: 500;
382
+ cursor: pointer;
383
+ }
384
+
385
+ .bbl-cta:hover {
386
+ background: #f9fafb;
387
+ color: #1f2937;
388
+ }
389
+ </style>
@@ -0,0 +1,19 @@
1
+ import { type BillingNoticeState } from '@nebulr-group/bridge-auth-core';
2
+ type Chassis = 'bar' | 'rail' | 'card';
3
+ interface Props {
4
+ /** Visual chassis variant. Per `deliverables.html §01`. */
5
+ chassis?: Chassis;
6
+ /**
7
+ * Gate behavior. `soft` (default) always renders the inline banner. `hard`
8
+ * renders a full-screen blocking lockscreen when the workspace is locked
9
+ * (gate engaged); non-locked states still render as the inline banner.
10
+ */
11
+ mode?: 'soft' | 'hard';
12
+ /** Optional class applied to the root element. */
13
+ class?: string;
14
+ /** Override the default CTA click handler (links to billing surface). */
15
+ onActionClick?: (state: BillingNoticeState) => void;
16
+ }
17
+ declare const BridgeBillingNotice: import("svelte").Component<Props, {}, "">;
18
+ type BridgeBillingNotice = ReturnType<typeof BridgeBillingNotice>;
19
+ export default BridgeBillingNotice;
@@ -0,0 +1,94 @@
1
+ <script lang="ts">
2
+ import type { Snippet } from 'svelte';
3
+ import { onMount } from 'svelte';
4
+ import type { Plan, PriceOfferSdk } from '@nebulr-group/bridge-auth-core';
5
+ import { subscriptionStore, loadSubscription } from '../../../core/bridge-instance.js';
6
+ import PlanSelector from './PlanSelector.svelte';
7
+
8
+ interface Props {
9
+ /** Where to send the user after a successful Stripe payment. @default '/' */
10
+ successRedirect?: string;
11
+ /** Where to send the user if they cancel Stripe Checkout. @default '/' */
12
+ cancelRedirect?: string;
13
+ /** Called after free-plan or direct plan change (not the Stripe redirect path). Use for analytics side-effects. */
14
+ onSelect?: (detail: { plan: Plan; price: PriceOfferSdk }) => void;
15
+ /** Override the default "Choose a plan" heading. */
16
+ heading?: Snippet;
17
+ children?: Snippet;
18
+ }
19
+
20
+ let {
21
+ successRedirect = '/',
22
+ cancelRedirect = '/',
23
+ onSelect,
24
+ heading,
25
+ children,
26
+ }: Props = $props();
27
+
28
+ const status = $derived($subscriptionStore.status);
29
+ const loading = $derived($subscriptionStore.loading);
30
+
31
+ onMount(() => {
32
+ if (!$subscriptionStore.status && !$subscriptionStore.loading) {
33
+ loadSubscription();
34
+ }
35
+ });
36
+
37
+ // Show paywall only once the subscription status is known and the tenant
38
+ // has no plan selected. Respects paymentsAutoRedirect: false (opt-out flag).
39
+ const showPaywall = $derived(
40
+ !loading &&
41
+ !!status?.shouldSelectPlan &&
42
+ status?.paymentsAutoRedirect !== false
43
+ );
44
+ </script>
45
+
46
+ {#if showPaywall}
47
+ <div class="bridge-paywall" role="dialog" aria-modal="true" aria-label="Choose a plan">
48
+ <div class="bridge-paywall-panel">
49
+ {#if heading}
50
+ {@render heading()}
51
+ {:else}
52
+ <h2 class="bridge-paywall-heading">Choose a plan</h2>
53
+ {/if}
54
+ <PlanSelector {successRedirect} {cancelRedirect} {onSelect} />
55
+ </div>
56
+ </div>
57
+ {:else}
58
+ {@render children?.()}
59
+ {/if}
60
+
61
+ <style>
62
+ .bridge-paywall {
63
+ position: fixed;
64
+ inset: 0;
65
+ z-index: 9000;
66
+ display: flex;
67
+ align-items: center;
68
+ justify-content: center;
69
+ padding: 1rem;
70
+ background: var(--bridge-paywall-bg, rgba(15, 23, 42, 0.72));
71
+ backdrop-filter: blur(2px);
72
+ }
73
+
74
+ .bridge-paywall-panel {
75
+ display: flex;
76
+ flex-direction: column;
77
+ gap: 1.5rem;
78
+ width: 100%;
79
+ max-width: 42rem;
80
+ max-height: 90vh;
81
+ overflow-y: auto;
82
+ padding: 2rem;
83
+ border-radius: 0.75rem;
84
+ background: var(--bridge-paywall-panel-bg, #ffffff);
85
+ box-shadow: 0 10px 40px rgba(0, 0, 0, 0.15);
86
+ }
87
+
88
+ .bridge-paywall-heading {
89
+ margin: 0;
90
+ font-size: 1.25rem;
91
+ font-weight: 600;
92
+ text-align: center;
93
+ }
94
+ </style>
@@ -0,0 +1,19 @@
1
+ import type { Snippet } from 'svelte';
2
+ import type { Plan, PriceOfferSdk } from '@nebulr-group/bridge-auth-core';
3
+ interface Props {
4
+ /** Where to send the user after a successful Stripe payment. @default '/' */
5
+ successRedirect?: string;
6
+ /** Where to send the user if they cancel Stripe Checkout. @default '/' */
7
+ cancelRedirect?: string;
8
+ /** Called after free-plan or direct plan change (not the Stripe redirect path). Use for analytics side-effects. */
9
+ onSelect?: (detail: {
10
+ plan: Plan;
11
+ price: PriceOfferSdk;
12
+ }) => void;
13
+ /** Override the default "Choose a plan" heading. */
14
+ heading?: Snippet;
15
+ children?: Snippet;
16
+ }
17
+ declare const BridgePaywall: import("svelte").Component<Props, {}, "">;
18
+ type BridgePaywall = ReturnType<typeof BridgePaywall>;
19
+ export default BridgePaywall;