()
| 242 | } |
| 243 | |
| 244 | async function fetchUserSettingsOnce(): Promise<SettingsSyncFetchResult> { |
| 245 | try { |
| 246 | const session = await getAuthRuntime().resolveSession({ allowRefresh: true }) |
| 247 | |
| 248 | const authHeaders = getSettingsSyncAuthHeaders(session) |
| 249 | if (authHeaders.error) { |
| 250 | return { |
| 251 | success: false, |
| 252 | error: authHeaders.error, |
| 253 | skipRetry: true, |
| 254 | } |
| 255 | } |
| 256 | |
| 257 | const headers: Record<string, string> = { |
| 258 | ...authHeaders.headers, |
| 259 | 'User-Agent': getNcodeUserAgent(), |
| 260 | } |
| 261 | |
| 262 | const endpoint = getSettingsSyncEndpoint() |
| 263 | const response = await axios.get(endpoint, { |
| 264 | headers, |
| 265 | timeout: SETTINGS_SYNC_TIMEOUT_MS, |
| 266 | validateStatus: status => status === 200 || status === 404, |
| 267 | }) |
| 268 | |
| 269 | // 404 means no settings exist yet |
| 270 | if (response.status === 404) { |
| 271 | logForDiagnosticsNoPII('info', 'settings_sync_fetch_empty') |
| 272 | return { |
| 273 | success: true, |
| 274 | isEmpty: true, |
| 275 | } |
| 276 | } |
| 277 | |
| 278 | const parsed = UserSyncDataSchema().safeParse(response.data) |
| 279 | if (!parsed.success) { |
| 280 | logForDiagnosticsNoPII('warn', 'settings_sync_fetch_invalid_format') |
| 281 | return { |
| 282 | success: false, |
| 283 | error: 'Invalid settings sync response format', |
| 284 | } |
| 285 | } |
| 286 | |
| 287 | logForDiagnosticsNoPII('info', 'settings_sync_fetch_success') |
| 288 | return { |
| 289 | success: true, |
| 290 | data: parsed.data, |
| 291 | isEmpty: false, |
| 292 | } |
| 293 | } catch (error) { |
| 294 | const { kind, message } = classifyAxiosError(error) |
| 295 | switch (kind) { |
| 296 | case 'auth': |
| 297 | return { |
| 298 | success: false, |
| 299 | error: 'Not authorized for settings sync', |
| 300 | skipRetry: true, |
| 301 | } |
no test coverage detected