@nebulr-group/bridge-svelte 0.1.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 (82) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +63 -0
  3. package/dist/auth/route-guard.d.ts +2 -0
  4. package/dist/auth/route-guard.js +28 -0
  5. package/dist/client/BridgeBootstrap.d.ts +8 -0
  6. package/dist/client/BridgeBootstrap.js +105 -0
  7. package/dist/client/BridgeBootstrap.svelte +43 -0
  8. package/dist/client/BridgeBootstrap.svelte.d.ts +6 -0
  9. package/dist/client/components/FeatureFlag.svelte +37 -0
  10. package/dist/client/components/FeatureFlag.svelte.d.ts +15 -0
  11. package/dist/client/components/ProfileName.svelte +18 -0
  12. package/dist/client/components/ProfileName.svelte.d.ts +6 -0
  13. package/dist/client/components/developer/ApiTokenManagement.svelte +365 -0
  14. package/dist/client/components/developer/ApiTokenManagement.svelte.d.ts +6 -0
  15. package/dist/client/components/sdk-auth/ForgotPassword.svelte +170 -0
  16. package/dist/client/components/sdk-auth/ForgotPassword.svelte.d.ts +10 -0
  17. package/dist/client/components/sdk-auth/LoginForm.svelte +414 -0
  18. package/dist/client/components/sdk-auth/LoginForm.svelte.d.ts +25 -0
  19. package/dist/client/components/sdk-auth/MagicLink.svelte +92 -0
  20. package/dist/client/components/sdk-auth/MagicLink.svelte.d.ts +9 -0
  21. package/dist/client/components/sdk-auth/MfaChallenge.svelte +150 -0
  22. package/dist/client/components/sdk-auth/MfaChallenge.svelte.d.ts +9 -0
  23. package/dist/client/components/sdk-auth/MfaSetup.svelte +182 -0
  24. package/dist/client/components/sdk-auth/MfaSetup.svelte.d.ts +8 -0
  25. package/dist/client/components/sdk-auth/PasskeyLogin.svelte +99 -0
  26. package/dist/client/components/sdk-auth/PasskeyLogin.svelte.d.ts +10 -0
  27. package/dist/client/components/sdk-auth/PasskeyRequestSetupLink.svelte +83 -0
  28. package/dist/client/components/sdk-auth/PasskeyRequestSetupLink.svelte.d.ts +7 -0
  29. package/dist/client/components/sdk-auth/PasskeySetup.svelte +151 -0
  30. package/dist/client/components/sdk-auth/PasskeySetup.svelte.d.ts +11 -0
  31. package/dist/client/components/sdk-auth/SignupForm.svelte +119 -0
  32. package/dist/client/components/sdk-auth/SignupForm.svelte.d.ts +14 -0
  33. package/dist/client/components/sdk-auth/SsoButton.svelte +70 -0
  34. package/dist/client/components/sdk-auth/SsoButton.svelte.d.ts +13 -0
  35. package/dist/client/components/sdk-auth/SsoProviderIcon.svelte +56 -0
  36. package/dist/client/components/sdk-auth/SsoProviderIcon.svelte.d.ts +7 -0
  37. package/dist/client/components/sdk-auth/TenantSelector.svelte +83 -0
  38. package/dist/client/components/sdk-auth/TenantSelector.svelte.d.ts +11 -0
  39. package/dist/client/components/sdk-auth/WorkspaceSelector.svelte +108 -0
  40. package/dist/client/components/sdk-auth/WorkspaceSelector.svelte.d.ts +16 -0
  41. package/dist/client/components/sdk-auth/shared/Alert.svelte +17 -0
  42. package/dist/client/components/sdk-auth/shared/Alert.svelte.d.ts +9 -0
  43. package/dist/client/components/sdk-auth/shared/AuthFormWrapper.svelte +23 -0
  44. package/dist/client/components/sdk-auth/shared/AuthFormWrapper.svelte.d.ts +10 -0
  45. package/dist/client/components/sdk-auth/shared/Spinner.svelte +16 -0
  46. package/dist/client/components/sdk-auth/shared/Spinner.svelte.d.ts +7 -0
  47. package/dist/client/components/subscription/PlanSelector.svelte +204 -0
  48. package/dist/client/components/subscription/PlanSelector.svelte.d.ts +22 -0
  49. package/dist/client/components/team/TeamAddUserDialog.svelte +125 -0
  50. package/dist/client/components/team/TeamAddUserDialog.svelte.d.ts +17 -0
  51. package/dist/client/components/team/TeamConfirmDialog.svelte +80 -0
  52. package/dist/client/components/team/TeamConfirmDialog.svelte.d.ts +21 -0
  53. package/dist/client/components/team/TeamEditUserDialog.svelte +131 -0
  54. package/dist/client/components/team/TeamEditUserDialog.svelte.d.ts +19 -0
  55. package/dist/client/components/team/TeamManagementPanel.svelte +72 -0
  56. package/dist/client/components/team/TeamManagementPanel.svelte.d.ts +20 -0
  57. package/dist/client/components/team/TeamProfileForm.svelte +129 -0
  58. package/dist/client/components/team/TeamProfileForm.svelte.d.ts +7 -0
  59. package/dist/client/components/team/TeamUserActionsMenu.svelte +63 -0
  60. package/dist/client/components/team/TeamUserActionsMenu.svelte.d.ts +9 -0
  61. package/dist/client/components/team/TeamUserList.svelte +226 -0
  62. package/dist/client/components/team/TeamUserList.svelte.d.ts +7 -0
  63. package/dist/client/components/team/TeamWorkspaceForm.svelte +134 -0
  64. package/dist/client/components/team/TeamWorkspaceForm.svelte.d.ts +7 -0
  65. package/dist/client/stores/config.store.d.ts +6 -0
  66. package/dist/client/stores/config.store.js +76 -0
  67. package/dist/client/utils/cn.d.ts +1 -0
  68. package/dist/client/utils/cn.js +3 -0
  69. package/dist/core/bridge-instance.d.ts +52 -0
  70. package/dist/core/bridge-instance.js +160 -0
  71. package/dist/index.d.ts +34 -0
  72. package/dist/index.js +39 -0
  73. package/dist/shared/feature-flag.d.ts +6 -0
  74. package/dist/shared/feature-flag.js +13 -0
  75. package/dist/shared/logger.d.ts +18 -0
  76. package/dist/shared/logger.js +32 -0
  77. package/dist/shared/profile.d.ts +18 -0
  78. package/dist/shared/profile.js +25 -0
  79. package/dist/shared/types/config.d.ts +42 -0
  80. package/dist/shared/types/config.js +1 -0
  81. package/dist/styles.css +1089 -0
  82. package/package.json +86 -0
@@ -0,0 +1,20 @@
1
+ import type { Snippet } from 'svelte';
2
+ import type { HTMLAttributes } from 'svelte/elements';
3
+ type Tab = {
4
+ id: 'users' | 'profile' | 'workspace';
5
+ label: string;
6
+ };
7
+ interface Props extends HTMLAttributes<HTMLDivElement> {
8
+ defaultTab?: 'users' | 'profile' | 'workspace';
9
+ showProfileTab?: boolean;
10
+ showWorkspaceTab?: boolean;
11
+ onError?: (error: Error) => void;
12
+ tabBar?: Snippet<[{
13
+ tabs: Tab[];
14
+ activeTab: string;
15
+ setTab: (id: string) => void;
16
+ }]>;
17
+ }
18
+ declare const TeamManagementPanel: import("svelte").Component<Props, {}, "">;
19
+ type TeamManagementPanel = ReturnType<typeof TeamManagementPanel>;
20
+ export default TeamManagementPanel;
@@ -0,0 +1,129 @@
1
+ <script lang="ts">
2
+ import type { TeamProfile } from '@thebridge/auth-core';
3
+ import type { HTMLAttributes } from 'svelte/elements';
4
+ import { onMount } from 'svelte';
5
+ import { getBridgeAuth } from '../../../core/bridge-instance.js';
6
+ import Alert from '../sdk-auth/shared/Alert.svelte';
7
+ import Spinner from '../sdk-auth/shared/Spinner.svelte';
8
+
9
+ interface Props extends HTMLAttributes<HTMLDivElement> {
10
+ onError?: (error: Error) => void;
11
+ }
12
+
13
+ let {
14
+ onError,
15
+ class: className,
16
+ style,
17
+ ...rest
18
+ }: Props = $props();
19
+
20
+ let profile = $state<TeamProfile | null>(null);
21
+ let firstName = $state('');
22
+ let lastName = $state('');
23
+ let loading = $state(true);
24
+ let saving = $state(false);
25
+ let error = $state<string | null>(null);
26
+ let success = $state<string | null>(null);
27
+
28
+ onMount(() => {
29
+ loadProfile();
30
+ });
31
+
32
+ async function loadProfile() {
33
+ loading = true;
34
+ error = null;
35
+ try {
36
+ const bridge = getBridgeAuth();
37
+ profile = await bridge.team.getProfile();
38
+ firstName = profile.firstName ?? '';
39
+ lastName = profile.lastName ?? '';
40
+ } catch (err) {
41
+ const e = err instanceof Error ? err : new Error('Failed to load profile');
42
+ error = e.message;
43
+ onError?.(e);
44
+ } finally {
45
+ loading = false;
46
+ }
47
+ }
48
+
49
+ async function handleSubmit() {
50
+ saving = true;
51
+ error = null;
52
+ success = null;
53
+ try {
54
+ const bridge = getBridgeAuth();
55
+ profile = await bridge.team.updateProfile({ firstName, lastName });
56
+ success = 'Profile updated successfully.';
57
+ } catch (err) {
58
+ const e = err instanceof Error ? err : new Error('Failed to update profile');
59
+ error = e.message;
60
+ onError?.(e);
61
+ } finally {
62
+ saving = false;
63
+ }
64
+ }
65
+ </script>
66
+
67
+ <div class={className} {style} data-bridge-team-profile {...rest}>
68
+ <h3 class="bridge-team-section-title">My Profile</h3>
69
+
70
+ {#if loading}
71
+ <div class="bridge-team-loading">
72
+ <Spinner size={32} />
73
+ <span>Loading profile...</span>
74
+ </div>
75
+ {:else}
76
+ {#if error}
77
+ <div class="bridge-team-alert"><Alert variant="error">{error}</Alert></div>
78
+ {/if}
79
+ {#if success}
80
+ <div class="bridge-team-alert"><Alert variant="success">{success}</Alert></div>
81
+ {/if}
82
+
83
+ <form class="bridge-team-form" onsubmit={(e) => { e.preventDefault(); handleSubmit(); }}>
84
+ <div class="bridge-team-form-group">
85
+ <label for="bridge-profile-email">Email</label>
86
+ <input id="bridge-profile-email" type="email" value={profile?.email ?? ''} disabled />
87
+ </div>
88
+
89
+ <div class="bridge-team-form-row">
90
+ <div class="bridge-team-form-group">
91
+ <label for="bridge-profile-first-name">First Name</label>
92
+ <input
93
+ id="bridge-profile-first-name"
94
+ type="text"
95
+ bind:value={firstName}
96
+ disabled={saving}
97
+ />
98
+ </div>
99
+
100
+ <div class="bridge-team-form-group">
101
+ <label for="bridge-profile-last-name">Last Name</label>
102
+ <input
103
+ id="bridge-profile-last-name"
104
+ type="text"
105
+ bind:value={lastName}
106
+ disabled={saving}
107
+ />
108
+ </div>
109
+ </div>
110
+
111
+ {#if profile?.role}
112
+ <div class="bridge-team-form-group">
113
+ <label>Role</label>
114
+ <div class="bridge-team-readonly">{profile.role}</div>
115
+ </div>
116
+ {/if}
117
+
118
+ <div class="bridge-team-form-actions">
119
+ <button
120
+ type="submit"
121
+ class="bridge-btn bridge-btn-primary"
122
+ disabled={saving}
123
+ >
124
+ {saving ? 'Saving...' : 'Save Changes'}
125
+ </button>
126
+ </div>
127
+ </form>
128
+ {/if}
129
+ </div>
@@ -0,0 +1,7 @@
1
+ import type { HTMLAttributes } from 'svelte/elements';
2
+ interface Props extends HTMLAttributes<HTMLDivElement> {
3
+ onError?: (error: Error) => void;
4
+ }
5
+ declare const TeamProfileForm: import("svelte").Component<Props, {}, "">;
6
+ type TeamProfileForm = ReturnType<typeof TeamProfileForm>;
7
+ export default TeamProfileForm;
@@ -0,0 +1,63 @@
1
+ <script lang="ts">
2
+ import type { HTMLAttributes } from 'svelte/elements';
3
+
4
+ interface Props extends HTMLAttributes<HTMLDivElement> {
5
+ onedit?: () => void;
6
+ onresetpassword?: () => void;
7
+ ondelete?: () => void;
8
+ }
9
+
10
+ let {
11
+ onedit,
12
+ onresetpassword,
13
+ ondelete,
14
+ class: className,
15
+ style,
16
+ ...rest
17
+ }: Props = $props();
18
+
19
+ let isOpen = $state(false);
20
+ let menuEl: HTMLDivElement | undefined = $state();
21
+
22
+ function toggle(e: MouseEvent) {
23
+ e.stopPropagation();
24
+ isOpen = !isOpen;
25
+ }
26
+
27
+ function handleAction(fn?: () => void) {
28
+ isOpen = false;
29
+ fn?.();
30
+ }
31
+
32
+ function handleClickOutside(e: MouseEvent) {
33
+ if (isOpen && menuEl && !menuEl.contains(e.target as Node)) {
34
+ isOpen = false;
35
+ }
36
+ }
37
+ </script>
38
+
39
+ <svelte:document onclick={handleClickOutside} />
40
+
41
+ <div class={className} {style} data-bridge-team-actions bind:this={menuEl} {...rest}>
42
+ <button class="bridge-team-actions-trigger" onclick={toggle} aria-label="Actions">
43
+ <svg width="16" height="16" viewBox="0 0 16 16" fill="currentColor">
44
+ <circle cx="8" cy="3" r="1.5" />
45
+ <circle cx="8" cy="8" r="1.5" />
46
+ <circle cx="8" cy="13" r="1.5" />
47
+ </svg>
48
+ </button>
49
+
50
+ {#if isOpen}
51
+ <div class="bridge-team-actions-menu">
52
+ <button class="bridge-team-actions-item" onclick={() => handleAction(onedit)}>
53
+ Edit
54
+ </button>
55
+ <button class="bridge-team-actions-item" onclick={() => handleAction(onresetpassword)}>
56
+ Reset Password
57
+ </button>
58
+ <button class="bridge-team-actions-item bridge-team-actions-item--danger" onclick={() => handleAction(ondelete)}>
59
+ Delete
60
+ </button>
61
+ </div>
62
+ {/if}
63
+ </div>
@@ -0,0 +1,9 @@
1
+ import type { HTMLAttributes } from 'svelte/elements';
2
+ interface Props extends HTMLAttributes<HTMLDivElement> {
3
+ onedit?: () => void;
4
+ onresetpassword?: () => void;
5
+ ondelete?: () => void;
6
+ }
7
+ declare const TeamUserActionsMenu: import("svelte").Component<Props, {}, "">;
8
+ type TeamUserActionsMenu = ReturnType<typeof TeamUserActionsMenu>;
9
+ export default TeamUserActionsMenu;
@@ -0,0 +1,226 @@
1
+ <script lang="ts">
2
+ import type { TeamUser } from '@thebridge/auth-core';
3
+ import type { HTMLAttributes } from 'svelte/elements';
4
+ import { onMount } from 'svelte';
5
+ import { getBridgeAuth } from '../../../core/bridge-instance.js';
6
+ import Alert from '../sdk-auth/shared/Alert.svelte';
7
+ import Spinner from '../sdk-auth/shared/Spinner.svelte';
8
+ import TeamAddUserDialog from './TeamAddUserDialog.svelte';
9
+ import TeamConfirmDialog from './TeamConfirmDialog.svelte';
10
+ import TeamEditUserDialog from './TeamEditUserDialog.svelte';
11
+ import TeamUserActionsMenu from './TeamUserActionsMenu.svelte';
12
+
13
+ interface Props extends HTMLAttributes<HTMLDivElement> {
14
+ onError?: (error: Error) => void;
15
+ }
16
+
17
+ let {
18
+ onError,
19
+ class: className,
20
+ style,
21
+ ...rest
22
+ }: Props = $props();
23
+
24
+ let users = $state<TeamUser[]>([]);
25
+ let roles = $state<string[]>([]);
26
+ let mfaEnabled = $state(false);
27
+ let loading = $state(true);
28
+ let error = $state<string | null>(null);
29
+
30
+ // Dialog states
31
+ let showAddDialog = $state(false);
32
+ let showEditDialog = $state(false);
33
+ let editingUser = $state<TeamUser | null>(null);
34
+ let showDeleteConfirm = $state(false);
35
+ let showResetConfirm = $state(false);
36
+ let deletingUser = $state<TeamUser | null>(null);
37
+ let resettingUser = $state<TeamUser | null>(null);
38
+ let actionLoading = $state(false);
39
+
40
+ onMount(() => {
41
+ loadData();
42
+ });
43
+
44
+ async function loadData() {
45
+ loading = true;
46
+ error = null;
47
+ try {
48
+ const bridge = getBridgeAuth();
49
+ const [userResult, rolesResult] = await Promise.all([
50
+ bridge.team.listUsers(),
51
+ bridge.team.listUserRoles(),
52
+ ]);
53
+ users = userResult.users;
54
+ mfaEnabled = userResult.mfaEnabled;
55
+ roles = rolesResult;
56
+ } catch (err) {
57
+ const e = err instanceof Error ? err : new Error('Failed to load users');
58
+ error = e.message;
59
+ onError?.(e);
60
+ } finally {
61
+ loading = false;
62
+ }
63
+ }
64
+
65
+ function openEdit(user: TeamUser) {
66
+ editingUser = user;
67
+ showEditDialog = true;
68
+ }
69
+
70
+ function openDelete(user: TeamUser) {
71
+ deletingUser = user;
72
+ showDeleteConfirm = true;
73
+ }
74
+
75
+ function openResetPassword(user: TeamUser) {
76
+ resettingUser = user;
77
+ showResetConfirm = true;
78
+ }
79
+
80
+ function handleUsersAdded(added: TeamUser[]) {
81
+ users = [...users, ...added];
82
+ }
83
+
84
+ function handleUserUpdated(updated: TeamUser) {
85
+ users = users.map((u) => (u.id === updated.id ? updated : u));
86
+ }
87
+
88
+ async function handleDeleteConfirm() {
89
+ if (!deletingUser) return;
90
+ actionLoading = true;
91
+ try {
92
+ const bridge = getBridgeAuth();
93
+ await bridge.team.deleteUser(deletingUser.id);
94
+ users = users.filter((u) => u.id !== deletingUser!.id);
95
+ showDeleteConfirm = false;
96
+ deletingUser = null;
97
+ } catch (err) {
98
+ const e = err instanceof Error ? err : new Error('Failed to delete user');
99
+ onError?.(e);
100
+ } finally {
101
+ actionLoading = false;
102
+ }
103
+ }
104
+
105
+ async function handleResetConfirm() {
106
+ if (!resettingUser) return;
107
+ actionLoading = true;
108
+ try {
109
+ const bridge = getBridgeAuth();
110
+ await bridge.team.sendPasswordResetLink(resettingUser.id);
111
+ showResetConfirm = false;
112
+ resettingUser = null;
113
+ } catch (err) {
114
+ const e = err instanceof Error ? err : new Error('Failed to send reset link');
115
+ onError?.(e);
116
+ } finally {
117
+ actionLoading = false;
118
+ }
119
+ }
120
+ </script>
121
+
122
+ <div class={className} {style} data-bridge-team-users {...rest}>
123
+ <div class="bridge-team-users-header">
124
+ <h3 class="bridge-team-users-title">Team Members</h3>
125
+ <button class="bridge-btn bridge-btn-primary" onclick={() => (showAddDialog = true)}>
126
+ Add Member
127
+ </button>
128
+ </div>
129
+
130
+ {#if loading}
131
+ <div class="bridge-team-loading">
132
+ <Spinner size={32} />
133
+ <span>Loading team members...</span>
134
+ </div>
135
+ {:else if error}
136
+ <Alert variant="error">{error}</Alert>
137
+ {:else if users.length === 0}
138
+ <div class="bridge-team-empty">
139
+ <p>No team members yet.</p>
140
+ <button class="bridge-btn bridge-btn-primary" onclick={() => (showAddDialog = true)}>
141
+ Add your first team member
142
+ </button>
143
+ </div>
144
+ {:else}
145
+ <div class="bridge-team-table-wrapper">
146
+ <table class="bridge-team-table">
147
+ <thead>
148
+ <tr>
149
+ <th>User</th>
150
+ <th>Role</th>
151
+ <th>Status</th>
152
+ <th></th>
153
+ </tr>
154
+ </thead>
155
+ <tbody>
156
+ {#each users as user (user.id)}
157
+ <tr>
158
+ <td>
159
+ <div class="bridge-team-user-cell">
160
+ <div class="bridge-team-user-name">
161
+ {user.fullName || user.username || user.email}
162
+ </div>
163
+ <div class="bridge-team-user-email">{user.email}</div>
164
+ </div>
165
+ </td>
166
+ <td>
167
+ <span class="bridge-team-badge">{user.role ?? '—'}</span>
168
+ </td>
169
+ <td>
170
+ <span
171
+ class="bridge-team-status"
172
+ data-state={user.enabled ? 'active' : 'disabled'}
173
+ >
174
+ {user.enabled ? 'Active' : 'Disabled'}
175
+ </span>
176
+ </td>
177
+ <td class="bridge-team-actions-cell">
178
+ <TeamUserActionsMenu
179
+ onedit={() => openEdit(user)}
180
+ onresetpassword={() => openResetPassword(user)}
181
+ ondelete={() => openDelete(user)}
182
+ />
183
+ </td>
184
+ </tr>
185
+ {/each}
186
+ </tbody>
187
+ </table>
188
+ </div>
189
+ {/if}
190
+ </div>
191
+
192
+ <TeamAddUserDialog
193
+ open={showAddDialog}
194
+ onclose={() => (showAddDialog = false)}
195
+ onadded={handleUsersAdded}
196
+ />
197
+
198
+ <TeamEditUserDialog
199
+ open={showEditDialog}
200
+ user={editingUser}
201
+ {roles}
202
+ onclose={() => { showEditDialog = false; editingUser = null; }}
203
+ onupdated={handleUserUpdated}
204
+ />
205
+
206
+ <TeamConfirmDialog
207
+ open={showDeleteConfirm}
208
+ title="Delete User"
209
+ message="Are you sure you want to delete {deletingUser?.email ?? 'this user'}? This action cannot be undone."
210
+ confirmLabel="Delete"
211
+ variant="danger"
212
+ loading={actionLoading}
213
+ onconfirm={handleDeleteConfirm}
214
+ oncancel={() => { showDeleteConfirm = false; deletingUser = null; }}
215
+ />
216
+
217
+ <TeamConfirmDialog
218
+ open={showResetConfirm}
219
+ title="Reset Password"
220
+ message="Send a password reset link to {resettingUser?.email ?? 'this user'}?"
221
+ confirmLabel="Send Reset Link"
222
+ variant="default"
223
+ loading={actionLoading}
224
+ onconfirm={handleResetConfirm}
225
+ oncancel={() => { showResetConfirm = false; resettingUser = null; }}
226
+ />
@@ -0,0 +1,7 @@
1
+ import type { HTMLAttributes } from 'svelte/elements';
2
+ interface Props extends HTMLAttributes<HTMLDivElement> {
3
+ onError?: (error: Error) => void;
4
+ }
5
+ declare const TeamUserList: import("svelte").Component<Props, {}, "">;
6
+ type TeamUserList = ReturnType<typeof TeamUserList>;
7
+ export default TeamUserList;
@@ -0,0 +1,134 @@
1
+ <script lang="ts">
2
+ import type { TeamWorkspace } from '@thebridge/auth-core';
3
+ import type { HTMLAttributes } from 'svelte/elements';
4
+ import { onMount } from 'svelte';
5
+ import { getBridgeAuth } from '../../../core/bridge-instance.js';
6
+ import Alert from '../sdk-auth/shared/Alert.svelte';
7
+ import Spinner from '../sdk-auth/shared/Spinner.svelte';
8
+
9
+ interface Props extends HTMLAttributes<HTMLDivElement> {
10
+ onError?: (error: Error) => void;
11
+ }
12
+
13
+ let {
14
+ onError,
15
+ class: className,
16
+ style,
17
+ ...rest
18
+ }: Props = $props();
19
+
20
+ let workspace = $state<TeamWorkspace | null>(null);
21
+ let name = $state('');
22
+ let locale = $state('');
23
+ let loading = $state(true);
24
+ let saving = $state(false);
25
+ let error = $state<string | null>(null);
26
+ let success = $state<string | null>(null);
27
+
28
+ onMount(() => {
29
+ loadWorkspace();
30
+ });
31
+
32
+ async function loadWorkspace() {
33
+ loading = true;
34
+ error = null;
35
+ try {
36
+ const bridge = getBridgeAuth();
37
+ workspace = await bridge.team.getWorkspace();
38
+ name = workspace.name ?? '';
39
+ locale = workspace.locale ?? '';
40
+ } catch (err) {
41
+ const e = err instanceof Error ? err : new Error('Failed to load workspace');
42
+ error = e.message;
43
+ onError?.(e);
44
+ } finally {
45
+ loading = false;
46
+ }
47
+ }
48
+
49
+ async function handleSubmit() {
50
+ saving = true;
51
+ error = null;
52
+ success = null;
53
+ try {
54
+ const bridge = getBridgeAuth();
55
+ await bridge.team.updateWorkspace({ name, locale });
56
+ success = 'Workspace updated successfully.';
57
+ } catch (err) {
58
+ const e = err instanceof Error ? err : new Error('Failed to update workspace');
59
+ error = e.message;
60
+ onError?.(e);
61
+ } finally {
62
+ saving = false;
63
+ }
64
+ }
65
+ </script>
66
+
67
+ <div class={className} {style} data-bridge-team-workspace {...rest}>
68
+ <h3 class="bridge-team-section-title">Workspace Settings</h3>
69
+
70
+ {#if loading}
71
+ <div class="bridge-team-loading">
72
+ <Spinner size={32} />
73
+ <span>Loading workspace...</span>
74
+ </div>
75
+ {:else}
76
+ {#if error}
77
+ <div class="bridge-team-alert"><Alert variant="error">{error}</Alert></div>
78
+ {/if}
79
+ {#if success}
80
+ <div class="bridge-team-alert"><Alert variant="success">{success}</Alert></div>
81
+ {/if}
82
+
83
+ {#if workspace?.logo}
84
+ <div class="bridge-team-logo">
85
+ <img src={workspace.logo} alt="{workspace.name} logo" />
86
+ </div>
87
+ {/if}
88
+
89
+ <form class="bridge-team-form" onsubmit={(e) => { e.preventDefault(); handleSubmit(); }}>
90
+ <div class="bridge-team-form-group">
91
+ <label for="bridge-workspace-name">Workspace Name</label>
92
+ <input
93
+ id="bridge-workspace-name"
94
+ type="text"
95
+ bind:value={name}
96
+ disabled={saving}
97
+ />
98
+ </div>
99
+
100
+ <div class="bridge-team-form-group">
101
+ <label for="bridge-workspace-locale">Locale</label>
102
+ <input
103
+ id="bridge-workspace-locale"
104
+ type="text"
105
+ bind:value={locale}
106
+ placeholder="en"
107
+ disabled={saving}
108
+ />
109
+ </div>
110
+
111
+ {#if workspace?.plan}
112
+ <div class="bridge-team-form-group">
113
+ <label>Current Plan</label>
114
+ <div class="bridge-team-readonly">{workspace.plan}</div>
115
+ </div>
116
+ {/if}
117
+
118
+ <div class="bridge-team-form-group">
119
+ <label>MFA</label>
120
+ <div class="bridge-team-readonly">{workspace?.mfa ? 'Enabled' : 'Disabled'}</div>
121
+ </div>
122
+
123
+ <div class="bridge-team-form-actions">
124
+ <button
125
+ type="submit"
126
+ class="bridge-btn bridge-btn-primary"
127
+ disabled={saving}
128
+ >
129
+ {saving ? 'Saving...' : 'Save Changes'}
130
+ </button>
131
+ </div>
132
+ </form>
133
+ {/if}
134
+ </div>
@@ -0,0 +1,7 @@
1
+ import type { HTMLAttributes } from 'svelte/elements';
2
+ interface Props extends HTMLAttributes<HTMLDivElement> {
3
+ onError?: (error: Error) => void;
4
+ }
5
+ declare const TeamWorkspaceForm: import("svelte").Component<Props, {}, "">;
6
+ type TeamWorkspaceForm = ReturnType<typeof TeamWorkspaceForm>;
7
+ export default TeamWorkspaceForm;
@@ -0,0 +1,6 @@
1
+ import type { BridgeConfig } from '../../shared/types/config';
2
+ export declare const bridgeConfig: import("svelte/store").Writable<BridgeConfig | null>;
3
+ export declare const readonlyConfig: import("svelte/store").Readable<BridgeConfig | null>;
4
+ export declare const configReady: import("svelte/store").Readable<boolean>;
5
+ export declare function initConfig(config: BridgeConfig): void;
6
+ export declare function getConfig(): BridgeConfig;
@@ -0,0 +1,76 @@
1
+ // src/lib/config/bridgeConfig.ts
2
+ import { derived, writable } from 'svelte/store';
3
+ import { initBridge } from '../../core/bridge-instance.js';
4
+ import { logger, setLoggerDebug } from '../../shared/logger.js';
5
+ const DEFAULT_CONFIG = {
6
+ apiBaseUrl: 'https://api.thebridge.dev',
7
+ defaultRedirectRoute: '/',
8
+ signupRoute: '/auth/signup',
9
+ debug: false
10
+ };
11
+ const { subscribe, set, update } = writable({
12
+ config: null,
13
+ routeConfig: null,
14
+ loaded: false
15
+ });
16
+ export const bridgeConfig = {
17
+ subscribe,
18
+ initConfig: (config, routeConfig) => {
19
+ if (!config?.appId) {
20
+ throw new Error('Bridge appId is required but was not provided in bridge configuration.');
21
+ }
22
+ // Default to /auth/oauth-callback if not provided
23
+ const DEFAULT_CALLBACK_PATH = '/auth/oauth-callback';
24
+ const defaultCallback = typeof window !== 'undefined'
25
+ ? `${window.location.origin}${DEFAULT_CALLBACK_PATH}`
26
+ : undefined;
27
+ const merged = {
28
+ ...DEFAULT_CONFIG,
29
+ callbackUrl: defaultCallback,
30
+ ...config
31
+ };
32
+ // Use user provided callbackUrl if present, otherwise use default
33
+ if (config.callbackUrl) {
34
+ merged.callbackUrl = config.callbackUrl;
35
+ }
36
+ else if (!merged.callbackUrl && defaultCallback) {
37
+ merged.callbackUrl = defaultCallback;
38
+ }
39
+ // Initialize logger debug mode before anything else
40
+ setLoggerDebug(!!merged.debug);
41
+ // Initialize the singleton BridgeAuth instance
42
+ initBridge(merged);
43
+ // Set bridge full config and mark it as loaded
44
+ set({
45
+ config: merged,
46
+ routeConfig: routeConfig || null,
47
+ loaded: true
48
+ });
49
+ // This will only print when debug is true
50
+ logger.debug('[config] initialized', merged);
51
+ }
52
+ };
53
+ // Convenience derived stores
54
+ export const configReady = derived(bridgeConfig, ($state) => $state.loaded);
55
+ export const readonlyConfig = derived(bridgeConfig, ($state) => $state.config);
56
+ // Synchronous access to latest config value
57
+ let _currentConfig = null;
58
+ let _currentRouteConfig = null;
59
+ bridgeConfig.subscribe(($state) => {
60
+ if ($state.loaded) {
61
+ _currentConfig = $state.config;
62
+ _currentRouteConfig = $state.routeConfig;
63
+ }
64
+ });
65
+ export function getConfig() {
66
+ if (!_currentConfig) {
67
+ throw new Error('Config has not been initialized. Call initConfig(...) early in app startup.');
68
+ }
69
+ return _currentConfig;
70
+ }
71
+ export function getRouteGuardConfig() {
72
+ if (!_currentRouteConfig) {
73
+ throw new Error('RouteGuardConfig has not been initialized. Call initConfig(...) early in app startup.');
74
+ }
75
+ return _currentRouteConfig;
76
+ }