@nebulr-group/bridge-svelte 0.4.0-beta.2 → 0.4.0-beta.3

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.
@@ -5,7 +5,6 @@ import { createRouteGuard } from '../auth/route-guard.js';
5
5
  import { getBridgeAuth, bridgeReadyStore, markReady, waitForBridge as _waitForBridge, } from '../core/bridge-instance.js';
6
6
  import { installBridgeAuthFetch } from '../core/bridge-runtime.js';
7
7
  import { useBridge } from '@nebulr-group/bridge-auth-core';
8
- import { featureFlags } from '../shared/feature-flag.js';
9
8
  import { logger } from '../shared/logger.js';
10
9
  import { bridgeConfig, getConfig } from './stores/config.store.js';
11
10
  export async function bridgeBootstrap(url, config, routeConfig = { rules: [], defaultAccess: 'protected' }, kitFetch) {
@@ -158,11 +157,17 @@ export async function bridgeBootstrap(url, config, routeConfig = { rules: [], de
158
157
  catch {
159
158
  // Non-fatal — gate fails open; the notice hydrates lazily on first render.
160
159
  }
161
- // 3. Fire flag fetch without awaiting so GQL queries can start in parallel.
162
- // The flagsReady promise is passed to the guard and returned to callers
163
- // so routes that need flags can still await before rendering.
164
- // Swallow errors feature flag failures should never crash the bootstrap.
165
- const flagsReady = featureFlags.refresh().catch((err) => {
160
+ // 3. Warm the route-guard's flag cache without awaiting so GQL queries can
161
+ // start in parallel. `loadFeatureFlags()` populates the same auth-core
162
+ // FeatureFlagService the route guard evaluates `featureFlag` rules
163
+ // against; the flagsReady promise is passed to the guard and returned to
164
+ // callers so routes that gate on flags can await before rendering.
165
+ // (Component/programmatic flag reads use FF 2.0 `useFlag` / <FeatureFlag>.)
166
+ // Errors are logged, never thrown — flag failures must not crash bootstrap.
167
+ const flagsReady = getBridgeAuth()
168
+ .loadFeatureFlags()
169
+ .then(() => { })
170
+ .catch((err) => {
166
171
  logger.warn('[bridgeBootstrap] Feature flags failed to load:', err);
167
172
  });
168
173
  // 4. Handle route guarding and redirects
@@ -39,8 +39,6 @@ export declare const isOnboarded: Readable<boolean>;
39
39
  export declare const hasMultiTenantAccess: Readable<boolean>;
40
40
  /** Tenant users for multi-tenant selection */
41
41
  export declare const tenantUsersStore: Readable<TenantUser[]>;
42
- /** Feature flags map */
43
- export declare const flagsStore: Readable<Record<string, boolean>>;
44
42
  /** Bridge ready state */
45
43
  export declare const bridgeReadyStore: Readable<boolean>;
46
44
  /** App-level config (SSO providers, feature flags, etc.) — loaded anonymously on init */
@@ -56,6 +54,5 @@ export declare const subscriptionStore: Readable<SubscriptionState>;
56
54
  export declare function loadSubscription(): Promise<void>;
57
55
  /** Lazy proxy to the BridgeAuth singleton — call methods directly: `auth.getToken()`, `auth.logout()`, etc. */
58
56
  export declare const auth: BridgeAuth;
59
- export declare const _flagsWritable: Writable<Record<string, boolean>>;
60
57
  export declare const _profileWritable: Writable<Profile | null | undefined>;
61
58
  export declare const _errorWritable: Writable<string | null>;
@@ -14,7 +14,6 @@ let _instance = null;
14
14
  const _tokens = writable(null);
15
15
  const _appConfig = writable(null);
16
16
  const _profile = writable(undefined);
17
- const _flags = writable({});
18
17
  const _authState = writable('unauthenticated');
19
18
  const _isLoading = writable(true);
20
19
  const _error = writable(null);
@@ -46,19 +45,18 @@ export function initBridge(config) {
46
45
  if (existingTokens) {
47
46
  _tokens.set(existingTokens);
48
47
  // Fetch profile for existing tokens
49
- _instance.getProfile().then((p) => _profile.set(p ?? null)).catch(() => { });
48
+ _instance.getProfile().then((p) => _profile.set(p ?? null)).catch((err) => logger.warn('[bridge-instance] profile fetch failed:', err));
50
49
  }
51
50
  _authState.set(_instance.getAuthState());
52
51
  _isLoading.set(false);
53
52
  // Wire auth-core events → Svelte stores
54
53
  _instance.on('auth:login', (tokens) => {
55
54
  _tokens.set(tokens);
56
- _instance.getProfile().then((p) => _profile.set(p ?? null)).catch(() => { });
55
+ _instance.getProfile().then((p) => _profile.set(p ?? null)).catch((err) => logger.warn('[bridge-instance] profile fetch failed:', err));
57
56
  });
58
57
  _instance.on('auth:logout', () => {
59
58
  _tokens.set(null);
60
59
  _profile.set(null);
61
- _flags.set({});
62
60
  });
63
61
  _instance.on('auth:token-refreshed', (tokens) => {
64
62
  _tokens.set(tokens);
@@ -77,9 +75,8 @@ export function initBridge(config) {
77
75
  });
78
76
  _instance.on('auth:workspace-changed', (tokens) => {
79
77
  _tokens.set(tokens);
80
- _flags.set({});
81
78
  _subscriptionWritable.set({ status: null, plans: null, loading: false, error: null });
82
- _instance.getProfile().then((p) => _profile.set(p ?? null)).catch(() => { });
79
+ _instance.getProfile().then((p) => _profile.set(p ?? null)).catch((err) => logger.warn('[bridge-instance] profile fetch failed:', err));
83
80
  });
84
81
  _instance.on('auth:error', (err) => {
85
82
  _error.set(err.message);
@@ -150,8 +147,6 @@ export const isOnboarded = _isOnboarded;
150
147
  export const hasMultiTenantAccess = _hasMultiTenantAccess;
151
148
  /** Tenant users for multi-tenant selection */
152
149
  export const tenantUsersStore = _tenantUsers;
153
- /** Feature flags map */
154
- export const flagsStore = _flags;
155
150
  /** Bridge ready state */
156
151
  export const bridgeReadyStore = _ready;
157
152
  /** App-level config (SSO providers, feature flags, etc.) — loaded anonymously on init */
@@ -183,11 +178,10 @@ export async function loadSubscription() {
183
178
  export const auth = new Proxy({}, {
184
179
  get(_, prop) {
185
180
  const instance = getBridgeAuth();
186
- const value = instance[prop];
181
+ const value = Reflect.get(instance, prop);
187
182
  return typeof value === 'function' ? value.bind(instance) : value;
188
183
  },
189
184
  });
190
185
  // ── Internal-only store writers (for use by wrapper modules) ───────────────────
191
- export const _flagsWritable = _flags;
192
186
  export const _profileWritable = _profile;
193
187
  export const _errorWritable = _error;
@@ -8,10 +8,9 @@
8
8
  * follow-up sub-tickets (TBP-322 `.load()` semantics + TBP-323 reactive
9
9
  * binding for loaded slices).
10
10
  *
11
- * Backward compat: this is purely additive. The legacy module-level stores
12
- * (`subscriptionStore`, `flagsStore`, `appConfigStore`, etc.) continue to
13
- * exist and are populated by the same internal state TBP-324 wraps them
14
- * in deprecation warnings; for now both surfaces coexist.
11
+ * Backward compat: this is purely additive. The module-level stores
12
+ * (`subscriptionStore`, `appConfigStore`, etc.) continue to exist and are
13
+ * populated by the same internal state; both surfaces coexist.
15
14
  *
16
15
  * Note: `useBridge()` (Svelte context hook) lands in TBP-320. This module
17
16
  * only exposes the singleton aggregate `bridge`; consumers can import it
@@ -8,10 +8,9 @@
8
8
  * follow-up sub-tickets (TBP-322 `.load()` semantics + TBP-323 reactive
9
9
  * binding for loaded slices).
10
10
  *
11
- * Backward compat: this is purely additive. The legacy module-level stores
12
- * (`subscriptionStore`, `flagsStore`, `appConfigStore`, etc.) continue to
13
- * exist and are populated by the same internal state TBP-324 wraps them
14
- * in deprecation warnings; for now both surfaces coexist.
11
+ * Backward compat: this is purely additive. The module-level stores
12
+ * (`subscriptionStore`, `appConfigStore`, etc.) continue to exist and are
13
+ * populated by the same internal state; both surfaces coexist.
15
14
  *
16
15
  * Note: `useBridge()` (Svelte context hook) lands in TBP-320. This module
17
16
  * only exposes the singleton aggregate `bridge`; consumers can import it
@@ -218,7 +218,7 @@ describe('bridge.app.plans lazy slice (Phase 4, TBP-321/322)', () => {
218
218
  });
219
219
  it('apply() updates a loaded slice (TBP-323 reactive binding primitive)', async () => {
220
220
  await bridge.app.plans.load();
221
- bridge.app.plans.apply([{ key: 'enterprise', name: 'Enterprise' }]);
222
- expect(get(bridge.app.plans)).toEqual([{ key: 'enterprise', name: 'Enterprise' }]);
221
+ bridge.app.plans.apply([{ key: 'enterprise', name: 'Enterprise', prices: [] }]);
222
+ expect(get(bridge.app.plans)).toEqual([{ key: 'enterprise', name: 'Enterprise', prices: [] }]);
223
223
  });
224
224
  });
@@ -27,11 +27,10 @@ export class BrowserIdentityStorage {
27
27
  constructor(mode, key = 'bridge.anon_id') {
28
28
  this.mode = mode;
29
29
  this.key = key;
30
- if (typeof globalThis === 'undefined' || !globalThis.window) {
30
+ if (typeof window === 'undefined') {
31
31
  throw new Error('BrowserIdentityStorage requires a window — use MemoryIdentityStorage on the server');
32
32
  }
33
- const w = globalThis.window;
34
- this.storage = mode === 'persistent' ? w.localStorage : w.sessionStorage;
33
+ this.storage = mode === 'persistent' ? window.localStorage : window.sessionStorage;
35
34
  }
36
35
  read() {
37
36
  try {
package/dist/index.d.ts CHANGED
@@ -5,10 +5,10 @@ export type { SubscriptionState } from './core/bridge-instance.js';
5
5
  export { bridge } from './core/bridge.js';
6
6
  export type { BridgeSurface, BridgeAppSurface, BridgeTenantSurface, } from './core/bridge.js';
7
7
  export type { BrandingSnapshot, SubscriptionSnapshot, UserSnapshot, SessionSnapshotData, } from './core/snapshot-stores.js';
8
- export type { BridgeEventHandlers } from './core/events.js';
8
+ export type { BridgeEventHandlers, BridgeEventsDispatcher } from './core/events.js';
9
9
  export { default as BridgeBootstrap, default as BridgeProvider } from './client/BridgeBootstrap.svelte';
10
10
  export { default as ApiTokenManagement } from './client/components/developer/ApiTokenManagement.svelte';
11
- export { default as FeatureFlag } from './client/components/FeatureFlag.svelte';
11
+ export { default as FeatureFlag } from './flags/FeatureFlag.svelte';
12
12
  export { default as ProfileName } from './client/components/ProfileName.svelte';
13
13
  export { default as TeamManagementPanel } from './client/components/team/TeamManagementPanel.svelte';
14
14
  export { default as TeamUserList } from './client/components/team/TeamUserList.svelte';
@@ -31,7 +31,6 @@ export { default as BridgeSubscriptionStatus } from './client/components/subscri
31
31
  export { default as BridgeBillingNotice } from './client/components/subscription/BridgeBillingNotice.svelte';
32
32
  export { default as BridgePaywall } from './client/components/subscription/BridgePaywall.svelte';
33
33
  export { default as BridgeQuotaBanner } from './client/components/subscription/BridgeQuotaBanner.svelte';
34
- export * from './shared/feature-flag.js';
35
34
  export * from './auth/route-guard.js';
36
35
  export * from './shared/profile.js';
37
36
  export * from './shared/types/config.js';
@@ -41,6 +40,7 @@ export type { RedditConversionEvent, RedditEcommerce, RedditEcommerceItem, Reddi
41
40
  export { sha256Email } from './client/tracking/pii-hashing.js';
42
41
  export type { AuthConfigResponse, AuthResult, AuthState, BridgeAuthConfig, BridgeAuthEventName, BridgeAuthEvents, FederationConnection, MagicLinkResult, MfaResult, PasskeyAuthOptions, PasskeyRegistrationOptions, PasskeyVerificationResult, SignupResult, SsoOptions, SsoResult, TenantUser, } from '@nebulr-group/bridge-auth-core';
43
42
  export { BridgeAuth, BridgeAuthError, HttpError, TeamService, ApiTokenService } from '@nebulr-group/bridge-auth-core';
43
+ export type { SessionStalePayload } from '@nebulr-group/bridge-auth-core';
44
44
  export type { ApiToken, CreateApiTokenInput, CreateApiTokenResponse, } from '@nebulr-group/bridge-auth-core';
45
45
  export type { TeamProfile, TeamProfileUpdateInput, TeamUser, TeamUserListResult, TeamUserUpdateInput, TeamWorkspace, TeamWorkspaceUpdateInput, } from '@nebulr-group/bridge-auth-core';
46
46
  export type { Plan, PriceOfferSdk, SubscriptionStatus, CheckoutSession, Workspace, } from '@nebulr-group/bridge-auth-core';
package/dist/index.js CHANGED
@@ -16,7 +16,7 @@ export { bridge } from './core/bridge.js';
16
16
  // Components (Svelte components must have `export default`)
17
17
  export { default as BridgeBootstrap, default as BridgeProvider } from './client/BridgeBootstrap.svelte';
18
18
  export { default as ApiTokenManagement } from './client/components/developer/ApiTokenManagement.svelte';
19
- export { default as FeatureFlag } from './client/components/FeatureFlag.svelte';
19
+ export { default as FeatureFlag } from './flags/FeatureFlag.svelte';
20
20
  export { default as ProfileName } from './client/components/ProfileName.svelte';
21
21
  export { default as TeamManagementPanel } from './client/components/team/TeamManagementPanel.svelte';
22
22
  export { default as TeamUserList } from './client/components/team/TeamUserList.svelte';
@@ -49,8 +49,6 @@ export { default as BridgeBillingNotice } from './client/components/subscription
49
49
  export { default as BridgePaywall } from './client/components/subscription/BridgePaywall.svelte';
50
50
  // Billing 2.0 (Phase C / US-11) — live quota counter banner.
51
51
  export { default as BridgeQuotaBanner } from './client/components/subscription/BridgeQuotaBanner.svelte';
52
- // Feature flags
53
- export * from './shared/feature-flag.js';
54
52
  // Auth route guards
55
53
  export * from './auth/route-guard.js';
56
54
  // Types
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nebulr-group/bridge-svelte",
3
- "version": "0.4.0-beta.2",
3
+ "version": "0.4.0-beta.3",
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",
@@ -1,37 +0,0 @@
1
- <script lang="ts">
2
- import type { Snippet } from 'svelte';
3
- import { onMount } from 'svelte';
4
- import { isFeatureEnabled } from '../../shared/feature-flag.js';
5
-
6
- type FlagRenderArgs = { enabled: boolean; rawEnabled: boolean };
7
-
8
- let {
9
- flagName,
10
- forceLive = false,
11
- negate = false,
12
- renderWhenDisabled = false,
13
- children
14
- }: {
15
- flagName: string;
16
- forceLive?: boolean;
17
- negate?: boolean;
18
- renderWhenDisabled?: boolean;
19
- children?: Snippet<[FlagRenderArgs]>;
20
- } = $props();
21
-
22
- let enabled = $state(false);
23
- let rawEnabled = $derived(enabled);
24
- let effectiveEnabled = $derived(negate ? !enabled : enabled);
25
-
26
- onMount(async () => {
27
- enabled = await isFeatureEnabled(flagName, forceLive);
28
- });
29
- </script>
30
-
31
- {#if children}
32
- {#if renderWhenDisabled}
33
- {@render children({ enabled: effectiveEnabled, rawEnabled })}
34
- {:else if effectiveEnabled}
35
- {@render children({ enabled: true, rawEnabled })}
36
- {/if}
37
- {/if}
@@ -1,15 +0,0 @@
1
- import type { Snippet } from 'svelte';
2
- type FlagRenderArgs = {
3
- enabled: boolean;
4
- rawEnabled: boolean;
5
- };
6
- type $$ComponentProps = {
7
- flagName: string;
8
- forceLive?: boolean;
9
- negate?: boolean;
10
- renderWhenDisabled?: boolean;
11
- children?: Snippet<[FlagRenderArgs]>;
12
- };
13
- declare const FeatureFlag: import("svelte").Component<$$ComponentProps, {}, "">;
14
- type FeatureFlag = ReturnType<typeof FeatureFlag>;
15
- export default FeatureFlag;
@@ -1,6 +0,0 @@
1
- export declare function loadFeatureFlags(): Promise<void>;
2
- export declare function isFeatureEnabled(flag: string, forceLive?: boolean): Promise<boolean>;
3
- export declare const featureFlags: {
4
- flags: import("svelte/store").Writable<Record<string, boolean>>;
5
- refresh: typeof loadFeatureFlags;
6
- };
@@ -1,13 +0,0 @@
1
- // src/lib/shared/feature-flag.ts — thin wrapper delegating to bridge-instance
2
- import { getBridgeAuth, _flagsWritable, flagsStore } from '../core/bridge-instance.js';
3
- export async function loadFeatureFlags() {
4
- const flags = await getBridgeAuth().loadFeatureFlags();
5
- _flagsWritable.set(flags);
6
- }
7
- export async function isFeatureEnabled(flag, forceLive = false) {
8
- return getBridgeAuth().isFeatureEnabled(flag, { forceLive });
9
- }
10
- export const featureFlags = {
11
- flags: flagsStore,
12
- refresh: loadFeatureFlags
13
- };