mirror of
https://github.com/aliasvault/aliasvault.git
synced 2026-05-13 09:55:33 -04:00
Add error code throw and detection to native vault sync logic implementation (#520)
This commit is contained in:
@@ -232,6 +232,8 @@ export default function Initialize() : React.ReactNode {
|
||||
* Show modal with error message for other errors
|
||||
*/
|
||||
Alert.alert(t('common.error'), error);
|
||||
router.replace('/unlock');
|
||||
return;
|
||||
},
|
||||
/**
|
||||
* On upgrade required.
|
||||
|
||||
@@ -11,6 +11,7 @@ import { useDb } from '@/context/DbContext';
|
||||
import { useWebApi } from '@/context/WebApiContext';
|
||||
import NativeVaultManager from '@/specs/NativeVaultManager';
|
||||
import { VaultVersionIncompatibleError } from '@/utils/types/errors/VaultVersionIncompatibleError';
|
||||
import { VaultSyncErrorCode, getVaultSyncErrorCode } from '@/utils/types/errors/VaultSyncErrorCodes';
|
||||
|
||||
/**
|
||||
* Utility function to ensure a minimum time has elapsed for an operation
|
||||
@@ -98,37 +99,41 @@ export const useVaultSync = () : {
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
}
|
||||
} catch (err) {
|
||||
const errorMessage = err instanceof Error ? err.message : t('common.errors.unknownError');
|
||||
console.error('VaultSync: syncVault error:', err);
|
||||
|
||||
// Check for authentication failure (401) - logout user
|
||||
if (errorMessage.includes('Authentication failed') || errorMessage.includes('please login again')) {
|
||||
await app.logout('Your session has expired. Please login again.');
|
||||
return false;
|
||||
// Get the error code from the native layer
|
||||
const errorCode = getVaultSyncErrorCode(err);
|
||||
|
||||
console.log('VaultSync: errorCode:', errorCode);
|
||||
|
||||
// Handle specific error codes
|
||||
switch (errorCode) {
|
||||
case VaultSyncErrorCode.SESSION_EXPIRED:
|
||||
case VaultSyncErrorCode.AUTHENTICATION_FAILED:
|
||||
await app.logout('Your session has expired. Please login again.');
|
||||
return false;
|
||||
|
||||
case VaultSyncErrorCode.PASSWORD_CHANGED:
|
||||
await app.logout(t('vault.errors.passwordChanged'));
|
||||
return false;
|
||||
|
||||
case VaultSyncErrorCode.CLIENT_VERSION_NOT_SUPPORTED:
|
||||
await app.logout(t('vault.errors.versionNotSupported'));
|
||||
return false;
|
||||
|
||||
case VaultSyncErrorCode.SERVER_UNAVAILABLE:
|
||||
onOffline?.();
|
||||
return false;
|
||||
|
||||
case VaultSyncErrorCode.NETWORK_ERROR:
|
||||
case VaultSyncErrorCode.TIMEOUT:
|
||||
onOffline?.();
|
||||
return false;
|
||||
|
||||
default:
|
||||
// Unknown error or no error code - rethrow
|
||||
throw err;
|
||||
}
|
||||
|
||||
// Check for specific error conditions from native layer
|
||||
if (errorMessage.includes('Server not available')) {
|
||||
onOffline?.();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (errorMessage.includes('Client version not supported')) {
|
||||
onError?.(t('vault.errors.versionNotSupported'));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (errorMessage.includes('Password has changed')) {
|
||||
await app.logout(t('vault.errors.passwordChanged'));
|
||||
return false;
|
||||
}
|
||||
|
||||
// Network error - go offline but don't fail
|
||||
if (errorMessage.includes('network') || errorMessage.includes('timeout')) {
|
||||
await NativeVaultManager.setOfflineMode(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
throw err;
|
||||
}
|
||||
|
||||
try {
|
||||
@@ -183,14 +188,35 @@ export const useVaultSync = () : {
|
||||
return false;
|
||||
}
|
||||
|
||||
const errorMessage = err instanceof Error ? err.message : t('common.errors.unknownError');
|
||||
// Check if it's a vault sync error with error code
|
||||
const errorCode = getVaultSyncErrorCode(err);
|
||||
if (errorCode) {
|
||||
switch (errorCode) {
|
||||
case VaultSyncErrorCode.SESSION_EXPIRED:
|
||||
case VaultSyncErrorCode.AUTHENTICATION_FAILED:
|
||||
await app.logout('Your session has expired. Please login again.');
|
||||
return false;
|
||||
|
||||
// Check if it's a network error
|
||||
if (errorMessage.includes('network') || errorMessage.includes('timeout')) {
|
||||
await NativeVaultManager.setOfflineMode(true);
|
||||
return true;
|
||||
case VaultSyncErrorCode.PASSWORD_CHANGED:
|
||||
await app.logout(t('vault.errors.passwordChanged'));
|
||||
return false;
|
||||
|
||||
case VaultSyncErrorCode.NETWORK_ERROR:
|
||||
case VaultSyncErrorCode.TIMEOUT:
|
||||
await NativeVaultManager.setOfflineMode(true);
|
||||
return true;
|
||||
|
||||
case VaultSyncErrorCode.SERVER_UNAVAILABLE:
|
||||
await NativeVaultManager.setOfflineMode(true);
|
||||
return true;
|
||||
|
||||
default:
|
||||
// Let the error be handled below
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const errorMessage = err instanceof Error ? err.message : t('common.errors.unknownError');
|
||||
onError?.(errorMessage);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -343,9 +343,9 @@ extension CredentialProviderViewController: PasskeyProviderDelegate {
|
||||
// Server connectivity check failed
|
||||
viewModel.setLoading(false)
|
||||
|
||||
// Show error dialog to user
|
||||
// Show appropriate error dialog based on error type
|
||||
await MainActor.run {
|
||||
self.showConnectivityErrorAlert(viewModel: viewModel)
|
||||
self.showSyncErrorAlert(error: error)
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -623,7 +623,7 @@ extension CredentialProviderViewController: PasskeyProviderDelegate {
|
||||
}
|
||||
|
||||
/**
|
||||
* Show connectivity error alert dialog
|
||||
* Show connectivity error alert dialog (kept for backward compatibility)
|
||||
*/
|
||||
private func showConnectivityErrorAlert(viewModel: PasskeyRegistrationViewModel) {
|
||||
let alert = UIAlertController(
|
||||
|
||||
@@ -279,4 +279,65 @@ public class CredentialProviderViewController: ASCredentialProviderViewControlle
|
||||
return true
|
||||
}
|
||||
|
||||
// MARK: - Error Handling
|
||||
|
||||
/**
|
||||
* Show sync error alert dialog with appropriate message based on error type
|
||||
* This method is internal so it can be used by both passkey and credential extensions
|
||||
*/
|
||||
internal func showSyncErrorAlert(error: Error) {
|
||||
var title = NSLocalizedString("connection_error_title", comment: "Connection Error")
|
||||
var message = NSLocalizedString("connection_error_message", comment: "No connection to the server can be made.")
|
||||
|
||||
// Check if it's a VaultSyncError and customize message accordingly
|
||||
if let syncError = error as? VaultSyncError {
|
||||
switch syncError {
|
||||
case .sessionExpired, .authenticationFailed:
|
||||
title = NSLocalizedString("session_expired_title", comment: "Session Expired")
|
||||
message = NSLocalizedString("session_expired_message", comment: "Your session has expired. Please sign in again.")
|
||||
|
||||
case .passwordChanged:
|
||||
title = NSLocalizedString("password_changed_title", comment: "Password Changed")
|
||||
message = NSLocalizedString("password_changed_message", comment: "Your password has been changed. Please sign in again.")
|
||||
|
||||
case .clientVersionNotSupported:
|
||||
title = NSLocalizedString("version_not_supported_title", comment: "Update Required")
|
||||
message = NSLocalizedString("version_not_supported_message", comment: "Your app version is no longer supported. Please update to the latest version.")
|
||||
|
||||
case .serverUnavailable:
|
||||
title = NSLocalizedString("server_unavailable_title", comment: "Server Unavailable")
|
||||
message = NSLocalizedString("server_unavailable_message", comment: "The server is currently unavailable. Please try again later.")
|
||||
|
||||
case .networkError, .timeout:
|
||||
title = NSLocalizedString("network_error_title", comment: "Network Error")
|
||||
message = NSLocalizedString("network_error_message", comment: "A network error occurred. Please check your connection and try again.")
|
||||
|
||||
default:
|
||||
// Use default connectivity error message for other errors
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
let alert = UIAlertController(
|
||||
title: title,
|
||||
message: message,
|
||||
preferredStyle: .alert
|
||||
)
|
||||
|
||||
alert.addAction(UIAlertAction(
|
||||
title: NSLocalizedString("ok", comment: "OK"),
|
||||
style: .default,
|
||||
handler: { _ in
|
||||
// User acknowledged the error
|
||||
}
|
||||
))
|
||||
|
||||
// Present the alert
|
||||
if let currentController = self.currentHostingController {
|
||||
currentController.present(alert, animated: true)
|
||||
} else {
|
||||
self.present(alert, animated: true)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Binary file not shown.
@@ -644,7 +644,13 @@ public class VaultManager: NSObject {
|
||||
} catch {
|
||||
print("VaultManager: Vault sync failed: \(error)")
|
||||
await MainActor.run {
|
||||
reject("SYNC_ERROR", "Failed to sync vault: \(error.localizedDescription)", error)
|
||||
// Map VaultSyncError to proper error codes for React Native
|
||||
if let syncError = error as? VaultSyncError {
|
||||
reject(syncError.code, syncError.message, error)
|
||||
} else {
|
||||
// Fallback for unknown errors
|
||||
reject("VAULT_SYNC_UNKNOWN_ERROR", "Failed to sync vault: \(error.localizedDescription)", error)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,13 +53,19 @@ extension VaultStore {
|
||||
|
||||
/// Fetch and validate server status
|
||||
private func fetchAndValidateStatus(using webApiService: WebApiService) async throws -> StatusResponse {
|
||||
let statusResponse = try await webApiService.executeRequest(
|
||||
method: "GET",
|
||||
endpoint: "Auth/status",
|
||||
body: nil,
|
||||
headers: [:],
|
||||
requiresAuth: true
|
||||
)
|
||||
let statusResponse: WebApiResponse
|
||||
do {
|
||||
statusResponse = try await webApiService.executeRequest(
|
||||
method: "GET",
|
||||
endpoint: "Auth/status",
|
||||
body: nil,
|
||||
headers: [:],
|
||||
requiresAuth: true
|
||||
)
|
||||
} catch {
|
||||
// Network error - convert to VaultSyncError
|
||||
throw VaultSyncError.networkError(underlyingError: error)
|
||||
}
|
||||
|
||||
// Check response status
|
||||
// Note: WebApiService already handles 401 with automatic token refresh and retry
|
||||
@@ -68,30 +74,18 @@ extension VaultStore {
|
||||
if statusResponse.statusCode == 401 {
|
||||
// Authentication failed even after token refresh attempt
|
||||
print("VaultStore: Authentication failed (401) - token refresh also failed")
|
||||
throw NSError(
|
||||
domain: "VaultStore",
|
||||
code: 401,
|
||||
userInfo: [NSLocalizedDescriptionKey: "Authentication failed - please login again"]
|
||||
)
|
||||
throw VaultSyncError.sessionExpired
|
||||
}
|
||||
|
||||
// Other error (5xx, network, etc.) - go offline
|
||||
setOfflineMode(true)
|
||||
throw NSError(
|
||||
domain: "VaultStore",
|
||||
code: statusResponse.statusCode,
|
||||
userInfo: [NSLocalizedDescriptionKey: "Server returned status \(statusResponse.statusCode)"]
|
||||
)
|
||||
throw VaultSyncError.serverUnavailable(statusCode: statusResponse.statusCode)
|
||||
}
|
||||
|
||||
guard let statusData = statusResponse.body.data(using: .utf8) else {
|
||||
print("VaultStore: Failed to convert status response to data")
|
||||
print("VaultStore: Response body: '\(statusResponse.body)'")
|
||||
throw NSError(
|
||||
domain: "VaultStore",
|
||||
code: -1,
|
||||
userInfo: [NSLocalizedDescriptionKey: "Failed to convert status response to data"]
|
||||
)
|
||||
throw VaultSyncError.parseError(message: "Failed to convert status response to data")
|
||||
}
|
||||
|
||||
let decoder = JSONDecoder()
|
||||
@@ -101,19 +95,11 @@ extension VaultStore {
|
||||
} catch {
|
||||
print("VaultStore: Failed to decode status response: \(error)")
|
||||
print("VaultStore: Response body: '\(statusResponse.body)'")
|
||||
throw NSError(
|
||||
domain: "VaultStore",
|
||||
code: -1,
|
||||
userInfo: [NSLocalizedDescriptionKey: "Failed to parse status response: \(error.localizedDescription)"]
|
||||
)
|
||||
throw VaultSyncError.parseError(message: "Failed to decode status response: \(error.localizedDescription)")
|
||||
}
|
||||
|
||||
guard status.clientVersionSupported else {
|
||||
throw NSError(
|
||||
domain: "VaultStore",
|
||||
code: -1,
|
||||
userInfo: [NSLocalizedDescriptionKey: "Client version not supported"]
|
||||
)
|
||||
throw VaultSyncError.clientVersionNotSupported
|
||||
}
|
||||
|
||||
try validateSrpSalt(status.srpSalt)
|
||||
@@ -129,30 +115,30 @@ extension VaultStore {
|
||||
}
|
||||
|
||||
if !srpSalt.isEmpty && srpSalt != params.salt {
|
||||
throw NSError(
|
||||
domain: "VaultStore",
|
||||
code: -2,
|
||||
userInfo: [NSLocalizedDescriptionKey: "Password has changed, please login again"]
|
||||
)
|
||||
throw VaultSyncError.passwordChanged
|
||||
}
|
||||
}
|
||||
|
||||
/// Download vault from server and store it locally
|
||||
private func downloadAndStoreVault(using webApiService: WebApiService, newRevision: Int) async throws {
|
||||
let vaultResponse = try await webApiService.executeRequest(
|
||||
method: "GET",
|
||||
endpoint: "Vault",
|
||||
body: nil,
|
||||
headers: [:],
|
||||
requiresAuth: true
|
||||
)
|
||||
let vaultResponse: WebApiResponse
|
||||
do {
|
||||
vaultResponse = try await webApiService.executeRequest(
|
||||
method: "GET",
|
||||
endpoint: "Vault",
|
||||
body: nil,
|
||||
headers: [:],
|
||||
requiresAuth: true
|
||||
)
|
||||
} catch {
|
||||
throw VaultSyncError.networkError(underlyingError: error)
|
||||
}
|
||||
|
||||
guard vaultResponse.statusCode == 200 else {
|
||||
throw NSError(
|
||||
domain: "VaultStore",
|
||||
code: -1,
|
||||
userInfo: [NSLocalizedDescriptionKey: "Failed to download vault"]
|
||||
)
|
||||
if vaultResponse.statusCode == 401 {
|
||||
throw VaultSyncError.sessionExpired
|
||||
}
|
||||
throw VaultSyncError.serverUnavailable(statusCode: vaultResponse.statusCode)
|
||||
}
|
||||
|
||||
let vault = try parseVaultResponse(vaultResponse.body)
|
||||
@@ -168,11 +154,7 @@ extension VaultStore {
|
||||
/// Parse vault response from JSON
|
||||
private func parseVaultResponse(_ body: String) throws -> VaultResponse {
|
||||
guard let vaultData = body.data(using: .utf8) else {
|
||||
throw NSError(
|
||||
domain: "VaultStore",
|
||||
code: -1,
|
||||
userInfo: [NSLocalizedDescriptionKey: "Failed to convert vault response to data"]
|
||||
)
|
||||
throw VaultSyncError.parseError(message: "Failed to convert vault response to data")
|
||||
}
|
||||
|
||||
do {
|
||||
@@ -180,11 +162,7 @@ extension VaultStore {
|
||||
} catch {
|
||||
print("VaultStore: Failed to decode vault response: \(error)")
|
||||
print("VaultStore: Response body: \(body)")
|
||||
throw NSError(
|
||||
domain: "VaultStore",
|
||||
code: -1,
|
||||
userInfo: [NSLocalizedDescriptionKey: "Failed to parse vault response: \(error.localizedDescription)"]
|
||||
)
|
||||
throw VaultSyncError.parseError(message: "Failed to decode vault response: \(error.localizedDescription)")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -194,23 +172,11 @@ extension VaultStore {
|
||||
case 0:
|
||||
return
|
||||
case 1:
|
||||
throw NSError(
|
||||
domain: "VaultStore",
|
||||
code: -1,
|
||||
userInfo: [NSLocalizedDescriptionKey: "Vault merge required"]
|
||||
)
|
||||
throw VaultSyncError.vaultMergeRequired
|
||||
case 2:
|
||||
throw NSError(
|
||||
domain: "VaultStore",
|
||||
code: -1,
|
||||
userInfo: [NSLocalizedDescriptionKey: "Vault outdated"]
|
||||
)
|
||||
throw VaultSyncError.vaultOutdated
|
||||
default:
|
||||
throw NSError(
|
||||
domain: "VaultStore",
|
||||
code: -1,
|
||||
userInfo: [NSLocalizedDescriptionKey: "Unknown vault status: \(status)"]
|
||||
)
|
||||
throw VaultSyncError.unknownError(message: "Unknown vault status: \(status)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
94
apps/mobile-app/ios/VaultStoreKit/VaultSyncError.swift
Normal file
94
apps/mobile-app/ios/VaultStoreKit/VaultSyncError.swift
Normal file
@@ -0,0 +1,94 @@
|
||||
import Foundation
|
||||
|
||||
/// Error codes for vault sync operations
|
||||
/// These error codes are language-independent and can be properly handled by the client
|
||||
public enum VaultSyncError: Error {
|
||||
// Authentication errors
|
||||
case authenticationFailed
|
||||
case sessionExpired
|
||||
case passwordChanged
|
||||
|
||||
// Network/connectivity errors
|
||||
case serverUnavailable(statusCode: Int)
|
||||
case networkError(underlyingError: Error)
|
||||
case timeout
|
||||
|
||||
// Version/compatibility errors
|
||||
case clientVersionNotSupported
|
||||
case vaultVersionIncompatible
|
||||
|
||||
// Vault status errors
|
||||
case vaultMergeRequired
|
||||
case vaultOutdated
|
||||
|
||||
// Decryption errors
|
||||
case vaultDecryptFailed
|
||||
|
||||
// Generic errors
|
||||
case unknownError(message: String)
|
||||
case parseError(message: String)
|
||||
|
||||
/// Get the error code string for React Native bridge
|
||||
public var code: String {
|
||||
switch self {
|
||||
case .authenticationFailed:
|
||||
return "VAULT_SYNC_AUTH_FAILED"
|
||||
case .sessionExpired:
|
||||
return "VAULT_SYNC_SESSION_EXPIRED"
|
||||
case .passwordChanged:
|
||||
return "VAULT_SYNC_PASSWORD_CHANGED"
|
||||
case .serverUnavailable:
|
||||
return "VAULT_SYNC_SERVER_UNAVAILABLE"
|
||||
case .networkError:
|
||||
return "VAULT_SYNC_NETWORK_ERROR"
|
||||
case .timeout:
|
||||
return "VAULT_SYNC_TIMEOUT"
|
||||
case .clientVersionNotSupported:
|
||||
return "VAULT_SYNC_CLIENT_VERSION_NOT_SUPPORTED"
|
||||
case .vaultVersionIncompatible:
|
||||
return "VAULT_SYNC_VAULT_VERSION_INCOMPATIBLE"
|
||||
case .vaultMergeRequired:
|
||||
return "VAULT_SYNC_MERGE_REQUIRED"
|
||||
case .vaultOutdated:
|
||||
return "VAULT_SYNC_OUTDATED"
|
||||
case .vaultDecryptFailed:
|
||||
return "VAULT_SYNC_DECRYPT_FAILED"
|
||||
case .unknownError:
|
||||
return "VAULT_SYNC_UNKNOWN_ERROR"
|
||||
case .parseError:
|
||||
return "VAULT_SYNC_PARSE_ERROR"
|
||||
}
|
||||
}
|
||||
|
||||
/// Get a user-friendly message (for logging/debugging)
|
||||
public var message: String {
|
||||
switch self {
|
||||
case .authenticationFailed:
|
||||
return "Authentication failed"
|
||||
case .sessionExpired:
|
||||
return "Session expired"
|
||||
case .passwordChanged:
|
||||
return "Password has changed"
|
||||
case .serverUnavailable(let statusCode):
|
||||
return "Server unavailable (status: \(statusCode))"
|
||||
case .networkError(let error):
|
||||
return "Network error: \(error.localizedDescription)"
|
||||
case .timeout:
|
||||
return "Request timeout"
|
||||
case .clientVersionNotSupported:
|
||||
return "Client version not supported"
|
||||
case .vaultVersionIncompatible:
|
||||
return "Vault version incompatible"
|
||||
case .vaultMergeRequired:
|
||||
return "Vault merge required"
|
||||
case .vaultOutdated:
|
||||
return "Vault outdated"
|
||||
case .vaultDecryptFailed:
|
||||
return "Failed to decrypt vault"
|
||||
case .unknownError(let message):
|
||||
return "Unknown error: \(message)"
|
||||
case .parseError(let message):
|
||||
return "Parse error: \(message)"
|
||||
}
|
||||
}
|
||||
}
|
||||
54
apps/mobile-app/utils/types/errors/VaultSyncErrorCodes.ts
Normal file
54
apps/mobile-app/utils/types/errors/VaultSyncErrorCodes.ts
Normal file
@@ -0,0 +1,54 @@
|
||||
/**
|
||||
* Error codes for vault sync operations
|
||||
* These codes are returned from native layer and should be used instead of parsing error messages
|
||||
* This enables multi-language support and robust error handling
|
||||
*/
|
||||
export enum VaultSyncErrorCode {
|
||||
// Authentication errors
|
||||
AUTHENTICATION_FAILED = 'VAULT_SYNC_AUTH_FAILED',
|
||||
SESSION_EXPIRED = 'VAULT_SYNC_SESSION_EXPIRED',
|
||||
PASSWORD_CHANGED = 'VAULT_SYNC_PASSWORD_CHANGED',
|
||||
|
||||
// Network/connectivity errors
|
||||
SERVER_UNAVAILABLE = 'VAULT_SYNC_SERVER_UNAVAILABLE',
|
||||
NETWORK_ERROR = 'VAULT_SYNC_NETWORK_ERROR',
|
||||
TIMEOUT = 'VAULT_SYNC_TIMEOUT',
|
||||
|
||||
// Version/compatibility errors
|
||||
CLIENT_VERSION_NOT_SUPPORTED = 'VAULT_SYNC_CLIENT_VERSION_NOT_SUPPORTED',
|
||||
VAULT_VERSION_INCOMPATIBLE = 'VAULT_SYNC_VAULT_VERSION_INCOMPATIBLE',
|
||||
|
||||
// Vault status errors
|
||||
VAULT_MERGE_REQUIRED = 'VAULT_SYNC_MERGE_REQUIRED',
|
||||
VAULT_OUTDATED = 'VAULT_SYNC_OUTDATED',
|
||||
|
||||
// Decryption errors
|
||||
VAULT_DECRYPT_FAILED = 'VAULT_SYNC_DECRYPT_FAILED',
|
||||
|
||||
// Generic errors
|
||||
UNKNOWN_ERROR = 'VAULT_SYNC_UNKNOWN_ERROR',
|
||||
PARSE_ERROR = 'VAULT_SYNC_PARSE_ERROR',
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if an error is a vault sync error
|
||||
*/
|
||||
export function isVaultSyncError(error: unknown): error is { code: string } {
|
||||
return (
|
||||
typeof error === 'object' &&
|
||||
error !== null &&
|
||||
'code' in error &&
|
||||
typeof (error as { code: unknown }).code === 'string' &&
|
||||
(error as { code: string }).code.startsWith('VAULT_SYNC_')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the vault sync error code from an error object
|
||||
*/
|
||||
export function getVaultSyncErrorCode(error: unknown): VaultSyncErrorCode | null {
|
||||
if (isVaultSyncError(error)) {
|
||||
return error.code as VaultSyncErrorCode;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
Reference in New Issue
Block a user