@slyxup/core 1.0.2 → 1.2.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/src/client.ts DELETED
@@ -1,766 +0,0 @@
1
- import {
2
- NetworkError,
3
- RateLimitError,
4
- SlyxupError,
5
- UnauthorizedError,
6
- } from './errors.js';
7
- import type {
8
- AuthResponse,
9
- ChangePasswordInput,
10
- CompleteSignInInput,
11
- ConnectedAccountsResponse,
12
- EnableTOTPResponse,
13
- ErrorResponse,
14
- Result,
15
- RevokeSessionsResponse,
16
- SessionResponse,
17
- SessionsResponse,
18
- SignInInput,
19
- SignInResponse,
20
- SignUpInput,
21
- SlyxupClientOptions,
22
- TOTPSetupResponse,
23
- TwoFactorRequiredResponse,
24
- TwoFactorStatusResponse,
25
- UpdateUserInput,
26
- UserResponse,
27
- } from './types.js';
28
-
29
- const DEFAULT_API_URL = 'https://auth.slyxup.online';
30
-
31
- /** Minimal cookie jar so the SDK works in Node/SSR (browsers manage cookies natively). */
32
- function createCookieJar() {
33
- let jar: string | undefined;
34
- return {
35
- capture(res: Response) {
36
- const setCookie = res.headers.get('set-cookie');
37
- if (setCookie) jar = setCookie.split(';')[0]?.trim();
38
- },
39
- header(): Record<string, string> {
40
- return jar ? { Cookie: jar } : {};
41
- },
42
- clear() {
43
- jar = undefined;
44
- },
45
- };
46
- }
47
-
48
- /**
49
- * SlyxUp Core Client.
50
- *
51
- * ```ts
52
- * const client = new SlyxupClient({ publishableKey: 'pk_test_xxx' });
53
- * await client.auth.signIn({ email: 'a@b.com', password: '12345678' });
54
- * const session = await client.sessions.get();
55
- * ```
56
- */
57
- export class SlyxupClient {
58
- readonly publishableKey?: string;
59
- readonly secretKey?: string;
60
- readonly apiUrl: string;
61
- private _getToken?: () => string | undefined;
62
- private _request?: <T>(path: string, init?: RequestInit) => Promise<T>;
63
-
64
- readonly auth: {
65
- signUp: (input: SignUpInput) => Promise<AuthResponse>;
66
- signIn: (input: SignInInput) => Promise<SignInResponse>;
67
- signOut: () => Promise<{ ok: true }>;
68
- resendVerification: (email: string) => Promise<{ ok: true }>;
69
- forgotPassword: (
70
- email: string,
71
- projectId?: string
72
- ) => Promise<{ ok: true }>;
73
- resetPassword: (token: string, password: string) => Promise<{ ok: true }>;
74
- verifyEmail: (token: string) => Promise<{ ok: true }>;
75
- /** Complete a 2FA challenge returned by signIn. */
76
- completeSignIn: (input: CompleteSignInInput) => Promise<AuthResponse>;
77
- };
78
-
79
- readonly sessions: {
80
- get: () => Promise<SessionResponse>;
81
- list: (opts?: {
82
- limit?: number;
83
- offset?: number;
84
- }) => Promise<SessionsResponse>;
85
- revoke: (sessionId: string) => Promise<{ ok: true }>;
86
- revokeOthers: () => Promise<RevokeSessionsResponse>;
87
- };
88
-
89
- readonly password: {
90
- change: (input: ChangePasswordInput) => Promise<{ ok: true }>;
91
- };
92
-
93
- readonly twoFactor: {
94
- setup: () => Promise<TOTPSetupResponse>;
95
- status: () => Promise<TwoFactorStatusResponse>;
96
- enable: (secret: string, code: string) => Promise<EnableTOTPResponse>;
97
- verify: (code: string) => Promise<{ ok: boolean; valid: boolean }>;
98
- disable: (code: string) => Promise<{ ok: true }>;
99
- };
100
-
101
- readonly accounts: {
102
- list: () => Promise<ConnectedAccountsResponse>;
103
- unlink: (
104
- accountId: string,
105
- provider: 'google' | 'github'
106
- ) => Promise<{ ok: true }>;
107
- };
108
-
109
- readonly users: {
110
- me: () => Promise<UserResponse>;
111
- update: (input: UpdateUserInput) => Promise<UserResponse>;
112
- delete: () => Promise<{ ok: true }>;
113
- };
114
-
115
- readonly admin: {
116
- /** Get current project details */
117
- getProject: () => Promise<{
118
- ok: true;
119
- project: { id: string; name: string; createdAt: string };
120
- }>;
121
- /** Get project statistics */
122
- getStats: () => Promise<{
123
- ok: true;
124
- stats: {
125
- totalUsers: number;
126
- totalSessions: number;
127
- blockedUsers: number;
128
- verifiedUsers: number;
129
- totalKeys: number;
130
- };
131
- }>;
132
- /** List users in the project */
133
- listUsers: (opts?: { limit?: number; offset?: number }) => Promise<{
134
- ok: true;
135
- users: Array<{
136
- id: string;
137
- email: string;
138
- firstName: string | null;
139
- lastName: string | null;
140
- emailVerified: boolean;
141
- blocked: boolean;
142
- blockedReason: string | null;
143
- role: string;
144
- createdAt: string;
145
- }>;
146
- total: number;
147
- }>;
148
- /** Get a single user by ID */
149
- getUser: (userId: string) => Promise<{
150
- ok: true;
151
- user: {
152
- id: string;
153
- email: string;
154
- firstName: string | null;
155
- lastName: string | null;
156
- emailVerified: boolean;
157
- blocked: boolean;
158
- blockedReason: string | null;
159
- role: string;
160
- twoFactorEnabled: boolean;
161
- createdAt: string;
162
- updatedAt: string;
163
- };
164
- }>;
165
- /** Block a user and revoke all their sessions */
166
- blockUser: (userId: string, reason?: string) => Promise<{ ok: true }>;
167
- /** Unblock a user */
168
- unblockUser: (userId: string) => Promise<{ ok: true }>;
169
- /** Delete a user and all their sessions */
170
- deleteUser: (userId: string) => Promise<{ ok: true }>;
171
- /** List sessions (paginated) */
172
- listSessions: (opts?: {
173
- limit?: number;
174
- offset?: number;
175
- }) => Promise<{
176
- ok: true;
177
- sessions: Array<{
178
- id: string;
179
- userId: string;
180
- ipAddress: string | null;
181
- userAgent: string | null;
182
- expiresAt: string;
183
- isExpired: boolean;
184
- createdAt: string;
185
- }>;
186
- total: number;
187
- }>;
188
- /** Revoke a specific session */
189
- revokeSession: (sessionId: string) => Promise<{ ok: true }>;
190
- /** Revoke all sessions for a user */
191
- revokeAllSessions: (userId: string) => Promise<{ ok: true }>;
192
- /** List API keys */
193
- listKeys: () => Promise<{
194
- ok: true;
195
- keys: Array<{
196
- id: string;
197
- name: string;
198
- prefix: string;
199
- environment: string;
200
- type: string;
201
- lastUsedAt: string | null;
202
- createdAt: string;
203
- }>;
204
- }>;
205
- /** Create an API key (full key returned only once) */
206
- createKey: (input: {
207
- name: string;
208
- type: 'publishable' | 'secret';
209
- environment: 'test' | 'live';
210
- }) => Promise<{ ok: true; id: string; key: string; prefix: string }>;
211
- /** Revoke an API key */
212
- revokeKey: (keyId: string) => Promise<{ ok: true }>;
213
- /** List audit logs */
214
- listAuditLogs: (opts?: {
215
- action?: string;
216
- userId?: string;
217
- limit?: number;
218
- offset?: number;
219
- }) => Promise<{
220
- ok: true;
221
- total: number;
222
- logs: Array<{
223
- id: string;
224
- action: string;
225
- userId: string | null;
226
- metadata: Record<string, unknown> | null;
227
- ipAddress: string | null;
228
- userAgent: string | null;
229
- createdAt: string;
230
- }>;
231
- }>;
232
- };
233
-
234
- /** Get current session token (for custom project APIs that need Bearer) */
235
- getToken(): string | undefined {
236
- return this._getToken?.();
237
- }
238
- /** Raw request for custom endpoints (uses same auth headers + cookies as SDK) */
239
- async request<T>(path: string, init?: RequestInit): Promise<T> {
240
- if (!this._request) throw new Error('Client not initialized');
241
- return this._request<T>(path, init);
242
- }
243
-
244
- constructor(options: SlyxupClientOptions = {}) {
245
- const jar = createCookieJar();
246
- // Persist token in localStorage so refresh keeps session for cross-origin (auth -> billing) and for dashboard project APIs
247
- const STORAGE_KEY = 'slyxup_session_token';
248
- let storedToken: string | undefined;
249
- try {
250
- if (typeof window !== 'undefined' && window.localStorage) {
251
- storedToken = window.localStorage.getItem(STORAGE_KEY) ?? undefined;
252
- }
253
- } catch {}
254
- const persistToken = (t: string | undefined) => {
255
- storedToken = t;
256
- try {
257
- if (typeof window !== 'undefined' && window.localStorage) {
258
- if (t) window.localStorage.setItem(STORAGE_KEY, t);
259
- else window.localStorage.removeItem(STORAGE_KEY);
260
- }
261
- } catch {}
262
- };
263
- this.publishableKey = options.publishableKey;
264
- this.secretKey = options.secretKey;
265
- this.apiUrl = (options.apiUrl ?? DEFAULT_API_URL).replace(/\/$/, '');
266
- this._getToken = () => storedToken;
267
- // _request will be assigned after `request` is defined below
268
-
269
- const requestInner = async <T>(
270
- path: string,
271
- init: RequestInit & { body?: string } = {},
272
- opts?: { captureError?: boolean }
273
- ): Promise<Result<T>> => {
274
- let res: Response;
275
- try {
276
- // Build headers: prefer cookie jar (SSR), fall back to stored Bearer token (browser cross-origin)
277
- const authHeaders: Record<string, string> = {};
278
- const jarHeader = jar.header();
279
- if (Object.keys(jarHeader).length > 0) {
280
- Object.assign(authHeaders, jarHeader);
281
- } else if (storedToken) {
282
- authHeaders.Authorization = `Bearer ${storedToken}`;
283
- }
284
- // Always send publishable key so server can scope auth to the correct project
285
- // and reject requests with invalid / missing keys (fixes demo-with-wrong-pk bug).
286
- if (this.publishableKey && this.publishableKey !== 'pk_test_missing') {
287
- authHeaders['X-Publishable-Key'] = this.publishableKey;
288
- }
289
-
290
- res = await fetch(`${this.apiUrl}${path}`, {
291
- ...init,
292
- headers: {
293
- 'Content-Type': 'application/json',
294
- ...authHeaders,
295
- ...init.headers,
296
- },
297
- credentials: 'include',
298
- });
299
- } catch {
300
- throw new NetworkError();
301
- }
302
- jar.capture(res);
303
-
304
- const data = (await res
305
- .json()
306
- .catch(() => ({ ok: false, error: 'Invalid response' }))) as object;
307
-
308
- if (!res.ok) {
309
- if (opts?.captureError) return data as Result<T>;
310
- if (res.status === 401)
311
- throw new UnauthorizedError(
312
- 'error' in data ? String(data.error) : undefined
313
- );
314
- if (res.status === 429) throw new RateLimitError();
315
- throw new SlyxupError(
316
- 'error' in data
317
- ? String(data.error)
318
- : `Request failed (${res.status})`,
319
- res.status,
320
- 'api_error'
321
- );
322
- }
323
-
324
- return data as Result<T>;
325
- };
326
-
327
- const post = <T>(
328
- path: string,
329
- body?: unknown,
330
- opts?: { captureError?: boolean }
331
- ) =>
332
- requestInner<T>(
333
- path,
334
- {
335
- method: 'POST',
336
- body: body === undefined ? undefined : JSON.stringify(body),
337
- },
338
- opts
339
- );
340
-
341
- this.auth = {
342
- signUp: async (input) => {
343
- const res = await post<AuthResponse>('/v1/auth/sign-up', input);
344
- if (!('user' in res))
345
- throw new SlyxupError(res.error, 400, 'api_error');
346
- if (res.sessionToken) persistToken(res.sessionToken);
347
- return res;
348
- },
349
- signIn: async (input) => {
350
- // The server may answer 403 with code 2FA_REQUIRED — we must surface
351
- // the challenge back to the caller instead of throwing a generic error.
352
- const res = await post<SignInResponse>('/v1/auth/sign-in', input, {
353
- captureError: true,
354
- });
355
- if ('challengeToken' in res) {
356
- return res as TwoFactorRequiredResponse;
357
- }
358
- if (!('user' in res))
359
- throw new UnauthorizedError(
360
- (res as { error?: string }).error ?? 'Sign in failed'
361
- );
362
- if (res.sessionToken) persistToken(res.sessionToken);
363
- return res;
364
- },
365
- completeSignIn: async (input) => {
366
- const res = await post<AuthResponse>('/v1/auth/sign-in/2fa', input);
367
- if (!('user' in res)) throw new UnauthorizedError(res.error);
368
- if (res.sessionToken) persistToken(res.sessionToken);
369
- return res;
370
- },
371
- signOut: async () => {
372
- const res = await post<{ ok: true }>('/v1/auth/sign-out');
373
- persistToken(undefined);
374
- jar.clear();
375
- return res as { ok: true };
376
- },
377
- resendVerification: async (email: string) => {
378
- const res = await post<{ ok: true }>('/v1/verification/resend', {
379
- email,
380
- });
381
- if (!res.ok) throw new SlyxupError(res.error, 400, 'api_error');
382
- return res as { ok: true };
383
- },
384
- forgotPassword: async (email: string, projectId?: string) => {
385
- const res = await post<{ ok: true }>(
386
- '/v1/verification/password/forgot',
387
- {
388
- email,
389
- projectId,
390
- }
391
- );
392
- if (!res.ok) throw new SlyxupError(res.error, 400, 'api_error');
393
- return res as { ok: true };
394
- },
395
- resetPassword: async (token: string, password: string) => {
396
- const res = await post<{ ok: true }>(
397
- '/v1/verification/password/reset',
398
- {
399
- token,
400
- password,
401
- }
402
- );
403
- if (!res.ok) throw new SlyxupError(res.error, 400, 'api_error');
404
- return res as { ok: true };
405
- },
406
- verifyEmail: async (token: string) => {
407
- const res = await post<{ ok: true }>('/v1/verification/verify', {
408
- token,
409
- });
410
- if (!res.ok) throw new SlyxupError(res.error, 400, 'api_error');
411
- return res as { ok: true };
412
- },
413
- };
414
-
415
- this.sessions = {
416
- get: async () => {
417
- const res = await requestInner<SessionResponse>('/v1/session');
418
- if (!('session' in res)) throw new UnauthorizedError(res.error);
419
- return res;
420
- },
421
- list: async (opts?: { limit?: number; offset?: number }) => {
422
- const params = new URLSearchParams();
423
- if (opts?.limit !== undefined) params.set('limit', String(opts.limit));
424
- if (opts?.offset !== undefined)
425
- params.set('offset', String(opts.offset));
426
- const qs = params.toString();
427
- const res = await requestInner<SessionsResponse>(
428
- `/v1/sessions${qs ? `?${qs}` : ''}`
429
- );
430
- if (!('sessions' in res))
431
- throw new SlyxupError(res.error, 400, 'api_error');
432
- return res;
433
- },
434
- revoke: async (sessionId: string) => {
435
- const res = await requestInner<{ ok: true }>(
436
- `/v1/sessions/${sessionId}`,
437
- {
438
- method: 'DELETE',
439
- }
440
- );
441
- return res as { ok: true };
442
- },
443
- revokeOthers: async () => {
444
- const res = await requestInner<RevokeSessionsResponse>('/v1/sessions', {
445
- method: 'DELETE',
446
- });
447
- if (!('revoked' in res))
448
- throw new SlyxupError(res.error, 400, 'api_error');
449
- return res;
450
- },
451
- };
452
-
453
- this.password = {
454
- change: async (input) => {
455
- const res = await post<{ ok: true }>('/v1/user/password', input);
456
- if (!res.ok) throw new SlyxupError(res.error, 400, 'api_error');
457
- return res as { ok: true };
458
- },
459
- };
460
-
461
- this.twoFactor = {
462
- setup: async () => {
463
- const res = await requestInner<TOTPSetupResponse>('/v1/user/2fa/setup');
464
- if (!('secret' in res))
465
- throw new SlyxupError(res.error, 400, 'api_error');
466
- return res;
467
- },
468
- status: async () => {
469
- const res = await requestInner<TwoFactorStatusResponse>(
470
- '/v1/user/2fa/status'
471
- );
472
- return res as TwoFactorStatusResponse;
473
- },
474
- enable: async (secret, code) => {
475
- const res = await post<EnableTOTPResponse>(
476
- '/v1/user/2fa/enable',
477
- { secret, code },
478
- { captureError: true }
479
- );
480
- if (!('recoveryCodes' in res))
481
- throw new SlyxupError(res.error, 400, 'api_error');
482
- return res;
483
- },
484
- verify: async (code) => {
485
- const res = await post<{ ok: boolean; valid: boolean }>(
486
- '/v1/user/2fa/verify',
487
- { code },
488
- { captureError: true }
489
- );
490
- if (!('valid' in res))
491
- throw new SlyxupError(res.error, 400, 'api_error');
492
- return res as { ok: boolean; valid: boolean };
493
- },
494
- disable: async (code) => {
495
- const res = await post<{ ok: true }>('/v1/user/2fa/disable', { code });
496
- if (!res.ok) throw new SlyxupError(res.error, 400, 'api_error');
497
- return res as { ok: true };
498
- },
499
- };
500
-
501
- this.accounts = {
502
- list: async () => {
503
- const res =
504
- await requestInner<ConnectedAccountsResponse>('/v1/user/accounts');
505
- if (!('accounts' in res))
506
- throw new SlyxupError(res.error, 400, 'api_error');
507
- return res;
508
- },
509
- unlink: async (accountId, provider) => {
510
- const res = await requestInner<{ ok: true }>(
511
- `/v1/user/accounts/${accountId}?provider=${encodeURIComponent(provider)}`,
512
- {
513
- method: 'DELETE',
514
- }
515
- );
516
- return res as { ok: true };
517
- },
518
- };
519
-
520
- this.users = {
521
- me: async () => {
522
- const res = await requestInner<UserResponse>('/v1/user');
523
- if (!('user' in res)) throw new UnauthorizedError(res.error);
524
- return res;
525
- },
526
- update: async (input) => {
527
- const res = await requestInner<UserResponse>('/v1/user', {
528
- method: 'PATCH',
529
- body: JSON.stringify(input),
530
- });
531
- if (!('user' in res))
532
- throw new SlyxupError(res.error, 400, 'api_error');
533
- return res;
534
- },
535
- delete: async () => {
536
- const res = await requestInner<{ ok: true }>('/v1/user', {
537
- method: 'DELETE',
538
- });
539
- return res as { ok: true };
540
- },
541
- };
542
-
543
- // ── Admin API (requires secretKey) ──
544
- const sk = options.secretKey;
545
- const adminRequest = async <T>(
546
- path: string,
547
- init: RequestInit = {}
548
- ): Promise<T> => {
549
- if (!sk)
550
- throw new SlyxupError(
551
- 'Secret key required for admin API',
552
- 400,
553
- 'api_error'
554
- );
555
- const res = await fetch(`${this.apiUrl}${path}`, {
556
- ...init,
557
- headers: {
558
- 'Content-Type': 'application/json',
559
- Authorization: `Bearer ${sk}`,
560
- ...init.headers,
561
- },
562
- });
563
- const data = (await res
564
- .json()
565
- .catch(() => ({ ok: false, error: 'Invalid response' }))) as object;
566
- if (!res.ok) {
567
- throw new SlyxupError(
568
- 'error' in data
569
- ? String((data as { error: unknown }).error)
570
- : `Request failed (${res.status})`,
571
- res.status,
572
- 'api_error'
573
- );
574
- }
575
- return data as T;
576
- };
577
-
578
- this.admin = {
579
- getProject: async () => {
580
- const res = await adminRequest<{
581
- ok: true;
582
- project: { id: string; name: string; createdAt: string };
583
- }>('/v1/admin/project');
584
- return res;
585
- },
586
- getStats: async () => {
587
- const res = await adminRequest<{
588
- ok: true;
589
- stats: {
590
- totalUsers: number;
591
- totalSessions: number;
592
- blockedUsers: number;
593
- verifiedUsers: number;
594
- totalKeys: number;
595
- };
596
- }>('/v1/admin/stats');
597
- return res;
598
- },
599
- listUsers: async (opts) => {
600
- const params = new URLSearchParams();
601
- if (opts?.limit !== undefined) params.set('limit', String(opts.limit));
602
- if (opts?.offset !== undefined)
603
- params.set('offset', String(opts.offset));
604
- const qs = params.toString();
605
- const res = await adminRequest<{
606
- ok: true;
607
- users: Array<{
608
- id: string;
609
- email: string;
610
- firstName: string | null;
611
- lastName: string | null;
612
- emailVerified: boolean;
613
- blocked: boolean;
614
- blockedReason: string | null;
615
- role: string;
616
- createdAt: string;
617
- }>;
618
- total: number;
619
- }>(`/v1/admin/users${qs ? `?${qs}` : ''}`);
620
- return res;
621
- },
622
- getUser: async (userId) => {
623
- const res = await adminRequest<{
624
- ok: true;
625
- user: {
626
- id: string;
627
- email: string;
628
- firstName: string | null;
629
- lastName: string | null;
630
- emailVerified: boolean;
631
- blocked: boolean;
632
- blockedReason: string | null;
633
- role: string;
634
- twoFactorEnabled: boolean;
635
- createdAt: string;
636
- updatedAt: string;
637
- };
638
- }>(`/v1/admin/users/${encodeURIComponent(userId)}`);
639
- return res;
640
- },
641
- blockUser: async (userId, reason) => {
642
- const res = await adminRequest<{ ok: true }>(
643
- `/v1/admin/users/${encodeURIComponent(userId)}/block`,
644
- {
645
- method: 'POST',
646
- body: JSON.stringify({ reason }),
647
- }
648
- );
649
- return res as { ok: true };
650
- },
651
- unblockUser: async (userId) => {
652
- const res = await adminRequest<{ ok: true }>(
653
- `/v1/admin/users/${encodeURIComponent(userId)}/unblock`,
654
- { method: 'POST' }
655
- );
656
- return res as { ok: true };
657
- },
658
- deleteUser: async (userId) => {
659
- const res = await adminRequest<{ ok: true }>(
660
- `/v1/admin/users/${encodeURIComponent(userId)}`,
661
- { method: 'DELETE' }
662
- );
663
- return res as { ok: true };
664
- },
665
- listSessions: async (opts) => {
666
- const params = new URLSearchParams();
667
- if (opts?.limit !== undefined) params.set('limit', String(opts.limit));
668
- if (opts?.offset !== undefined)
669
- params.set('offset', String(opts.offset));
670
- const qs = params.toString();
671
- const res = await adminRequest<{
672
- ok: true;
673
- sessions: Array<{
674
- id: string;
675
- userId: string;
676
- ipAddress: string | null;
677
- userAgent: string | null;
678
- expiresAt: string;
679
- isExpired: boolean;
680
- createdAt: string;
681
- }>;
682
- total: number;
683
- }>(`/v1/admin/sessions${qs ? `?${qs}` : ''}`);
684
- return res;
685
- },
686
- revokeSession: async (sessionId) => {
687
- const res = await adminRequest<{ ok: true }>(
688
- `/v1/admin/sessions/${encodeURIComponent(sessionId)}`,
689
- { method: 'DELETE' }
690
- );
691
- return res as { ok: true };
692
- },
693
- revokeAllSessions: async (userId) => {
694
- const res = await adminRequest<{ ok: true }>(
695
- `/v1/admin/sessions?userId=${encodeURIComponent(userId)}`,
696
- { method: 'DELETE' }
697
- );
698
- return res as { ok: true };
699
- },
700
- listKeys: async () => {
701
- const res = await adminRequest<{
702
- ok: true;
703
- keys: Array<{
704
- id: string;
705
- name: string;
706
- prefix: string;
707
- environment: string;
708
- type: string;
709
- lastUsedAt: string | null;
710
- createdAt: string;
711
- }>;
712
- }>('/v1/admin/keys');
713
- return res;
714
- },
715
- createKey: async (input) => {
716
- const res = await adminRequest<{
717
- ok: true;
718
- id: string;
719
- key: string;
720
- prefix: string;
721
- }>('/v1/admin/keys', { method: 'POST', body: JSON.stringify(input) });
722
- return res;
723
- },
724
- revokeKey: async (keyId) => {
725
- const res = await adminRequest<{ ok: true }>(
726
- `/v1/admin/keys/${encodeURIComponent(keyId)}`,
727
- { method: 'DELETE' }
728
- );
729
- return res as { ok: true };
730
- },
731
- listAuditLogs: async (opts) => {
732
- const params = new URLSearchParams();
733
- if (opts?.action) params.set('action', opts.action);
734
- if (opts?.userId) params.set('userId', opts.userId);
735
- if (opts?.limit !== undefined) params.set('limit', String(opts.limit));
736
- if (opts?.offset !== undefined)
737
- params.set('offset', String(opts.offset));
738
- const qs = params.toString();
739
- const res = await adminRequest<{
740
- ok: true;
741
- total: number;
742
- logs: Array<{
743
- id: string;
744
- action: string;
745
- userId: string | null;
746
- metadata: Record<string, unknown> | null;
747
- ipAddress: string | null;
748
- userAgent: string | null;
749
- createdAt: string;
750
- }>;
751
- }>(`/v1/admin/audit${qs ? `?${qs}` : ''}`);
752
- return res;
753
- },
754
- };
755
-
756
- this._request = requestInner as unknown as <T>(
757
- path: string,
758
- init?: RequestInit
759
- ) => Promise<T>;
760
- }
761
- }
762
-
763
- // Re-exports so consumers can import from '@slyxup/core'
764
- export * from './types.js';
765
- export * from './errors.js';
766
- // test publish after fix