@korajs/auth 0.4.0 → 0.6.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.
@@ -0,0 +1,722 @@
1
+ import { KoraError } from '@korajs/core';
2
+
3
+ /**
4
+ * Thrown when a device key store operation fails.
5
+ * Provides context about the operation and the underlying cause.
6
+ */
7
+ declare class DeviceKeyStoreError extends KoraError {
8
+ constructor(message: string, context?: Record<string, unknown>);
9
+ }
10
+ /**
11
+ * Persistent storage interface for ECDSA P-256 device key pairs.
12
+ *
13
+ * Device key pairs are used for proof-of-possession authentication:
14
+ * the private key never leaves the device, and the public key is
15
+ * registered with the server. This store persists key pairs across
16
+ * page reloads and app restarts.
17
+ *
18
+ * In the browser, CryptoKey objects are structured-cloneable, so they
19
+ * can be stored directly in IndexedDB without serialization. In Node.js
20
+ * or test environments, an in-memory implementation is used instead.
21
+ */
22
+ interface DeviceKeyStore {
23
+ /**
24
+ * Persist a key pair for the given device.
25
+ *
26
+ * Overwrites any previously stored key pair for the same device ID.
27
+ *
28
+ * @param deviceId - The unique device identifier (typically a UUID v7)
29
+ * @param keyPair - The ECDSA P-256 CryptoKeyPair to store
30
+ * @throws {DeviceKeyStoreError} If the storage operation fails
31
+ */
32
+ saveKeyPair(deviceId: string, keyPair: CryptoKeyPair): Promise<void>;
33
+ /**
34
+ * Load a previously stored key pair for the given device.
35
+ *
36
+ * @param deviceId - The unique device identifier
37
+ * @returns The stored CryptoKeyPair, or null if no key pair exists for the device
38
+ * @throws {DeviceKeyStoreError} If the storage operation fails
39
+ */
40
+ loadKeyPair(deviceId: string): Promise<CryptoKeyPair | null>;
41
+ /**
42
+ * Delete a stored key pair for the given device.
43
+ *
44
+ * No-op if no key pair exists for the device ID.
45
+ *
46
+ * @param deviceId - The unique device identifier
47
+ * @throws {DeviceKeyStoreError} If the storage operation fails
48
+ */
49
+ deleteKeyPair(deviceId: string): Promise<void>;
50
+ /**
51
+ * Check whether a key pair exists for the given device.
52
+ *
53
+ * @param deviceId - The unique device identifier
54
+ * @returns True if a key pair is stored for the device, false otherwise
55
+ * @throws {DeviceKeyStoreError} If the storage operation fails
56
+ */
57
+ hasKeyPair(deviceId: string): Promise<boolean>;
58
+ }
59
+ /**
60
+ * Browser-based device key store backed by IndexedDB.
61
+ *
62
+ * CryptoKey objects are structured-cloneable, so they can be stored
63
+ * directly in IndexedDB without needing to export/import them as JWK.
64
+ * This preserves the non-extractable flag on private keys, ensuring
65
+ * they cannot be read even from storage.
66
+ *
67
+ * The database uses a single object store (`keypairs`) with the device ID
68
+ * as the key and the full CryptoKeyPair as the value.
69
+ *
70
+ * @example
71
+ * ```typescript
72
+ * const store = new IndexedDBDeviceKeyStore()
73
+ * const keyPair = await generateDeviceKeyPair()
74
+ * await store.saveKeyPair('device-123', keyPair)
75
+ *
76
+ * const loaded = await store.loadKeyPair('device-123')
77
+ * // loaded.privateKey is still non-extractable
78
+ * ```
79
+ */
80
+ declare class IndexedDBDeviceKeyStore implements DeviceKeyStore {
81
+ private dbPromise;
82
+ /**
83
+ * Opens (or creates) the IndexedDB database.
84
+ *
85
+ * The database connection is lazily initialized on first use and
86
+ * reused for subsequent operations. If the database does not exist,
87
+ * it is created with the `keypairs` object store.
88
+ */
89
+ private openDatabase;
90
+ /** @inheritdoc */
91
+ saveKeyPair(deviceId: string, keyPair: CryptoKeyPair): Promise<void>;
92
+ /** @inheritdoc */
93
+ loadKeyPair(deviceId: string): Promise<CryptoKeyPair | null>;
94
+ /** @inheritdoc */
95
+ deleteKeyPair(deviceId: string): Promise<void>;
96
+ /** @inheritdoc */
97
+ hasKeyPair(deviceId: string): Promise<boolean>;
98
+ }
99
+ /**
100
+ * In-memory device key store for Node.js and testing environments.
101
+ *
102
+ * Key pairs are stored in a plain Map and do not survive process restarts.
103
+ * This is suitable for server-side rendering, Node.js scripts, and unit tests
104
+ * where IndexedDB is not available.
105
+ *
106
+ * @example
107
+ * ```typescript
108
+ * const store = new InMemoryDeviceKeyStore()
109
+ * const keyPair = await generateDeviceKeyPair()
110
+ * await store.saveKeyPair('device-123', keyPair)
111
+ *
112
+ * const loaded = await store.loadKeyPair('device-123')
113
+ * ```
114
+ */
115
+ declare class InMemoryDeviceKeyStore implements DeviceKeyStore {
116
+ private readonly store;
117
+ /** @inheritdoc */
118
+ saveKeyPair(deviceId: string, keyPair: CryptoKeyPair): Promise<void>;
119
+ /** @inheritdoc */
120
+ loadKeyPair(deviceId: string): Promise<CryptoKeyPair | null>;
121
+ /** @inheritdoc */
122
+ deleteKeyPair(deviceId: string): Promise<void>;
123
+ /** @inheritdoc */
124
+ hasKeyPair(deviceId: string): Promise<boolean>;
125
+ }
126
+ /**
127
+ * Creates a DeviceKeyStore appropriate for the current environment.
128
+ *
129
+ * In browsers where IndexedDB is available, returns an {@link IndexedDBDeviceKeyStore}
130
+ * that persists CryptoKeyPair objects as structured clones. In Node.js, SSR,
131
+ * or environments without IndexedDB, returns an {@link InMemoryDeviceKeyStore}.
132
+ *
133
+ * @returns A DeviceKeyStore instance for the current environment
134
+ *
135
+ * @example
136
+ * ```typescript
137
+ * const store = createDeviceKeyStore()
138
+ * const keyPair = await generateDeviceKeyPair()
139
+ * await store.saveKeyPair(deviceId, keyPair)
140
+ * ```
141
+ */
142
+ declare function createDeviceKeyStore(): DeviceKeyStore;
143
+
144
+ type MaybePromise$1<T> = T | Promise<T>;
145
+ /**
146
+ * Minimal key-value credential storage interface used by Kora auth adapters.
147
+ *
148
+ * This intentionally matches the shape of secure stores across runtimes:
149
+ * browser Storage, Tauri secure storage plugins, Expo SecureStore, iOS Keychain,
150
+ * Android Keystore wrappers, and encrypted desktop stores.
151
+ */
152
+ interface AuthKeyValueStorage {
153
+ getItem(key: string): MaybePromise$1<string | null>;
154
+ setItem(key: string, value: string): MaybePromise$1<void>;
155
+ removeItem(key: string): MaybePromise$1<void>;
156
+ }
157
+ interface AuthTokenStorageOptions {
158
+ /** Backing credential store. */
159
+ store: AuthKeyValueStorage;
160
+ /** Storage key prefix. Defaults to `kora_auth`. */
161
+ prefix?: string;
162
+ }
163
+ /**
164
+ * Creates an `AuthTokenStorage` adapter from a runtime key-value store.
165
+ *
166
+ * Use this with platform credential stores instead of wiring `AuthClient`
167
+ * directly to browser localStorage in desktop and mobile apps.
168
+ */
169
+ declare function createAuthTokenStorage(options: AuthTokenStorageOptions): AuthTokenStorage;
170
+ /**
171
+ * Creates an in-memory token storage adapter.
172
+ *
173
+ * Useful for tests, demos, and SSR. Production desktop and mobile apps should
174
+ * prefer a secure platform-backed store.
175
+ */
176
+ declare function createMemoryAuthTokenStorage(): AuthTokenStorage;
177
+ /**
178
+ * Adapts Web Storage-compatible APIs such as `localStorage` or `sessionStorage`.
179
+ */
180
+ declare function createWebStorageAuthTokenStorage(storage: Storage, prefix?: string): AuthTokenStorage;
181
+
182
+ interface AuthDeviceIdentity {
183
+ /** Stable local device ID used in token `dev` claims. */
184
+ deviceId: string;
185
+ /** Public proof-of-possession key serialized as a JSON Web Key string. */
186
+ devicePublicKey: string;
187
+ }
188
+ interface AuthDeviceIdentityProvider {
189
+ getDeviceIdentity(): Promise<AuthDeviceIdentity>;
190
+ }
191
+ interface PersistentDeviceIdentityOptions {
192
+ /** Store for the stable device ID. Use a platform credential store in production. */
193
+ storage: AuthKeyValueStorage;
194
+ /** Store for the non-extractable device key pair. Defaults to IndexedDB when available. */
195
+ keyStore?: DeviceKeyStore;
196
+ /** Storage key for the device ID. Defaults to `kora_auth_device_id`. */
197
+ deviceIdKey?: string;
198
+ /** Optional device ID generator for tests or custom device registries. */
199
+ generateDeviceId?: () => string;
200
+ }
201
+ declare class AuthDeviceIdentityError extends KoraError {
202
+ constructor(message: string, context?: Record<string, unknown>);
203
+ }
204
+ /**
205
+ * Creates a persistent device identity provider for `AuthClient`.
206
+ *
207
+ * The provider keeps a stable device ID in the supplied key-value store and a
208
+ * non-extractable ECDSA P-256 key pair in the supplied `DeviceKeyStore`. The
209
+ * public key is returned during sign-up/sign-in so the server can bind tokens
210
+ * to a real offline device instead of a transient browser session.
211
+ */
212
+ declare function createPersistentDeviceIdentity(options: PersistentDeviceIdentityOptions): AuthDeviceIdentityProvider;
213
+
214
+ /**
215
+ * Thrown when an authentication operation fails.
216
+ * Includes a machine-readable code and optional context for debugging.
217
+ */
218
+ declare class AuthError extends KoraError {
219
+ constructor(message: string, code: string, context?: Record<string, unknown>);
220
+ }
221
+ /**
222
+ * Possible authentication states for the client.
223
+ * - 'loading': Initial state while restoring tokens from storage
224
+ * - 'authenticated': User is signed in with a valid session
225
+ * - 'unauthenticated': No valid session exists
226
+ */
227
+ type AuthState = 'loading' | 'authenticated' | 'unauthenticated';
228
+ /**
229
+ * Authenticated user information.
230
+ */
231
+ interface AuthUser {
232
+ /** Unique user identifier */
233
+ id: string;
234
+ /** User email address */
235
+ email: string;
236
+ /** Display name (may be absent if user did not provide one) */
237
+ name: string | null;
238
+ }
239
+ interface LinkedOAuthAccount {
240
+ id: string;
241
+ userId: string;
242
+ provider: string;
243
+ providerUserId: string;
244
+ email: string | null;
245
+ linkedAt: number;
246
+ }
247
+ interface OAuthAuthorizationResult {
248
+ url: string;
249
+ state: string;
250
+ }
251
+ interface OAuthAuthorizationOptions {
252
+ /**
253
+ * Redirect the current browser window to the provider after creating the URL.
254
+ * Defaults to true when `window.location.assign` is available.
255
+ */
256
+ redirect?: boolean;
257
+ /**
258
+ * Optional app-specific return path stored in OAuth state metadata.
259
+ */
260
+ returnTo?: string;
261
+ /**
262
+ * Optional extra metadata stored in OAuth state. Use this for app handoff data.
263
+ */
264
+ metadata?: Record<string, unknown>;
265
+ deviceId?: string;
266
+ devicePublicKey?: string;
267
+ }
268
+ interface OAuthCallbackParams {
269
+ code: string;
270
+ state: string;
271
+ deviceId?: string;
272
+ devicePublicKey?: string;
273
+ }
274
+ /**
275
+ * Configuration for the AuthClient.
276
+ */
277
+ interface AuthClientConfig {
278
+ /** Base URL of the auth server (e.g. 'http://localhost:3001') */
279
+ serverUrl: string;
280
+ /** Storage key prefix for tokens. Defaults to 'kora_auth' */
281
+ storageKey?: string;
282
+ /**
283
+ * Optional token storage adapter.
284
+ *
285
+ * Use this for runtimes where localStorage is not the right place for
286
+ * credentials, such as React Native/Expo SecureStore, iOS Keychain,
287
+ * Android Keystore, or a Tauri secure storage plugin.
288
+ */
289
+ storage?: AuthTokenStorage;
290
+ /**
291
+ * Optional fetch implementation. Defaults to globalThis.fetch.
292
+ * Useful for tests, SSR adapters, and mobile runtimes with a custom fetch.
293
+ */
294
+ fetch?: typeof fetch;
295
+ /**
296
+ * Optional local device identity provider.
297
+ *
298
+ * When configured, sign-up and sign-in automatically include stable
299
+ * `deviceId` and `devicePublicKey` fields unless the caller provides them.
300
+ */
301
+ deviceIdentity?: AuthDeviceIdentityProvider;
302
+ }
303
+ type MaybePromise<T> = T | Promise<T>;
304
+ interface AuthTokenStorage {
305
+ getAccessToken(): MaybePromise<string | null>;
306
+ getRefreshToken(): MaybePromise<string | null>;
307
+ setTokens(access: string, refresh: string): MaybePromise<void>;
308
+ clear(): MaybePromise<void>;
309
+ }
310
+ /**
311
+ * Client-side authentication manager for Kora.js.
312
+ *
313
+ * Manages token storage, session restoration, sign-up, sign-in, sign-out,
314
+ * token refresh, and auth state change notifications. Framework-agnostic --
315
+ * works in any JavaScript environment with `fetch` and optionally `localStorage`.
316
+ *
317
+ * @example
318
+ * ```typescript
319
+ * const auth = new AuthClient({ serverUrl: 'http://localhost:3001' })
320
+ * await auth.initialize()
321
+ *
322
+ * if (!auth.isAuthenticated) {
323
+ * await auth.signIn({ email: 'user@example.com', password: 'secret' })
324
+ * }
325
+ *
326
+ * const unsub = auth.onAuthChange((state) => {
327
+ * console.log('Auth state:', state)
328
+ * })
329
+ * ```
330
+ */
331
+ declare class AuthClient {
332
+ private readonly serverUrl;
333
+ private readonly storage;
334
+ private readonly fetchFn;
335
+ private readonly deviceIdentity;
336
+ private readonly listeners;
337
+ private _state;
338
+ private _user;
339
+ private _refreshPromise;
340
+ private _initialized;
341
+ /**
342
+ * Creates a new AuthClient.
343
+ *
344
+ * @param config - Auth client configuration
345
+ */
346
+ constructor(config: AuthClientConfig);
347
+ /** Current authentication state. */
348
+ get state(): AuthState;
349
+ /** Current authenticated user, or null if not signed in. */
350
+ get currentUser(): AuthUser | null;
351
+ /** Whether the user is currently authenticated. */
352
+ get isAuthenticated(): boolean;
353
+ /**
354
+ * Initialize the auth client by restoring a session from stored tokens.
355
+ *
356
+ * Loads tokens from storage, validates the access token, and attempts a
357
+ * refresh if the access token is expired but a refresh token is available.
358
+ * Safe to call multiple times -- subsequent calls are no-ops once initialized.
359
+ */
360
+ initialize(): Promise<void>;
361
+ /**
362
+ * Register a new user account.
363
+ *
364
+ * @param params - Sign-up credentials
365
+ * @returns The newly created AuthUser
366
+ * @throws {AuthError} If the request fails or the server returns an error
367
+ */
368
+ signUp(params: {
369
+ email: string;
370
+ password: string;
371
+ name?: string;
372
+ deviceId?: string;
373
+ devicePublicKey?: string;
374
+ }): Promise<AuthUser>;
375
+ /**
376
+ * Sign in with email and password.
377
+ *
378
+ * @param params - Sign-in credentials
379
+ * @returns The authenticated AuthUser
380
+ * @throws {AuthError} If the credentials are invalid or the request fails
381
+ */
382
+ signIn(params: {
383
+ email: string;
384
+ password: string;
385
+ deviceId?: string;
386
+ devicePublicKey?: string;
387
+ }): Promise<AuthUser>;
388
+ /**
389
+ * Create an OAuth authorization URL and optionally redirect the current window.
390
+ *
391
+ * For web apps, call this from a button click and keep the default redirect behavior.
392
+ * For desktop/mobile, pass `redirect: false`, open the returned URL with the runtime's
393
+ * browser API, then call `completeOAuthSignIn()` after receiving the callback.
394
+ */
395
+ signInWithOAuth(provider: string, options?: OAuthAuthorizationOptions): Promise<OAuthAuthorizationResult>;
396
+ /**
397
+ * Complete an OAuth sign-in callback and store the issued Kora tokens.
398
+ */
399
+ completeOAuthSignIn(provider: string, params: OAuthCallbackParams): Promise<AuthUser>;
400
+ /**
401
+ * Create an OAuth authorization URL for linking another provider to the current user.
402
+ */
403
+ getOAuthAuthorizationUrl(provider: string, options?: OAuthAuthorizationOptions): Promise<OAuthAuthorizationResult>;
404
+ /**
405
+ * Link an OAuth provider to the current authenticated user.
406
+ */
407
+ linkOAuth(provider: string, params: OAuthCallbackParams): Promise<LinkedOAuthAccount>;
408
+ /**
409
+ * List OAuth accounts linked to the current authenticated user.
410
+ */
411
+ listLinkedAccounts(): Promise<LinkedOAuthAccount[]>;
412
+ /**
413
+ * Unlink an OAuth provider from the current authenticated user.
414
+ */
415
+ unlinkOAuth(provider: string): Promise<void>;
416
+ /**
417
+ * Sign out the current user.
418
+ *
419
+ * Clears local tokens and attempts to revoke the refresh token on the server
420
+ * (best-effort — succeeds even if the server is unreachable). This ensures that
421
+ * stolen refresh tokens cannot be used after the user explicitly signs out.
422
+ */
423
+ signOut(): Promise<void>;
424
+ /**
425
+ * Get a valid access token, automatically refreshing if expired.
426
+ *
427
+ * @returns A valid access token string, or null if the user is not
428
+ * authenticated and refresh is not possible
429
+ */
430
+ getAccessToken(): Promise<string | null>;
431
+ /**
432
+ * Get a valid token for the sync engine handshake.
433
+ * Alias for {@link getAccessToken}.
434
+ *
435
+ * @returns A valid access token string, or null if unavailable
436
+ */
437
+ getSyncToken(): Promise<string | null>;
438
+ /**
439
+ * Subscribe to authentication state changes.
440
+ *
441
+ * The callback is invoked whenever the auth state transitions (e.g., from
442
+ * 'unauthenticated' to 'authenticated' on sign-in).
443
+ *
444
+ * @param callback - Function called with the new AuthState on each change
445
+ * @returns An unsubscribe function that removes the listener
446
+ *
447
+ * @example
448
+ * ```typescript
449
+ * const unsub = auth.onAuthChange((state) => {
450
+ * console.log('Auth state changed to:', state)
451
+ * })
452
+ * // Later: unsub()
453
+ * ```
454
+ */
455
+ onAuthChange(callback: (state: AuthState) => void): () => void;
456
+ /**
457
+ * Update internal state and notify all listeners.
458
+ */
459
+ private setState;
460
+ /**
461
+ * Restore a session from a valid access token by fetching the user profile.
462
+ * Falls back to extracting the user ID from the token payload if the
463
+ * /auth/me request fails (offline scenario).
464
+ */
465
+ private restoreSession;
466
+ /**
467
+ * Fetch the current user profile from the server.
468
+ */
469
+ private fetchUserProfile;
470
+ private createOAuthAuthorization;
471
+ private withDeviceIdentity;
472
+ /**
473
+ * Refresh the access token using a refresh token.
474
+ * De-duplicates concurrent refresh calls so only one network request is made.
475
+ */
476
+ private refreshAccessToken;
477
+ private requireAccessToken;
478
+ /**
479
+ * Execute the token refresh network request.
480
+ */
481
+ private performRefresh;
482
+ /**
483
+ * Make an HTTP request to the auth server.
484
+ *
485
+ * @param path - URL path relative to serverUrl (e.g. '/auth/signin')
486
+ * @param options - Request options
487
+ * @returns Parsed JSON response body
488
+ * @throws {AuthError} On network failure or non-2xx response
489
+ */
490
+ private request;
491
+ }
492
+
493
+ interface AuthSessionSnapshot {
494
+ state: AuthState;
495
+ user: AuthUser | null;
496
+ isAuthenticated: boolean;
497
+ isLoading: boolean;
498
+ initError: Error | null;
499
+ error: string | null;
500
+ }
501
+ interface AuthSession {
502
+ readonly client: AuthClient;
503
+ getSnapshot(): AuthSessionSnapshot;
504
+ subscribe(listener: () => void): () => void;
505
+ signUp(params: {
506
+ email: string;
507
+ password: string;
508
+ name?: string;
509
+ deviceId?: string;
510
+ devicePublicKey?: string;
511
+ }): Promise<void>;
512
+ signIn(params: {
513
+ email: string;
514
+ password: string;
515
+ deviceId?: string;
516
+ devicePublicKey?: string;
517
+ }): Promise<void>;
518
+ signInWithOAuth(provider: string, options?: OAuthAuthorizationOptions): Promise<OAuthAuthorizationResult>;
519
+ completeOAuthSignIn(provider: string, params: OAuthCallbackParams): Promise<void>;
520
+ getOAuthAuthorizationUrl(provider: string, options?: OAuthAuthorizationOptions): Promise<OAuthAuthorizationResult>;
521
+ linkOAuth(provider: string, params: OAuthCallbackParams): Promise<LinkedOAuthAccount | null>;
522
+ listLinkedAccounts(): Promise<LinkedOAuthAccount[]>;
523
+ unlinkOAuth(provider: string): Promise<void>;
524
+ signOut(): Promise<void>;
525
+ destroy(): void;
526
+ }
527
+ /**
528
+ * Framework-agnostic auth session: initialization, reactive snapshot, and client methods.
529
+ */
530
+ declare function createAuthSession(client: AuthClient): AuthSession;
531
+
532
+ /**
533
+ * Organization info returned by the server.
534
+ */
535
+ interface ClientOrganization {
536
+ id: string;
537
+ name: string;
538
+ slug: string;
539
+ ownerId: string;
540
+ createdAt: number;
541
+ updatedAt: number;
542
+ metadata: Record<string, unknown>;
543
+ }
544
+ /**
545
+ * Membership info returned by the server.
546
+ */
547
+ interface ClientMembership {
548
+ id: string;
549
+ orgId: string;
550
+ userId: string;
551
+ role: string;
552
+ invitedBy: string | null;
553
+ joinedAt: number;
554
+ }
555
+ /**
556
+ * Invitation info returned by the server.
557
+ */
558
+ interface ClientInvitation {
559
+ id: string;
560
+ orgId: string;
561
+ email: string;
562
+ role: string;
563
+ invitedBy: string;
564
+ token: string;
565
+ createdAt: number;
566
+ expiresAt: number;
567
+ status: string;
568
+ }
569
+ /**
570
+ * Configuration for the OrgClient.
571
+ */
572
+ interface OrgClientConfig {
573
+ /** Base URL of the auth/org server */
574
+ serverUrl: string;
575
+ /** Function that returns a valid access token for authenticated requests */
576
+ getAccessToken: () => Promise<string | null>;
577
+ }
578
+ /**
579
+ * Thrown when an org operation fails.
580
+ */
581
+ declare class OrgClientError extends KoraError {
582
+ constructor(message: string, code: string, context?: Record<string, unknown>);
583
+ }
584
+ /**
585
+ * Client-side organization manager.
586
+ *
587
+ * Handles org CRUD, member management, invitations, and active org switching.
588
+ * Framework-agnostic — works in any JavaScript environment with `fetch`.
589
+ *
590
+ * @example
591
+ * ```typescript
592
+ * const orgClient = new OrgClient({
593
+ * serverUrl: 'http://localhost:3001',
594
+ * getAccessToken: () => authClient.getAccessToken(),
595
+ * })
596
+ *
597
+ * const org = await orgClient.createOrg({ name: 'Acme Inc', slug: 'acme' })
598
+ * orgClient.switchOrg(org.id)
599
+ * ```
600
+ */
601
+ declare class OrgClient {
602
+ private readonly serverUrl;
603
+ private readonly getAccessToken;
604
+ private readonly listeners;
605
+ private _activeOrgId;
606
+ private _activeOrg;
607
+ private _activeRole;
608
+ constructor(config: OrgClientConfig);
609
+ /** Currently active organization ID */
610
+ get activeOrgId(): string | null;
611
+ /** Currently active organization */
612
+ get activeOrg(): ClientOrganization | null;
613
+ /** Current user's role in the active organization */
614
+ get activeRole(): string | null;
615
+ /**
616
+ * Create a new organization.
617
+ */
618
+ createOrg(params: {
619
+ name: string;
620
+ slug?: string;
621
+ metadata?: Record<string, unknown>;
622
+ }): Promise<ClientOrganization>;
623
+ /**
624
+ * List all organizations the current user belongs to.
625
+ */
626
+ listOrgs(): Promise<ClientOrganization[]>;
627
+ /**
628
+ * Get an organization by ID.
629
+ */
630
+ getOrg(orgId: string): Promise<ClientOrganization>;
631
+ /**
632
+ * Update an organization.
633
+ */
634
+ updateOrg(orgId: string, params: {
635
+ name?: string;
636
+ slug?: string;
637
+ metadata?: Record<string, unknown>;
638
+ }): Promise<ClientOrganization>;
639
+ /**
640
+ * Delete an organization.
641
+ */
642
+ deleteOrg(orgId: string): Promise<void>;
643
+ /**
644
+ * Switch the active organization context.
645
+ * Fetches the org details and the user's membership/role.
646
+ */
647
+ switchOrg(orgId: string): Promise<void>;
648
+ /**
649
+ * Clear the active organization (no org selected).
650
+ */
651
+ clearActiveOrg(): void;
652
+ /**
653
+ * List members of an organization.
654
+ */
655
+ listMembers(orgId: string): Promise<ClientMembership[]>;
656
+ /**
657
+ * Remove a member from an organization.
658
+ */
659
+ removeMember(orgId: string, userId: string): Promise<void>;
660
+ /**
661
+ * Update a member's role.
662
+ */
663
+ updateMemberRole(orgId: string, userId: string, role: string): Promise<ClientMembership>;
664
+ /**
665
+ * Transfer ownership to another member.
666
+ */
667
+ transferOwnership(orgId: string, newOwnerId: string): Promise<void>;
668
+ /**
669
+ * Leave an organization (remove yourself).
670
+ */
671
+ leaveOrg(orgId: string): Promise<void>;
672
+ /**
673
+ * Invite a user to an organization by email.
674
+ */
675
+ inviteMember(orgId: string, params: {
676
+ email: string;
677
+ role: string;
678
+ }): Promise<ClientInvitation>;
679
+ /**
680
+ * Accept an invitation by token.
681
+ */
682
+ acceptInvitation(token: string): Promise<ClientMembership>;
683
+ /**
684
+ * List pending invitations for an organization.
685
+ */
686
+ listInvitations(orgId: string): Promise<ClientInvitation[]>;
687
+ /**
688
+ * Revoke a pending invitation.
689
+ */
690
+ revokeInvitation(orgId: string, invitationId: string): Promise<void>;
691
+ /**
692
+ * List pending invitations for the current user's email.
693
+ */
694
+ listMyInvitations(email: string): Promise<ClientInvitation[]>;
695
+ /**
696
+ * Subscribe to active org changes.
697
+ * @returns Unsubscribe function
698
+ */
699
+ onOrgChange(callback: (orgId: string | null) => void): () => void;
700
+ private notifyListeners;
701
+ private request;
702
+ }
703
+
704
+ interface OrgSnapshot {
705
+ orgId: string | null;
706
+ org: ClientOrganization | null;
707
+ role: string | null;
708
+ }
709
+ interface OrgSession {
710
+ readonly client: OrgClient;
711
+ getSnapshot(): OrgSnapshot;
712
+ subscribe(listener: () => void): () => void;
713
+ checkPermission(requiredRole: string): boolean;
714
+ destroy(): void;
715
+ }
716
+ declare function checkOrgPermission(currentRole: string | null, requiredRole: string): boolean;
717
+ /**
718
+ * Framework-agnostic org session with reactive snapshot subscription.
719
+ */
720
+ declare function createOrgSession(client: OrgClient): OrgSession;
721
+
722
+ export { type AuthClientConfig as A, createDeviceKeyStore as B, type ClientInvitation as C, type DeviceKeyStore as D, createMemoryAuthTokenStorage as E, createOrgSession as F, createPersistentDeviceIdentity as G, createWebStorageAuthTokenStorage as H, InMemoryDeviceKeyStore as I, type LinkedOAuthAccount as L, type OAuthAuthorizationOptions as O, type PersistentDeviceIdentityOptions as P, type AuthTokenStorage as a, type AuthKeyValueStorage as b, type AuthDeviceIdentityProvider as c, AuthClient as d, type AuthState as e, type AuthDeviceIdentity as f, AuthDeviceIdentityError as g, AuthError as h, type AuthSession as i, type AuthSessionSnapshot as j, type AuthTokenStorageOptions as k, type AuthUser as l, type ClientMembership as m, type ClientOrganization as n, DeviceKeyStoreError as o, IndexedDBDeviceKeyStore as p, type OAuthAuthorizationResult as q, type OAuthCallbackParams as r, OrgClient as s, type OrgClientConfig as t, OrgClientError as u, type OrgSession as v, type OrgSnapshot as w, checkOrgPermission as x, createAuthSession as y, createAuthTokenStorage as z };