@nebulr-group/bridge-svelte 0.9.0-beta.8 → 0.9.1-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.
package/README.md CHANGED
@@ -1,18 +1,35 @@
1
+ <p align="center">
2
+ <a href="https://thebridge.dev/?utm_source=npm&utm_medium=readme&utm_campaign=bridge-svelte"><picture><source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/thebridgedev/bridge-svelte/main/.github/assets/banner.png"><img src="https://raw.githubusercontent.com/thebridgedev/bridge-svelte/main/.github/assets/banner-light.png" alt="The Bridge for SvelteKit" width="100%"></picture></a>
3
+ </p>
1
4
 
2
- ## @nebulr-group/bridge-svelte
5
+ <p align="center">
6
+ <a href="https://www.npmjs.com/package/@nebulr-group/bridge-svelte"><img src="https://img.shields.io/npm/v/@nebulr-group/bridge-svelte?color=20006b&label=npm" alt="npm version"></a>
7
+ <a href="https://github.com/thebridgedev/bridge-svelte/blob/main/LICENSE"><img src="https://img.shields.io/npm/l/@nebulr-group/bridge-svelte?color=20006b" alt="MIT license"></a>
8
+ <a href="https://madewithsvelte.com/p/the-bridge/shield-link"><img src="https://madewithsvelte.com/storage/repo-shields/5996-shield.svg" alt="Made with Svelte"></a>
9
+ </p>
3
10
 
4
- [![MadeWithSvelte.com shield](https://madewithsvelte.com/storage/repo-shields/5996-shield.svg)](https://madewithsvelte.com/p/the-bridge/shield-link)
11
+ <p align="center">
12
+ <a href="https://thebridge.dev/?utm_source=npm&utm_medium=readme&utm_campaign=bridge-svelte"><b>Website</b></a> ·
13
+ <a href="https://thebridge.dev/docs/quickstart/svelte/?utm_source=npm&utm_medium=readme&utm_campaign=bridge-svelte"><b>Quickstart</b></a> ·
14
+ <a href="https://thebridge.dev/docs/?utm_source=npm&utm_medium=readme&utm_campaign=bridge-svelte"><b>Docs</b></a> ·
15
+ <a href="https://thebridge.dev/docs/ai-assistants/mcp/?utm_source=npm&utm_medium=readme&utm_campaign=bridge-svelte"><b>Set up with your AI assistant</b></a>
16
+ </p>
5
17
 
18
+ # The Bridge for SvelteKit
6
19
 
7
- Bridge Svelte library. Add Bridge auth, feature flags, and payments to your SvelteKit 2 + Svelte 5 apps.
20
+ `@nebulr-group/bridge-svelte` adds sign-in, workspaces and roles, feature flags, Stripe subscriptions and plan limits to a SvelteKit 2 + Svelte 5 app, with one `.env` line and three files.
8
21
 
9
- ### Install
22
+ **[The Bridge](https://thebridge.dev/?utm_source=npm&utm_medium=readme&utm_campaign=bridge-svelte)** is a hosted backend for SaaS apps. It gives you sign-in (passwords, magic links, passkeys, social login and SSO), multi-tenant workspaces with roles, Stripe subscriptions with plan limits, and feature flags, all managed from one dashboard. Your AI coding assistant can set it up for you through the [Bridge MCP server](https://thebridge.dev/docs/ai-assistants/mcp/?utm_source=npm&utm_medium=readme&utm_campaign=bridge-svelte).
23
+
24
+ > **Let your AI assistant set it up.** Connect the [Bridge MCP server](https://thebridge.dev/docs/ai-assistants/mcp/?utm_source=npm&utm_medium=readme&utm_campaign=bridge-svelte) to Claude, Cursor, Copilot or Gemini CLI and ask it to add Bridge to your app. Not using MCP? Run `npx @nebulr-group/bridge-cli guide add-login` in your project: it detects your framework from `package.json` and prints the steps for your assistant to follow. `npx @nebulr-group/bridge-cli doctor` checks the result.
25
+
26
+ ## Install
10
27
 
11
28
  ```bash
12
29
  npm i @nebulr-group/bridge-svelte
13
30
  ```
14
31
 
15
- ### Usage
32
+ ## Usage
16
33
 
17
34
  The whole integration is one `.env` line and three files:
18
35
 
@@ -52,48 +69,28 @@ A stage or local app also sets `VITE_BRIDGE_API_BASE_URL`. Add `loginRoute: '/au
52
69
 
53
70
  [How Bridge works](https://github.com/thebridgedev/bridge-svelte/blob/main/learning/mechanisms.md) explains plan limits, the three UI levels and the four customisation levels; the [learning docs](https://github.com/thebridgedev/bridge-svelte/tree/main/learning) cover everything else. Coding agents: `npx @nebulr-group/bridge-cli guide svelte`.
54
71
 
55
- ### Build
56
-
57
-
58
- ```bash
59
- npm run build
60
- ```
72
+ ## Learn more
61
73
 
62
- Artifacts are emitted to `dist/` via `svelte-package`.
74
+ - [Quickstart](https://thebridge.dev/docs/quickstart/svelte/?utm_source=npm&utm_medium=readme&utm_campaign=bridge-svelte)
75
+ - [Authentication](https://thebridge.dev/docs/auth/svelte/?utm_source=npm&utm_medium=readme&utm_campaign=bridge-svelte)
76
+ - [Sign-in inside your app](https://thebridge.dev/docs/sdk-auth/svelte/?utm_source=npm&utm_medium=readme&utm_campaign=bridge-svelte)
77
+ - [Feature flags](https://thebridge.dev/docs/feature-flags/svelte/?utm_source=npm&utm_medium=readme&utm_campaign=bridge-svelte)
78
+ - [Branding](https://thebridge.dev/docs/branding/svelte/?utm_source=npm&utm_medium=readme&utm_campaign=bridge-svelte)
79
+ - [Live updates](https://thebridge.dev/docs/live-updates/svelte/?utm_source=npm&utm_medium=readme&utm_campaign=bridge-svelte)
80
+ - [Subscriptions and plan limits](https://thebridge.dev/docs/billing/how-it-works/?utm_source=npm&utm_medium=readme&utm_campaign=bridge-svelte)
63
81
 
64
- ### Release (branch-protected main)
65
-
66
- ```bash
67
- # 1) Create release branch
68
- git checkout -b release/v0.1.0-beta.1
69
- git push -u origin release/v0.1.0-beta.1
82
+ ## Other Bridge packages
70
83
 
71
- # 2) Open a PR: release/v0.1.0-beta.1 -> main, approve and merge
84
+ | Package | For |
85
+ |---|---|
86
+ | [`@nebulr-group/bridge-react`](https://www.npmjs.com/package/@nebulr-group/bridge-react) | React |
87
+ | [`@nebulr-group/bridge-nextjs`](https://www.npmjs.com/package/@nebulr-group/bridge-nextjs) | Next.js |
88
+ | [`@nebulr-group/bridge-angular`](https://www.npmjs.com/package/@nebulr-group/bridge-angular) | Angular |
89
+ | [`@nebulr-group/bridge-nestjs`](https://www.npmjs.com/package/@nebulr-group/bridge-nestjs) | NestJS |
90
+ | [`@nebulr-group/bridge-express`](https://www.npmjs.com/package/@nebulr-group/bridge-express) | Express |
91
+ | [`@nebulr-group/bridge-cli`](https://www.npmjs.com/package/@nebulr-group/bridge-cli) | CLI for people and AI agents |
92
+ | [`@nebulr-group/bridge-auth-core`](https://www.npmjs.com/package/@nebulr-group/bridge-auth-core) | Any JavaScript app (core) |
72
93
 
73
- # 3) After merge to main, tag and push
74
- git checkout main && git pull
75
- git tag v0.1.0-beta.1
76
- git push origin v0.1.0-beta.1
77
-
78
- # 4) Monitor GitHub Actions "Publish to npm"
79
- ```
80
-
81
- ### Commit signing (required)
82
-
83
- Ensure your commits are verified before opening PRs:
84
-
85
- ```bash
86
- # Option A: SSH signing (recommended)
87
- git config --global gpg.format ssh
88
- git config --global user.signingkey ~/.ssh/id_ed25519.pub
89
- git config --global commit.gpgsign true
90
-
91
- # Option B: GPG signing
92
- gpg --full-generate-key
93
- gpg --list-secret-keys --keyid-format=long
94
- git config --global user.signingkey <KEY_ID>
95
- git config --global commit.gpgsign true
96
- ```
94
+ ## License
97
95
 
98
- ### License
99
- MIT © thebridgedev
96
+ [MIT](https://github.com/thebridgedev/bridge-svelte/blob/main/LICENSE) © Nebulr. Built by [The Bridge](https://thebridge.dev/?utm_source=npm&utm_medium=readme&utm_campaign=bridge-svelte).
@@ -42,6 +42,18 @@ export declare function ensureBillingState(maxAgeMs?: number): Promise<void>;
42
42
  * every trigger of the refresh rule call. Never rejects.
43
43
  */
44
44
  export declare function refreshBilling(): Promise<void>;
45
+ /**
46
+ * TBP-810 — re-read the plan's features after a checkout return or a plan
47
+ * change. Bridge does not always push `entitlements.changed` for a plan change,
48
+ * and nothing else re-read them, so flags kept the old plan's answer.
49
+ *
50
+ * Not `entitlementsStore.refresh()`: that marks the store unloaded while it
51
+ * fetches, and every flag evaluated in that window reads "not entitled". This
52
+ * keeps the current map until the new one arrives, then writes it to both
53
+ * copies (auth-core's, which flags read, and `bridge.tenant.entitlements`).
54
+ * Never rejects.
55
+ */
56
+ export declare function refreshEntitlements(): Promise<void>;
45
57
  /** On mount: read what has not been read, or was read more than 30 s ago. */
46
58
  export declare function ensureBilling(maxAgeMs?: number): Promise<void>;
47
59
  /** Live events after which the billing stores are re-read. */
@@ -21,6 +21,7 @@
21
21
  */
22
22
  import { fetchBillingState, useBridge as useBillingBridge, } from '@nebulr-group/bridge-auth-core';
23
23
  import { billingRetryDelay, ensureSubscription, getBridgeAuth, loadSubscription, subscriptionWanted, tokenStore, } from './bridge-instance.js';
24
+ import { applyEntitlementsChanged } from './snapshot-stores.js';
24
25
  import { logger } from '../shared/logger.js';
25
26
  /** A read older than this is re-read when the tab regains focus. */
26
27
  export const BILLING_STALE_AFTER_MS = 30_000;
@@ -175,6 +176,7 @@ export async function refreshBilling() {
175
176
  reads.push(loadSubscription());
176
177
  if (_stateWanted || stateInStore())
177
178
  reads.push(refreshBillingState());
179
+ reads.push(refreshEntitlements());
178
180
  await Promise.all(reads);
179
181
  }
180
182
  catch (err) {
@@ -182,6 +184,69 @@ export async function refreshBilling() {
182
184
  logger.debug('[bridge-billing] refresh skipped:', err);
183
185
  }
184
186
  }
187
+ // ── Entitlements (what `bridge:billing.entitlement.*` flag rules read) ───────
188
+ let _entInflight = null;
189
+ let _entAgain = false;
190
+ /**
191
+ * TBP-810 — re-read the plan's features after a checkout return or a plan
192
+ * change. Bridge does not always push `entitlements.changed` for a plan change,
193
+ * and nothing else re-read them, so flags kept the old plan's answer.
194
+ *
195
+ * Not `entitlementsStore.refresh()`: that marks the store unloaded while it
196
+ * fetches, and every flag evaluated in that window reads "not entitled". This
197
+ * keeps the current map until the new one arrives, then writes it to both
198
+ * copies (auth-core's, which flags read, and `bridge.tenant.entitlements`).
199
+ * Never rejects.
200
+ */
201
+ export function refreshEntitlements() {
202
+ if (_entInflight) {
203
+ _entAgain = true;
204
+ return _entInflight;
205
+ }
206
+ _entInflight = (async () => {
207
+ try {
208
+ do {
209
+ _entAgain = false;
210
+ await readEntitlementsOnce();
211
+ } while (_entAgain);
212
+ }
213
+ finally {
214
+ _entInflight = null;
215
+ }
216
+ })();
217
+ return _entInflight;
218
+ }
219
+ async function readEntitlementsOnce() {
220
+ try {
221
+ // Nothing loaded them yet: there is no old answer to correct, and an app
222
+ // that never reads entitlements makes no extra request.
223
+ const store = useBillingBridge().entitlementsStore;
224
+ if (!store?.isHydrated())
225
+ return;
226
+ const opts = billingReadOptions();
227
+ if (!opts)
228
+ return;
229
+ const res = await globalThis.fetch(`${opts.apiBaseUrl.replace(/\/+$/, '')}/entitlements`, {
230
+ method: 'GET',
231
+ headers: { Authorization: `Bearer ${opts.accessToken}`, 'x-app-id': opts.appId ?? '' },
232
+ });
233
+ if (!res.ok)
234
+ return;
235
+ const body = (await res.json());
236
+ const map = body?.entitlements;
237
+ if (!map || typeof map !== 'object' || Array.isArray(map))
238
+ return;
239
+ // Signed out or switched workspace while this was in flight.
240
+ if (tokenChangedWorkspace(opts.accessToken))
241
+ return;
242
+ store.applyEntitlementsChanged(map);
243
+ applyEntitlementsChanged({ entitlements: map });
244
+ }
245
+ catch (err) {
246
+ // Best-effort: the next plan event, reconnect or token renewal repairs it.
247
+ logger.debug('[bridge-billing] entitlements refresh skipped:', err);
248
+ }
249
+ }
185
250
  /** On mount: read what has not been read, or was read more than 30 s ago. */
186
251
  export async function ensureBilling(maxAgeMs = BILLING_STALE_AFTER_MS) {
187
252
  try {
@@ -295,4 +360,6 @@ export function __resetBillingStoreForTests() {
295
360
  _stateLoadedAt = 0;
296
361
  _stateWanted = false;
297
362
  _stateToken = null;
363
+ _entInflight = null;
364
+ _entAgain = false;
298
365
  }
@@ -245,8 +245,30 @@ export function createBridgeFlags(config = {}) {
245
245
  });
246
246
  notifyAllFlagsChanged();
247
247
  });
248
+ // TBP-810 — the billing provider reads these stores at eval time, so a
249
+ // mounted `<FeatureFlag>` / `useFlag()` only sees a plan change if something
250
+ // re-evaluates after the store moved. Without this, an upgraded user kept
251
+ // the old plan's answer until the component re-mounted. One re-eval per
252
+ // burst: plan_changed, entitlements and quota updates land together.
253
+ let _billingReevalQueued = false;
254
+ const onBillingStoreChange = () => {
255
+ if (_billingReevalQueued)
256
+ return;
257
+ _billingReevalQueued = true;
258
+ queueMicrotask(() => {
259
+ _billingReevalQueued = false;
260
+ notifyAllFlagsChanged();
261
+ });
262
+ };
263
+ const unsubscribeBilling = [
264
+ useBridge().subscription.subscribe(onBillingStoreChange),
265
+ useBridge().quotas.subscribe(onBillingStoreChange),
266
+ useBridge().entitlementsStore.subscribe(onBillingStoreChange),
267
+ ];
248
268
  const stop = async () => {
249
269
  unsubscribeAuth();
270
+ for (const off of unsubscribeBilling)
271
+ off();
250
272
  unsubscribeOpen();
251
273
  await telemetry.stop();
252
274
  };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@nebulr-group/bridge-svelte",
3
- "version": "0.9.0-beta.8",
4
- "description": "Bridge Svelte library, This library helps you to add bridge authentication and feature flags, and payments to your svelte application.",
3
+ "version": "0.9.1-beta.0",
4
+ "description": "The Bridge for SvelteKit: sign-in, workspaces and roles, feature flags, Stripe subscriptions and plan limits in one .env line and three files.",
5
5
  "author": "Iman Pouya",
6
6
  "license": "MIT",
7
7
  "repository": {
@@ -11,7 +11,7 @@
11
11
  "bugs": {
12
12
  "url": "https://github.com/thebridgedev/bridge-svelte/issues"
13
13
  },
14
- "homepage": "https://thebridge.dev",
14
+ "homepage": "https://thebridge.dev/docs/quickstart/svelte/",
15
15
  "scripts": {
16
16
  "dev": "vite dev",
17
17
  "build": "bun run prepack",
@@ -65,7 +65,7 @@
65
65
  }
66
66
  },
67
67
  "dependencies": {
68
- "@nebulr-group/bridge-auth-core": "^0.8.0-beta.5",
68
+ "@nebulr-group/bridge-auth-core": "^0.8.0",
69
69
  "@simplewebauthn/browser": "^13.0.0"
70
70
  },
71
71
  "devDependencies": {
@@ -82,12 +82,23 @@
82
82
  },
83
83
  "keywords": [
84
84
  "svelte",
85
+ "sveltekit",
86
+ "svelte5",
87
+ "passkeys",
88
+ "magic-link",
89
+ "the-bridge",
85
90
  "bridge",
86
- "nebulr",
91
+ "authentication",
87
92
  "auth",
93
+ "login",
94
+ "sso",
88
95
  "feature-flags",
89
- "payments",
90
96
  "stripe",
91
- "saas control center"
97
+ "subscriptions",
98
+ "billing",
99
+ "plan-limits",
100
+ "multi-tenant",
101
+ "rbac",
102
+ "saas"
92
103
  ]
93
104
  }