@rebasepro/client 0.0.1-canary.4d4fb3e
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/LICENSE +6 -0
- package/dist/admin.d.ts +94 -0
- package/dist/auth.d.ts +95 -0
- package/dist/collection.d.ts +19 -0
- package/dist/index.d.ts +37 -0
- package/dist/index.es.js +1882 -0
- package/dist/index.es.js.map +1 -0
- package/dist/index.umd.js +1886 -0
- package/dist/index.umd.js.map +1 -0
- package/dist/query_builder.d.ts +53 -0
- package/dist/storage.d.ts +3 -0
- package/dist/transport.d.ts +33 -0
- package/dist/websocket.d.ts +93 -0
- package/package.json +79 -0
- package/src/admin.ts +119 -0
- package/src/auth.ts +400 -0
- package/src/collection.ts +198 -0
- package/src/index.ts +154 -0
- package/src/query_builder.ts +126 -0
- package/src/storage.ts +181 -0
- package/src/transport.ts +173 -0
- package/src/websocket.ts +1114 -0
package/src/admin.ts
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { Transport } from "./transport";
|
|
2
|
+
|
|
3
|
+
export interface AdminUser {
|
|
4
|
+
uid: string;
|
|
5
|
+
email: string;
|
|
6
|
+
displayName: string | null;
|
|
7
|
+
photoURL: string | null;
|
|
8
|
+
provider: string;
|
|
9
|
+
roles: string[];
|
|
10
|
+
createdAt: string;
|
|
11
|
+
updatedAt: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface RebaseRole {
|
|
15
|
+
id: string;
|
|
16
|
+
name: string;
|
|
17
|
+
isAdmin: boolean;
|
|
18
|
+
defaultPermissions: Record<string, any> | null;
|
|
19
|
+
config: Record<string, any> | null;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface CreateAdminOptions {
|
|
23
|
+
adminPath?: string;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function createAdmin(transport: Transport, options?: CreateAdminOptions) {
|
|
27
|
+
const opts = options || {};
|
|
28
|
+
const adminPath = opts.adminPath || "/admin";
|
|
29
|
+
|
|
30
|
+
async function listUsers() {
|
|
31
|
+
return transport.request<{ users: AdminUser[] }>(adminPath + "/users", { method: "GET" });
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
async function listUsersPaginated(options?: { search?: string; limit?: number; offset?: number; orderBy?: string; orderDir?: "asc" | "desc" }) {
|
|
35
|
+
const params = new URLSearchParams();
|
|
36
|
+
if (options?.limit !== undefined) params.set("limit", String(options.limit));
|
|
37
|
+
if (options?.offset !== undefined) params.set("offset", String(options.offset));
|
|
38
|
+
if (options?.search) params.set("search", options.search);
|
|
39
|
+
if (options?.orderBy) params.set("orderBy", options.orderBy);
|
|
40
|
+
if (options?.orderDir) params.set("orderDir", options.orderDir);
|
|
41
|
+
const qs = params.toString();
|
|
42
|
+
return transport.request<{ users: AdminUser[]; total: number; limit: number; offset: number }>(
|
|
43
|
+
adminPath + "/users" + (qs ? "?" + qs : ""), { method: "GET" }
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
async function getUser(userId: string) {
|
|
48
|
+
return transport.request<{ user: AdminUser }>(adminPath + "/users/" + encodeURIComponent(userId), { method: "GET" });
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
async function createUser(data: { email: string, displayName?: string, password?: string, roles?: string[] }) {
|
|
52
|
+
return transport.request<{ user: AdminUser }>(adminPath + "/users", {
|
|
53
|
+
method: "POST",
|
|
54
|
+
body: JSON.stringify(data),
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
async function updateUser(userId: string, data: { email?: string, displayName?: string, password?: string, roles?: string[] }) {
|
|
59
|
+
return transport.request<{ user: AdminUser }>(adminPath + "/users/" + encodeURIComponent(userId), {
|
|
60
|
+
method: "PUT",
|
|
61
|
+
body: JSON.stringify(data),
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
async function deleteUser(userId: string) {
|
|
66
|
+
return transport.request<{ success: boolean }>(adminPath + "/users/" + encodeURIComponent(userId), {
|
|
67
|
+
method: "DELETE",
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
async function listRoles() {
|
|
72
|
+
return transport.request<{ roles: RebaseRole[] }>(adminPath + "/roles", { method: "GET" });
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
async function getRole(roleId: string) {
|
|
76
|
+
return transport.request<{ role: RebaseRole }>(adminPath + "/roles/" + encodeURIComponent(roleId), { method: "GET" });
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
async function createRole(data: { id: string, name: string, isAdmin?: boolean, defaultPermissions?: any, config?: any }) {
|
|
80
|
+
return transport.request<{ role: RebaseRole }>(adminPath + "/roles", {
|
|
81
|
+
method: "POST",
|
|
82
|
+
body: JSON.stringify(data),
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
async function updateRole(roleId: string, data: { name?: string, isAdmin?: boolean, defaultPermissions?: any, config?: any }) {
|
|
87
|
+
return transport.request<{ role: RebaseRole }>(adminPath + "/roles/" + encodeURIComponent(roleId), {
|
|
88
|
+
method: "PUT",
|
|
89
|
+
body: JSON.stringify(data),
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
async function deleteRole(roleId: string) {
|
|
94
|
+
return transport.request<{ success: boolean }>(adminPath + "/roles/" + encodeURIComponent(roleId), {
|
|
95
|
+
method: "DELETE",
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
async function bootstrap() {
|
|
100
|
+
return transport.request<{ success: boolean; message: string; user: { uid: string; roles: string[] } }>(adminPath + "/bootstrap", {
|
|
101
|
+
method: "POST",
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
return {
|
|
106
|
+
listUsers,
|
|
107
|
+
listUsersPaginated,
|
|
108
|
+
getUser,
|
|
109
|
+
createUser,
|
|
110
|
+
updateUser,
|
|
111
|
+
deleteUser,
|
|
112
|
+
listRoles,
|
|
113
|
+
getRole,
|
|
114
|
+
createRole,
|
|
115
|
+
updateRole,
|
|
116
|
+
deleteRole,
|
|
117
|
+
bootstrap,
|
|
118
|
+
};
|
|
119
|
+
}
|
package/src/auth.ts
ADDED
|
@@ -0,0 +1,400 @@
|
|
|
1
|
+
import { RebaseApiError, Transport } from "./transport";
|
|
2
|
+
|
|
3
|
+
export interface RebaseUser {
|
|
4
|
+
uid: string;
|
|
5
|
+
email: string | null;
|
|
6
|
+
displayName: string | null;
|
|
7
|
+
photoURL: string | null;
|
|
8
|
+
emailVerified?: boolean;
|
|
9
|
+
roles?: string[];
|
|
10
|
+
providerId: string;
|
|
11
|
+
isAnonymous: boolean;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface RebaseTokens {
|
|
15
|
+
accessToken: string;
|
|
16
|
+
refreshToken: string;
|
|
17
|
+
accessTokenExpiresAt: number;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface RebaseSession {
|
|
21
|
+
accessToken: string;
|
|
22
|
+
refreshToken: string;
|
|
23
|
+
expiresAt: number;
|
|
24
|
+
user: RebaseUser;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export type AuthChangeEvent = 'SIGNED_IN' | 'SIGNED_OUT' | 'TOKEN_REFRESHED' | 'USER_UPDATED';
|
|
28
|
+
|
|
29
|
+
export interface AuthConfig {
|
|
30
|
+
needsSetup: boolean;
|
|
31
|
+
registrationEnabled: boolean;
|
|
32
|
+
googleEnabled: boolean;
|
|
33
|
+
emailServiceEnabled: boolean;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export interface AuthStorage {
|
|
37
|
+
getItem: (key: string) => string | null;
|
|
38
|
+
setItem: (key: string, value: string) => void;
|
|
39
|
+
removeItem: (key: string) => void;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function createMemoryStorage(): AuthStorage {
|
|
43
|
+
const store: Record<string, string> = {};
|
|
44
|
+
return {
|
|
45
|
+
getItem(key) { return store[key] ?? null; },
|
|
46
|
+
setItem(key, value) { store[key] = value; },
|
|
47
|
+
removeItem(key) { delete store[key]; },
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function detectStorage(): AuthStorage {
|
|
52
|
+
try {
|
|
53
|
+
if (typeof localStorage !== "undefined") {
|
|
54
|
+
localStorage.setItem("__rebase_test__", "1");
|
|
55
|
+
localStorage.removeItem("__rebase_test__");
|
|
56
|
+
return localStorage;
|
|
57
|
+
}
|
|
58
|
+
} catch (e) { /* ignore */ }
|
|
59
|
+
return createMemoryStorage();
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export interface CreateAuthOptions {
|
|
63
|
+
storage?: AuthStorage;
|
|
64
|
+
authPath?: string;
|
|
65
|
+
autoRefresh?: boolean;
|
|
66
|
+
persistSession?: boolean;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export function createAuth(transport: Transport, options?: CreateAuthOptions) {
|
|
70
|
+
const opts = options || {};
|
|
71
|
+
const storage = opts.storage || detectStorage();
|
|
72
|
+
const authPath = opts.authPath || "/auth";
|
|
73
|
+
const autoRefresh = opts.autoRefresh !== false;
|
|
74
|
+
const persistSession = opts.persistSession !== false;
|
|
75
|
+
|
|
76
|
+
const STORAGE_KEY = "rebase_auth";
|
|
77
|
+
const REFRESH_BUFFER_MS = 120000;
|
|
78
|
+
|
|
79
|
+
let currentSession: RebaseSession | null = null;
|
|
80
|
+
const listeners = new Set<(event: AuthChangeEvent, session: RebaseSession | null) => void>();
|
|
81
|
+
let refreshTimeout: ReturnType<typeof setTimeout> | null = null;
|
|
82
|
+
|
|
83
|
+
function authUrl(endpoint: string) {
|
|
84
|
+
return transport.baseUrl + transport.apiPath + authPath + endpoint;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function getFetch() {
|
|
88
|
+
return transport.fetchFn || globalThis.fetch;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function throwApiError(status: number, body: { error?: { message?: string; code?: string; details?: unknown }; message?: string; code?: string; details?: unknown } | undefined, statusText: string): never {
|
|
92
|
+
throw new RebaseApiError(
|
|
93
|
+
status,
|
|
94
|
+
body?.error?.message || body?.message || statusText,
|
|
95
|
+
body?.error?.code || body?.code,
|
|
96
|
+
body?.error?.details || body?.details,
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function emit(event: AuthChangeEvent, session: RebaseSession | null) {
|
|
101
|
+
for (const fn of listeners) {
|
|
102
|
+
try { fn(event, session); } catch (e) { /* ignore */ }
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function saveSession(session: RebaseSession) {
|
|
107
|
+
if (!persistSession) return;
|
|
108
|
+
try {
|
|
109
|
+
storage.setItem(STORAGE_KEY, JSON.stringify(session));
|
|
110
|
+
} catch (e) { /* ignore */ }
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function clearStoredSession() {
|
|
114
|
+
try {
|
|
115
|
+
storage.removeItem(STORAGE_KEY);
|
|
116
|
+
} catch (e) { /* ignore */ }
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function loadStoredSession(): RebaseSession | null {
|
|
120
|
+
try {
|
|
121
|
+
const raw = storage.getItem(STORAGE_KEY);
|
|
122
|
+
if (raw) return JSON.parse(raw) as RebaseSession;
|
|
123
|
+
} catch (e) { /* ignore */ }
|
|
124
|
+
return null;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function scheduleRefresh(expiresAt: number) {
|
|
128
|
+
if (refreshTimeout) clearTimeout(refreshTimeout);
|
|
129
|
+
if (!autoRefresh) return;
|
|
130
|
+
|
|
131
|
+
const delay = (expiresAt - REFRESH_BUFFER_MS) - Date.now();
|
|
132
|
+
|
|
133
|
+
if (delay <= 0) {
|
|
134
|
+
refreshSession().catch(() => signOut());
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
refreshTimeout = setTimeout(async () => {
|
|
139
|
+
try {
|
|
140
|
+
await refreshSession();
|
|
141
|
+
} catch (e) {
|
|
142
|
+
signOut();
|
|
143
|
+
}
|
|
144
|
+
}, delay);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function handleAuthResponse(data: { tokens: RebaseTokens, user: RebaseUser }, event?: AuthChangeEvent): RebaseSession {
|
|
148
|
+
const session: RebaseSession = {
|
|
149
|
+
accessToken: data.tokens.accessToken,
|
|
150
|
+
refreshToken: data.tokens.refreshToken,
|
|
151
|
+
expiresAt: data.tokens.accessTokenExpiresAt,
|
|
152
|
+
user: data.user,
|
|
153
|
+
};
|
|
154
|
+
currentSession = session;
|
|
155
|
+
saveSession(session);
|
|
156
|
+
transport.setToken(session.accessToken);
|
|
157
|
+
scheduleRefresh(session.expiresAt);
|
|
158
|
+
emit(event || "SIGNED_IN", session);
|
|
159
|
+
return session;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
async function signInWithEmail(email: string, password: string) {
|
|
163
|
+
const fetchFn = getFetch();
|
|
164
|
+
const res = await fetchFn(authUrl("/login"), {
|
|
165
|
+
method: "POST",
|
|
166
|
+
headers: { "Content-Type": "application/json" },
|
|
167
|
+
body: JSON.stringify({ email, password }),
|
|
168
|
+
});
|
|
169
|
+
const body = await res.json().catch(() => ({}));
|
|
170
|
+
if (!res.ok) throwApiError(res.status, body, res.statusText);
|
|
171
|
+
const session = handleAuthResponse(body, "SIGNED_IN");
|
|
172
|
+
return { user: session.user, accessToken: session.accessToken, refreshToken: session.refreshToken };
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
async function signUp(email: string, password: string, displayName?: string) {
|
|
176
|
+
const fetchFn = getFetch();
|
|
177
|
+
const payload: Record<string, string> = { email, password };
|
|
178
|
+
if (displayName !== undefined) payload.displayName = displayName;
|
|
179
|
+
const res = await fetchFn(authUrl("/register"), {
|
|
180
|
+
method: "POST",
|
|
181
|
+
headers: { "Content-Type": "application/json" },
|
|
182
|
+
body: JSON.stringify(payload),
|
|
183
|
+
});
|
|
184
|
+
const body = await res.json().catch(() => ({}));
|
|
185
|
+
if (!res.ok) throwApiError(res.status, body, res.statusText);
|
|
186
|
+
const session = handleAuthResponse(body, "SIGNED_IN");
|
|
187
|
+
return { user: session.user, accessToken: session.accessToken, refreshToken: session.refreshToken };
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
async function signInWithGoogle(idToken: string) {
|
|
191
|
+
const fetchFn = getFetch();
|
|
192
|
+
const res = await fetchFn(authUrl("/google"), {
|
|
193
|
+
method: "POST",
|
|
194
|
+
headers: { "Content-Type": "application/json" },
|
|
195
|
+
body: JSON.stringify({ idToken }),
|
|
196
|
+
});
|
|
197
|
+
const body = await res.json().catch(() => ({}));
|
|
198
|
+
if (!res.ok) throwApiError(res.status, body, res.statusText);
|
|
199
|
+
const session = handleAuthResponse(body, "SIGNED_IN");
|
|
200
|
+
return { user: session.user, accessToken: session.accessToken, refreshToken: session.refreshToken };
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
async function signOut() {
|
|
204
|
+
const fetchFn = getFetch();
|
|
205
|
+
try {
|
|
206
|
+
if (currentSession?.refreshToken) {
|
|
207
|
+
await fetchFn(authUrl("/logout"), {
|
|
208
|
+
method: "POST",
|
|
209
|
+
headers: { "Content-Type": "application/json" },
|
|
210
|
+
body: JSON.stringify({ refreshToken: currentSession.refreshToken }),
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
} catch (e) { /* ignore */ }
|
|
214
|
+
currentSession = null;
|
|
215
|
+
clearStoredSession();
|
|
216
|
+
if (refreshTimeout) {
|
|
217
|
+
clearTimeout(refreshTimeout);
|
|
218
|
+
refreshTimeout = null;
|
|
219
|
+
}
|
|
220
|
+
transport.setToken(null);
|
|
221
|
+
emit("SIGNED_OUT", null);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
async function refreshSession() {
|
|
225
|
+
if (!currentSession?.refreshToken) {
|
|
226
|
+
throw new Error("No active session to refresh");
|
|
227
|
+
}
|
|
228
|
+
const fetchFn = getFetch();
|
|
229
|
+
const res = await fetchFn(authUrl("/refresh"), {
|
|
230
|
+
method: "POST",
|
|
231
|
+
headers: { "Content-Type": "application/json" },
|
|
232
|
+
body: JSON.stringify({ refreshToken: currentSession.refreshToken }),
|
|
233
|
+
});
|
|
234
|
+
const body = await res.json().catch(() => ({}));
|
|
235
|
+
if (!res.ok) throwApiError(res.status, body, res.statusText);
|
|
236
|
+
const session: RebaseSession = {
|
|
237
|
+
accessToken: body.tokens.accessToken,
|
|
238
|
+
refreshToken: body.tokens.refreshToken,
|
|
239
|
+
expiresAt: body.tokens.accessTokenExpiresAt,
|
|
240
|
+
user: currentSession.user,
|
|
241
|
+
};
|
|
242
|
+
currentSession = session;
|
|
243
|
+
saveSession(session);
|
|
244
|
+
transport.setToken(session.accessToken);
|
|
245
|
+
scheduleRefresh(session.expiresAt);
|
|
246
|
+
emit("TOKEN_REFRESHED", session);
|
|
247
|
+
return session;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
async function getUser() {
|
|
251
|
+
const data = await transport.request<{ user: RebaseUser }>(authPath + "/me", { method: "GET" });
|
|
252
|
+
return data.user;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
async function updateUser(updates: { displayName?: string, photoURL?: string }) {
|
|
256
|
+
const data = await transport.request<{ user: RebaseUser }>(authPath + "/me", {
|
|
257
|
+
method: "PATCH",
|
|
258
|
+
body: JSON.stringify(updates),
|
|
259
|
+
});
|
|
260
|
+
if (currentSession) {
|
|
261
|
+
currentSession = { ...currentSession, user: data.user };
|
|
262
|
+
saveSession(currentSession);
|
|
263
|
+
emit("USER_UPDATED", currentSession);
|
|
264
|
+
}
|
|
265
|
+
return data.user;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
async function resetPasswordForEmail(email: string) {
|
|
269
|
+
const fetchFn = getFetch();
|
|
270
|
+
const res = await fetchFn(authUrl("/forgot-password"), {
|
|
271
|
+
method: "POST",
|
|
272
|
+
headers: { "Content-Type": "application/json" },
|
|
273
|
+
body: JSON.stringify({ email }),
|
|
274
|
+
});
|
|
275
|
+
const body = await res.json().catch(() => ({}));
|
|
276
|
+
if (!res.ok) throwApiError(res.status, body, res.statusText);
|
|
277
|
+
return body as { success: boolean; message: string; };
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
async function resetPassword(token: string, password: string) {
|
|
281
|
+
const fetchFn = getFetch();
|
|
282
|
+
const res = await fetchFn(authUrl("/reset-password"), {
|
|
283
|
+
method: "POST",
|
|
284
|
+
headers: { "Content-Type": "application/json" },
|
|
285
|
+
body: JSON.stringify({ token, password }),
|
|
286
|
+
});
|
|
287
|
+
const body = await res.json().catch(() => ({}));
|
|
288
|
+
if (!res.ok) throwApiError(res.status, body, res.statusText);
|
|
289
|
+
return body as { success: boolean; message: string; };
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
async function changePassword(oldPassword: string, newPassword: string) {
|
|
293
|
+
return transport.request<{ success: boolean; message: string; }>(authPath + "/change-password", {
|
|
294
|
+
method: "POST",
|
|
295
|
+
body: JSON.stringify({ oldPassword, newPassword }),
|
|
296
|
+
});
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
async function sendVerificationEmail() {
|
|
300
|
+
return transport.request<{ success: boolean; message: string; }>(authPath + "/send-verification", {
|
|
301
|
+
method: "POST",
|
|
302
|
+
});
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
async function verifyEmail(token: string) {
|
|
306
|
+
const fetchFn = getFetch();
|
|
307
|
+
const res = await fetchFn(authUrl("/verify-email?token=" + encodeURIComponent(token)), {
|
|
308
|
+
method: "GET",
|
|
309
|
+
headers: { "Content-Type": "application/json" },
|
|
310
|
+
});
|
|
311
|
+
const body = await res.json().catch(() => ({}));
|
|
312
|
+
if (!res.ok) throwApiError(res.status, body, res.statusText);
|
|
313
|
+
return body as { success: boolean; message: string; };
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
async function getSessions() {
|
|
317
|
+
const data = await transport.request<{ sessions: Record<string, unknown>[] }>(authPath + "/sessions", { method: "GET" });
|
|
318
|
+
return data.sessions;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
async function revokeSession(sessionId: string) {
|
|
322
|
+
return transport.request<{ success: boolean }>(authPath + "/sessions/" + encodeURIComponent(sessionId), {
|
|
323
|
+
method: "DELETE",
|
|
324
|
+
});
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
async function revokeAllSessions() {
|
|
328
|
+
const result = await transport.request<{ success: boolean }>(authPath + "/sessions", {
|
|
329
|
+
method: "DELETE",
|
|
330
|
+
});
|
|
331
|
+
currentSession = null;
|
|
332
|
+
clearStoredSession();
|
|
333
|
+
if (refreshTimeout) {
|
|
334
|
+
clearTimeout(refreshTimeout);
|
|
335
|
+
refreshTimeout = null;
|
|
336
|
+
}
|
|
337
|
+
transport.setToken(null);
|
|
338
|
+
emit("SIGNED_OUT", null);
|
|
339
|
+
return result;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
async function getAuthConfig() {
|
|
343
|
+
const fetchFn = getFetch();
|
|
344
|
+
const res = await fetchFn(authUrl("/config"), {
|
|
345
|
+
method: "GET",
|
|
346
|
+
headers: { "Content-Type": "application/json" },
|
|
347
|
+
});
|
|
348
|
+
const body = await res.json().catch(() => ({}));
|
|
349
|
+
if (!res.ok) throwApiError(res.status, body, res.statusText);
|
|
350
|
+
return body as AuthConfig;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
function getSession() {
|
|
354
|
+
return currentSession;
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
function onAuthStateChange(callback: (event: AuthChangeEvent, session: RebaseSession | null) => void) {
|
|
358
|
+
listeners.add(callback);
|
|
359
|
+
return () => listeners.delete(callback);
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
if (persistSession) {
|
|
363
|
+
const stored = loadStoredSession();
|
|
364
|
+
if (stored && stored.accessToken && stored.refreshToken) {
|
|
365
|
+
if (stored.expiresAt > Date.now()) {
|
|
366
|
+
currentSession = stored;
|
|
367
|
+
transport.setToken(stored.accessToken);
|
|
368
|
+
scheduleRefresh(stored.expiresAt);
|
|
369
|
+
} else if (stored.refreshToken) {
|
|
370
|
+
currentSession = stored;
|
|
371
|
+
refreshSession().catch(() => {
|
|
372
|
+
currentSession = null;
|
|
373
|
+
clearStoredSession();
|
|
374
|
+
transport.setToken(null);
|
|
375
|
+
});
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
return {
|
|
381
|
+
signInWithEmail,
|
|
382
|
+
signUp,
|
|
383
|
+
signInWithGoogle,
|
|
384
|
+
signOut,
|
|
385
|
+
refreshSession,
|
|
386
|
+
getUser,
|
|
387
|
+
updateUser,
|
|
388
|
+
resetPasswordForEmail,
|
|
389
|
+
resetPassword,
|
|
390
|
+
changePassword,
|
|
391
|
+
sendVerificationEmail,
|
|
392
|
+
verifyEmail,
|
|
393
|
+
getSessions,
|
|
394
|
+
revokeSession,
|
|
395
|
+
revokeAllSessions,
|
|
396
|
+
getAuthConfig,
|
|
397
|
+
getSession,
|
|
398
|
+
onAuthStateChange,
|
|
399
|
+
};
|
|
400
|
+
}
|