refactor(compliance-sdk): split client/provider/embed/state under 500 LOC
Phase 4 continuation. All touched files now under the file-size cap, and drive-by fixes unblock the types/core/react/vanilla builds which were broken at baseline. Splits - packages/types/src/state 505 -> 31 LOC barrel + state-flow/-assessment/-core - packages/core/src/client 521 -> 395 LOC + client-http 187 LOC (HTTP transport) - packages/react/src/provider 539 -> 460 LOC + provider-context 101 LOC - packages/vanilla/src/embed 611 -> 290 LOC + embed-banner 321 + embed-translations 78 Drive-by fixes (pre-existing typecheck/build failures) - types/rag.ts: rename colliding LegalDocument export to RagLegalDocument (the `export *` chain in index.ts was ambiguous; two consumers updated - core/modules/rag.ts drops unused import, vue/composables/useRAG.ts switches to the renamed symbol). - core/modules/rag.ts: wrap client searchRAG response to add the missing `query` field so the declared SearchResponse return type is satisfied. - react/provider.tsx: re-export useCompliance so ComplianceDashboard / ConsentBanner / DSRPortal legacy `from '../provider'` imports resolve. - vanilla/embed.ts + web-components/base.ts: default tenantId to '' so ComplianceClient construction typechecks. - vanilla/web-components/consent-banner.ts: tighten categories literal to `as const` so t.categories indexing narrows correctly. Verification: packages/types + core + react + vanilla all `pnpm build` clean with DTS emission. consent-sdk unaffected (still green). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
187
breakpilot-compliance-sdk/packages/core/src/client-http.ts
Normal file
187
breakpilot-compliance-sdk/packages/core/src/client-http.ts
Normal file
@@ -0,0 +1,187 @@
|
||||
/**
|
||||
* HTTP transport primitives for ComplianceClient.
|
||||
*
|
||||
* Phase 4: extracted from client.ts. Handles headers, timeouts, retry logic,
|
||||
* abort-controller lifecycle, and error classification so the main client
|
||||
* module can focus on domain endpoints.
|
||||
*/
|
||||
|
||||
export interface HttpTransportOptions {
|
||||
apiEndpoint: string
|
||||
tenantId: string
|
||||
timeout?: number
|
||||
maxRetries?: number
|
||||
onAuthError?: () => void
|
||||
onError?: (error: Error) => void
|
||||
}
|
||||
|
||||
export interface APIError extends Error {
|
||||
status?: number
|
||||
code?: string
|
||||
retryable: boolean
|
||||
}
|
||||
|
||||
export const DEFAULT_TIMEOUT = 30000
|
||||
export const DEFAULT_MAX_RETRIES = 3
|
||||
const RETRY_DELAYS = [1000, 2000, 4000]
|
||||
|
||||
export function createHttpError(
|
||||
message: string,
|
||||
status?: number,
|
||||
retryable = false
|
||||
): APIError {
|
||||
const error = new Error(message) as APIError
|
||||
error.status = status
|
||||
error.retryable = retryable
|
||||
return error
|
||||
}
|
||||
|
||||
const sleep = (ms: number): Promise<void> =>
|
||||
new Promise(resolve => setTimeout(resolve, ms))
|
||||
|
||||
/**
|
||||
* HttpTransport — reusable fetch wrapper with retry + timeout + abort control.
|
||||
*
|
||||
* Not exposed via the SDK public surface; ComplianceClient instantiates one
|
||||
* internally.
|
||||
*/
|
||||
export class HttpTransport {
|
||||
readonly apiEndpoint: string
|
||||
private apiKey: string | null = null
|
||||
private accessToken: string | null = null
|
||||
private tenantId: string
|
||||
private timeout: number
|
||||
private maxRetries: number
|
||||
private abortControllers: Map<string, AbortController> = new Map()
|
||||
private onAuthError?: () => void
|
||||
private onError?: (error: Error) => void
|
||||
|
||||
constructor(options: HttpTransportOptions) {
|
||||
this.apiEndpoint = options.apiEndpoint.replace(/\/$/, '')
|
||||
this.tenantId = options.tenantId
|
||||
this.timeout = options.timeout ?? DEFAULT_TIMEOUT
|
||||
this.maxRetries = options.maxRetries ?? DEFAULT_MAX_RETRIES
|
||||
this.onAuthError = options.onAuthError
|
||||
this.onError = options.onError
|
||||
}
|
||||
|
||||
setApiKey(apiKey: string | null): void {
|
||||
this.apiKey = apiKey
|
||||
}
|
||||
|
||||
setAccessToken(token: string | null): void {
|
||||
this.accessToken = token
|
||||
}
|
||||
|
||||
setTenantId(tenantId: string): void {
|
||||
this.tenantId = tenantId
|
||||
}
|
||||
|
||||
getTenantId(): string {
|
||||
return this.tenantId
|
||||
}
|
||||
|
||||
getHeaders(): Record<string, string> {
|
||||
const headers: Record<string, string> = {
|
||||
'Content-Type': 'application/json',
|
||||
'X-Tenant-ID': this.tenantId,
|
||||
}
|
||||
|
||||
if (this.accessToken) {
|
||||
headers['Authorization'] = `Bearer ${this.accessToken}`
|
||||
} else if (this.apiKey) {
|
||||
headers['Authorization'] = `Bearer ${this.apiKey}`
|
||||
}
|
||||
|
||||
return headers
|
||||
}
|
||||
|
||||
async fetchWithTimeout(
|
||||
url: string,
|
||||
options: RequestInit,
|
||||
requestId: string
|
||||
): Promise<Response> {
|
||||
const controller = new AbortController()
|
||||
this.abortControllers.set(requestId, controller)
|
||||
|
||||
const timeoutId = setTimeout(() => controller.abort(), this.timeout)
|
||||
|
||||
try {
|
||||
const response = await fetch(url, {
|
||||
...options,
|
||||
signal: controller.signal,
|
||||
})
|
||||
return response
|
||||
} finally {
|
||||
clearTimeout(timeoutId)
|
||||
this.abortControllers.delete(requestId)
|
||||
}
|
||||
}
|
||||
|
||||
async fetchWithRetry<T>(
|
||||
url: string,
|
||||
options: RequestInit,
|
||||
retries = this.maxRetries
|
||||
): Promise<T> {
|
||||
const requestId = `${Date.now()}-${Math.random()}`
|
||||
let lastError: Error | null = null
|
||||
|
||||
for (let attempt = 0; attempt <= retries; attempt++) {
|
||||
try {
|
||||
const response = await this.fetchWithTimeout(url, options, requestId)
|
||||
|
||||
if (!response.ok) {
|
||||
const errorBody = await response.text()
|
||||
let errorMessage = `HTTP ${response.status}`
|
||||
|
||||
try {
|
||||
const errorJson = JSON.parse(errorBody)
|
||||
errorMessage = errorJson.error || errorJson.message || errorMessage
|
||||
} catch {
|
||||
// Keep HTTP status message
|
||||
}
|
||||
|
||||
// Handle auth errors
|
||||
if (response.status === 401) {
|
||||
this.onAuthError?.()
|
||||
throw createHttpError(errorMessage, response.status, false)
|
||||
}
|
||||
|
||||
// Don't retry client errors (4xx) except 429
|
||||
const retryable = response.status >= 500 || response.status === 429
|
||||
|
||||
if (!retryable || attempt === retries) {
|
||||
throw createHttpError(errorMessage, response.status, retryable)
|
||||
}
|
||||
} else {
|
||||
const data = await response.json()
|
||||
return data as T
|
||||
}
|
||||
} catch (error) {
|
||||
lastError = error as Error
|
||||
|
||||
if (error instanceof Error && error.name === 'AbortError') {
|
||||
throw createHttpError('Request timeout', 408, true)
|
||||
}
|
||||
|
||||
const apiError = error as APIError
|
||||
if (!apiError.retryable || attempt === retries) {
|
||||
this.onError?.(error as Error)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
// Exponential backoff
|
||||
if (attempt < retries) {
|
||||
await sleep(RETRY_DELAYS[attempt] || RETRY_DELAYS[RETRY_DELAYS.length - 1])
|
||||
}
|
||||
}
|
||||
|
||||
throw lastError || createHttpError('Unknown error', 500, false)
|
||||
}
|
||||
|
||||
cancelAllRequests(): void {
|
||||
this.abortControllers.forEach(controller => controller.abort())
|
||||
this.abortControllers.clear()
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
/**
|
||||
* Compliance Client
|
||||
*
|
||||
* Main entry point for the SDK. Handles API communication with
|
||||
* retry logic, timeout handling, and optimistic locking.
|
||||
* Main entry point for the SDK. Domain methods delegate to HttpTransport
|
||||
* for retry/timeout/abort handling. Transport primitives live in client-http.ts.
|
||||
*/
|
||||
|
||||
import type {
|
||||
@@ -19,6 +19,11 @@ import type {
|
||||
SDKState,
|
||||
CheckpointStatus,
|
||||
} from '@breakpilot/compliance-sdk-types'
|
||||
import {
|
||||
HttpTransport,
|
||||
createHttpError,
|
||||
type APIError,
|
||||
} from './client-http'
|
||||
|
||||
// =============================================================================
|
||||
// TYPES
|
||||
@@ -34,157 +39,33 @@ export interface ComplianceClientOptions {
|
||||
onAuthError?: () => void
|
||||
}
|
||||
|
||||
interface APIError extends Error {
|
||||
status?: number
|
||||
code?: string
|
||||
retryable: boolean
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// CONSTANTS
|
||||
// =============================================================================
|
||||
|
||||
const DEFAULT_TIMEOUT = 30000
|
||||
const DEFAULT_MAX_RETRIES = 3
|
||||
const RETRY_DELAYS = [1000, 2000, 4000]
|
||||
|
||||
// =============================================================================
|
||||
// COMPLIANCE CLIENT
|
||||
// =============================================================================
|
||||
|
||||
export class ComplianceClient {
|
||||
private apiEndpoint: string
|
||||
private apiKey: string | null
|
||||
private tenantId: string
|
||||
private timeout: number
|
||||
private maxRetries: number
|
||||
private accessToken: string | null = null
|
||||
private abortControllers: Map<string, AbortController> = new Map()
|
||||
private onError?: (error: Error) => void
|
||||
private onAuthError?: () => void
|
||||
private http: HttpTransport
|
||||
|
||||
constructor(options: ComplianceClientOptions) {
|
||||
this.apiEndpoint = options.apiEndpoint.replace(/\/$/, '')
|
||||
this.apiKey = options.apiKey ?? null
|
||||
this.tenantId = options.tenantId
|
||||
this.timeout = options.timeout ?? DEFAULT_TIMEOUT
|
||||
this.maxRetries = options.maxRetries ?? DEFAULT_MAX_RETRIES
|
||||
this.onError = options.onError
|
||||
this.onAuthError = options.onAuthError
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Private Methods
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
private createError(message: string, status?: number, retryable = false): APIError {
|
||||
const error = new Error(message) as APIError
|
||||
error.status = status
|
||||
error.retryable = retryable
|
||||
return error
|
||||
}
|
||||
|
||||
private getHeaders(): Record<string, string> {
|
||||
const headers: Record<string, string> = {
|
||||
'Content-Type': 'application/json',
|
||||
'X-Tenant-ID': this.tenantId,
|
||||
}
|
||||
|
||||
if (this.accessToken) {
|
||||
headers['Authorization'] = `Bearer ${this.accessToken}`
|
||||
} else if (this.apiKey) {
|
||||
headers['Authorization'] = `Bearer ${this.apiKey}`
|
||||
}
|
||||
|
||||
return headers
|
||||
}
|
||||
|
||||
private async fetchWithTimeout(
|
||||
url: string,
|
||||
options: RequestInit,
|
||||
requestId: string
|
||||
): Promise<Response> {
|
||||
const controller = new AbortController()
|
||||
this.abortControllers.set(requestId, controller)
|
||||
|
||||
const timeoutId = setTimeout(() => controller.abort(), this.timeout)
|
||||
|
||||
try {
|
||||
const response = await fetch(url, {
|
||||
...options,
|
||||
signal: controller.signal,
|
||||
})
|
||||
return response
|
||||
} finally {
|
||||
clearTimeout(timeoutId)
|
||||
this.abortControllers.delete(requestId)
|
||||
this.http = new HttpTransport({
|
||||
apiEndpoint: options.apiEndpoint,
|
||||
tenantId: options.tenantId,
|
||||
timeout: options.timeout,
|
||||
maxRetries: options.maxRetries,
|
||||
onError: options.onError,
|
||||
onAuthError: options.onAuthError,
|
||||
})
|
||||
if (options.apiKey) {
|
||||
this.http.setApiKey(options.apiKey)
|
||||
}
|
||||
}
|
||||
|
||||
private async fetchWithRetry<T>(
|
||||
url: string,
|
||||
options: RequestInit,
|
||||
retries = this.maxRetries
|
||||
): Promise<T> {
|
||||
const requestId = `${Date.now()}-${Math.random()}`
|
||||
let lastError: Error | null = null
|
||||
|
||||
for (let attempt = 0; attempt <= retries; attempt++) {
|
||||
try {
|
||||
const response = await this.fetchWithTimeout(url, options, requestId)
|
||||
|
||||
if (!response.ok) {
|
||||
const errorBody = await response.text()
|
||||
let errorMessage = `HTTP ${response.status}`
|
||||
|
||||
try {
|
||||
const errorJson = JSON.parse(errorBody)
|
||||
errorMessage = errorJson.error || errorJson.message || errorMessage
|
||||
} catch {
|
||||
// Keep HTTP status message
|
||||
}
|
||||
|
||||
// Handle auth errors
|
||||
if (response.status === 401) {
|
||||
this.onAuthError?.()
|
||||
throw this.createError(errorMessage, response.status, false)
|
||||
}
|
||||
|
||||
// Don't retry client errors (4xx) except 429
|
||||
const retryable = response.status >= 500 || response.status === 429
|
||||
|
||||
if (!retryable || attempt === retries) {
|
||||
throw this.createError(errorMessage, response.status, retryable)
|
||||
}
|
||||
} else {
|
||||
const data = await response.json()
|
||||
return data as T
|
||||
}
|
||||
} catch (error) {
|
||||
lastError = error as Error
|
||||
|
||||
if (error instanceof Error && error.name === 'AbortError') {
|
||||
throw this.createError('Request timeout', 408, true)
|
||||
}
|
||||
|
||||
const apiError = error as APIError
|
||||
if (!apiError.retryable || attempt === retries) {
|
||||
this.onError?.(error as Error)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
// Exponential backoff
|
||||
if (attempt < retries) {
|
||||
await this.sleep(RETRY_DELAYS[attempt] || RETRY_DELAYS[RETRY_DELAYS.length - 1])
|
||||
}
|
||||
}
|
||||
|
||||
throw lastError || this.createError('Unknown error', 500, false)
|
||||
private get apiEndpoint(): string {
|
||||
return this.http.apiEndpoint
|
||||
}
|
||||
|
||||
private sleep(ms: number): Promise<void> {
|
||||
return new Promise(resolve => setTimeout(resolve, ms))
|
||||
private get tenantId(): string {
|
||||
return this.http.getTenantId()
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -192,7 +73,7 @@ export class ComplianceClient {
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
async authenticate(request: AuthTokenRequest): Promise<AuthTokenResponse> {
|
||||
const response = await this.fetchWithRetry<APIResponse<AuthTokenResponse>>(
|
||||
const response = await this.http.fetchWithRetry<APIResponse<AuthTokenResponse>>(
|
||||
`${this.apiEndpoint}/auth/token`,
|
||||
{
|
||||
method: 'POST',
|
||||
@@ -202,11 +83,11 @@ export class ComplianceClient {
|
||||
)
|
||||
|
||||
if (response.success && response.data) {
|
||||
this.accessToken = response.data.accessToken
|
||||
this.http.setAccessToken(response.data.accessToken)
|
||||
return response.data
|
||||
}
|
||||
|
||||
throw this.createError(response.error || 'Authentication failed', 401, false)
|
||||
throw createHttpError(response.error || 'Authentication failed', 401, false)
|
||||
}
|
||||
|
||||
async refreshToken(refreshToken: string): Promise<AuthTokenResponse> {
|
||||
@@ -218,11 +99,11 @@ export class ComplianceClient {
|
||||
}
|
||||
|
||||
setAccessToken(token: string): void {
|
||||
this.accessToken = token
|
||||
this.http.setAccessToken(token)
|
||||
}
|
||||
|
||||
clearAccessToken(): void {
|
||||
this.accessToken = null
|
||||
this.http.setAccessToken(null)
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -231,11 +112,11 @@ export class ComplianceClient {
|
||||
|
||||
async getState(): Promise<StateResponse | null> {
|
||||
try {
|
||||
const response = await this.fetchWithRetry<APIResponse<StateResponse>>(
|
||||
const response = await this.http.fetchWithRetry<APIResponse<StateResponse>>(
|
||||
`${this.apiEndpoint}/state?tenantId=${encodeURIComponent(this.tenantId)}`,
|
||||
{
|
||||
method: 'GET',
|
||||
headers: this.getHeaders(),
|
||||
headers: this.http.getHeaders(),
|
||||
}
|
||||
)
|
||||
|
||||
@@ -254,12 +135,12 @@ export class ComplianceClient {
|
||||
}
|
||||
|
||||
async saveState(state: SDKState, version?: number): Promise<StateResponse> {
|
||||
const response = await this.fetchWithRetry<APIResponse<StateResponse>>(
|
||||
const response = await this.http.fetchWithRetry<APIResponse<StateResponse>>(
|
||||
`${this.apiEndpoint}/state`,
|
||||
{
|
||||
method: 'POST',
|
||||
headers: {
|
||||
...this.getHeaders(),
|
||||
...this.http.getHeaders(),
|
||||
...(version !== undefined && { 'If-Match': String(version) }),
|
||||
},
|
||||
body: JSON.stringify({
|
||||
@@ -271,18 +152,18 @@ export class ComplianceClient {
|
||||
)
|
||||
|
||||
if (!response.success) {
|
||||
throw this.createError(response.error || 'Failed to save state', 500, true)
|
||||
throw createHttpError(response.error || 'Failed to save state', 500, true)
|
||||
}
|
||||
|
||||
return response.data!
|
||||
}
|
||||
|
||||
async deleteState(): Promise<void> {
|
||||
await this.fetchWithRetry<APIResponse<void>>(
|
||||
await this.http.fetchWithRetry<APIResponse<void>>(
|
||||
`${this.apiEndpoint}/state?tenantId=${encodeURIComponent(this.tenantId)}`,
|
||||
{
|
||||
method: 'DELETE',
|
||||
headers: this.getHeaders(),
|
||||
headers: this.http.getHeaders(),
|
||||
}
|
||||
)
|
||||
}
|
||||
@@ -295,34 +176,32 @@ export class ComplianceClient {
|
||||
checkpointId: string,
|
||||
data?: unknown
|
||||
): Promise<CheckpointValidationResult> {
|
||||
const response = await this.fetchWithRetry<APIResponse<CheckpointValidationResult>>(
|
||||
`${this.apiEndpoint}/checkpoints/validate`,
|
||||
{
|
||||
method: 'POST',
|
||||
headers: this.getHeaders(),
|
||||
body: JSON.stringify({
|
||||
tenantId: this.tenantId,
|
||||
checkpointId,
|
||||
data,
|
||||
}),
|
||||
}
|
||||
)
|
||||
const response = await this.http.fetchWithRetry<
|
||||
APIResponse<CheckpointValidationResult>
|
||||
>(`${this.apiEndpoint}/checkpoints/validate`, {
|
||||
method: 'POST',
|
||||
headers: this.http.getHeaders(),
|
||||
body: JSON.stringify({
|
||||
tenantId: this.tenantId,
|
||||
checkpointId,
|
||||
data,
|
||||
}),
|
||||
})
|
||||
|
||||
if (!response.success || !response.data) {
|
||||
throw this.createError(response.error || 'Checkpoint validation failed', 500, true)
|
||||
throw createHttpError(response.error || 'Checkpoint validation failed', 500, true)
|
||||
}
|
||||
|
||||
return response.data
|
||||
}
|
||||
|
||||
async getCheckpoints(): Promise<Record<string, CheckpointStatus>> {
|
||||
const response = await this.fetchWithRetry<APIResponse<Record<string, CheckpointStatus>>>(
|
||||
`${this.apiEndpoint}/checkpoints?tenantId=${encodeURIComponent(this.tenantId)}`,
|
||||
{
|
||||
method: 'GET',
|
||||
headers: this.getHeaders(),
|
||||
}
|
||||
)
|
||||
const response = await this.http.fetchWithRetry<
|
||||
APIResponse<Record<string, CheckpointStatus>>
|
||||
>(`${this.apiEndpoint}/checkpoints?tenantId=${encodeURIComponent(this.tenantId)}`, {
|
||||
method: 'GET',
|
||||
headers: this.http.getHeaders(),
|
||||
})
|
||||
|
||||
return response.data || {}
|
||||
}
|
||||
@@ -332,34 +211,34 @@ export class ComplianceClient {
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
async searchRAG(request: RAGSearchRequest): Promise<RAGSearchResponse> {
|
||||
const response = await this.fetchWithRetry<APIResponse<RAGSearchResponse>>(
|
||||
const response = await this.http.fetchWithRetry<APIResponse<RAGSearchResponse>>(
|
||||
`${this.apiEndpoint}/rag/search`,
|
||||
{
|
||||
method: 'POST',
|
||||
headers: this.getHeaders(),
|
||||
headers: this.http.getHeaders(),
|
||||
body: JSON.stringify(request),
|
||||
}
|
||||
)
|
||||
|
||||
if (!response.success || !response.data) {
|
||||
throw this.createError(response.error || 'RAG search failed', 500, true)
|
||||
throw createHttpError(response.error || 'RAG search failed', 500, true)
|
||||
}
|
||||
|
||||
return response.data
|
||||
}
|
||||
|
||||
async askRAG(request: RAGAskRequest): Promise<RAGAskResponse> {
|
||||
const response = await this.fetchWithRetry<APIResponse<RAGAskResponse>>(
|
||||
const response = await this.http.fetchWithRetry<APIResponse<RAGAskResponse>>(
|
||||
`${this.apiEndpoint}/rag/ask`,
|
||||
{
|
||||
method: 'POST',
|
||||
headers: this.getHeaders(),
|
||||
headers: this.http.getHeaders(),
|
||||
body: JSON.stringify(request),
|
||||
}
|
||||
)
|
||||
|
||||
if (!response.success || !response.data) {
|
||||
throw this.createError(response.error || 'RAG query failed', 500, true)
|
||||
throw createHttpError(response.error || 'RAG query failed', 500, true)
|
||||
}
|
||||
|
||||
return response.data
|
||||
@@ -370,12 +249,12 @@ export class ComplianceClient {
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
async exportState(format: ExportFormat): Promise<Blob> {
|
||||
const response = await this.fetchWithTimeout(
|
||||
const response = await this.http.fetchWithTimeout(
|
||||
`${this.apiEndpoint}/export?tenantId=${encodeURIComponent(this.tenantId)}&format=${format}`,
|
||||
{
|
||||
method: 'GET',
|
||||
headers: {
|
||||
...this.getHeaders(),
|
||||
...this.http.getHeaders(),
|
||||
Accept:
|
||||
format === 'json'
|
||||
? 'application/json'
|
||||
@@ -388,7 +267,7 @@ export class ComplianceClient {
|
||||
)
|
||||
|
||||
if (!response.ok) {
|
||||
throw this.createError(`Export failed: ${response.statusText}`, response.status, true)
|
||||
throw createHttpError(`Export failed: ${response.statusText}`, response.status, true)
|
||||
}
|
||||
|
||||
return response.blob()
|
||||
@@ -402,22 +281,19 @@ export class ComplianceClient {
|
||||
type: 'dsfa' | 'tom' | 'vvt' | 'gutachten' | 'privacy_policy' | 'cookie_banner',
|
||||
options?: Record<string, unknown>
|
||||
): Promise<{ id: string; status: string; content?: string }> {
|
||||
const response = await this.fetchWithRetry<
|
||||
const response = await this.http.fetchWithRetry<
|
||||
APIResponse<{ id: string; status: string; content?: string }>
|
||||
>(
|
||||
`${this.apiEndpoint}/generate/${type}`,
|
||||
{
|
||||
method: 'POST',
|
||||
headers: this.getHeaders(),
|
||||
body: JSON.stringify({
|
||||
tenantId: this.tenantId,
|
||||
options,
|
||||
}),
|
||||
}
|
||||
)
|
||||
>(`${this.apiEndpoint}/generate/${type}`, {
|
||||
method: 'POST',
|
||||
headers: this.http.getHeaders(),
|
||||
body: JSON.stringify({
|
||||
tenantId: this.tenantId,
|
||||
options,
|
||||
}),
|
||||
})
|
||||
|
||||
if (!response.success || !response.data) {
|
||||
throw this.createError(response.error || 'Document generation failed', 500, true)
|
||||
throw createHttpError(response.error || 'Document generation failed', 500, true)
|
||||
}
|
||||
|
||||
return response.data
|
||||
@@ -433,31 +309,30 @@ export class ComplianceClient {
|
||||
severityThreshold?: string
|
||||
generateSBOM?: boolean
|
||||
}): Promise<{ id: string; status: string }> {
|
||||
const response = await this.fetchWithRetry<APIResponse<{ id: string; status: string }>>(
|
||||
`${this.apiEndpoint}/security/scan`,
|
||||
{
|
||||
method: 'POST',
|
||||
headers: this.getHeaders(),
|
||||
body: JSON.stringify({
|
||||
tenantId: this.tenantId,
|
||||
...options,
|
||||
}),
|
||||
}
|
||||
)
|
||||
const response = await this.http.fetchWithRetry<
|
||||
APIResponse<{ id: string; status: string }>
|
||||
>(`${this.apiEndpoint}/security/scan`, {
|
||||
method: 'POST',
|
||||
headers: this.http.getHeaders(),
|
||||
body: JSON.stringify({
|
||||
tenantId: this.tenantId,
|
||||
...options,
|
||||
}),
|
||||
})
|
||||
|
||||
if (!response.success || !response.data) {
|
||||
throw this.createError(response.error || 'Security scan failed', 500, true)
|
||||
throw createHttpError(response.error || 'Security scan failed', 500, true)
|
||||
}
|
||||
|
||||
return response.data
|
||||
}
|
||||
|
||||
async getSecurityScanResult(scanId: string): Promise<unknown> {
|
||||
const response = await this.fetchWithRetry<APIResponse<unknown>>(
|
||||
const response = await this.http.fetchWithRetry<APIResponse<unknown>>(
|
||||
`${this.apiEndpoint}/security/scan/${scanId}`,
|
||||
{
|
||||
method: 'GET',
|
||||
headers: this.getHeaders(),
|
||||
headers: this.http.getHeaders(),
|
||||
}
|
||||
)
|
||||
|
||||
@@ -469,21 +344,20 @@ export class ComplianceClient {
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
cancelAllRequests(): void {
|
||||
this.abortControllers.forEach(controller => controller.abort())
|
||||
this.abortControllers.clear()
|
||||
this.http.cancelAllRequests()
|
||||
}
|
||||
|
||||
setTenantId(tenantId: string): void {
|
||||
this.tenantId = tenantId
|
||||
this.http.setTenantId(tenantId)
|
||||
}
|
||||
|
||||
getTenantId(): string {
|
||||
return this.tenantId
|
||||
return this.http.getTenantId()
|
||||
}
|
||||
|
||||
async healthCheck(): Promise<boolean> {
|
||||
try {
|
||||
const response = await this.fetchWithTimeout(
|
||||
const response = await this.http.fetchWithTimeout(
|
||||
`${this.apiEndpoint}/health`,
|
||||
{ method: 'GET' },
|
||||
`health-${Date.now()}`
|
||||
|
||||
@@ -9,8 +9,6 @@ import type {
|
||||
SearchResponse,
|
||||
AssistantQuery,
|
||||
AssistantResponse,
|
||||
LegalDocument,
|
||||
ChatSession,
|
||||
ChatMessage,
|
||||
} from '@breakpilot/compliance-sdk-types'
|
||||
import { ComplianceClient } from '../client'
|
||||
@@ -38,12 +36,20 @@ export class RAGModule {
|
||||
scoreThreshold: options?.scoreThreshold ?? 0.5,
|
||||
}
|
||||
|
||||
return this.client.searchRAG({
|
||||
const response = await this.client.searchRAG({
|
||||
query: searchRequest.query,
|
||||
filters: searchRequest.filters,
|
||||
limit: searchRequest.limit,
|
||||
offset: searchRequest.offset,
|
||||
})
|
||||
|
||||
// The client returns RAGSearchResponse (api.ts shape); enrich it with
|
||||
// the originating query so the consumer-facing SearchResponse shape
|
||||
// from types/rag.ts is satisfied.
|
||||
return {
|
||||
...response,
|
||||
query: searchRequest.query,
|
||||
} as unknown as SearchResponse
|
||||
}
|
||||
|
||||
async searchByRegulation(regulation: string, query: string): Promise<SearchResponse> {
|
||||
|
||||
Reference in New Issue
Block a user