@oxyhq/core 1.8.0 → 1.9.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/dist/cjs/.tsbuildinfo +1 -0
- package/dist/cjs/HttpService.js +8 -55
- package/dist/cjs/crypto/signatureService.js +1 -1
- package/dist/cjs/index.js +8 -1
- package/dist/cjs/mixins/OxyServices.assets.js +13 -15
- package/dist/cjs/utils/accountUtils.js +49 -0
- package/dist/cjs/utils/avatarUtils.js +28 -0
- package/dist/esm/.tsbuildinfo +1 -0
- package/dist/esm/HttpService.js +8 -55
- package/dist/esm/crypto/signatureService.js +1 -1
- package/dist/esm/index.js +4 -0
- package/dist/esm/mixins/OxyServices.assets.js +13 -15
- package/dist/esm/utils/accountUtils.js +44 -0
- package/dist/esm/utils/avatarUtils.js +25 -0
- package/dist/types/.tsbuildinfo +1 -0
- package/dist/types/HttpService.d.ts +5 -57
- package/dist/types/OxyServices.d.ts +1 -0
- package/dist/types/index.d.ts +3 -0
- package/dist/types/mixins/OxyServices.assets.d.ts +12 -3
- package/dist/types/mixins/OxyServices.user.d.ts +8 -1
- package/dist/types/models/interfaces.d.ts +9 -0
- package/dist/types/utils/accountUtils.d.ts +36 -0
- package/dist/types/utils/avatarUtils.d.ts +16 -0
- package/package.json +2 -2
- package/src/HttpService.ts +13 -60
- package/src/OxyServices.ts +3 -0
- package/src/crypto/signatureService.ts +2 -2
- package/src/index.ts +7 -0
- package/src/mixins/OxyServices.assets.ts +16 -17
- package/src/mixins/OxyServices.user.ts +4 -1
- package/src/models/interfaces.ts +11 -0
- package/src/types/expo-crypto.d.ts +16 -0
- package/src/types/expo-secure-store.d.ts +17 -0
- package/src/utils/accountUtils.ts +69 -0
- package/src/utils/avatarUtils.ts +37 -0
package/src/OxyServices.ts
CHANGED
|
@@ -141,6 +141,9 @@ export interface OxyServices extends InstanceType<ReturnType<typeof composeOxySe
|
|
|
141
141
|
authSocket(options?: {
|
|
142
142
|
debug?: boolean;
|
|
143
143
|
}): (socket: any, next: (err?: Error) => void) => Promise<void>;
|
|
144
|
+
|
|
145
|
+
// Asset management
|
|
146
|
+
assetUpdateVisibility(fileId: string, visibility: 'private' | 'public' | 'unlisted'): Promise<unknown>;
|
|
144
147
|
}
|
|
145
148
|
|
|
146
149
|
// Re-export error classes for convenience
|
|
@@ -80,8 +80,8 @@ export class SignatureService {
|
|
|
80
80
|
if (isReactNative()) {
|
|
81
81
|
const Crypto = await initExpoCrypto();
|
|
82
82
|
const randomBytes = await Crypto.getRandomBytesAsync(32);
|
|
83
|
-
return Array.from(randomBytes)
|
|
84
|
-
.map((b
|
|
83
|
+
return Array.from(new Uint8Array(randomBytes))
|
|
84
|
+
.map((b) => b.toString(16).padStart(2, '0'))
|
|
85
85
|
.join('');
|
|
86
86
|
}
|
|
87
87
|
|
package/src/index.ts
CHANGED
|
@@ -163,6 +163,13 @@ export {
|
|
|
163
163
|
} from './utils/loggerUtils';
|
|
164
164
|
export type { LogContext } from './utils/loggerUtils';
|
|
165
165
|
|
|
166
|
+
// --- Avatar Utilities ---
|
|
167
|
+
export { updateAvatarVisibility } from './utils/avatarUtils';
|
|
168
|
+
|
|
169
|
+
// --- Account Utilities ---
|
|
170
|
+
export { buildAccountsArray, createQuickAccount } from './utils/accountUtils';
|
|
171
|
+
export type { QuickAccount } from './utils/accountUtils';
|
|
172
|
+
|
|
166
173
|
// Default export
|
|
167
174
|
import { OxyServices } from './OxyServices';
|
|
168
175
|
export default OxyServices;
|
|
@@ -160,28 +160,27 @@ export function OxyServicesAssetsMixin<T extends typeof OxyServicesBase>(Base: T
|
|
|
160
160
|
}
|
|
161
161
|
|
|
162
162
|
/**
|
|
163
|
-
* Upload file using Central Asset Service
|
|
163
|
+
* Upload file using Central Asset Service.
|
|
164
|
+
*
|
|
165
|
+
* Accepts either a web File/Blob or a React Native file descriptor
|
|
166
|
+
* ({uri, type, name, size}). RN descriptors are passed directly to
|
|
167
|
+
* FormData.append, which handles them natively.
|
|
164
168
|
*/
|
|
165
|
-
async assetUpload(file: File, visibility?: 'private' | 'public' | 'unlisted', metadata?: Record<string, any>, onProgress?: (progress: number) => void): Promise<any> {
|
|
166
|
-
const fileName = file.name
|
|
167
|
-
const fileSize = file.size;
|
|
168
|
-
|
|
169
|
+
async assetUpload(file: File | { uri: string; type?: string; name?: string; size?: number }, visibility?: 'private' | 'public' | 'unlisted', metadata?: Record<string, any>, onProgress?: (progress: number) => void): Promise<any> {
|
|
170
|
+
const fileName = 'name' in file && file.name ? file.name : 'unknown';
|
|
171
|
+
const fileSize = 'size' in file && file.size ? file.size : 0;
|
|
172
|
+
|
|
169
173
|
try {
|
|
170
174
|
const formData = new FormData();
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
} else if (typeof (file as any).blob === 'function') {
|
|
178
|
-
// Use async blob() method if available (Expo 54+ recommended approach)
|
|
179
|
-
fileBlob = await (file as any).blob();
|
|
175
|
+
|
|
176
|
+
if ('uri' in file && typeof file.uri === 'string') {
|
|
177
|
+
// React Native file descriptor — RN's FormData handles {uri, type, name} natively
|
|
178
|
+
formData.append('file', file as unknown as Blob, fileName);
|
|
179
|
+
} else if (file instanceof Blob) {
|
|
180
|
+
formData.append('file', file, fileName);
|
|
180
181
|
} else {
|
|
181
|
-
|
|
182
|
-
fileBlob = new Blob([file], { type: (file as any).type || 'application/octet-stream' });
|
|
182
|
+
formData.append('file', new Blob([file as unknown as BlobPart], { type: 'application/octet-stream' }), fileName);
|
|
183
183
|
}
|
|
184
|
-
formData.append('file', fileBlob, fileName);
|
|
185
184
|
if (visibility) {
|
|
186
185
|
formData.append('visibility', visibility);
|
|
187
186
|
}
|
|
@@ -93,8 +93,11 @@ export function OxyServicesUserMixin<T extends typeof OxyServicesBase>(Base: T)
|
|
|
93
93
|
username: string;
|
|
94
94
|
name?: { first?: string; last?: string; full?: string };
|
|
95
95
|
description?: string;
|
|
96
|
+
isFederated?: boolean;
|
|
97
|
+
instance?: string;
|
|
98
|
+
federation?: { actorUri?: string; domain?: string; actorId?: string };
|
|
96
99
|
_count?: { followers: number; following: number };
|
|
97
|
-
[key: string]:
|
|
100
|
+
[key: string]: unknown;
|
|
98
101
|
}>> {
|
|
99
102
|
return this.withAuthRetry(async () => {
|
|
100
103
|
return await this.makeRequest('GET', '/profiles/recommendations', undefined, { cache: true });
|
package/src/models/interfaces.ts
CHANGED
|
@@ -30,6 +30,8 @@ export interface User {
|
|
|
30
30
|
email?: string;
|
|
31
31
|
// Avatar file id (asset id)
|
|
32
32
|
avatar?: string;
|
|
33
|
+
// Named color preset (e.g. 'teal', 'blue', 'purple')
|
|
34
|
+
color?: string;
|
|
33
35
|
// Privacy and security settings
|
|
34
36
|
privacySettings?: {
|
|
35
37
|
[key: string]: unknown;
|
|
@@ -58,6 +60,15 @@ export interface User {
|
|
|
58
60
|
following?: number;
|
|
59
61
|
};
|
|
60
62
|
accountExpiresAfterInactivityDays?: number | null; // Days of inactivity before account expires (null = never expire)
|
|
63
|
+
// Fediverse / ActivityPub support
|
|
64
|
+
type?: 'local' | 'federated';
|
|
65
|
+
isFederated?: boolean;
|
|
66
|
+
instance?: string;
|
|
67
|
+
federation?: {
|
|
68
|
+
actorUri?: string;
|
|
69
|
+
domain?: string;
|
|
70
|
+
actorId?: string;
|
|
71
|
+
};
|
|
61
72
|
[key: string]: unknown;
|
|
62
73
|
}
|
|
63
74
|
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
declare module 'expo-crypto' {
|
|
2
|
+
export enum CryptoDigestAlgorithm {
|
|
3
|
+
SHA256 = 'SHA-256',
|
|
4
|
+
SHA384 = 'SHA-384',
|
|
5
|
+
SHA512 = 'SHA-512',
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export function digestStringAsync(
|
|
9
|
+
algorithm: CryptoDigestAlgorithm,
|
|
10
|
+
data: string,
|
|
11
|
+
): Promise<string>;
|
|
12
|
+
|
|
13
|
+
export function getRandomBytes(byteCount: number): Uint8Array;
|
|
14
|
+
|
|
15
|
+
export function getRandomBytesAsync(byteCount: number): Promise<Uint8Array>;
|
|
16
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
declare module 'expo-secure-store' {
|
|
2
|
+
export interface SecureStoreOptions {
|
|
3
|
+
keychainAccessible?: number;
|
|
4
|
+
keychainAccessGroup?: string;
|
|
5
|
+
keychainService?: string;
|
|
6
|
+
requireAuthentication?: boolean;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export const WHEN_UNLOCKED: number;
|
|
10
|
+
export const AFTER_FIRST_UNLOCK: number;
|
|
11
|
+
export const WHEN_UNLOCKED_THIS_DEVICE_ONLY: number;
|
|
12
|
+
export const AFTER_FIRST_UNLOCK_THIS_DEVICE_ONLY: number;
|
|
13
|
+
|
|
14
|
+
export function getItemAsync(key: string, options?: SecureStoreOptions): Promise<string | null>;
|
|
15
|
+
export function setItemAsync(key: string, value: string, options?: SecureStoreOptions): Promise<void>;
|
|
16
|
+
export function deleteItemAsync(key: string, options?: SecureStoreOptions): Promise<void>;
|
|
17
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared account types and pure helper functions.
|
|
3
|
+
* Used by both @oxyhq/services (React Native) and @oxyhq/auth (Web) account stores.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export interface QuickAccount {
|
|
7
|
+
sessionId: string;
|
|
8
|
+
userId?: string;
|
|
9
|
+
username: string;
|
|
10
|
+
displayName: string;
|
|
11
|
+
avatar?: string;
|
|
12
|
+
avatarUrl?: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Build an ordered array of QuickAccounts from a map and order list.
|
|
17
|
+
*/
|
|
18
|
+
export const buildAccountsArray = (
|
|
19
|
+
accounts: Record<string, QuickAccount>,
|
|
20
|
+
order: string[]
|
|
21
|
+
): QuickAccount[] => {
|
|
22
|
+
const result: QuickAccount[] = [];
|
|
23
|
+
for (const id of order) {
|
|
24
|
+
const account = accounts[id];
|
|
25
|
+
if (account) result.push(account);
|
|
26
|
+
}
|
|
27
|
+
return result;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Create a QuickAccount from user data returned by the API.
|
|
32
|
+
*
|
|
33
|
+
* @param sessionId - Session identifier
|
|
34
|
+
* @param userData - Raw user object from the API
|
|
35
|
+
* @param existingAccount - Previously cached account (to preserve avatarUrl if unchanged)
|
|
36
|
+
* @param getFileDownloadUrl - Function to generate avatar download URL from file ID
|
|
37
|
+
*/
|
|
38
|
+
export const createQuickAccount = (
|
|
39
|
+
sessionId: string,
|
|
40
|
+
userData: {
|
|
41
|
+
name?: { full?: string; first?: string };
|
|
42
|
+
username?: string;
|
|
43
|
+
id?: string;
|
|
44
|
+
_id?: { toString(): string } | string;
|
|
45
|
+
avatar?: string;
|
|
46
|
+
},
|
|
47
|
+
existingAccount?: QuickAccount,
|
|
48
|
+
getFileDownloadUrl?: (fileId: string, variant: string) => string
|
|
49
|
+
): QuickAccount => {
|
|
50
|
+
const displayName = userData.name?.full || userData.name?.first || userData.username || 'Account';
|
|
51
|
+
const userId = userData.id || (typeof userData._id === 'string' ? userData._id : userData._id?.toString());
|
|
52
|
+
|
|
53
|
+
// Preserve existing avatarUrl if avatar hasn't changed (prevents image reload)
|
|
54
|
+
let avatarUrl: string | undefined;
|
|
55
|
+
if (existingAccount && existingAccount.avatar === userData.avatar && existingAccount.avatarUrl) {
|
|
56
|
+
avatarUrl = existingAccount.avatarUrl;
|
|
57
|
+
} else if (userData.avatar && getFileDownloadUrl) {
|
|
58
|
+
avatarUrl = getFileDownloadUrl(userData.avatar, 'thumb');
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return {
|
|
62
|
+
sessionId,
|
|
63
|
+
userId,
|
|
64
|
+
username: userData.username || '',
|
|
65
|
+
displayName,
|
|
66
|
+
avatar: userData.avatar,
|
|
67
|
+
avatarUrl,
|
|
68
|
+
};
|
|
69
|
+
};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Minimal interface for services that can update asset visibility.
|
|
3
|
+
* Kept loose to avoid mixin type-inference issues with the OxyServices class.
|
|
4
|
+
*/
|
|
5
|
+
export interface AssetVisibilityService {
|
|
6
|
+
assetUpdateVisibility(fileId: string, visibility: 'private' | 'public' | 'unlisted'): Promise<unknown>;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Updates file visibility to public for avatar use.
|
|
11
|
+
* Logs non-404 errors to help debug upload issues.
|
|
12
|
+
*
|
|
13
|
+
* @param fileId - The file ID to update visibility for
|
|
14
|
+
* @param oxyServices - OxyServices instance (or any object with assetUpdateVisibility)
|
|
15
|
+
* @param contextName - Context name for error logging
|
|
16
|
+
*/
|
|
17
|
+
export async function updateAvatarVisibility(
|
|
18
|
+
fileId: string | undefined,
|
|
19
|
+
oxyServices: AssetVisibilityService,
|
|
20
|
+
contextName: string = 'AvatarUtils'
|
|
21
|
+
): Promise<void> {
|
|
22
|
+
if (!fileId || fileId.startsWith('temp-')) {
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
try {
|
|
27
|
+
await oxyServices.assetUpdateVisibility(fileId, 'public');
|
|
28
|
+
} catch (visError: unknown) {
|
|
29
|
+
// 404 is expected when asset doesn't exist yet — skip logging
|
|
30
|
+
const status = (visError instanceof Error && 'status' in visError)
|
|
31
|
+
? (visError as Error & { status: number }).status
|
|
32
|
+
: undefined;
|
|
33
|
+
if (status !== 404) {
|
|
34
|
+
console.error(`[${contextName}] Failed to update avatar visibility for ${fileId}:`, visError);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|