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>
121 lines
2.9 KiB
TypeScript
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()
|
|
}
|