@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.
- package/README.md +81 -28
- package/dist/chunk-7OXBRSJL.js +203 -0
- package/dist/chunk-7OXBRSJL.js.map +1 -0
- package/dist/create-org-session-RsDj9cl4.d.cts +722 -0
- package/dist/create-org-session-RsDj9cl4.d.ts +722 -0
- package/dist/index.cjs +1146 -586
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +67 -118
- package/dist/index.d.ts +67 -118
- package/dist/index.js +654 -258
- package/dist/index.js.map +1 -1
- package/dist/react.cjs +297 -183
- package/dist/react.cjs.map +1 -1
- package/dist/react.d.cts +25 -209
- package/dist/react.d.ts +25 -209
- package/dist/react.js +134 -209
- package/dist/react.js.map +1 -1
- package/dist/server.cjs +1611 -730
- package/dist/server.cjs.map +1 -1
- package/dist/server.d.cts +415 -224
- package/dist/server.d.ts +415 -224
- package/dist/server.js +1565 -704
- package/dist/server.js.map +1 -1
- package/dist/svelte.cjs +512 -0
- package/dist/svelte.cjs.map +1 -0
- package/dist/svelte.d.cts +100 -0
- package/dist/svelte.d.ts +100 -0
- package/dist/svelte.js +278 -0
- package/dist/svelte.js.map +1 -0
- package/dist/vue.cjs +565 -0
- package/dist/vue.cjs.map +1 -0
- package/dist/vue.d.cts +136 -0
- package/dist/vue.d.ts +136 -0
- package/dist/vue.js +338 -0
- package/dist/vue.js.map +1 -0
- package/package.json +40 -6
- package/src/svelte/AuthProvider.svelte +37 -0
- package/src/svelte/OrgProvider.svelte +22 -0
- package/dist/org-client-BVTLKcIk.d.cts +0 -355
- package/dist/org-client-BVTLKcIk.d.ts +0 -355
package/README.md
CHANGED
|
@@ -17,6 +17,23 @@ Offline-first authentication for Kora.js applications.
|
|
|
17
17
|
- **Passkeys (WebAuthn)** -- passwordless authentication with platform authenticators
|
|
18
18
|
- **Encrypted token storage** -- AES-256-GCM encryption for sensitive environments
|
|
19
19
|
- **End-to-end encryption** -- encrypt operation data before sync with `OperationEncryptor`
|
|
20
|
+
- **Sync auth binding** -- `createKoraAuthSync()` wires tokens, JWT scopes, and device node ids to `createApp`
|
|
21
|
+
|
|
22
|
+
The client APIs work in browser, Tauri desktop WebView, and mobile JavaScript environments. For desktop apps, run auth routes on your remote sync/auth server and point `AuthClient.serverUrl` at that server. Email/password auth, token refresh, sync authorization, MFA, organizations, and RBAC work across web and desktop clients. Passkeys should be feature-detected because WebAuthn support depends on the operating system WebView.
|
|
23
|
+
|
|
24
|
+
For production desktop and mobile apps, pass a custom token storage adapter backed by the platform credential store and attach a stable device identity:
|
|
25
|
+
|
|
26
|
+
```typescript
|
|
27
|
+
import { createKoraAuth } from '@korajs/auth'
|
|
28
|
+
|
|
29
|
+
const authClient = createKoraAuth({
|
|
30
|
+
serverUrl: 'https://acme.example.com',
|
|
31
|
+
credentialStore: secureStore,
|
|
32
|
+
deviceKeyStore,
|
|
33
|
+
})
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
`createKoraAuth()` uses IndexedDB for the device key pair when available. React Native and other runtimes without IndexedDB should pass a platform-backed `deviceKeyStore`.
|
|
20
37
|
|
|
21
38
|
## Installation
|
|
22
39
|
|
|
@@ -29,10 +46,10 @@ pnpm add @korajs/auth
|
|
|
29
46
|
### Client-side (React)
|
|
30
47
|
|
|
31
48
|
```tsx
|
|
32
|
-
import {
|
|
49
|
+
import { createKoraAuth } from '@korajs/auth'
|
|
33
50
|
import { AuthProvider, useAuth } from '@korajs/auth/react'
|
|
34
51
|
|
|
35
|
-
const authClient =
|
|
52
|
+
const authClient = createKoraAuth({ serverUrl: 'http://localhost:3001' })
|
|
36
53
|
|
|
37
54
|
function App() {
|
|
38
55
|
return (
|
|
@@ -43,15 +60,20 @@ function App() {
|
|
|
43
60
|
}
|
|
44
61
|
|
|
45
62
|
function MyApp() {
|
|
46
|
-
const { user, isAuthenticated, isLoading, signIn, signOut, error } = useAuth()
|
|
63
|
+
const { user, isAuthenticated, isLoading, signIn, signInWithOAuth, signOut, error } = useAuth()
|
|
47
64
|
|
|
48
65
|
if (isLoading) return <div>Loading...</div>
|
|
49
66
|
|
|
50
67
|
if (!isAuthenticated) {
|
|
51
68
|
return (
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
69
|
+
<>
|
|
70
|
+
<button onClick={() => signIn({ email: 'user@example.com', password: 'password' })}>
|
|
71
|
+
Sign In
|
|
72
|
+
</button>
|
|
73
|
+
<button onClick={() => signInWithOAuth('google')}>
|
|
74
|
+
Sign In with Google
|
|
75
|
+
</button>
|
|
76
|
+
</>
|
|
55
77
|
)
|
|
56
78
|
}
|
|
57
79
|
|
|
@@ -64,41 +86,66 @@ function MyApp() {
|
|
|
64
86
|
}
|
|
65
87
|
```
|
|
66
88
|
|
|
67
|
-
###
|
|
68
|
-
|
|
69
|
-
```typescript
|
|
70
|
-
import { BuiltInAuthRoutes, InMemoryUserStore, TokenManager } from '@korajs/auth/server'
|
|
71
|
-
|
|
72
|
-
const userStore = new InMemoryUserStore()
|
|
73
|
-
const tokenManager = new TokenManager({ secret: process.env.AUTH_SECRET! })
|
|
74
|
-
const authRoutes = new BuiltInAuthRoutes({ userStore, tokenManager })
|
|
89
|
+
### Sync integration
|
|
75
90
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
91
|
+
```tsx
|
|
92
|
+
import { createKoraAuthSync } from '@korajs/auth'
|
|
93
|
+
import { createApp } from 'korajs'
|
|
94
|
+
|
|
95
|
+
const app = createApp({
|
|
96
|
+
schema,
|
|
97
|
+
sync: {
|
|
98
|
+
url: 'ws://localhost:3001/kora-sync',
|
|
99
|
+
authClient: createKoraAuthSync({ authClient, schema }),
|
|
100
|
+
},
|
|
80
101
|
})
|
|
102
|
+
```
|
|
81
103
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
104
|
+
### Server-side
|
|
105
|
+
|
|
106
|
+
```typescript
|
|
107
|
+
import {
|
|
108
|
+
createKoraAuthServer,
|
|
109
|
+
createSqliteOAuthStores,
|
|
110
|
+
googleProvider,
|
|
111
|
+
} from '@korajs/auth/server'
|
|
112
|
+
|
|
113
|
+
const oauthStores = await createSqliteOAuthStores({
|
|
114
|
+
filename: './auth.db',
|
|
85
115
|
})
|
|
86
116
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
117
|
+
const auth = createKoraAuthServer({
|
|
118
|
+
jwtSecret: process.env.KORA_AUTH_SECRET!,
|
|
119
|
+
oauth: {
|
|
120
|
+
providers: [
|
|
121
|
+
googleProvider({
|
|
122
|
+
clientId: process.env.GOOGLE_CLIENT_ID!,
|
|
123
|
+
clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
|
|
124
|
+
redirectUri: 'https://app.example.com/auth/oauth/google/callback',
|
|
125
|
+
}),
|
|
126
|
+
],
|
|
127
|
+
stateStore: oauthStores.stateStore,
|
|
128
|
+
linkedIdentityStore: oauthStores.linkedIdentityStore,
|
|
129
|
+
},
|
|
90
130
|
})
|
|
91
131
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
const result = await
|
|
132
|
+
// Wire into your HTTP server:
|
|
133
|
+
app.all('/auth/*', async (req, res) => {
|
|
134
|
+
const result = await auth.handleRequest({
|
|
135
|
+
method: req.method,
|
|
136
|
+
path: req.path,
|
|
137
|
+
body: req.body,
|
|
138
|
+
headers: req.headers,
|
|
139
|
+
query: req.query,
|
|
140
|
+
ip: req.ip,
|
|
141
|
+
})
|
|
95
142
|
res.status(result.status).json(result.body)
|
|
96
143
|
})
|
|
97
144
|
|
|
98
145
|
// Bridge to Kora sync server:
|
|
99
146
|
const syncServer = new KoraSyncServer({
|
|
100
147
|
store,
|
|
101
|
-
auth:
|
|
148
|
+
auth: auth.auth,
|
|
102
149
|
})
|
|
103
150
|
```
|
|
104
151
|
|
|
@@ -108,6 +155,8 @@ const syncServer = new KoraSyncServer({
|
|
|
108
155
|
|
|
109
156
|
| Export | Description |
|
|
110
157
|
|--------|-------------|
|
|
158
|
+
| `createKoraAuth` | Quickstart client factory with storage and device identity defaults |
|
|
159
|
+
| `createKoraAuthSync` | Sync auth binding for `createApp({ sync: { authClient } })` |
|
|
111
160
|
| `AuthClient` | Client-side auth manager (sign-up, sign-in, sign-out, token refresh) |
|
|
112
161
|
| `OrgClient` | Client-side organization management |
|
|
113
162
|
| `TokenStore` | Client-side token persistence (localStorage) |
|
|
@@ -139,10 +188,14 @@ const syncServer = new KoraSyncServer({
|
|
|
139
188
|
|
|
140
189
|
| Export | Description |
|
|
141
190
|
|--------|-------------|
|
|
191
|
+
| `createKoraAuthServer` | Quickstart server factory with auth routes and sync provider |
|
|
142
192
|
| `BuiltInAuthRoutes` | HTTP route handlers for all auth operations |
|
|
143
193
|
| `TokenManager` | JWT issuing, validation, refresh rotation, revocation |
|
|
144
194
|
| `InMemoryUserStore` | Dev/test user store |
|
|
145
195
|
| `InMemoryTokenRevocationStore` | Dev/test token revocation store |
|
|
196
|
+
| `OAuthManager` / provider helpers | OAuth authorization code flow and provider configs |
|
|
197
|
+
| `InMemoryLinkedIdentityStore` | Dev/test OAuth account-linking store |
|
|
198
|
+
| `createSqliteOAuthStores` / `createPostgresOAuthStores` | Durable OAuth state and linked identity stores |
|
|
146
199
|
| `SessionManager` / `InMemorySessionStore` | Server-side session management |
|
|
147
200
|
| `TotpManager` / `InMemoryTotpStore` | TOTP MFA with recovery codes |
|
|
148
201
|
| `OrgRoutes` / `InMemoryOrgStore` | Organization CRUD, invitations, member management |
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
// src/bindings/create-auth-session.ts
|
|
2
|
+
function createAuthSession(client) {
|
|
3
|
+
let state = client.state;
|
|
4
|
+
let isLoading = true;
|
|
5
|
+
let initError = null;
|
|
6
|
+
let lastError = null;
|
|
7
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
8
|
+
let snapshot = buildSnapshot2();
|
|
9
|
+
const refreshSnapshot = () => {
|
|
10
|
+
snapshot = buildSnapshot2();
|
|
11
|
+
};
|
|
12
|
+
const notify = () => {
|
|
13
|
+
refreshSnapshot();
|
|
14
|
+
for (const listener of listeners) {
|
|
15
|
+
listener();
|
|
16
|
+
}
|
|
17
|
+
};
|
|
18
|
+
function buildSnapshot2() {
|
|
19
|
+
return {
|
|
20
|
+
state,
|
|
21
|
+
user: client.currentUser,
|
|
22
|
+
isAuthenticated: state === "authenticated",
|
|
23
|
+
isLoading,
|
|
24
|
+
initError,
|
|
25
|
+
error: lastError
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
const captureError = (error) => {
|
|
29
|
+
lastError = error instanceof Error ? error.message : String(error);
|
|
30
|
+
notify();
|
|
31
|
+
};
|
|
32
|
+
const run = async (action) => {
|
|
33
|
+
lastError = null;
|
|
34
|
+
try {
|
|
35
|
+
await action();
|
|
36
|
+
} catch (error) {
|
|
37
|
+
captureError(error);
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
const runWithResult = async (action) => {
|
|
41
|
+
lastError = null;
|
|
42
|
+
try {
|
|
43
|
+
return await action();
|
|
44
|
+
} catch (error) {
|
|
45
|
+
captureError(error);
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
const unsubscribeAuth = client.onAuthChange((nextState) => {
|
|
50
|
+
state = nextState;
|
|
51
|
+
notify();
|
|
52
|
+
});
|
|
53
|
+
void client.initialize().then(() => {
|
|
54
|
+
state = client.state;
|
|
55
|
+
isLoading = false;
|
|
56
|
+
notify();
|
|
57
|
+
}).catch((error) => {
|
|
58
|
+
initError = error instanceof Error ? error : new Error(String(error));
|
|
59
|
+
isLoading = false;
|
|
60
|
+
notify();
|
|
61
|
+
});
|
|
62
|
+
return {
|
|
63
|
+
client,
|
|
64
|
+
getSnapshot: () => snapshot,
|
|
65
|
+
subscribe(listener) {
|
|
66
|
+
listeners.add(listener);
|
|
67
|
+
return () => {
|
|
68
|
+
listeners.delete(listener);
|
|
69
|
+
};
|
|
70
|
+
},
|
|
71
|
+
signUp: (params) => run(async () => {
|
|
72
|
+
await client.signUp(params);
|
|
73
|
+
}),
|
|
74
|
+
signIn: (params) => run(async () => {
|
|
75
|
+
await client.signIn(params);
|
|
76
|
+
}),
|
|
77
|
+
signInWithOAuth: async (provider, options) => {
|
|
78
|
+
lastError = null;
|
|
79
|
+
try {
|
|
80
|
+
return await client.signInWithOAuth(provider, options);
|
|
81
|
+
} catch (error) {
|
|
82
|
+
captureError(error);
|
|
83
|
+
throw error instanceof Error ? error : new Error(String(error));
|
|
84
|
+
}
|
|
85
|
+
},
|
|
86
|
+
completeOAuthSignIn: (provider, params) => run(async () => {
|
|
87
|
+
await client.completeOAuthSignIn(provider, params);
|
|
88
|
+
}),
|
|
89
|
+
getOAuthAuthorizationUrl: async (provider, options) => {
|
|
90
|
+
lastError = null;
|
|
91
|
+
try {
|
|
92
|
+
return await client.getOAuthAuthorizationUrl(provider, options);
|
|
93
|
+
} catch (error) {
|
|
94
|
+
captureError(error);
|
|
95
|
+
throw error instanceof Error ? error : new Error(String(error));
|
|
96
|
+
}
|
|
97
|
+
},
|
|
98
|
+
linkOAuth: (provider, params) => runWithResult(async () => client.linkOAuth(provider, params)),
|
|
99
|
+
listLinkedAccounts: () => runWithResult(async () => client.listLinkedAccounts()).then((value) => value ?? []),
|
|
100
|
+
unlinkOAuth: (provider) => run(async () => {
|
|
101
|
+
await client.unlinkOAuth(provider);
|
|
102
|
+
}),
|
|
103
|
+
signOut: () => run(async () => {
|
|
104
|
+
await client.signOut();
|
|
105
|
+
}),
|
|
106
|
+
destroy() {
|
|
107
|
+
unsubscribeAuth();
|
|
108
|
+
listeners.clear();
|
|
109
|
+
}
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// src/bindings/create-org-session.ts
|
|
114
|
+
var ROLE_LEVELS = {
|
|
115
|
+
viewer: 10,
|
|
116
|
+
billing: 15,
|
|
117
|
+
member: 20,
|
|
118
|
+
admin: 30,
|
|
119
|
+
owner: 40
|
|
120
|
+
};
|
|
121
|
+
function checkOrgPermission(currentRole, requiredRole) {
|
|
122
|
+
if (!currentRole) return false;
|
|
123
|
+
const currentLevel = ROLE_LEVELS[currentRole] ?? 0;
|
|
124
|
+
const requiredLevel = ROLE_LEVELS[requiredRole] ?? 0;
|
|
125
|
+
return currentLevel >= requiredLevel;
|
|
126
|
+
}
|
|
127
|
+
function createOrgSession(client) {
|
|
128
|
+
let snapshot = buildSnapshot(client);
|
|
129
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
130
|
+
const notify = () => {
|
|
131
|
+
snapshot = buildSnapshot(client);
|
|
132
|
+
for (const listener of listeners) {
|
|
133
|
+
listener();
|
|
134
|
+
}
|
|
135
|
+
};
|
|
136
|
+
const unsubscribeOrgChange = client.onOrgChange(notify);
|
|
137
|
+
return {
|
|
138
|
+
client,
|
|
139
|
+
getSnapshot: () => snapshot,
|
|
140
|
+
subscribe(listener) {
|
|
141
|
+
listeners.add(listener);
|
|
142
|
+
return () => {
|
|
143
|
+
listeners.delete(listener);
|
|
144
|
+
};
|
|
145
|
+
},
|
|
146
|
+
checkPermission(requiredRole) {
|
|
147
|
+
return checkOrgPermission(snapshot.role, requiredRole);
|
|
148
|
+
},
|
|
149
|
+
destroy() {
|
|
150
|
+
unsubscribeOrgChange();
|
|
151
|
+
listeners.clear();
|
|
152
|
+
}
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
function buildSnapshot(client) {
|
|
156
|
+
return {
|
|
157
|
+
orgId: client.activeOrgId,
|
|
158
|
+
org: client.activeOrg,
|
|
159
|
+
role: client.activeRole
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
function createOrgMembersActions(client, orgId, onError) {
|
|
163
|
+
return {
|
|
164
|
+
async refresh() {
|
|
165
|
+
await client.listMembers(orgId);
|
|
166
|
+
},
|
|
167
|
+
async invite(email, role) {
|
|
168
|
+
try {
|
|
169
|
+
return await client.inviteMember(orgId, { email, role });
|
|
170
|
+
} catch (error) {
|
|
171
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
172
|
+
onError(message);
|
|
173
|
+
throw error;
|
|
174
|
+
}
|
|
175
|
+
},
|
|
176
|
+
async removeMember(userId) {
|
|
177
|
+
try {
|
|
178
|
+
await client.removeMember(orgId, userId);
|
|
179
|
+
} catch (error) {
|
|
180
|
+
onError(error instanceof Error ? error.message : String(error));
|
|
181
|
+
}
|
|
182
|
+
},
|
|
183
|
+
async updateRole(userId, role) {
|
|
184
|
+
try {
|
|
185
|
+
await client.updateMemberRole(orgId, userId, role);
|
|
186
|
+
} catch (error) {
|
|
187
|
+
onError(error instanceof Error ? error.message : String(error));
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
async function loadOrgMembers(client, orgId) {
|
|
193
|
+
return client.listMembers(orgId);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
export {
|
|
197
|
+
createAuthSession,
|
|
198
|
+
checkOrgPermission,
|
|
199
|
+
createOrgSession,
|
|
200
|
+
createOrgMembersActions,
|
|
201
|
+
loadOrgMembers
|
|
202
|
+
};
|
|
203
|
+
//# sourceMappingURL=chunk-7OXBRSJL.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/bindings/create-auth-session.ts","../src/bindings/create-org-session.ts"],"sourcesContent":["import type {\n\tAuthClient,\n\tAuthState,\n\tAuthUser,\n\tLinkedOAuthAccount,\n\tOAuthAuthorizationOptions,\n\tOAuthAuthorizationResult,\n\tOAuthCallbackParams,\n} from '../client/auth-client'\n\nexport interface AuthSessionSnapshot {\n\tstate: AuthState\n\tuser: AuthUser | null\n\tisAuthenticated: boolean\n\tisLoading: boolean\n\tinitError: Error | null\n\terror: string | null\n}\n\nexport interface AuthSession {\n\treadonly client: AuthClient\n\tgetSnapshot(): AuthSessionSnapshot\n\tsubscribe(listener: () => void): () => void\n\tsignUp(params: {\n\t\temail: string\n\t\tpassword: string\n\t\tname?: string\n\t\tdeviceId?: string\n\t\tdevicePublicKey?: string\n\t}): Promise<void>\n\tsignIn(params: {\n\t\temail: string\n\t\tpassword: string\n\t\tdeviceId?: string\n\t\tdevicePublicKey?: string\n\t}): Promise<void>\n\tsignInWithOAuth(\n\t\tprovider: string,\n\t\toptions?: OAuthAuthorizationOptions,\n\t): Promise<OAuthAuthorizationResult>\n\tcompleteOAuthSignIn(provider: string, params: OAuthCallbackParams): Promise<void>\n\tgetOAuthAuthorizationUrl(\n\t\tprovider: string,\n\t\toptions?: OAuthAuthorizationOptions,\n\t): Promise<OAuthAuthorizationResult>\n\tlinkOAuth(provider: string, params: OAuthCallbackParams): Promise<LinkedOAuthAccount | null>\n\tlistLinkedAccounts(): Promise<LinkedOAuthAccount[]>\n\tunlinkOAuth(provider: string): Promise<void>\n\tsignOut(): Promise<void>\n\tdestroy(): void\n}\n\n/**\n * Framework-agnostic auth session: initialization, reactive snapshot, and client methods.\n */\nexport function createAuthSession(client: AuthClient): AuthSession {\n\tlet state: AuthState = client.state\n\tlet isLoading = true\n\tlet initError: Error | null = null\n\tlet lastError: string | null = null\n\n\tconst listeners = new Set<() => void>()\n\tlet snapshot = buildSnapshot()\n\n\tconst refreshSnapshot = (): void => {\n\t\tsnapshot = buildSnapshot()\n\t}\n\n\tconst notify = (): void => {\n\t\trefreshSnapshot()\n\t\tfor (const listener of listeners) {\n\t\t\tlistener()\n\t\t}\n\t}\n\n\tfunction buildSnapshot(): AuthSessionSnapshot {\n\t\treturn {\n\t\t\tstate,\n\t\t\tuser: client.currentUser,\n\t\t\tisAuthenticated: state === 'authenticated',\n\t\t\tisLoading,\n\t\t\tinitError,\n\t\t\terror: lastError,\n\t\t}\n\t}\n\n\tconst captureError = (error: unknown): void => {\n\t\tlastError = error instanceof Error ? error.message : String(error)\n\t\tnotify()\n\t}\n\n\tconst run = async (action: () => Promise<void>): Promise<void> => {\n\t\tlastError = null\n\t\ttry {\n\t\t\tawait action()\n\t\t} catch (error) {\n\t\t\tcaptureError(error)\n\t\t}\n\t}\n\n\tconst runWithResult = async <T>(action: () => Promise<T>): Promise<T | null> => {\n\t\tlastError = null\n\t\ttry {\n\t\t\treturn await action()\n\t\t} catch (error) {\n\t\t\tcaptureError(error)\n\t\t\treturn null\n\t\t}\n\t}\n\n\tconst unsubscribeAuth = client.onAuthChange((nextState) => {\n\t\tstate = nextState\n\t\tnotify()\n\t})\n\n\tvoid client\n\t\t.initialize()\n\t\t.then(() => {\n\t\t\tstate = client.state\n\t\t\tisLoading = false\n\t\t\tnotify()\n\t\t})\n\t\t.catch((error: unknown) => {\n\t\t\tinitError = error instanceof Error ? error : new Error(String(error))\n\t\t\tisLoading = false\n\t\t\tnotify()\n\t\t})\n\n\treturn {\n\t\tclient,\n\t\tgetSnapshot: () => snapshot,\n\t\tsubscribe(listener) {\n\t\t\tlisteners.add(listener)\n\t\t\treturn () => {\n\t\t\t\tlisteners.delete(listener)\n\t\t\t}\n\t\t},\n\t\tsignUp: (params) =>\n\t\t\trun(async () => {\n\t\t\t\tawait client.signUp(params)\n\t\t\t}),\n\t\tsignIn: (params) =>\n\t\t\trun(async () => {\n\t\t\t\tawait client.signIn(params)\n\t\t\t}),\n\t\tsignInWithOAuth: async (provider, options) => {\n\t\t\tlastError = null\n\t\t\ttry {\n\t\t\t\treturn await client.signInWithOAuth(provider, options)\n\t\t\t} catch (error) {\n\t\t\t\tcaptureError(error)\n\t\t\t\tthrow error instanceof Error ? error : new Error(String(error))\n\t\t\t}\n\t\t},\n\t\tcompleteOAuthSignIn: (provider, params) =>\n\t\t\trun(async () => {\n\t\t\t\tawait client.completeOAuthSignIn(provider, params)\n\t\t\t}),\n\t\tgetOAuthAuthorizationUrl: async (provider, options) => {\n\t\t\tlastError = null\n\t\t\ttry {\n\t\t\t\treturn await client.getOAuthAuthorizationUrl(provider, options)\n\t\t\t} catch (error) {\n\t\t\t\tcaptureError(error)\n\t\t\t\tthrow error instanceof Error ? error : new Error(String(error))\n\t\t\t}\n\t\t},\n\t\tlinkOAuth: (provider, params) =>\n\t\t\trunWithResult(async () => client.linkOAuth(provider, params)),\n\t\tlistLinkedAccounts: () =>\n\t\t\trunWithResult(async () => client.listLinkedAccounts()).then((value) => value ?? []),\n\t\tunlinkOAuth: (provider) =>\n\t\t\trun(async () => {\n\t\t\t\tawait client.unlinkOAuth(provider)\n\t\t\t}),\n\t\tsignOut: () =>\n\t\t\trun(async () => {\n\t\t\t\tawait client.signOut()\n\t\t\t}),\n\t\tdestroy() {\n\t\t\tunsubscribeAuth()\n\t\t\tlisteners.clear()\n\t\t},\n\t}\n}\n","import type {\n\tClientInvitation,\n\tClientMembership,\n\tClientOrganization,\n\tOrgClient,\n} from '../client/org-client'\n\nexport interface OrgSnapshot {\n\torgId: string | null\n\torg: ClientOrganization | null\n\trole: string | null\n}\n\nexport interface OrgSession {\n\treadonly client: OrgClient\n\tgetSnapshot(): OrgSnapshot\n\tsubscribe(listener: () => void): () => void\n\tcheckPermission(requiredRole: string): boolean\n\tdestroy(): void\n}\n\nconst ROLE_LEVELS: Record<string, number> = {\n\tviewer: 10,\n\tbilling: 15,\n\tmember: 20,\n\tadmin: 30,\n\towner: 40,\n}\n\nexport function checkOrgPermission(currentRole: string | null, requiredRole: string): boolean {\n\tif (!currentRole) return false\n\tconst currentLevel = ROLE_LEVELS[currentRole] ?? 0\n\tconst requiredLevel = ROLE_LEVELS[requiredRole] ?? 0\n\treturn currentLevel >= requiredLevel\n}\n\n/**\n * Framework-agnostic org session with reactive snapshot subscription.\n */\nexport function createOrgSession(client: OrgClient): OrgSession {\n\tlet snapshot = buildSnapshot(client)\n\tconst listeners = new Set<() => void>()\n\n\tconst notify = (): void => {\n\t\tsnapshot = buildSnapshot(client)\n\t\tfor (const listener of listeners) {\n\t\t\tlistener()\n\t\t}\n\t}\n\n\tconst unsubscribeOrgChange = client.onOrgChange(notify)\n\n\treturn {\n\t\tclient,\n\t\tgetSnapshot: () => snapshot,\n\t\tsubscribe(listener: () => void): () => void {\n\t\t\tlisteners.add(listener)\n\t\t\treturn () => {\n\t\t\t\tlisteners.delete(listener)\n\t\t\t}\n\t\t},\n\t\tcheckPermission(requiredRole: string): boolean {\n\t\t\treturn checkOrgPermission(snapshot.role, requiredRole)\n\t\t},\n\t\tdestroy(): void {\n\t\t\tunsubscribeOrgChange()\n\t\t\tlisteners.clear()\n\t\t},\n\t}\n}\n\nfunction buildSnapshot(client: OrgClient): OrgSnapshot {\n\treturn {\n\t\torgId: client.activeOrgId,\n\t\torg: client.activeOrg,\n\t\trole: client.activeRole,\n\t}\n}\n\nexport interface UseOrgMembersActions {\n\trefresh: () => Promise<void>\n\tinvite: (email: string, role: string) => Promise<ClientInvitation>\n\tremoveMember: (userId: string) => Promise<void>\n\tupdateRole: (userId: string, role: string) => Promise<void>\n}\n\n/**\n * Shared org member management actions (loading state remains framework-specific).\n */\nexport function createOrgMembersActions(\n\tclient: OrgClient,\n\torgId: string,\n\tonError: (message: string) => void,\n): UseOrgMembersActions {\n\treturn {\n\t\tasync refresh(): Promise<void> {\n\t\t\tawait client.listMembers(orgId)\n\t\t},\n\t\tasync invite(email: string, role: string): Promise<ClientInvitation> {\n\t\t\ttry {\n\t\t\t\treturn await client.inviteMember(orgId, { email, role })\n\t\t\t} catch (error) {\n\t\t\t\tconst message = error instanceof Error ? error.message : String(error)\n\t\t\t\tonError(message)\n\t\t\t\tthrow error\n\t\t\t}\n\t\t},\n\t\tasync removeMember(userId: string): Promise<void> {\n\t\t\ttry {\n\t\t\t\tawait client.removeMember(orgId, userId)\n\t\t\t} catch (error) {\n\t\t\t\tonError(error instanceof Error ? error.message : String(error))\n\t\t\t}\n\t\t},\n\t\tasync updateRole(userId: string, role: string): Promise<void> {\n\t\t\ttry {\n\t\t\t\tawait client.updateMemberRole(orgId, userId, role)\n\t\t\t} catch (error) {\n\t\t\t\tonError(error instanceof Error ? error.message : String(error))\n\t\t\t}\n\t\t},\n\t}\n}\n\nexport async function loadOrgMembers(\n\tclient: OrgClient,\n\torgId: string,\n): Promise<ClientMembership[]> {\n\treturn client.listMembers(orgId)\n}\n"],"mappings":"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","names":["buildSnapshot"]}
|