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>
This commit is contained in:
414
breakpilot-compliance-sdk/packages/core/src/state.ts
Normal file
414
breakpilot-compliance-sdk/packages/core/src/state.ts
Normal file
@@ -0,0 +1,414 @@
|
||||
/**
|
||||
* 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 }
|
||||
}
|
||||
Reference in New Issue
Block a user