@oxy.so/core 4.5.0-oxy-one.1 → 4.5.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 +1 -1
- package/dist/cjs/HttpService.js +63 -19
- package/dist/cjs/api/accounts.js +10 -3
- package/dist/cjs/api/agency.js +3 -1
- package/dist/cjs/api/apps.js +15 -4
- package/dist/cjs/api/assets.js +39 -11
- package/dist/cjs/api/auth.js +28 -8
- package/dist/cjs/api/billing.js +24 -7
- package/dist/cjs/api/civic.js +14 -4
- package/dist/cjs/api/follows.js +9 -2
- package/dist/cjs/api/identity.js +31 -9
- package/dist/cjs/api/nodes.js +3 -1
- package/dist/cjs/api/privacy.js +8 -2
- package/dist/cjs/api/session.js +15 -4
- package/dist/cjs/api/store.js +15 -5
- package/dist/cjs/api/topics.js +10 -3
- package/dist/cjs/api/users.js +16 -4
- package/dist/cjs/boot/sessionColdBoot.js +40 -13
- package/dist/cjs/constants/version.js +6 -6
- package/dist/cjs/crypto/identityMarker.js +3 -1
- package/dist/cjs/crypto/identityProof.js +6 -1
- package/dist/cjs/crypto/keyManager.js +87 -32
- package/dist/cjs/crypto/recoveryPhrase.js +2 -2
- package/dist/cjs/crypto/signatureService.js +3 -4
- package/dist/cjs/i18n/index.js +16 -3
- package/dist/cjs/i18n/locales/ar-SA.js +366 -366
- package/dist/cjs/i18n/locales/ca-ES.js +366 -366
- package/dist/cjs/i18n/locales/de-DE.js +366 -366
- package/dist/cjs/i18n/locales/en-US.js +1994 -1994
- package/dist/cjs/i18n/locales/es-ES.js +1994 -1994
- package/dist/cjs/i18n/locales/fr-FR.js +366 -366
- package/dist/cjs/i18n/locales/it-IT.js +366 -366
- package/dist/cjs/i18n/locales/ja-JP.js +366 -366
- package/dist/cjs/i18n/locales/ko-KR.js +366 -366
- package/dist/cjs/i18n/locales/pt-PT.js +366 -366
- package/dist/cjs/i18n/locales/zh-CN.js +366 -366
- package/dist/cjs/inference/OxyInferenceClient.js +12 -18
- package/dist/cjs/models/interfaces.js +11 -11
- package/dist/cjs/server/OxyServer.js +86 -28
- package/dist/cjs/server/agentAccount.js +6 -2
- package/dist/cjs/server/aliaMachineCredential.js +39 -14
- package/dist/cjs/server/auth.js +5 -4
- package/dist/cjs/server/capabilityTicket.js +5 -5
- package/dist/cjs/server/index.js +2 -1
- package/dist/cjs/server/liveCapabilityTicket.js +4 -1
- package/dist/cjs/server/middleware.js +61 -42
- package/dist/cjs/server/namespaces.js +28 -7
- package/dist/cjs/server/requesterAssertion.js +36 -30
- package/dist/cjs/server/serviceTokenLifetime.js +7 -4
- package/dist/cjs/server/traffic.js +53 -14
- package/dist/cjs/server/trafficNodeHttp.js +33 -8
- package/dist/cjs/server/workloadIdentity.client.js +4 -0
- package/dist/cjs/server/workloadIdentity.js +28 -1
- package/dist/cjs/session/SessionClient.js +54 -17
- package/dist/cjs/session/accountDelegationAccess.js +4 -6
- package/dist/cjs/session/accountDialogController.js +1 -1
- package/dist/cjs/session/authStateStore.js +18 -9
- package/dist/cjs/session/identityPin.js +6 -2
- package/dist/cjs/session/refresh.js +81 -23
- package/dist/cjs/session/sharedDeviceCredential.js +28 -15
- package/dist/cjs/shared/utils/errorUtils.js +1 -1
- package/dist/cjs/shared/utils/networkUtils.js +1 -1
- package/dist/cjs/shared/utils/themeUtils.js +1 -1
- package/dist/cjs/utils/accountCacheSweep.js +3 -1
- package/dist/cjs/utils/accountUtils.js +1 -3
- package/dist/cjs/utils/asyncUtils.js +1 -1
- package/dist/cjs/utils/authHelpers.js +2 -1
- package/dist/cjs/utils/avatarUtils.js +4 -2
- package/dist/cjs/utils/cache.js +1 -1
- package/dist/cjs/utils/cacheKey.js +3 -1
- package/dist/cjs/utils/deviceManager.js +4 -4
- package/dist/cjs/utils/errorUtils.js +6 -4
- package/dist/cjs/utils/languageUtils.js +404 -57
- package/dist/cjs/utils/profileLinks.js +1 -3
- package/dist/cjs/utils/requestUtils.js +7 -1
- package/dist/cjs/utils/userIdentity.js +1 -1
- package/dist/cjs/utils/validationUtils.js +5 -6
- package/dist/esm/HttpService.js +64 -20
- package/dist/esm/api/accounts.js +11 -4
- package/dist/esm/api/agency.js +3 -1
- package/dist/esm/api/apps.js +15 -4
- package/dist/esm/api/assets.js +39 -11
- package/dist/esm/api/auth.js +28 -8
- package/dist/esm/api/billing.js +25 -8
- package/dist/esm/api/civic.js +14 -4
- package/dist/esm/api/follows.js +9 -2
- package/dist/esm/api/identity.js +31 -9
- package/dist/esm/api/nodes.js +3 -1
- package/dist/esm/api/privacy.js +8 -2
- package/dist/esm/api/session.js +15 -4
- package/dist/esm/api/store.js +15 -5
- package/dist/esm/api/topics.js +10 -3
- package/dist/esm/api/users.js +16 -4
- package/dist/esm/boot/sessionColdBoot.js +41 -14
- package/dist/esm/constants/version.js +6 -6
- package/dist/esm/crypto/identityMarker.js +3 -1
- package/dist/esm/crypto/identityProof.js +6 -1
- package/dist/esm/crypto/internal.js +1 -1
- package/dist/esm/crypto/keyManager.js +87 -32
- package/dist/esm/crypto/recoveryPhrase.js +2 -2
- package/dist/esm/crypto/signatureService.js +3 -4
- package/dist/esm/i18n/index.js +16 -3
- package/dist/esm/i18n/locales/ar-SA.js +366 -366
- package/dist/esm/i18n/locales/ca-ES.js +366 -366
- package/dist/esm/i18n/locales/de-DE.js +366 -366
- package/dist/esm/i18n/locales/en-US.js +1994 -1994
- package/dist/esm/i18n/locales/es-ES.js +1994 -1994
- package/dist/esm/i18n/locales/fr-FR.js +366 -366
- package/dist/esm/i18n/locales/it-IT.js +366 -366
- package/dist/esm/i18n/locales/ja-JP.js +366 -366
- package/dist/esm/i18n/locales/ko-KR.js +366 -366
- package/dist/esm/i18n/locales/pt-PT.js +366 -366
- package/dist/esm/i18n/locales/zh-CN.js +366 -366
- package/dist/esm/index.js +1 -1
- package/dist/esm/inference/OxyInferenceClient.js +13 -19
- package/dist/esm/models/interfaces.js +11 -11
- package/dist/esm/server/OxyServer.js +87 -29
- package/dist/esm/server/agentAccount.js +6 -2
- package/dist/esm/server/aliaMachineCredential.js +39 -14
- package/dist/esm/server/auth.js +6 -5
- package/dist/esm/server/capabilityTicket.js +6 -6
- package/dist/esm/server/index.js +5 -5
- package/dist/esm/server/liveCapabilityTicket.js +6 -3
- package/dist/esm/server/middleware.js +62 -43
- package/dist/esm/server/namespaces.js +31 -10
- package/dist/esm/server/requesterAssertion.js +36 -30
- package/dist/esm/server/serviceTokenLifetime.js +7 -4
- package/dist/esm/server/traffic.js +55 -16
- package/dist/esm/server/trafficNodeHttp.js +34 -9
- package/dist/esm/server/userInvalidation.js +1 -1
- package/dist/esm/server/workloadIdentity.client.js +3 -0
- package/dist/esm/server/workloadIdentity.js +27 -1
- package/dist/esm/session/SessionClient.js +54 -17
- package/dist/esm/session/accountDelegationAccess.js +5 -7
- package/dist/esm/session/accountDialogController.js +1 -1
- package/dist/esm/session/authStateStore.js +18 -9
- package/dist/esm/session/identityPin.js +6 -2
- package/dist/esm/session/refresh.js +82 -24
- package/dist/esm/session/sharedDeviceCredential.js +28 -15
- package/dist/esm/shared/utils/errorUtils.js +1 -1
- package/dist/esm/shared/utils/networkUtils.js +1 -1
- package/dist/esm/shared/utils/themeUtils.js +1 -1
- package/dist/esm/utils/accountCacheSweep.js +3 -1
- package/dist/esm/utils/accountUtils.js +1 -3
- package/dist/esm/utils/asyncUtils.js +1 -1
- package/dist/esm/utils/authHelpers.js +2 -1
- package/dist/esm/utils/avatarUtils.js +4 -2
- package/dist/esm/utils/cache.js +1 -1
- package/dist/esm/utils/cacheKey.js +3 -1
- package/dist/esm/utils/deviceManager.js +4 -4
- package/dist/esm/utils/errorUtils.js +6 -4
- package/dist/esm/utils/languageUtils.js +404 -57
- package/dist/esm/utils/profileLinks.js +1 -3
- package/dist/esm/utils/requestUtils.js +7 -1
- package/dist/esm/utils/userIdentity.js +1 -1
- package/dist/esm/utils/validationUtils.js +5 -6
- package/dist/types/HttpService.d.ts +1 -1
- package/dist/types/api/accounts.d.ts +1 -1
- package/dist/types/api/apps.d.ts +4 -4
- package/dist/types/crypto/internal.d.ts +1 -1
- package/dist/types/index.d.ts +4 -4
- package/dist/types/server/index.d.ts +6 -6
- package/dist/types/server/workloadIdentity.client.d.ts +5 -1
- package/dist/types/server/workloadIdentity.d.ts +26 -0
- package/dist/types/session/accountDelegationAccess.d.ts +1 -1
- package/dist/types/session/index.d.ts +1 -1
- package/dist/types/utils/userIdentity.d.ts +1 -1
- package/dist/types/utils/validationUtils.d.ts +4 -5
- package/package.json +2 -3
- package/src/HttpService.ts +183 -53
- package/src/OxyServices.errors.ts +7 -3
- package/src/OxyServices.ts +6 -2
- package/src/api/accounts.ts +76 -26
- package/src/api/agency.ts +24 -9
- package/src/api/appData.ts +12 -2
- package/src/api/apps.ts +81 -31
- package/src/api/assets.ts +113 -43
- package/src/api/auth.ts +145 -77
- package/src/api/billing.ts +112 -65
- package/src/api/civic.ts +61 -19
- package/src/api/contacts.ts +4 -1
- package/src/api/devices.ts +54 -21
- package/src/api/follows.ts +88 -31
- package/src/api/identity.ts +101 -33
- package/src/api/linkedAccounts.ts +26 -8
- package/src/api/nodes.ts +18 -6
- package/src/api/notifications.ts +26 -6
- package/src/api/privacy.ts +42 -8
- package/src/api/reputation.ts +18 -7
- package/src/api/session.ts +23 -5
- package/src/api/store.ts +81 -31
- package/src/api/topics.ts +18 -6
- package/src/api/users.ts +98 -35
- package/src/boot/sessionColdBoot.ts +60 -19
- package/src/civic/payloads.ts +1 -4
- package/src/client/context.ts +6 -1
- package/src/constants/version.ts +7 -6
- package/src/crypto/aead.ts +1 -5
- package/src/crypto/ecdh.ts +1 -4
- package/src/crypto/identityDerivations.ts +5 -1
- package/src/crypto/identityMarker.ts +3 -1
- package/src/crypto/identityProof.ts +6 -1
- package/src/crypto/index.ts +11 -11
- package/src/crypto/internal.ts +6 -1
- package/src/crypto/keyManager.ts +348 -98
- package/src/crypto/recoveryPhrase.ts +14 -9
- package/src/crypto/registrationPow.ts +1 -1
- package/src/crypto/signatureService.ts +14 -6
- package/src/i18n/accountCategoryLabels.ts +1 -4
- package/src/i18n/accountRoleLabels.ts +1 -4
- package/src/i18n/index.ts +25 -7
- package/src/i18n/locales/ar-SA.ts +375 -366
- package/src/i18n/locales/ca-ES.ts +382 -366
- package/src/i18n/locales/de-DE.ts +387 -366
- package/src/i18n/locales/en-US.ts +2034 -1994
- package/src/i18n/locales/es-ES.ts +2043 -1994
- package/src/i18n/locales/fr-FR.ts +387 -366
- package/src/i18n/locales/it-IT.ts +380 -366
- package/src/i18n/locales/ja-JP.ts +380 -366
- package/src/i18n/locales/ko-KR.ts +377 -366
- package/src/i18n/locales/pt-PT.ts +383 -366
- package/src/i18n/locales/zh-CN.ts +373 -366
- package/src/i18n/trustTierLabels.ts +1 -4
- package/src/index.ts +371 -349
- package/src/inference/OxyInferenceClient.ts +784 -780
- package/src/models/interfaces.ts +30 -27
- package/src/server/OxyServer.ts +227 -68
- package/src/server/agentAccount.ts +8 -2
- package/src/server/aliaMachineCredential.ts +57 -16
- package/src/server/auth.ts +9 -5
- package/src/server/capabilityTicket.ts +30 -21
- package/src/server/index.ts +29 -6
- package/src/server/liveCapabilityTicket.ts +31 -9
- package/src/server/middleware.ts +212 -121
- package/src/server/namespaces.ts +247 -94
- package/src/server/rateLimit.ts +2 -4
- package/src/server/requesterAssertion.ts +93 -54
- package/src/server/serviceTokenLifetime.ts +9 -4
- package/src/server/traffic.ts +117 -23
- package/src/server/trafficNodeHttp.ts +61 -14
- package/src/server/userInvalidation.ts +1 -4
- package/src/server/workloadIdentity.client.ts +12 -1
- package/src/server/workloadIdentity.ts +52 -5
- package/src/session/SessionClient.ts +170 -49
- package/src/session/accountDelegationAccess.ts +24 -31
- package/src/session/accountDialogController.ts +59 -14
- package/src/session/accountSwitchTargets.ts +10 -15
- package/src/session/authStateStore.ts +121 -81
- package/src/session/deviceDirectory.ts +1 -3
- package/src/session/identityPin.ts +10 -11
- package/src/session/identitySession.ts +3 -1
- package/src/session/index.ts +87 -82
- package/src/session/refresh.ts +138 -44
- package/src/session/sessionClientHost.ts +1 -3
- package/src/session/sharedDeviceCredential.ts +35 -14
- package/src/session/socketLoader.ts +9 -2
- package/src/shared/utils/errorUtils.ts +5 -2
- package/src/shared/utils/networkUtils.ts +5 -8
- package/src/shared/utils/themeUtils.ts +2 -6
- package/src/types/buffer.d.ts +66 -21
- package/src/types/expo-secure-store.d.ts +5 -1
- package/src/utils/accountCacheSweep.ts +4 -5
- package/src/utils/accountUtils.ts +107 -108
- package/src/utils/apiUtils.ts +3 -3
- package/src/utils/asyncUtils.ts +2 -2
- package/src/utils/authHelpers.ts +10 -5
- package/src/utils/avatarUtils.ts +11 -6
- package/src/utils/cache.ts +9 -10
- package/src/utils/cacheKey.ts +5 -1
- package/src/utils/coldBoot.ts +1 -3
- package/src/utils/commonsDelivery.ts +1 -4
- package/src/utils/deviceManager.ts +15 -14
- package/src/utils/errorUtils.ts +68 -86
- package/src/utils/languageUtils.ts +407 -58
- package/src/utils/oauthPkce.ts +1 -4
- package/src/utils/platform.ts +0 -1
- package/src/utils/profileLinks.ts +2 -5
- package/src/utils/requestUtils.ts +18 -14
- package/src/utils/sessionUtils.ts +34 -33
- package/src/utils/userHandle.ts +3 -1
- package/src/utils/userIdentity.ts +1 -1
- package/src/utils/validationUtils.ts +11 -9
package/src/utils/asyncUtils.ts
CHANGED
|
@@ -88,7 +88,7 @@ export async function retryAsync<T>(
|
|
|
88
88
|
operation: () => Promise<T>,
|
|
89
89
|
maxRetriesOrOptions: number | RetryOptions = 3,
|
|
90
90
|
baseDelayArg = 1000,
|
|
91
|
-
shouldRetryArg?: (error: unknown) => boolean
|
|
91
|
+
shouldRetryArg?: (error: unknown) => boolean,
|
|
92
92
|
): Promise<T> {
|
|
93
93
|
// An options-object overload rather than a fifth positional parameter:
|
|
94
94
|
// `retryOnError` below and other in-tree callers pass positionally, and a
|
|
@@ -163,7 +163,7 @@ export async function retryAsync<T>(
|
|
|
163
163
|
break;
|
|
164
164
|
}
|
|
165
165
|
|
|
166
|
-
await new Promise(resolve => setTimeout(resolve, delay));
|
|
166
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
167
167
|
}
|
|
168
168
|
}
|
|
169
169
|
|
package/src/utils/authHelpers.ts
CHANGED
|
@@ -36,7 +36,7 @@ export class AuthenticationFailedError extends Error {
|
|
|
36
36
|
export async function ensureValidToken(
|
|
37
37
|
oxyServices: OxyServices,
|
|
38
38
|
_activeSessionId: string | null | undefined,
|
|
39
|
-
syncSession?: () => Promise<unknown
|
|
39
|
+
syncSession?: () => Promise<unknown>,
|
|
40
40
|
): Promise<void> {
|
|
41
41
|
if (oxyServices.session.isAuthenticated) {
|
|
42
42
|
return;
|
|
@@ -51,7 +51,10 @@ export async function ensureValidToken(
|
|
|
51
51
|
} catch (syncError) {
|
|
52
52
|
const errorMessage = syncError instanceof Error ? syncError.message : String(syncError);
|
|
53
53
|
|
|
54
|
-
if (
|
|
54
|
+
if (
|
|
55
|
+
errorMessage.includes('AUTH_REQUIRED_OFFLINE_SESSION') ||
|
|
56
|
+
errorMessage.includes('offline')
|
|
57
|
+
) {
|
|
55
58
|
throw new SessionSyncRequiredError();
|
|
56
59
|
}
|
|
57
60
|
|
|
@@ -59,7 +62,9 @@ export async function ensureValidToken(
|
|
|
59
62
|
}
|
|
60
63
|
}
|
|
61
64
|
|
|
62
|
-
throw new SessionSyncRequiredError(
|
|
65
|
+
throw new SessionSyncRequiredError(
|
|
66
|
+
'No active access token is available. Sync the session before calling authenticated APIs.',
|
|
67
|
+
);
|
|
63
68
|
}
|
|
64
69
|
|
|
65
70
|
/**
|
|
@@ -98,7 +103,7 @@ export function isAuthenticationError(error: unknown): boolean {
|
|
|
98
103
|
*/
|
|
99
104
|
export async function withAuthErrorHandling<T>(
|
|
100
105
|
apiCall: () => Promise<T>,
|
|
101
|
-
options?: HandleApiErrorOptions
|
|
106
|
+
options?: HandleApiErrorOptions,
|
|
102
107
|
): Promise<T> {
|
|
103
108
|
try {
|
|
104
109
|
return await apiCall();
|
|
@@ -136,7 +141,7 @@ export async function authenticatedApiCall<T>(
|
|
|
136
141
|
oxyServices: OxyServices,
|
|
137
142
|
activeSessionId: string | null | undefined,
|
|
138
143
|
apiCall: () => Promise<T>,
|
|
139
|
-
syncSession?: () => Promise<unknown
|
|
144
|
+
syncSession?: () => Promise<unknown>,
|
|
140
145
|
): Promise<T> {
|
|
141
146
|
await ensureValidToken(oxyServices, activeSessionId, syncSession);
|
|
142
147
|
|
package/src/utils/avatarUtils.ts
CHANGED
|
@@ -2,7 +2,9 @@ import { logger } from '../logger';
|
|
|
2
2
|
|
|
3
3
|
/** What `updateAvatarVisibility` needs of a client: `oxy.assets.setVisibility`. */
|
|
4
4
|
export interface AssetVisibilityService {
|
|
5
|
-
assets: {
|
|
5
|
+
assets: {
|
|
6
|
+
setVisibility(fileId: string, visibility: 'private' | 'public' | 'unlisted'): Promise<unknown>;
|
|
7
|
+
};
|
|
6
8
|
}
|
|
7
9
|
|
|
8
10
|
/**
|
|
@@ -16,7 +18,7 @@ export interface AssetVisibilityService {
|
|
|
16
18
|
export async function updateAvatarVisibility(
|
|
17
19
|
fileId: string | undefined,
|
|
18
20
|
oxyServices: AssetVisibilityService,
|
|
19
|
-
contextName = 'AvatarUtils'
|
|
21
|
+
contextName = 'AvatarUtils',
|
|
20
22
|
): Promise<void> {
|
|
21
23
|
if (!fileId || fileId.startsWith('temp-')) {
|
|
22
24
|
return;
|
|
@@ -26,11 +28,14 @@ export async function updateAvatarVisibility(
|
|
|
26
28
|
await oxyServices.assets.setVisibility(fileId, 'public');
|
|
27
29
|
} catch (visError: unknown) {
|
|
28
30
|
// 404 is expected when asset doesn't exist yet — skip logging
|
|
29
|
-
const status =
|
|
30
|
-
|
|
31
|
-
|
|
31
|
+
const status =
|
|
32
|
+
visError instanceof Error && 'status' in visError
|
|
33
|
+
? (visError as Error & { status: number }).status
|
|
34
|
+
: undefined;
|
|
32
35
|
if (status !== 404) {
|
|
33
|
-
logger.error(`[${contextName}] Failed to update avatar visibility for ${fileId}`, visError, {
|
|
36
|
+
logger.error(`[${contextName}] Failed to update avatar visibility for ${fileId}`, visError, {
|
|
37
|
+
component: contextName,
|
|
38
|
+
});
|
|
34
39
|
}
|
|
35
40
|
}
|
|
36
41
|
}
|
package/src/utils/cache.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Centralized cache utility with TTL support
|
|
3
|
-
*
|
|
3
|
+
*
|
|
4
4
|
* This is a production-ready cache implementation used across the codebase
|
|
5
5
|
* for consistent caching behavior and performance optimization.
|
|
6
6
|
*/
|
|
@@ -26,26 +26,26 @@ export interface CacheStats {
|
|
|
26
26
|
|
|
27
27
|
/**
|
|
28
28
|
* TTL-based cache implementation
|
|
29
|
-
*
|
|
29
|
+
*
|
|
30
30
|
* Features:
|
|
31
31
|
* - Automatic expiration based on TTL
|
|
32
32
|
* - Manual cleanup of expired entries
|
|
33
33
|
* - Statistics tracking (hits, misses, hit rate)
|
|
34
34
|
* - Type-safe generic interface
|
|
35
|
-
*
|
|
35
|
+
*
|
|
36
36
|
* @example
|
|
37
37
|
* ```typescript
|
|
38
38
|
* const cache = new TTLCache<string>(5 * 60 * 1000); // 5 minutes
|
|
39
|
-
*
|
|
39
|
+
*
|
|
40
40
|
* // Set with default TTL
|
|
41
41
|
* cache.set('key', 'value');
|
|
42
|
-
*
|
|
42
|
+
*
|
|
43
43
|
* // Set with custom TTL
|
|
44
44
|
* cache.set('key', 'value', 10 * 60 * 1000); // 10 minutes
|
|
45
|
-
*
|
|
45
|
+
*
|
|
46
46
|
* // Get value
|
|
47
47
|
* const value = cache.get('key');
|
|
48
|
-
*
|
|
48
|
+
*
|
|
49
49
|
* // Get statistics
|
|
50
50
|
* const stats = cache.getStats();
|
|
51
51
|
* ```
|
|
@@ -244,7 +244,7 @@ export class TTLCache<T> {
|
|
|
244
244
|
|
|
245
245
|
/**
|
|
246
246
|
* Create a TTL cache instance (convenience function)
|
|
247
|
-
*
|
|
247
|
+
*
|
|
248
248
|
* @example
|
|
249
249
|
* ```typescript
|
|
250
250
|
* const cache = createCache<string>(5 * 60 * 1000);
|
|
@@ -272,7 +272,7 @@ export function registerCacheForCleanup(cache: TTLCache<unknown>): void {
|
|
|
272
272
|
|
|
273
273
|
/** Empty registered caches need no timer; importing a client must stay inert. */
|
|
274
274
|
function updateCleanupInterval(): void {
|
|
275
|
-
const hasEntries = [...activeCaches].some(cache => cache.size() > 0);
|
|
275
|
+
const hasEntries = [...activeCaches].some((cache) => cache.size() > 0);
|
|
276
276
|
if (!hasEntries) {
|
|
277
277
|
if (cleanupInterval) clearInterval(cleanupInterval);
|
|
278
278
|
cleanupInterval = null;
|
|
@@ -303,4 +303,3 @@ export function stopAllCleanupIntervals(): void {
|
|
|
303
303
|
}
|
|
304
304
|
activeCaches.clear();
|
|
305
305
|
}
|
|
306
|
-
|
package/src/utils/cacheKey.ts
CHANGED
|
@@ -85,5 +85,9 @@ export function computeIdentityTag(accessToken: string | null): string {
|
|
|
85
85
|
const decoded = decodeTokenClaims(accessToken);
|
|
86
86
|
// An undecodable token is still partitioned away from anon and from other
|
|
87
87
|
// tokens via a hash — never silently ANON_IDENTITY.
|
|
88
|
-
return (
|
|
88
|
+
return (
|
|
89
|
+
(decoded?.userId as string | undefined) ||
|
|
90
|
+
(decoded?.id as string | undefined) ||
|
|
91
|
+
`t${fnv1a32(accessToken)}`
|
|
92
|
+
);
|
|
89
93
|
}
|
package/src/utils/coldBoot.ts
CHANGED
|
@@ -141,9 +141,7 @@ export interface RunColdBootOptions<S> {
|
|
|
141
141
|
* - `{ kind: 'skip' }` → continue.
|
|
142
142
|
* 4. After the loop with no winner → `{ kind: 'unauthenticated' }`.
|
|
143
143
|
*/
|
|
144
|
-
export async function runColdBoot<S>(
|
|
145
|
-
options: RunColdBootOptions<S>
|
|
146
|
-
): Promise<ColdBootOutcome<S>> {
|
|
144
|
+
export async function runColdBoot<S>(options: RunColdBootOptions<S>): Promise<ColdBootOutcome<S>> {
|
|
147
145
|
const { steps, onStepError, overallDeadlineMs, onStepDeadline } = options;
|
|
148
146
|
|
|
149
147
|
// Arm the optional overall deadline. The budget is SHARED across the whole
|
|
@@ -108,10 +108,7 @@ export function selectCommonsDelivery(facts: CommonsDeliveryFacts): CommonsDeliv
|
|
|
108
108
|
* `delivered: false` with `targets > 0`, which must NOT park the user on
|
|
109
109
|
* "check your phone".
|
|
110
110
|
*/
|
|
111
|
-
export function pushTargetsFromDelivery(result: {
|
|
112
|
-
delivered: boolean;
|
|
113
|
-
targets: number;
|
|
114
|
-
}): number {
|
|
111
|
+
export function pushTargetsFromDelivery(result: { delivered: boolean; targets: number }): number {
|
|
115
112
|
return result.delivered ? result.targets : 0;
|
|
116
113
|
}
|
|
117
114
|
|
|
@@ -30,14 +30,14 @@ export interface StoredDeviceInfo {
|
|
|
30
30
|
// biome-ignore lint/complexity/noStaticOnlyClass: a public, static-only API surface (`X.method()`) that consumers and tests call and spy on by name.
|
|
31
31
|
export class DeviceManager {
|
|
32
32
|
private static DEVICE_KEY = 'oxy_device_info';
|
|
33
|
-
|
|
33
|
+
|
|
34
34
|
/**
|
|
35
35
|
* Check if we're in React Native environment
|
|
36
36
|
*/
|
|
37
37
|
private static isReactNative(): boolean {
|
|
38
38
|
return typeof navigator !== 'undefined' && navigator.product === 'ReactNative';
|
|
39
39
|
}
|
|
40
|
-
|
|
40
|
+
|
|
41
41
|
/**
|
|
42
42
|
* Get appropriate storage for the platform
|
|
43
43
|
*/
|
|
@@ -67,11 +67,11 @@ export class DeviceManager {
|
|
|
67
67
|
return {
|
|
68
68
|
getItem: async (key: string) => localStorage.getItem(key),
|
|
69
69
|
setItem: async (key: string, value: string) => localStorage.setItem(key, value),
|
|
70
|
-
removeItem: async (key: string) => localStorage.removeItem(key)
|
|
70
|
+
removeItem: async (key: string) => localStorage.removeItem(key),
|
|
71
71
|
};
|
|
72
72
|
}
|
|
73
73
|
}
|
|
74
|
-
|
|
74
|
+
|
|
75
75
|
/**
|
|
76
76
|
* Get or create device fingerprint for current device
|
|
77
77
|
*/
|
|
@@ -80,7 +80,8 @@ export class DeviceManager {
|
|
|
80
80
|
userAgent: typeof navigator !== 'undefined' ? navigator.userAgent : 'unknown',
|
|
81
81
|
platform: typeof navigator !== 'undefined' ? navigator.platform : 'unknown',
|
|
82
82
|
language: typeof navigator !== 'undefined' ? navigator.language : undefined,
|
|
83
|
-
timezone:
|
|
83
|
+
timezone:
|
|
84
|
+
typeof Intl !== 'undefined' ? Intl.DateTimeFormat().resolvedOptions().timeZone : undefined,
|
|
84
85
|
};
|
|
85
86
|
|
|
86
87
|
// Add screen info if available
|
|
@@ -88,7 +89,7 @@ export class DeviceManager {
|
|
|
88
89
|
fingerprint.screen = {
|
|
89
90
|
width: screen.width,
|
|
90
91
|
height: screen.height,
|
|
91
|
-
colorDepth: screen.colorDepth
|
|
92
|
+
colorDepth: screen.colorDepth,
|
|
92
93
|
};
|
|
93
94
|
}
|
|
94
95
|
|
|
@@ -102,17 +103,17 @@ export class DeviceManager {
|
|
|
102
103
|
try {
|
|
103
104
|
const storage = await DeviceManager.getStorage();
|
|
104
105
|
const stored = await storage.getItem(DeviceManager.DEVICE_KEY);
|
|
105
|
-
|
|
106
|
+
|
|
106
107
|
if (stored) {
|
|
107
108
|
const deviceInfo: StoredDeviceInfo = JSON.parse(stored);
|
|
108
|
-
|
|
109
|
+
|
|
109
110
|
// Update last used timestamp
|
|
110
111
|
deviceInfo.lastUsed = new Date().toISOString();
|
|
111
112
|
await DeviceManager.saveDeviceInfo(deviceInfo);
|
|
112
|
-
|
|
113
|
+
|
|
113
114
|
return deviceInfo;
|
|
114
115
|
}
|
|
115
|
-
|
|
116
|
+
|
|
116
117
|
// Create new device info
|
|
117
118
|
return await DeviceManager.createNewDeviceInfo();
|
|
118
119
|
} catch (error) {
|
|
@@ -129,7 +130,7 @@ export class DeviceManager {
|
|
|
129
130
|
deviceId: DeviceManager.generateDeviceId(),
|
|
130
131
|
fingerprint: JSON.stringify(DeviceManager.getDeviceFingerprint()),
|
|
131
132
|
createdAt: new Date().toISOString(),
|
|
132
|
-
lastUsed: new Date().toISOString()
|
|
133
|
+
lastUsed: new Date().toISOString(),
|
|
133
134
|
};
|
|
134
135
|
|
|
135
136
|
await DeviceManager.saveDeviceInfo(deviceInfo);
|
|
@@ -180,7 +181,7 @@ export class DeviceManager {
|
|
|
180
181
|
if (typeof crypto !== 'undefined' && crypto.getRandomValues) {
|
|
181
182
|
const array = new Uint8Array(32);
|
|
182
183
|
crypto.getRandomValues(array);
|
|
183
|
-
return Array.from(array, byte => byte.toString(16).padStart(2, '0')).join('');
|
|
184
|
+
return Array.from(array, (byte) => byte.toString(16).padStart(2, '0')).join('');
|
|
184
185
|
}
|
|
185
186
|
throw new Error('No secure random source available for device ID generation');
|
|
186
187
|
}
|
|
@@ -191,14 +192,14 @@ export class DeviceManager {
|
|
|
191
192
|
static getDefaultDeviceName(): string {
|
|
192
193
|
const fingerprint = DeviceManager.getDeviceFingerprint();
|
|
193
194
|
const platform = (fingerprint.platform || '').toLowerCase();
|
|
194
|
-
|
|
195
|
+
|
|
195
196
|
if (platform.includes('win')) return 'Windows Computer';
|
|
196
197
|
if (platform.includes('mac')) return 'Mac Computer';
|
|
197
198
|
if (platform.includes('linux')) return 'Linux Computer';
|
|
198
199
|
if (platform.includes('iphone')) return 'iPhone';
|
|
199
200
|
if (platform.includes('ipad')) return 'iPad';
|
|
200
201
|
if (platform.includes('android')) return 'Android Device';
|
|
201
|
-
|
|
202
|
+
|
|
202
203
|
return 'Unknown Device';
|
|
203
204
|
}
|
|
204
205
|
}
|
package/src/utils/errorUtils.ts
CHANGED
|
@@ -14,23 +14,23 @@ export const ErrorCodes = {
|
|
|
14
14
|
FORBIDDEN: 'FORBIDDEN',
|
|
15
15
|
INVALID_TOKEN: 'INVALID_TOKEN',
|
|
16
16
|
MISSING_TOKEN: 'MISSING_TOKEN',
|
|
17
|
-
|
|
17
|
+
|
|
18
18
|
// Validation errors
|
|
19
19
|
VALIDATION_ERROR: 'VALIDATION_ERROR',
|
|
20
20
|
BAD_REQUEST: 'BAD_REQUEST',
|
|
21
21
|
MISSING_PARAMETER: 'MISSING_PARAMETER',
|
|
22
22
|
INVALID_FORMAT: 'INVALID_FORMAT',
|
|
23
|
-
|
|
23
|
+
|
|
24
24
|
// Resource errors
|
|
25
25
|
NOT_FOUND: 'NOT_FOUND',
|
|
26
26
|
ALREADY_EXISTS: 'ALREADY_EXISTS',
|
|
27
27
|
CONFLICT: 'CONFLICT',
|
|
28
|
-
|
|
28
|
+
|
|
29
29
|
// Server errors
|
|
30
30
|
INTERNAL_ERROR: 'INTERNAL_ERROR',
|
|
31
31
|
SERVICE_UNAVAILABLE: 'SERVICE_UNAVAILABLE',
|
|
32
32
|
TIMEOUT: 'TIMEOUT',
|
|
33
|
-
|
|
33
|
+
|
|
34
34
|
// Network errors
|
|
35
35
|
NETWORK_ERROR: 'NETWORK_ERROR',
|
|
36
36
|
CONNECTION_FAILED: 'CONNECTION_FAILED',
|
|
@@ -47,7 +47,7 @@ export const ErrorCodes = {
|
|
|
47
47
|
* nothing may retry it. See `retryAsync`'s default predicate, which refuses
|
|
48
48
|
* this code specifically.
|
|
49
49
|
*/
|
|
50
|
-
CANCELLED: 'CANCELLED'
|
|
50
|
+
CANCELLED: 'CANCELLED',
|
|
51
51
|
} as const;
|
|
52
52
|
|
|
53
53
|
/**
|
|
@@ -61,22 +61,22 @@ export const ErrorCodes = {
|
|
|
61
61
|
* Narrow a caught value with {@link isHttpRequestError} instead of asserting.
|
|
62
62
|
*/
|
|
63
63
|
export interface HttpRequestError extends Error {
|
|
64
|
-
|
|
64
|
+
/** HTTP status of the failed response. */
|
|
65
|
+
status: number;
|
|
66
|
+
/** Machine-readable code the server sent, when it sent one. */
|
|
67
|
+
code?: string;
|
|
68
|
+
/** Structured error detail the server sent, when it sent an object. */
|
|
69
|
+
details?: Record<string, unknown>;
|
|
70
|
+
/**
|
|
71
|
+
* Present on errors thrown directly by `HttpService`. `data` is the parsed
|
|
72
|
+
* JSON error body verbatim — the escape hatch for any server field the SDK
|
|
73
|
+
* does not lift onto `code`/`details`.
|
|
74
|
+
*/
|
|
75
|
+
response?: {
|
|
65
76
|
status: number;
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
details?: Record<string, unknown>;
|
|
70
|
-
/**
|
|
71
|
-
* Present on errors thrown directly by `HttpService`. `data` is the parsed
|
|
72
|
-
* JSON error body verbatim — the escape hatch for any server field the SDK
|
|
73
|
-
* does not lift onto `code`/`details`.
|
|
74
|
-
*/
|
|
75
|
-
response?: {
|
|
76
|
-
status: number;
|
|
77
|
-
statusText: string;
|
|
78
|
-
data?: unknown;
|
|
79
|
-
};
|
|
77
|
+
statusText: string;
|
|
78
|
+
data?: unknown;
|
|
79
|
+
};
|
|
80
80
|
}
|
|
81
81
|
|
|
82
82
|
/**
|
|
@@ -87,26 +87,26 @@ export interface HttpRequestError extends Error {
|
|
|
87
87
|
* first if you need one normalized.
|
|
88
88
|
*/
|
|
89
89
|
export function isHttpRequestError(value: unknown): value is HttpRequestError {
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
90
|
+
if (!(value instanceof Error)) {
|
|
91
|
+
return false;
|
|
92
|
+
}
|
|
93
|
+
return typeof (value as Partial<HttpRequestError>).status === 'number';
|
|
94
94
|
}
|
|
95
95
|
|
|
96
96
|
/**
|
|
97
97
|
* The fields {@link parseHttpErrorBody} lifts off a parsed error response body.
|
|
98
98
|
*/
|
|
99
99
|
export interface ParsedHttpErrorBody {
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
100
|
+
message?: string;
|
|
101
|
+
code?: string;
|
|
102
|
+
details?: Record<string, unknown>;
|
|
103
103
|
}
|
|
104
104
|
|
|
105
105
|
const isPlainRecord = (value: unknown): value is Record<string, unknown> =>
|
|
106
|
-
|
|
106
|
+
typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
107
107
|
|
|
108
108
|
const nonEmptyString = (value: unknown): string | undefined =>
|
|
109
|
-
|
|
109
|
+
typeof value === 'string' && value.trim().length > 0 ? value : undefined;
|
|
110
110
|
|
|
111
111
|
/**
|
|
112
112
|
* Extract `message` / `code` / `details` from a parsed HTTP error response body.
|
|
@@ -131,27 +131,27 @@ const nonEmptyString = (value: unknown): string | undefined =>
|
|
|
131
131
|
* its status-based fallback message. Total function: never throws.
|
|
132
132
|
*/
|
|
133
133
|
export function parseHttpErrorBody(body: unknown): ParsedHttpErrorBody {
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
134
|
+
if (!isPlainRecord(body)) {
|
|
135
|
+
return {};
|
|
136
|
+
}
|
|
137
137
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
138
|
+
const nested = isPlainRecord(body.error) ? body.error : undefined;
|
|
139
|
+
const errorString = nonEmptyString(body.error);
|
|
140
|
+
// A sibling that proves the top-level `error` is a CODE rather than prose.
|
|
141
|
+
const siblingMessage = nonEmptyString(body.message) ?? nonEmptyString(body.error_description);
|
|
142
|
+
|
|
143
|
+
return {
|
|
144
|
+
message: siblingMessage ?? (nested ? nonEmptyString(nested.message) : errorString),
|
|
145
|
+
code:
|
|
146
|
+
(nested ? nonEmptyString(nested.code) : undefined) ??
|
|
147
|
+
nonEmptyString(body.code) ??
|
|
148
|
+
(siblingMessage ? errorString : undefined),
|
|
149
|
+
details: isPlainRecord(body.details)
|
|
150
|
+
? body.details
|
|
151
|
+
: nested && isPlainRecord(nested.details)
|
|
152
|
+
? nested.details
|
|
153
|
+
: undefined,
|
|
154
|
+
};
|
|
155
155
|
}
|
|
156
156
|
|
|
157
157
|
/**
|
|
@@ -161,13 +161,13 @@ export function createApiError(
|
|
|
161
161
|
message: string,
|
|
162
162
|
code: string = ErrorCodes.INTERNAL_ERROR,
|
|
163
163
|
status = 500,
|
|
164
|
-
details?: Record<string, unknown
|
|
164
|
+
details?: Record<string, unknown>,
|
|
165
165
|
): ApiError {
|
|
166
166
|
return {
|
|
167
167
|
message,
|
|
168
168
|
code,
|
|
169
169
|
status,
|
|
170
|
-
details
|
|
170
|
+
details,
|
|
171
171
|
};
|
|
172
172
|
}
|
|
173
173
|
|
|
@@ -251,28 +251,24 @@ export function handleHttpError(error: unknown): ApiError {
|
|
|
251
251
|
// anywhere else, and it stays TIMEOUT: a caller that never passed a signal
|
|
252
252
|
// cannot have cancelled anything.
|
|
253
253
|
if (isAbortLike(error)) {
|
|
254
|
-
return createApiError(
|
|
255
|
-
'Request timeout or cancelled',
|
|
256
|
-
ErrorCodes.TIMEOUT,
|
|
257
|
-
0
|
|
258
|
-
);
|
|
254
|
+
return createApiError('Request timeout or cancelled', ErrorCodes.TIMEOUT, 0);
|
|
259
255
|
}
|
|
260
256
|
|
|
261
257
|
// Handle TypeError (network failures, CORS, etc.)
|
|
262
258
|
if (error instanceof TypeError) {
|
|
263
259
|
// Check if it's a network-related TypeError
|
|
264
|
-
if (
|
|
260
|
+
if (
|
|
261
|
+
error.message.includes('fetch') ||
|
|
262
|
+
error.message.includes('network') ||
|
|
263
|
+
error.message.includes('Failed to fetch')
|
|
264
|
+
) {
|
|
265
265
|
return createApiError(
|
|
266
266
|
'Network error - failed to connect to server',
|
|
267
267
|
ErrorCodes.NETWORK_ERROR,
|
|
268
|
-
0
|
|
268
|
+
0,
|
|
269
269
|
);
|
|
270
270
|
}
|
|
271
|
-
return createApiError(
|
|
272
|
-
error.message || 'Network error occurred',
|
|
273
|
-
ErrorCodes.NETWORK_ERROR,
|
|
274
|
-
0
|
|
275
|
-
);
|
|
271
|
+
return createApiError(error.message || 'Network error occurred', ErrorCodes.NETWORK_ERROR, 0);
|
|
276
272
|
}
|
|
277
273
|
|
|
278
274
|
// Handle fetch Response errors - check if it has response property with status
|
|
@@ -298,7 +294,7 @@ export function handleHttpError(error: unknown): ApiError {
|
|
|
298
294
|
fetchError.message || `HTTP ${status} error`,
|
|
299
295
|
getErrorCodeFromStatus(status),
|
|
300
296
|
status,
|
|
301
|
-
isPlainRecord(fetchError.details) ? fetchError.details : undefined
|
|
297
|
+
isPlainRecord(fetchError.details) ? fetchError.details : undefined,
|
|
302
298
|
);
|
|
303
299
|
}
|
|
304
300
|
}
|
|
@@ -307,36 +303,24 @@ export function handleHttpError(error: unknown): ApiError {
|
|
|
307
303
|
if (error instanceof Error) {
|
|
308
304
|
// Check for common error patterns
|
|
309
305
|
if (error.message.includes('timeout') || error.message.includes('aborted')) {
|
|
310
|
-
return createApiError(
|
|
311
|
-
'Request timeout',
|
|
312
|
-
ErrorCodes.TIMEOUT,
|
|
313
|
-
0
|
|
314
|
-
);
|
|
306
|
+
return createApiError('Request timeout', ErrorCodes.TIMEOUT, 0);
|
|
315
307
|
}
|
|
316
|
-
|
|
308
|
+
|
|
317
309
|
if (error.message.includes('network') || error.message.includes('fetch')) {
|
|
318
|
-
return createApiError(
|
|
319
|
-
error.message || 'Network error occurred',
|
|
320
|
-
ErrorCodes.NETWORK_ERROR,
|
|
321
|
-
0
|
|
322
|
-
);
|
|
310
|
+
return createApiError(error.message || 'Network error occurred', ErrorCodes.NETWORK_ERROR, 0);
|
|
323
311
|
}
|
|
324
312
|
|
|
325
313
|
return createApiError(
|
|
326
314
|
error.message || 'Unknown error occurred',
|
|
327
315
|
ErrorCodes.INTERNAL_ERROR,
|
|
328
|
-
500
|
|
316
|
+
500,
|
|
329
317
|
);
|
|
330
318
|
}
|
|
331
319
|
|
|
332
320
|
// Handle other errors - ensure we always return a non-empty message
|
|
333
321
|
const errorString = error ? String(error) : '';
|
|
334
322
|
const message = errorString.trim() || 'Unknown error occurred';
|
|
335
|
-
return createApiError(
|
|
336
|
-
message,
|
|
337
|
-
ErrorCodes.INTERNAL_ERROR,
|
|
338
|
-
500
|
|
339
|
-
);
|
|
323
|
+
return createApiError(message, ErrorCodes.INTERNAL_ERROR, 500);
|
|
340
324
|
}
|
|
341
325
|
|
|
342
326
|
/**
|
|
@@ -395,13 +379,13 @@ export function extractErrorStatus(error: unknown): number | undefined {
|
|
|
395
379
|
* Validate required fields and throw error if missing
|
|
396
380
|
*/
|
|
397
381
|
export function validateRequiredFields(data: Record<string, unknown>, fields: string[]): void {
|
|
398
|
-
const missing = fields.filter(field => !data[field]);
|
|
399
|
-
|
|
382
|
+
const missing = fields.filter((field) => !data[field]);
|
|
383
|
+
|
|
400
384
|
if (missing.length > 0) {
|
|
401
385
|
throw createApiError(
|
|
402
386
|
`Missing required fields: ${missing.join(', ')}`,
|
|
403
387
|
ErrorCodes.MISSING_PARAMETER,
|
|
404
|
-
400
|
|
388
|
+
400,
|
|
405
389
|
);
|
|
406
390
|
}
|
|
407
391
|
}
|
|
@@ -423,5 +407,3 @@ export function logError(error: unknown, context?: string): void {
|
|
|
423
407
|
});
|
|
424
408
|
}
|
|
425
409
|
}
|
|
426
|
-
|
|
427
|
-
|