refactor(admin): split lib/sdk/context.tsx (1280 LOC) into focused modules
Extract the monolithic SDK context provider into seven focused modules: - context-types.ts (203 LOC): SDKContextValue interface, initialState, ExtendedSDKAction - context-reducer.ts (353 LOC): sdkReducer with all action handlers - context-provider.tsx (495 LOC): SDKProvider component + SDKContext - context-hooks.ts (17 LOC): useSDK hook - context-validators.ts (94 LOC): local checkpoint validation logic - context-projects.ts (67 LOC): project management API helpers - context-sync-helpers.ts (145 LOC): sync infrastructure init/cleanup/callbacks - context.tsx (23 LOC): barrel re-export preserving existing import paths All files under the 500-line hard cap. Build verified with `npx next build`. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
145
admin-compliance/lib/sdk/context-sync-helpers.ts
Normal file
145
admin-compliance/lib/sdk/context-sync-helpers.ts
Normal file
@@ -0,0 +1,145 @@
|
||||
import React from 'react'
|
||||
import { SDKState } from './types'
|
||||
import { SDKApiClient, getSDKApiClient, resetSDKApiClient } from './api-client'
|
||||
import { StateSyncManager, createStateSyncManager, SyncState, SyncCallbacks } from './sync'
|
||||
import { ExtendedSDKAction } from './context-types'
|
||||
|
||||
// =============================================================================
|
||||
// SYNC CALLBACK BUILDER
|
||||
// =============================================================================
|
||||
|
||||
/**
|
||||
* Builds the SyncCallbacks object used by the StateSyncManager.
|
||||
* Keeps the provider component cleaner by extracting this factory.
|
||||
*/
|
||||
export function buildSyncCallbacks(
|
||||
setSyncState: React.Dispatch<React.SetStateAction<SyncState>>,
|
||||
setIsOnline: React.Dispatch<React.SetStateAction<boolean>>,
|
||||
dispatch: React.Dispatch<ExtendedSDKAction>,
|
||||
stateRef: React.MutableRefObject<SDKState>
|
||||
): SyncCallbacks {
|
||||
return {
|
||||
onSyncStart: () => {
|
||||
setSyncState(prev => ({ ...prev, status: 'syncing' }))
|
||||
},
|
||||
onSyncComplete: (syncedState) => {
|
||||
setSyncState(prev => ({
|
||||
...prev,
|
||||
status: 'idle',
|
||||
lastSyncedAt: new Date(),
|
||||
pendingChanges: 0,
|
||||
}))
|
||||
if (syncedState.lastModified > stateRef.current.lastModified) {
|
||||
dispatch({ type: 'SET_STATE', payload: syncedState })
|
||||
}
|
||||
},
|
||||
onSyncError: (error) => {
|
||||
setSyncState(prev => ({
|
||||
...prev,
|
||||
status: 'error',
|
||||
error: error.message,
|
||||
}))
|
||||
},
|
||||
onConflict: () => {
|
||||
setSyncState(prev => ({ ...prev, status: 'conflict' }))
|
||||
},
|
||||
onOffline: () => {
|
||||
setIsOnline(false)
|
||||
setSyncState(prev => ({ ...prev, status: 'offline' }))
|
||||
},
|
||||
onOnline: () => {
|
||||
setIsOnline(true)
|
||||
setSyncState(prev => ({ ...prev, status: 'idle' }))
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// INITIAL STATE LOADER
|
||||
// =============================================================================
|
||||
|
||||
/**
|
||||
* Loads SDK state from localStorage and optionally from the server,
|
||||
* dispatching SET_STATE as appropriate.
|
||||
*/
|
||||
export async function loadInitialState(params: {
|
||||
storageKey: string
|
||||
enableBackendSync: boolean
|
||||
projectId?: string
|
||||
syncManager: StateSyncManager | null
|
||||
apiClient: SDKApiClient | null
|
||||
dispatch: React.Dispatch<ExtendedSDKAction>
|
||||
}): Promise<void> {
|
||||
const { storageKey, enableBackendSync, projectId, syncManager, apiClient, dispatch } = params
|
||||
|
||||
// First, try loading from localStorage
|
||||
const stored = localStorage.getItem(storageKey)
|
||||
if (stored) {
|
||||
const parsed = JSON.parse(stored)
|
||||
if (parsed.lastModified) {
|
||||
parsed.lastModified = new Date(parsed.lastModified)
|
||||
}
|
||||
dispatch({ type: 'SET_STATE', payload: parsed })
|
||||
}
|
||||
|
||||
// Then, try loading from server if backend sync is enabled
|
||||
if (enableBackendSync && syncManager) {
|
||||
const serverState = await syncManager.loadFromServer()
|
||||
if (serverState) {
|
||||
const localTime = stored ? new Date(JSON.parse(stored).lastModified).getTime() : 0
|
||||
const serverTime = new Date(serverState.lastModified).getTime()
|
||||
if (serverTime > localTime) {
|
||||
dispatch({ type: 'SET_STATE', payload: serverState })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Load project metadata (name, status, etc.) from backend
|
||||
if (enableBackendSync && projectId && apiClient) {
|
||||
try {
|
||||
const info = await apiClient.getProject(projectId)
|
||||
dispatch({ type: 'SET_STATE', payload: { projectInfo: info } })
|
||||
} catch (err) {
|
||||
console.warn('Failed to load project info:', err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// INIT / CLEANUP HELPERS
|
||||
// =============================================================================
|
||||
|
||||
export function initSyncInfra(
|
||||
enableBackendSync: boolean,
|
||||
tenantId: string,
|
||||
projectId: string | undefined,
|
||||
apiClientRef: React.MutableRefObject<SDKApiClient | null>,
|
||||
syncManagerRef: React.MutableRefObject<StateSyncManager | null>,
|
||||
callbacks: SyncCallbacks
|
||||
): void {
|
||||
if (!enableBackendSync || typeof window === 'undefined') return
|
||||
|
||||
apiClientRef.current = getSDKApiClient(tenantId, projectId)
|
||||
syncManagerRef.current = createStateSyncManager(
|
||||
apiClientRef.current,
|
||||
tenantId,
|
||||
{ debounceMs: 2000, maxRetries: 3 },
|
||||
callbacks,
|
||||
projectId
|
||||
)
|
||||
}
|
||||
|
||||
export function cleanupSyncInfra(
|
||||
enableBackendSync: boolean,
|
||||
syncManagerRef: React.MutableRefObject<StateSyncManager | null>,
|
||||
apiClientRef: React.MutableRefObject<SDKApiClient | null>
|
||||
): void {
|
||||
if (syncManagerRef.current) {
|
||||
syncManagerRef.current.destroy()
|
||||
syncManagerRef.current = null
|
||||
}
|
||||
if (enableBackendSync) {
|
||||
resetSDKApiClient()
|
||||
apiClientRef.current = null
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user