Files
breakpilot-compliance/admin-compliance/lib/sdk/api-client-state.ts
Sharang Parnerkar e07e1de6c9 refactor(admin): split api-client.ts (885 LOC) and endpoints.ts (1262 LOC) into focused modules
api-client.ts is now a thin delegating class (263 LOC) backed by:
  - api-client-types.ts (84) — shared types, config, FetchContext
  - api-client-state.ts (120) — state CRUD + export
  - api-client-projects.ts (160) — project management
  - api-client-wiki.ts (116) — wiki knowledge base
  - api-client-operations.ts (299) — checkpoints, flow, modules, UCCA, import, screening

endpoints.ts is now a barrel (25 LOC) aggregating the 4 existing domain files
(endpoints-python-core, endpoints-python-gdpr, endpoints-python-ops, endpoints-go).

All files stay under the 500-line hard cap. Build verified with `npx next build`.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 19:17:38 +02:00

121 lines
2.9 KiB
TypeScript

/**
* SDK API Client — State management methods.
* (getState, saveState, deleteState, exportState)
*/
import {
APIResponse,
APIError,
StateResponse,
FetchContext,
SDKState,
} from './api-client-types'
/**
* Load SDK state for the current tenant
*/
export async function getState(ctx: FetchContext): Promise<StateResponse | null> {
try {
const params = new URLSearchParams({ tenantId: ctx.tenantId })
if (ctx.projectId) params.set('projectId', ctx.projectId)
const response = await ctx.fetchWithRetry<APIResponse<StateResponse>>(
`${ctx.baseUrl}/state?${params.toString()}`,
{
method: 'GET',
headers: { 'Content-Type': 'application/json' },
}
)
if (response.success && response.data) {
return response.data
}
return null
} catch (error) {
const apiError = error as APIError
// 404 means no state exists yet - that's okay
if (apiError.status === 404) {
return null
}
throw error
}
}
/**
* Save SDK state for the current tenant.
* Supports optimistic locking via version parameter.
*/
export async function saveState(
ctx: FetchContext,
state: SDKState,
version?: number
): Promise<StateResponse> {
const response = await ctx.fetchWithRetry<APIResponse<StateResponse>>(
`${ctx.baseUrl}/state`,
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
...(version !== undefined && { 'If-Match': String(version) }),
},
body: JSON.stringify({
tenantId: ctx.tenantId,
projectId: ctx.projectId,
state,
version,
}),
}
)
if (!response.success) {
throw ctx.createError(response.error || 'Failed to save state', 500, true)
}
return response.data!
}
/**
* Delete SDK state for the current tenant
*/
export async function deleteState(ctx: FetchContext): Promise<void> {
const params = new URLSearchParams({ tenantId: ctx.tenantId })
if (ctx.projectId) params.set('projectId', ctx.projectId)
await ctx.fetchWithRetry<APIResponse<void>>(
`${ctx.baseUrl}/state?${params.toString()}`,
{
method: 'DELETE',
headers: { 'Content-Type': 'application/json' },
}
)
}
/**
* Export SDK state in various formats
*/
export async function exportState(
ctx: FetchContext,
format: 'json' | 'pdf' | 'zip'
): Promise<Blob> {
const response = await ctx.fetchWithTimeout(
`${ctx.baseUrl}/export?tenantId=${encodeURIComponent(ctx.tenantId)}&format=${format}`,
{
method: 'GET',
headers: {
'Accept':
format === 'json'
? 'application/json'
: format === 'pdf'
? 'application/pdf'
: 'application/zip',
},
},
`export-${Date.now()}`
)
if (!response.ok) {
throw ctx.createError(`Export failed: ${response.statusText}`, response.status, true)
}
return response.blob()
}