@makolabs/ripple 1.2.4 → 1.2.9

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,487 @@
1
+ /**
2
+ * User Management Remote Functions
3
+ *
4
+ * Complete implementation template for user management remote functions.
5
+ * This follows the same pattern as users.remote.ts and is designed to be
6
+ * used as-is in SvelteKit applications like sharkfin-frontend.
7
+ *
8
+ * Configuration:
9
+ * - Set environment variables: CLERK_SECRET_KEY, ADMIN_API_KEY, PRIVATE_BASE_AUTH_URL, ALLOWED_ORG_ID
10
+ * - Configure CLIENT_ID via ADMIN_CONFIG or env variable (CLIENT_ID)
11
+ * - Adjust permission prefix filter via PERMISSION_PREFIX env variable
12
+ *
13
+ * @see https://svelte.dev/docs/kit/remote-functions
14
+ *
15
+ * Usage in your app:
16
+ * ```svelte
17
+ * <script>
18
+ * import { UserManagement } from '@makolabs/ripple';
19
+ * import * as adapter from '../UserManagement.remote';
20
+ * </script>
21
+ *
22
+ * <UserManagement adapter={adapter} />
23
+ * ```
24
+ */
25
+ import { query, command } from '$app/server';
26
+ import { env } from '$env/dynamic/private';
27
+ /**
28
+ * Configuration
29
+ * Override these values or import from your config file
30
+ */
31
+ // Option 1: Import from your config (recommended)
32
+ // import { ADMIN_CONFIG } from '../../constants/permissions';
33
+ // const CLIENT_ID = ADMIN_CONFIG.CLIENT_ID;
34
+ // Option 2: Use environment variable
35
+ const CLIENT_ID = env.CLIENT_ID || 'sharkfin'; // Default fallback
36
+ // Permission prefix filter - adjust based on your permission structure
37
+ // Example: 'sharkfin:' filters to only sharkfin permissions
38
+ const PERMISSION_PREFIX = env.PERMISSION_PREFIX || 'sharkfin:';
39
+ // Organization ID for adding users to organizations
40
+ const ORGANIZATION_ID = env.ALLOWED_ORG_ID;
41
+ /**
42
+ * Helper: Make authenticated request to Clerk API
43
+ */
44
+ async function makeClerkRequest(endpoint, options = {}) {
45
+ const CLERK_SECRET_KEY = env.CLERK_SECRET_KEY;
46
+ if (!CLERK_SECRET_KEY) {
47
+ throw new Error('CLERK_SECRET_KEY environment variable is required');
48
+ }
49
+ const response = await fetch(`https://api.clerk.com/v1${endpoint}`, {
50
+ ...options,
51
+ headers: {
52
+ Authorization: `Bearer ${CLERK_SECRET_KEY}`,
53
+ 'Content-Type': 'application/json',
54
+ ...options.headers
55
+ }
56
+ });
57
+ if (!response.ok) {
58
+ const errorText = await response.text();
59
+ console.error(`❌ [Clerk API] ${response.status} ${response.statusText} - ${errorText}`);
60
+ // Try to parse error details
61
+ let errorDetails;
62
+ try {
63
+ errorDetails = JSON.parse(errorText);
64
+ }
65
+ catch {
66
+ errorDetails = { message: errorText || `${response.status} ${response.statusText}` };
67
+ }
68
+ // Throw error with status and details for better frontend handling
69
+ const error = new Error(errorDetails.message || `Clerk API request failed: ${response.status} ${response.statusText}`);
70
+ error.status = response.status;
71
+ error.details = errorDetails;
72
+ throw error;
73
+ }
74
+ return response.json();
75
+ }
76
+ /**
77
+ * Helper: Make authenticated request to Admin API
78
+ */
79
+ async function makeAdminRequest(endpoint, options = {}) {
80
+ const ADMIN_API_KEY = env.ADMIN_API_KEY;
81
+ const PRIVATE_BASE_AUTH_URL = env.PRIVATE_BASE_AUTH_URL;
82
+ if (!ADMIN_API_KEY || !PRIVATE_BASE_AUTH_URL) {
83
+ const missing = [];
84
+ if (!ADMIN_API_KEY)
85
+ missing.push('ADMIN_API_KEY');
86
+ if (!PRIVATE_BASE_AUTH_URL)
87
+ missing.push('PRIVATE_BASE_AUTH_URL');
88
+ throw new Error(`Admin API configuration missing: ${missing.join(', ')}`);
89
+ }
90
+ const url = `${PRIVATE_BASE_AUTH_URL}${endpoint}`;
91
+ const response = await fetch(url, {
92
+ ...options,
93
+ headers: {
94
+ 'X-Admin-API-Key': ADMIN_API_KEY,
95
+ 'Content-Type': 'application/json',
96
+ ...options.headers
97
+ }
98
+ });
99
+ if (!response.ok) {
100
+ const errorText = await response.text();
101
+ console.error(`❌ [Admin API] ${response.status} ${response.statusText} - ${errorText}`);
102
+ throw new Error(`Admin API request failed: ${response.status} ${response.statusText} - ${errorText}`);
103
+ }
104
+ return response.json();
105
+ }
106
+ /**
107
+ * Helper: Create admin key with permissions for a user
108
+ *
109
+ * @param userId - The user ID (used as 'sub' in admin API)
110
+ * @param permissions - Array of permission strings
111
+ * @param clientId - Client ID (defaults to CLIENT_ID config)
112
+ */
113
+ async function createUserPermissions(userId, permissions, clientId = CLIENT_ID) {
114
+ console.log(`🔑 [createUserPermissions] Creating admin key for user ${userId}`);
115
+ // Filter permissions by prefix if configured
116
+ const filteredPermissions = PERMISSION_PREFIX
117
+ ? permissions.filter((scope) => scope.startsWith(PERMISSION_PREFIX))
118
+ : permissions;
119
+ if (filteredPermissions.length === 0) {
120
+ console.log(`⚠️ [createUserPermissions] No ${PERMISSION_PREFIX || ''} permissions provided, skipping`);
121
+ return null;
122
+ }
123
+ const createData = await makeAdminRequest('/admin/keys', {
124
+ method: 'POST',
125
+ body: JSON.stringify({
126
+ client_id: clientId,
127
+ sub: userId, // userId is used as 'sub' parameter
128
+ scopes: filteredPermissions
129
+ })
130
+ });
131
+ console.log(`✅ [createUserPermissions] Admin key created successfully for user ${userId}`);
132
+ return createData;
133
+ }
134
+ /**
135
+ * Get users with pagination and sorting
136
+ *
137
+ * Transforms adapter options to Clerk API format
138
+ */
139
+ export const getUsers = query('unchecked', async (options) => {
140
+ console.log('🔍 [getUsers] Fetching users with options:', options);
141
+ try {
142
+ // Transform adapter options to Clerk API format
143
+ const limit = options.pageSize;
144
+ const offset = (options.page - 1) * options.pageSize;
145
+ // Build orderBy string
146
+ let orderBy = '';
147
+ if (options.sortBy) {
148
+ const prefix = options.sortOrder === 'desc' ? '-' : '';
149
+ orderBy = `${prefix}${options.sortBy}`;
150
+ }
151
+ else {
152
+ orderBy = '-created_at'; // Default
153
+ }
154
+ // Build Clerk API parameters
155
+ const params = new URLSearchParams({
156
+ limit: limit.toString(),
157
+ offset: offset.toString(),
158
+ order_by: orderBy
159
+ });
160
+ if (options.query)
161
+ params.append('query', options.query);
162
+ // Fetch users and count in parallel
163
+ const [usersData, countData] = await Promise.all([
164
+ makeClerkRequest(`/users?${params}`),
165
+ makeClerkRequest(`/users/count?${params}`)
166
+ ]);
167
+ console.log(`✅ [getUsers] Fetched ${usersData.length} users, total: ${countData.total_count}`);
168
+ return {
169
+ users: usersData,
170
+ totalUsers: countData.total_count || 0
171
+ };
172
+ }
173
+ catch (error) {
174
+ console.error('❌ [getUsers] Error:', error);
175
+ throw new Error(`Failed to fetch users: ${error instanceof Error ? error.message : 'Unknown error'}`);
176
+ }
177
+ });
178
+ /**
179
+ * Create a new user
180
+ *
181
+ * Creates user in Clerk, optionally adds to organization, and creates admin key with permissions
182
+ */
183
+ export const createUser = command('unchecked', async (userData) => {
184
+ // Transform User to Clerk API format
185
+ const emailAddress = userData.email_addresses?.[0]?.email_address || '';
186
+ if (!emailAddress) {
187
+ throw new Error('Email address is required');
188
+ }
189
+ console.log('📝 [createUser] Creating new user:', emailAddress);
190
+ try {
191
+ // Extract permissions from userData
192
+ const { permissions, ...userDataOnly } = userData;
193
+ // Transform userData to match Clerk Backend API format (snake_case)
194
+ const clerkUserData = {
195
+ first_name: userDataOnly.first_name || '',
196
+ last_name: userDataOnly.last_name || '',
197
+ email_address: [emailAddress], // Clerk expects an array
198
+ ...(userDataOnly.username && { username: userDataOnly.username }),
199
+ ...(userDataOnly.private_metadata && { private_metadata: userDataOnly.private_metadata })
200
+ };
201
+ // Step 1: Create user in Clerk
202
+ console.log('📝 [createUser] Creating user in Clerk...');
203
+ let result = await makeClerkRequest('/users', {
204
+ method: 'POST',
205
+ body: JSON.stringify(clerkUserData)
206
+ });
207
+ // Step 1.5: Add user to organization if configured
208
+ if (ORGANIZATION_ID) {
209
+ try {
210
+ console.log(`🏢 [createUser] Adding user ${result.id} to organization ${ORGANIZATION_ID}...`);
211
+ await makeClerkRequest(`/organizations/${ORGANIZATION_ID}/memberships`, {
212
+ method: 'POST',
213
+ body: JSON.stringify({
214
+ user_id: result.id,
215
+ role: 'org:member' // Standard Clerk organization role
216
+ })
217
+ });
218
+ console.log(`✅ [createUser] User added to organization successfully`);
219
+ }
220
+ catch (orgError) {
221
+ console.error(`❌ [createUser] Failed to add user to organization:`, orgError);
222
+ // Clean up: delete the user since they can't be added to the org
223
+ try {
224
+ await makeClerkRequest(`/users/${result.id}`, {
225
+ method: 'DELETE'
226
+ });
227
+ console.log(`🗑️ [createUser] User ${result.id} deleted due to org membership failure`);
228
+ }
229
+ catch (deleteError) {
230
+ console.error(`❌ [createUser] Failed to delete user after org membership failure:`, deleteError);
231
+ }
232
+ throw new Error(`Failed to add user to organization. User creation rolled back. Error: ${orgError instanceof Error ? orgError.message : String(orgError)}`);
233
+ }
234
+ }
235
+ else {
236
+ console.warn('⚠️ [createUser] ALLOWED_ORG_ID not configured, skipping organization membership');
237
+ }
238
+ // Step 2: Create admin key with permissions if permissions are provided
239
+ if (permissions && permissions.length > 0) {
240
+ try {
241
+ console.log(`🔑 [createUser] Creating permissions for new user: ${result.id}`);
242
+ const adminKeyResult = await createUserPermissions(result.id, permissions);
243
+ // Extract the API key from the admin service response
244
+ const apiKey = adminKeyResult?.data?.key;
245
+ if (adminKeyResult && apiKey) {
246
+ // Update user's private metadata with the API key
247
+ const updatedUser = await makeClerkRequest(`/users/${result.id}`, {
248
+ method: 'PATCH',
249
+ body: JSON.stringify({
250
+ private_metadata: {
251
+ ...result.private_metadata,
252
+ mako_api_key: apiKey
253
+ }
254
+ })
255
+ });
256
+ // Update the result with the updated user data
257
+ result = updatedUser;
258
+ console.log(`✅ [createUser] API key stored in user's private metadata`);
259
+ }
260
+ else {
261
+ console.warn(`⚠️ [createUser] No API key found in admin key result`);
262
+ }
263
+ // Add permission info to result
264
+ result.adminKey = adminKeyResult;
265
+ result.permissionsAssigned = permissions;
266
+ console.log(`✅ [createUser] User ${result.id} created with permissions successfully`);
267
+ }
268
+ catch (permissionError) {
269
+ console.error(`❌ [createUser] Failed to assign permissions:`, permissionError);
270
+ // Don't fail the entire operation, but warn
271
+ result.warning = 'User created but permissions assignment failed';
272
+ result.permissionError =
273
+ permissionError instanceof Error ? permissionError.message : String(permissionError);
274
+ }
275
+ }
276
+ else {
277
+ console.log(`⚠️ [createUser] User ${result.id} created without permissions`);
278
+ }
279
+ return result;
280
+ }
281
+ catch (error) {
282
+ console.error('❌ [createUser] Error:', error);
283
+ // Handle Clerk API errors with detailed information
284
+ if (error && typeof error === 'object' && 'status' in error && 'details' in error) {
285
+ const enrichedError = new Error('message' in error && typeof error.message === 'string' ? error.message : 'Unknown error');
286
+ enrichedError.status = error.status;
287
+ enrichedError.details = error.details;
288
+ enrichedError.clerkError = true;
289
+ throw enrichedError;
290
+ }
291
+ throw new Error(`Failed to create user: ${error instanceof Error ? error.message : 'Unknown error'}`);
292
+ }
293
+ });
294
+ /**
295
+ * Update an existing user
296
+ */
297
+ export const updateUser = command('unchecked', async (options) => {
298
+ const { userId, userData } = options;
299
+ console.log(`📝 [updateUser] Updating user ${userId}`);
300
+ try {
301
+ // Transform User to Clerk API format
302
+ // Only include fields that Clerk accepts
303
+ const updateData = {};
304
+ if (userData.first_name !== undefined)
305
+ updateData.first_name = userData.first_name;
306
+ if (userData.last_name !== undefined)
307
+ updateData.last_name = userData.last_name;
308
+ if (userData.username !== undefined && userData.username !== '') {
309
+ // Only include username if it's not empty (prevents "username already exists" error)
310
+ updateData.username = userData.username;
311
+ }
312
+ if (userData.private_metadata !== undefined) {
313
+ updateData.private_metadata = userData.private_metadata;
314
+ }
315
+ const result = await makeClerkRequest(`/users/${userId}`, {
316
+ method: 'PATCH',
317
+ body: JSON.stringify(updateData)
318
+ });
319
+ // If permissions changed, update them separately
320
+ if (userData.permissions !== undefined) {
321
+ try {
322
+ await updateUserPermissions({ userId, permissions: userData.permissions });
323
+ }
324
+ catch (permError) {
325
+ console.error(`❌ [updateUser] Failed to update permissions:`, permError);
326
+ // Don't fail the entire operation
327
+ }
328
+ }
329
+ console.log(`✅ [updateUser] User ${userId} updated successfully`);
330
+ return result;
331
+ }
332
+ catch (error) {
333
+ console.error('❌ [updateUser] Error:', error);
334
+ // Handle Clerk API errors with detailed information
335
+ if (error && typeof error === 'object' && 'status' in error && 'details' in error) {
336
+ const enrichedError = new Error('message' in error && typeof error.message === 'string' ? error.message : 'Unknown error');
337
+ enrichedError.status = error.status;
338
+ enrichedError.details = error.details;
339
+ enrichedError.clerkError = true;
340
+ throw enrichedError;
341
+ }
342
+ throw new Error(`Failed to update user: ${error instanceof Error ? error.message : 'Unknown error'}`);
343
+ }
344
+ });
345
+ /**
346
+ * Delete a single user
347
+ */
348
+ export const deleteUser = command('unchecked', async (userId) => {
349
+ console.log(`🗑️ [deleteUser] Deleting user ${userId}`);
350
+ try {
351
+ await makeClerkRequest(`/users/${userId}`, {
352
+ method: 'DELETE'
353
+ });
354
+ console.log(`✅ [deleteUser] User ${userId} deleted successfully`);
355
+ }
356
+ catch (error) {
357
+ console.error('❌ [deleteUser] Error:', error);
358
+ throw new Error(`Failed to delete user: ${error instanceof Error ? error.message : 'Unknown error'}`);
359
+ }
360
+ });
361
+ /**
362
+ * Delete multiple users
363
+ */
364
+ export const deleteUsers = command('unchecked', async (userIds) => {
365
+ console.log(`🗑️ [deleteUsers] Deleting ${userIds.length} users`);
366
+ try {
367
+ await Promise.all(userIds.map((userId) => makeClerkRequest(`/users/${userId}`, {
368
+ method: 'DELETE'
369
+ })));
370
+ console.log(`✅ [deleteUsers] ${userIds.length} users deleted successfully`);
371
+ }
372
+ catch (error) {
373
+ console.error('❌ [deleteUsers] Error:', error);
374
+ throw new Error(`Failed to delete users: ${error instanceof Error ? error.message : 'Unknown error'}`);
375
+ }
376
+ });
377
+ /**
378
+ * Get permissions for a specific user
379
+ *
380
+ * Uses 'sub' (userId) parameter in admin API calls
381
+ */
382
+ export const getUserPermissions = query('unchecked', async (userId) => {
383
+ console.log(`🔍 [getUserPermissions] Fetching permissions for user: ${userId}`);
384
+ try {
385
+ // Try direct lookup first using client_id and sub (userId)
386
+ try {
387
+ const userData = await makeAdminRequest(`/admin/keys?client_id=${CLIENT_ID}&sub=${userId}`);
388
+ console.log(`✅ [getUserPermissions] Direct lookup successful for user ${userId}`);
389
+ // Filter the response to only include active keys
390
+ if (userData?.data?.data && Array.isArray(userData.data.data)) {
391
+ userData.data.data = userData.data.data.filter((key) => key.status === 'active');
392
+ console.log(`🔍 [getUserPermissions] Filtered to ${userData.data.data.length} active key(s)`);
393
+ }
394
+ // Extract scopes from the response
395
+ if (userData?.data?.data && Array.isArray(userData.data.data)) {
396
+ return userData.data.data.flatMap((key) => key.scopes || []);
397
+ }
398
+ else if (userData?.scopes) {
399
+ return Array.isArray(userData.scopes) ? userData.scopes : [userData.scopes];
400
+ }
401
+ return [];
402
+ }
403
+ catch {
404
+ console.log(`❌ [getUserPermissions] Direct lookup failed, trying search by sub field`);
405
+ // If direct lookup fails with 404, search all keys by sub field
406
+ try {
407
+ const allKeysData = await makeAdminRequest('/admin/keys');
408
+ console.log(`🔍 [getUserPermissions] Searching through ${allKeysData.data?.data?.length || 0} keys`);
409
+ // Find the ACTIVE key for this user (ignore revoked keys)
410
+ // Match by sub (userId) and client_id
411
+ const userKey = allKeysData.data.data.find((key) => key.sub === userId && key.client_id === CLIENT_ID && key.status === 'active');
412
+ if (userKey) {
413
+ console.log(`✅ [getUserPermissions] Found active user key by sub field`);
414
+ return Array.isArray(userKey.scopes) ? userKey.scopes : [userKey.scopes];
415
+ }
416
+ else {
417
+ console.log(`❌ [getUserPermissions] No user found, returning empty permissions`);
418
+ return [];
419
+ }
420
+ }
421
+ catch (searchError) {
422
+ console.error('❌ [getUserPermissions] Error searching for user by sub:', searchError);
423
+ throw new Error('Failed to fetch user permissions');
424
+ }
425
+ }
426
+ }
427
+ catch (error) {
428
+ console.error('❌ [getUserPermissions] Error:', error);
429
+ throw new Error(`Failed to fetch user permissions: ${error instanceof Error ? error.message : 'Unknown error'}`);
430
+ }
431
+ });
432
+ /**
433
+ * Update permissions for a specific user
434
+ *
435
+ * Uses 'sub' (userId) parameter in admin API calls
436
+ */
437
+ export const updateUserPermissions = command('unchecked', async (options) => {
438
+ const { userId, permissions } = options;
439
+ console.log(`🔑 [updateUserPermissions] Updating permissions for user ${userId}`);
440
+ try {
441
+ let adminKeyId = userId; // Use userId as default key ID
442
+ // Try direct update first
443
+ try {
444
+ await makeAdminRequest(`/admin/keys/${adminKeyId}`, {
445
+ method: 'PUT',
446
+ body: JSON.stringify({ scopes: permissions })
447
+ });
448
+ console.log(`✅ [updateUserPermissions] Permissions updated successfully`);
449
+ return;
450
+ }
451
+ catch {
452
+ // If direct update fails, try to find the correct adminKeyId
453
+ console.log(`⚠️ [updateUserPermissions] Direct update failed, searching for key ID...`);
454
+ try {
455
+ const allKeysData = await makeAdminRequest('/admin/keys');
456
+ // Find the ACTIVE key for this user (ignore revoked keys)
457
+ // Match by sub (userId) and client_id
458
+ const userKey = allKeysData.data.data.find((key) => key.sub === userId && key.client_id === CLIENT_ID && key.status === 'active');
459
+ if (userKey) {
460
+ // Use the found key ID for update
461
+ adminKeyId = userKey.id;
462
+ await makeAdminRequest(`/admin/keys/${adminKeyId}`, {
463
+ method: 'PUT',
464
+ body: JSON.stringify({ scopes: permissions })
465
+ });
466
+ console.log(`✅ [updateUserPermissions] Permissions updated successfully (after search)`);
467
+ return;
468
+ }
469
+ else {
470
+ // User doesn't exist, create new admin key
471
+ console.log(`📝 [updateUserPermissions] Creating new admin key...`);
472
+ await createUserPermissions(userId, permissions);
473
+ console.log(`✅ [updateUserPermissions] New admin key created successfully`);
474
+ return;
475
+ }
476
+ }
477
+ catch (searchError) {
478
+ console.error('❌ [updateUserPermissions] Error during permission update:', searchError);
479
+ throw new Error('Failed to update permissions');
480
+ }
481
+ }
482
+ }
483
+ catch (error) {
484
+ console.error('❌ [updateUserPermissions] Error:', error);
485
+ throw new Error(`Failed to update user permissions: ${error instanceof Error ? error.message : 'Unknown error'}`);
486
+ }
487
+ });
@@ -0,0 +1,10 @@
1
+ /**
2
+ * User Management Adapters
3
+ *
4
+ * Adapters are remote function modules (.remote.ts files) that export
5
+ * query/command functions.
6
+ *
7
+ * @see https://svelte.dev/docs/kit/remote-functions
8
+ */
9
+ export type { GetUsersOptions, GetUsersResult } from './types.js';
10
+ export * from './mockUserManagement.js';
@@ -0,0 +1,12 @@
1
+ /**
2
+ * User Management Adapters
3
+ *
4
+ * Adapters are remote function modules (.remote.ts files) that export
5
+ * query/command functions.
6
+ *
7
+ * @see https://svelte.dev/docs/kit/remote-functions
8
+ */
9
+ // Export mock adapter functions for testing/storybook
10
+ export * from './mockUserManagement.js';
11
+ // Note: UserManagement.remote.ts is a template file showing how to implement
12
+ // remote functions in your SvelteKit app. It is not exported from this library.
@@ -0,0 +1,70 @@
1
+ /**
2
+ * Mock User Management Functions
3
+ *
4
+ * Mock implementation of user management functions for testing and development.
5
+ * These are regular async functions that match the UserManagementAdapter interface.
6
+ * They store data in memory.
7
+ *
8
+ * Note: This file uses .remote.ts extension for naming consistency, but these are
9
+ * NOT actual SvelteKit remote functions. They are regular async functions that
10
+ * work in both test and library contexts.
11
+ *
12
+ * For actual remote functions in your app, see UserManagement.remote.ts template.
13
+ *
14
+ * To set initial data, use the resetState function:
15
+ * ```ts
16
+ * import { resetState } from './mockUserManagement.js';
17
+ * await resetState({ initialUsers: [...], simulateDelay: false });
18
+ * ```
19
+ */
20
+ import type { User } from '../user-management.js';
21
+ import type { GetUsersOptions, GetUsersResult } from './types.js';
22
+ /**
23
+ * Reset mock adapter state
24
+ */
25
+ export declare function resetState(options?: {
26
+ initialUsers?: User[];
27
+ simulateDelay?: boolean;
28
+ delayMs?: number;
29
+ }): Promise<void>;
30
+ /**
31
+ * Get users with pagination and sorting
32
+ * Matches UserManagementAdapter.getUsers signature
33
+ */
34
+ export declare function getUsers(options: GetUsersOptions): Promise<GetUsersResult>;
35
+ /**
36
+ * Create a new user
37
+ * Matches UserManagementAdapter.createUser signature
38
+ */
39
+ export declare function createUser(userData: Partial<User>): Promise<User>;
40
+ /**
41
+ * Update an existing user
42
+ * Matches UserManagementAdapter.updateUser signature
43
+ */
44
+ export declare function updateUser(options: {
45
+ userId: string;
46
+ userData: Partial<User>;
47
+ }): Promise<User>;
48
+ /**
49
+ * Delete a single user
50
+ * Matches UserManagementAdapter.deleteUser signature
51
+ */
52
+ export declare function deleteUser(userId: string): Promise<void>;
53
+ /**
54
+ * Delete multiple users
55
+ * Matches UserManagementAdapter.deleteUsers signature
56
+ */
57
+ export declare function deleteUsers(userIds: string[]): Promise<void>;
58
+ /**
59
+ * Get permissions for a specific user
60
+ * Matches UserManagementAdapter.getUserPermissions signature
61
+ */
62
+ export declare function getUserPermissions(userId: string): Promise<string[]>;
63
+ /**
64
+ * Update permissions for a specific user
65
+ * Matches UserManagementAdapter.updateUserPermissions signature
66
+ */
67
+ export declare function updateUserPermissions(options: {
68
+ userId: string;
69
+ permissions: string[];
70
+ }): Promise<void>;