@nebulr-group/bridge-svelte 0.3.0-beta.0 → 0.3.0-beta.2

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.
@@ -3,7 +3,7 @@
3
3
  import type { HTMLAttributes } from 'svelte/elements';
4
4
  import type { AppConfig, FederationConnection } from '@thebridge/auth-core';
5
5
  import { onMount } from 'svelte';
6
- import { getBridgeAuth, authState, appConfigStore } from '../../../core/bridge-instance.js';
6
+ import { getBridgeAuth, authState, appConfigStore, ensureAppConfig } from '../../../core/bridge-instance.js';
7
7
  import { getConfig } from '../../stores/config.store.js';
8
8
  import AuthFormWrapper from './shared/AuthFormWrapper.svelte';
9
9
  import Spinner from './shared/Spinner.svelte';
@@ -33,6 +33,13 @@
33
33
  onSsoClick?: (connectionType: string) => void;
34
34
  heading?: string;
35
35
  ssoConnections?: FederationConnection[];
36
+ /**
37
+ * SSO kickoff strategy for the built-in SsoButtons.
38
+ * - 'redirect' (default): full-page navigation to the federation endpoint.
39
+ * - 'popup': opens window.open and resolves via postMessage.
40
+ * Ignored when `onSsoClick` is provided (the caller handles the click).
41
+ */
42
+ ssoMode?: 'redirect' | 'popup';
36
43
  footer?: Snippet;
37
44
  }
38
45
 
@@ -48,6 +55,7 @@
48
55
  onSsoClick,
49
56
  heading = '',
50
57
  ssoConnections = [],
58
+ ssoMode = 'redirect',
51
59
  footer,
52
60
  class: className,
53
61
  style,
@@ -164,6 +172,15 @@
164
172
 
165
173
  onMount(async () => {
166
174
  if (typeof window === 'undefined') return;
175
+
176
+ // Ensure the anonymous app config is loaded. `initBridge()` kicks off the
177
+ // same fetch on startup, but LoginForm can mount before that promise
178
+ // settles (slow network, failed init, etc.), so we call it again here.
179
+ // `ensureAppConfig()` is idempotent and logs errors — the store update
180
+ // triggers reactivity and the SSO buttons / magic-link / passkey toggles
181
+ // render when it resolves.
182
+ void ensureAppConfig();
183
+
167
184
  const params = new URLSearchParams(window.location.search);
168
185
  const magicToken = params.get('bridge_magic_link_token');
169
186
  if (!magicToken) return;
@@ -391,6 +408,7 @@
391
408
  {:else}
392
409
  <SsoButton
393
410
  connection={conn}
411
+ mode={ssoMode}
394
412
  onSuccess={onLogin}
395
413
  onError={onError}
396
414
  class="bridge-btn bridge-btn-secondary bridge-sso-btn"
@@ -18,6 +18,13 @@ interface Props extends HTMLAttributes<HTMLDivElement> {
18
18
  onSsoClick?: (connectionType: string) => void;
19
19
  heading?: string;
20
20
  ssoConnections?: FederationConnection[];
21
+ /**
22
+ * SSO kickoff strategy for the built-in SsoButtons.
23
+ * - 'redirect' (default): full-page navigation to the federation endpoint.
24
+ * - 'popup': opens window.open and resolves via postMessage.
25
+ * Ignored when `onSsoClick` is provided (the caller handles the click).
26
+ */
27
+ ssoMode?: 'redirect' | 'popup';
21
28
  footer?: Snippet;
22
29
  }
23
30
  declare const LoginForm: import("svelte").Component<Props, {}, "">;
@@ -8,6 +8,14 @@
8
8
  interface Props extends HTMLButtonAttributes {
9
9
  connection: FederationConnection;
10
10
  label?: string;
11
+ /**
12
+ * SSO kickoff strategy.
13
+ * - 'redirect' (default): full-page navigation to the federation endpoint,
14
+ * provider returns via the normal OAuth callback chain.
15
+ * - 'popup': opens window.open and resolves via postMessage — useful for
16
+ * embedded widgets that must not unload the host page.
17
+ */
18
+ mode?: 'redirect' | 'popup';
11
19
  onSuccess?: () => void;
12
20
  onError?: (error: Error) => void;
13
21
  icon?: Snippet;
@@ -16,6 +24,7 @@
16
24
  let {
17
25
  connection,
18
26
  label,
27
+ mode = 'redirect',
19
28
  onSuccess,
20
29
  onError,
21
30
  icon,
@@ -31,7 +40,7 @@
31
40
  if (loading) return;
32
41
  loading = true;
33
42
  try {
34
- const result = await getBridgeAuth().startSsoLogin(connection.type);
43
+ const result = await getBridgeAuth().startSsoLogin(connection.type, { mode });
35
44
  if (result.type === 'auth_success') {
36
45
  onSuccess?.();
37
46
  } else if (result.type === 'auth_error') {
@@ -4,6 +4,14 @@ import type { FederationConnection } from '@thebridge/auth-core';
4
4
  interface Props extends HTMLButtonAttributes {
5
5
  connection: FederationConnection;
6
6
  label?: string;
7
+ /**
8
+ * SSO kickoff strategy.
9
+ * - 'redirect' (default): full-page navigation to the federation endpoint,
10
+ * provider returns via the normal OAuth callback chain.
11
+ * - 'popup': opens window.open and resolves via postMessage — useful for
12
+ * embedded widgets that must not unload the host page.
13
+ */
14
+ mode?: 'redirect' | 'popup';
7
15
  onSuccess?: () => void;
8
16
  onError?: (error: Error) => void;
9
17
  icon?: Snippet;
@@ -8,6 +8,16 @@
8
8
  import { BridgeAuth, type AppConfig, type AuthState, type BridgeAuthConfig, type Plan, type Profile, type SubscriptionStatus, type TenantUser, type TokenSet } from '@nebulr-group/bridge-auth-core';
9
9
  import { type Readable, type Writable } from 'svelte/store';
10
10
  export declare function initBridge(config: BridgeAuthConfig): BridgeAuth;
11
+ /**
12
+ * Load the anonymous app config into `appConfigStore` if it isn't already.
13
+ *
14
+ * Idempotent: concurrent callers share the in-flight fetch, and once the
15
+ * store holds a value this function resolves immediately.
16
+ *
17
+ * Resolves with the loaded config on success or `null` on failure (the
18
+ * fetch error is logged — it is not silently swallowed).
19
+ */
20
+ export declare function ensureAppConfig(): Promise<AppConfig | null>;
11
21
  export declare function getBridgeAuth(): BridgeAuth;
12
22
  export declare function markReady(): void;
13
23
  export declare function waitForBridge(): Promise<void>;
@@ -28,6 +28,12 @@ let _resolveReady = null;
28
28
  const _readyPromise = new Promise((resolve) => {
29
29
  _resolveReady = resolve;
30
30
  });
31
+ // ── App config load gate ───────────────────────────────────────────────────────
32
+ //
33
+ // The anonymous app config drives SSO button visibility, signup/magic-link
34
+ // toggles, etc. on LoginForm. We cache the in-flight fetch so concurrent
35
+ // callers share a single network request and can await the result.
36
+ let _appConfigPromise = null;
31
37
  // ── Init / access ──────────────────────────────────────────────────────────────
32
38
  export function initBridge(config) {
33
39
  if (_instance) {
@@ -78,11 +84,42 @@ export function initBridge(config) {
78
84
  _instance.on('auth:error', (err) => {
79
85
  _error.set(err.message);
80
86
  });
81
- // Load app config anonymously (drives SSO buttons etc.)
82
- _instance.getAppConfig().then((cfg) => _appConfig.set(cfg)).catch(() => { });
87
+ // Load app config anonymously (drives SSO buttons, signup toggle, etc.).
88
+ // Cached in `_appConfigPromise` so LoginForm (and others) can await the
89
+ // result via `ensureAppConfig()` instead of racing the store update.
90
+ void ensureAppConfig();
83
91
  logger.debug('[bridge-instance] initialized');
84
92
  return _instance;
85
93
  }
94
+ /**
95
+ * Load the anonymous app config into `appConfigStore` if it isn't already.
96
+ *
97
+ * Idempotent: concurrent callers share the in-flight fetch, and once the
98
+ * store holds a value this function resolves immediately.
99
+ *
100
+ * Resolves with the loaded config on success or `null` on failure (the
101
+ * fetch error is logged — it is not silently swallowed).
102
+ */
103
+ export function ensureAppConfig() {
104
+ const existing = get(_appConfig);
105
+ if (existing)
106
+ return Promise.resolve(existing);
107
+ if (_appConfigPromise)
108
+ return _appConfigPromise;
109
+ _appConfigPromise = getBridgeAuth()
110
+ .getAppConfig()
111
+ .then((cfg) => {
112
+ _appConfig.set(cfg);
113
+ return cfg;
114
+ })
115
+ .catch((err) => {
116
+ logger.warn('[bridge-instance] getAppConfig failed:', err);
117
+ // Allow a later call to retry
118
+ _appConfigPromise = null;
119
+ return null;
120
+ });
121
+ return _appConfigPromise;
122
+ }
86
123
  export function getBridgeAuth() {
87
124
  if (!_instance) {
88
125
  throw new Error('BridgeAuth not initialized. Call initBridge() first (via bridgeConfig.initConfig).');
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  export * from './client/BridgeBootstrap.js';
2
2
  export * from './client/stores/config.store.js';
3
- export { initBridge, getBridgeAuth, markReady, waitForBridge, auth, tokenStore, isAuthenticated, isLoading, authError, authState, isOnboarded, hasMultiTenantAccess, tenantUsersStore, profileStore, flagsStore, bridgeReadyStore, appConfigStore, subscriptionStore, loadSubscription, } from './core/bridge-instance.js';
3
+ export { initBridge, getBridgeAuth, markReady, waitForBridge, auth, tokenStore, isAuthenticated, isLoading, authError, authState, isOnboarded, hasMultiTenantAccess, tenantUsersStore, profileStore, flagsStore, bridgeReadyStore, appConfigStore, ensureAppConfig, subscriptionStore, loadSubscription, } from './core/bridge-instance.js';
4
4
  export { default as BridgeBootstrap, default as BridgeProvider } from './client/BridgeBootstrap.svelte';
5
5
  export { default as ApiTokenManagement } from './client/components/developer/ApiTokenManagement.svelte';
6
6
  export { default as FeatureFlag } from './client/components/FeatureFlag.svelte';
package/dist/index.js CHANGED
@@ -2,7 +2,7 @@
2
2
  export * from './client/BridgeBootstrap.js';
3
3
  export * from './client/stores/config.store.js';
4
4
  // Bridge instance (singleton + Svelte stores)
5
- export { initBridge, getBridgeAuth, markReady, waitForBridge, auth, tokenStore, isAuthenticated, isLoading, authError, authState, isOnboarded, hasMultiTenantAccess, tenantUsersStore, profileStore, flagsStore, bridgeReadyStore, appConfigStore, subscriptionStore, loadSubscription, } from './core/bridge-instance.js';
5
+ export { initBridge, getBridgeAuth, markReady, waitForBridge, auth, tokenStore, isAuthenticated, isLoading, authError, authState, isOnboarded, hasMultiTenantAccess, tenantUsersStore, profileStore, flagsStore, bridgeReadyStore, appConfigStore, ensureAppConfig, subscriptionStore, loadSubscription, } from './core/bridge-instance.js';
6
6
  // Components (Svelte components must have `export default`)
7
7
  export { default as BridgeBootstrap, default as BridgeProvider } from './client/BridgeBootstrap.svelte';
8
8
  export { default as ApiTokenManagement } from './client/components/developer/ApiTokenManagement.svelte';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nebulr-group/bridge-svelte",
3
- "version": "0.3.0-beta.0",
3
+ "version": "0.3.0-beta.2",
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",
@@ -60,7 +60,7 @@
60
60
  }
61
61
  },
62
62
  "dependencies": {
63
- "@nebulr-group/bridge-auth-core": "^0.1.0-beta.0"
63
+ "@nebulr-group/bridge-auth-core": "0.1.0-beta.2"
64
64
  },
65
65
  "devDependencies": {
66
66
  "@sveltejs/kit": "^2.16.0",