Files
breakpilot-compliance/breakpilot-compliance-sdk/packages/core/src/state.ts
Benjamin Boenisch 4435e7ea0a Initial commit: breakpilot-compliance - Compliance SDK Platform
Services: Admin-Compliance, Backend-Compliance,
AI-Compliance-SDK, Consent-SDK, Developer-Portal,
PCA-Platform, DSMS

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-11 23:47:28 +01:00

415 lines
10 KiB
TypeScript

/**
* SDK State Management
*
* Reducer-based state management with persistence support
*/
import type {
SDKState,
SDKAction,
UserPreferences,
SDKPhase,
SubscriptionTier,
getStepById,
} from '@breakpilot/compliance-sdk-types'
// =============================================================================
// INITIAL STATE
// =============================================================================
const initialPreferences: UserPreferences = {
language: 'de',
theme: 'light',
compactMode: false,
showHints: true,
autoSave: true,
autoValidate: true,
}
export const initialState: SDKState = {
// Metadata
version: '1.0.0',
lastModified: new Date(),
// Tenant & User
tenantId: '',
userId: '',
subscription: 'PROFESSIONAL' as SubscriptionTier,
// Progress
currentPhase: 1 as SDKPhase,
currentStep: 'use-case-workshop',
completedSteps: [],
checkpoints: {},
// Phase 1 Data
useCases: [],
activeUseCase: null,
screening: null,
modules: [],
requirements: [],
controls: [],
evidence: [],
checklist: [],
risks: [],
// Phase 2 Data
aiActClassification: null,
obligations: [],
dsfa: null,
toms: [],
retentionPolicies: [],
vvt: [],
documents: [],
cookieBanner: null,
consents: [],
dsrConfig: null,
dsrRequests: [],
escalationWorkflows: [],
// Security
sbom: null,
securityIssues: [],
securityBacklog: [],
// UI State
commandBarHistory: [],
recentSearches: [],
preferences: initialPreferences,
}
// =============================================================================
// REDUCER
// =============================================================================
export function sdkReducer(state: SDKState, action: SDKAction): SDKState {
const updateState = (updates: Partial<SDKState>): SDKState => ({
...state,
...updates,
lastModified: new Date(),
})
switch (action.type) {
case 'SET_STATE':
return updateState(action.payload)
case 'SET_CURRENT_STEP': {
// Import dynamically to avoid circular dependencies
const { getStepById } = require('@breakpilot/compliance-sdk-types')
const step = getStepById(action.payload)
return updateState({
currentStep: action.payload,
currentPhase: step?.phase || state.currentPhase,
})
}
case 'COMPLETE_STEP':
if (state.completedSteps.includes(action.payload)) {
return state
}
return updateState({
completedSteps: [...state.completedSteps, action.payload],
})
case 'SET_CHECKPOINT_STATUS':
return updateState({
checkpoints: {
...state.checkpoints,
[action.payload.id]: action.payload.status,
},
})
// Use Cases
case 'ADD_USE_CASE':
return updateState({
useCases: [...state.useCases, action.payload],
})
case 'UPDATE_USE_CASE':
return updateState({
useCases: state.useCases.map(uc =>
uc.id === action.payload.id ? { ...uc, ...action.payload.data } : uc
),
})
case 'DELETE_USE_CASE':
return updateState({
useCases: state.useCases.filter(uc => uc.id !== action.payload),
activeUseCase: state.activeUseCase === action.payload ? null : state.activeUseCase,
})
case 'SET_ACTIVE_USE_CASE':
return updateState({ activeUseCase: action.payload })
// Screening
case 'SET_SCREENING':
return updateState({ screening: action.payload })
// Modules
case 'ADD_MODULE':
return updateState({
modules: [...state.modules, action.payload],
})
case 'UPDATE_MODULE':
return updateState({
modules: state.modules.map(m =>
m.id === action.payload.id ? { ...m, ...action.payload.data } : m
),
})
// Requirements
case 'ADD_REQUIREMENT':
return updateState({
requirements: [...state.requirements, action.payload],
})
case 'UPDATE_REQUIREMENT':
return updateState({
requirements: state.requirements.map(r =>
r.id === action.payload.id ? { ...r, ...action.payload.data } : r
),
})
// Controls
case 'ADD_CONTROL':
return updateState({
controls: [...state.controls, action.payload],
})
case 'UPDATE_CONTROL':
return updateState({
controls: state.controls.map(c =>
c.id === action.payload.id ? { ...c, ...action.payload.data } : c
),
})
// Evidence
case 'ADD_EVIDENCE':
return updateState({
evidence: [...state.evidence, action.payload],
})
case 'UPDATE_EVIDENCE':
return updateState({
evidence: state.evidence.map(e =>
e.id === action.payload.id ? { ...e, ...action.payload.data } : e
),
})
case 'DELETE_EVIDENCE':
return updateState({
evidence: state.evidence.filter(e => e.id !== action.payload),
})
// Risks
case 'ADD_RISK':
return updateState({
risks: [...state.risks, action.payload],
})
case 'UPDATE_RISK':
return updateState({
risks: state.risks.map(r =>
r.id === action.payload.id ? { ...r, ...action.payload.data } : r
),
})
case 'DELETE_RISK':
return updateState({
risks: state.risks.filter(r => r.id !== action.payload),
})
// AI Act
case 'SET_AI_ACT_RESULT':
return updateState({ aiActClassification: action.payload })
// Obligations
case 'ADD_OBLIGATION':
return updateState({
obligations: [...state.obligations, action.payload],
})
case 'UPDATE_OBLIGATION':
return updateState({
obligations: state.obligations.map(o =>
o.id === action.payload.id ? { ...o, ...action.payload.data } : o
),
})
// DSFA
case 'SET_DSFA':
return updateState({ dsfa: action.payload })
// TOMs
case 'ADD_TOM':
return updateState({
toms: [...state.toms, action.payload],
})
case 'UPDATE_TOM':
return updateState({
toms: state.toms.map(t =>
t.id === action.payload.id ? { ...t, ...action.payload.data } : t
),
})
// Retention Policies
case 'ADD_RETENTION_POLICY':
return updateState({
retentionPolicies: [...state.retentionPolicies, action.payload],
})
case 'UPDATE_RETENTION_POLICY':
return updateState({
retentionPolicies: state.retentionPolicies.map(p =>
p.id === action.payload.id ? { ...p, ...action.payload.data } : p
),
})
// Processing Activities (VVT)
case 'ADD_PROCESSING_ACTIVITY':
return updateState({
vvt: [...state.vvt, action.payload],
})
case 'UPDATE_PROCESSING_ACTIVITY':
return updateState({
vvt: state.vvt.map(p =>
p.id === action.payload.id ? { ...p, ...action.payload.data } : p
),
})
// Documents
case 'ADD_DOCUMENT':
return updateState({
documents: [...state.documents, action.payload],
})
case 'UPDATE_DOCUMENT':
return updateState({
documents: state.documents.map(d =>
d.id === action.payload.id ? { ...d, ...action.payload.data } : d
),
})
// Cookie Banner
case 'SET_COOKIE_BANNER':
return updateState({ cookieBanner: action.payload })
// DSR
case 'SET_DSR_CONFIG':
return updateState({ dsrConfig: action.payload })
case 'ADD_DSR_REQUEST':
return updateState({
dsrRequests: [...state.dsrRequests, action.payload],
})
case 'UPDATE_DSR_REQUEST':
return updateState({
dsrRequests: state.dsrRequests.map(r =>
r.id === action.payload.id ? { ...r, ...action.payload.data } : r
),
})
// Escalation Workflows
case 'ADD_ESCALATION_WORKFLOW':
return updateState({
escalationWorkflows: [...state.escalationWorkflows, action.payload],
})
case 'UPDATE_ESCALATION_WORKFLOW':
return updateState({
escalationWorkflows: state.escalationWorkflows.map(w =>
w.id === action.payload.id ? { ...w, ...action.payload.data } : w
),
})
// Security
case 'ADD_SECURITY_ISSUE':
return updateState({
securityIssues: [...state.securityIssues, action.payload],
})
case 'UPDATE_SECURITY_ISSUE':
return updateState({
securityIssues: state.securityIssues.map(i =>
i.id === action.payload.id ? { ...i, ...action.payload.data } : i
),
})
case 'ADD_BACKLOG_ITEM':
return updateState({
securityBacklog: [...state.securityBacklog, action.payload],
})
case 'UPDATE_BACKLOG_ITEM':
return updateState({
securityBacklog: state.securityBacklog.map(i =>
i.id === action.payload.id ? { ...i, ...action.payload.data } : i
),
})
// UI State
case 'ADD_COMMAND_HISTORY':
return updateState({
commandBarHistory: [action.payload, ...state.commandBarHistory].slice(0, 50),
})
case 'SET_PREFERENCES':
return updateState({
preferences: { ...state.preferences, ...action.payload },
})
case 'RESET_STATE':
return { ...initialState, lastModified: new Date() }
default:
return state
}
}
// =============================================================================
// STORE
// =============================================================================
export interface SDKStoreOptions {
tenantId: string
userId: string
initialState?: Partial<SDKState>
onChange?: (state: SDKState) => void
}
export interface SDKStore {
getState: () => SDKState
dispatch: (action: SDKAction) => void
subscribe: (listener: (state: SDKState) => void) => () => void
}
export function createStore(options: SDKStoreOptions): SDKStore {
let state: SDKState = {
...initialState,
tenantId: options.tenantId,
userId: options.userId,
...options.initialState,
}
const listeners = new Set<(state: SDKState) => void>()
const getState = () => state
const dispatch = (action: SDKAction) => {
state = sdkReducer(state, action)
listeners.forEach(listener => listener(state))
options.onChange?.(state)
}
const subscribe = (listener: (state: SDKState) => void) => {
listeners.add(listener)
return () => listeners.delete(listener)
}
return { getState, dispatch, subscribe }
}