{"version":3,"sources":["../src/retention.ts"],"sourcesContent":["/**\n * Retention and Purge Policies for postgres.do\n *\n * Implements comprehensive data retention management with support for:\n * - Time-based retention (e.g., keep last 30 days)\n * - Size-based retention (e.g., keep under 1GB)\n * - Count-based retention (e.g., keep last 1000 records)\n * - Custom policies with complex predicates\n *\n * Compliance features:\n * - GDPR right-to-erasure support\n * - Legal hold capability\n * - Audit trail for all deletions\n * - Cascade policies across related data\n *\n * @module retention\n */\n\n/**\n * Types of retention policies\n */\nexport type RetentionType = 'time' | 'size' | 'count' | 'custom'\n\n/**\n * Time units for time-based retention\n */\nexport type TimeUnit = 'seconds' | 'minutes' | 'hours' | 'days' | 'weeks' | 'months' | 'years'\n\n/**\n * Size units for size-based retention\n */\nexport type SizeUnit = 'bytes' | 'kb' | 'mb' | 'gb' | 'tb'\n\n/**\n * Purge action types\n */\nexport type PurgeAction = 'soft_delete' | 'hard_delete' | 'archive' | 'cryptographic_erasure'\n\n/**\n * Status of a retention policy\n */\nexport type PolicyStatus = 'active' | 'paused' | 'disabled'\n\n/**\n * Compliance framework identifiers\n */\nexport type ComplianceFramework = 'gdpr' | 'hipaa' | 'sox' | 'pci_dss' | 'ccpa' | 'custom'\n\n/**\n * Base retention policy interface\n */\nexport interface RetentionPolicy {\n  /** Unique identifier for the policy */\n  id: string\n  /** Human-readable name */\n  name: string\n  /** Description of the policy */\n  description?: string | undefined\n  /** Type of retention policy */\n  type: RetentionType\n  /** Numeric value for the retention threshold */\n  value: number\n  /** Unit for the value (time, size, or count) */\n  unit?: TimeUnit | SizeUnit | undefined\n  /** Target table(s) or data type(s) */\n  targets: RetentionTarget[]\n  /** Current status of the policy */\n  status: PolicyStatus\n  /** When the policy was created */\n  createdAt: Date\n  /** When the policy was last updated */\n  updatedAt: Date\n  /** Priority (lower = higher priority) */\n  priority: number\n  /** Compliance frameworks this policy supports */\n  compliance?: ComplianceFramework[]\n  /** Schedule for policy evaluation (cron expression or interval) */\n  schedule?: RetentionSchedule\n  /** Action to take when policy triggers */\n  action: PurgeAction\n  /** Whether to cascade to related data */\n  cascade?: CascadePolicy\n}\n\n/**\n * Target specification for retention policies\n */\nexport interface RetentionTarget {\n  /** Table name or pattern */\n  table: string\n  /** Schema name (default: public) */\n  schema?: string\n  /** Column to evaluate for time-based retention */\n  timestampColumn?: string\n  /** Column to evaluate for size calculation */\n  sizeColumn?: string\n  /** Additional WHERE clause predicate */\n  predicate?: string\n}\n\n/**\n * Schedule configuration for automatic policy execution\n */\nexport interface RetentionSchedule {\n  /** Cron expression (e.g., \"0 0 * * *\" for daily at midnight) */\n  cron?: string\n  /** Interval in milliseconds */\n  intervalMs?: number\n  /** Timezone for cron expressions */\n  timezone?: string\n  /** Whether to run immediately on policy activation */\n  runOnActivation?: boolean\n}\n\n/**\n * Cascade policy for related data deletion\n */\nexport interface CascadePolicy {\n  /** Enable cascade deletion */\n  enabled: boolean\n  /** Tables to cascade to (in order) */\n  targets?: string[]\n  /** Foreign key columns to follow */\n  foreignKeys?: string[]\n  /** Maximum depth for cascade (default: 10) */\n  maxDepth?: number\n}\n\n/**\n * Time-based retention policy\n */\nexport interface TimeRetentionPolicy extends RetentionPolicy {\n  type: 'time'\n  unit: TimeUnit\n  /** Optional grace period before deletion */\n  gracePeriod?: {\n    value: number\n    unit: TimeUnit\n  }\n}\n\n/**\n * Size-based retention policy\n */\nexport interface SizeRetentionPolicy extends RetentionPolicy {\n  type: 'size'\n  unit: SizeUnit\n  /** Strategy when size exceeded: oldest_first, largest_first, custom */\n  strategy?: 'oldest_first' | 'largest_first' | 'custom'\n}\n\n/**\n * Count-based retention policy\n */\nexport interface CountRetentionPolicy extends RetentionPolicy {\n  type: 'count'\n  /** Order by clause for determining which records to keep */\n  orderBy?: {\n    column: string\n    direction: 'asc' | 'desc'\n  }\n}\n\n/**\n * Custom retention policy with predicate\n */\nexport interface CustomRetentionPolicy extends RetentionPolicy {\n  type: 'custom'\n  /** SQL predicate that returns true for records to delete */\n  deletePredicate: string\n  /** Optional SQL to run before deletion */\n  preDeleteHook?: string\n  /** Optional SQL to run after deletion */\n  postDeleteHook?: string\n}\n\n/**\n * Legal hold configuration\n */\nexport interface LegalHold {\n  /** Unique identifier */\n  id: string\n  /** Name of the legal hold */\n  name: string\n  /** Description and legal reference */\n  description: string\n  /** Tables/data affected */\n  scope: LegalHoldScope[]\n  /** When the hold was created */\n  createdAt: Date\n  /** Who created the hold */\n  createdBy: string\n  /** Expected release date (informational) */\n  expectedReleaseDate?: Date | undefined\n  /** Whether the hold is active */\n  active: boolean\n  /** Legal case reference */\n  caseReference?: string | undefined\n  /** Additional metadata */\n  metadata?: Record<string, unknown> | undefined\n}\n\n/**\n * Scope definition for legal holds\n */\nexport interface LegalHoldScope {\n  /** Table name */\n  table: string\n  /** Schema name */\n  schema?: string\n  /** Optional predicate to limit scope */\n  predicate?: string\n  /** Specific record IDs (if known) */\n  recordIds?: string[]\n}\n\n/**\n * GDPR erasure request\n */\nexport interface GdprErasureRequest {\n  /** Unique request identifier */\n  id: string\n  /** Subject identifier (user ID, email, etc.) */\n  subjectId: string\n  /** Type of identifier */\n  subjectIdType: 'user_id' | 'email' | 'phone' | 'custom'\n  /** When the request was received */\n  requestedAt: Date\n  /** Deadline for completion (72 hours from request per GDPR) */\n  deadline: Date\n  /** Current status */\n  status: 'pending' | 'in_progress' | 'completed' | 'failed' | 'blocked'\n  /** Tables to scan for subject data */\n  tables: GdprTableMapping[]\n  /** Results of the erasure */\n  result?: GdprErasureResult | undefined\n  /** Reason if blocked (e.g., legal hold) */\n  blockedReason?: string | undefined\n  /** Audit trail */\n  auditTrail: AuditEntry[]\n}\n\n/**\n * Table mapping for GDPR erasure\n */\nexport interface GdprTableMapping {\n  /** Table name */\n  table: string\n  /** Schema name */\n  schema?: string\n  /** Column containing the subject identifier */\n  subjectColumn: string\n  /** Action: delete, anonymize, or pseudonymize */\n  action: 'delete' | 'anonymize' | 'pseudonymize'\n  /** Columns to anonymize (if action is anonymize) */\n  anonymizeColumns?: string[]\n  /** Pseudonymization mapping (if action is pseudonymize) */\n  pseudonymizeMap?: Record<string, string>\n}\n\n/**\n * Result of a GDPR erasure operation\n */\nexport interface GdprErasureResult {\n  /** When erasure completed */\n  completedAt: Date\n  /** Total records affected */\n  totalRecords: number\n  /** Records per table */\n  recordsByTable: Record<string, number>\n  /** Any errors encountered */\n  errors: GdprErasureError[]\n  /** Verification hash (proof of erasure) */\n  verificationHash?: string\n}\n\n/**\n * Error during GDPR erasure\n */\nexport interface GdprErasureError {\n  /** Table where error occurred */\n  table: string\n  /** Error message */\n  message: string\n  /** Whether the error was recoverable */\n  recoverable: boolean\n  /** Record IDs that failed (if known) */\n  failedRecordIds?: string[]\n}\n\n/**\n * Audit entry for tracking deletions\n */\nexport interface AuditEntry {\n  /** Timestamp of the action */\n  timestamp: Date\n  /** Action taken */\n  action: string\n  /** Actor (user or system) */\n  actor: string\n  /** Details of the action */\n  details: Record<string, unknown>\n  /** Affected table */\n  table?: string\n  /** Number of records affected */\n  recordCount?: number\n  /** Hash of deleted data (for compliance verification) */\n  dataHash?: string\n}\n\n/**\n * Purge operation request\n */\nexport interface PurgeRequest {\n  /** Policy ID triggering the purge */\n  policyId?: string\n  /** Target tables */\n  targets: PurgeTarget[]\n  /** Action type */\n  action: PurgeAction\n  /** Whether to run in dry-run mode */\n  dryRun?: boolean\n  /** Maximum records to purge in this operation */\n  batchSize?: number\n  /** Timeout for the operation in milliseconds */\n  timeoutMs?: number\n  /** Reason for the purge */\n  reason?: string\n  /** Actor initiating the purge */\n  actor?: string\n}\n\n/**\n * Target for a purge operation\n */\nexport interface PurgeTarget {\n  /** Table name */\n  table: string\n  /** Schema name */\n  schema?: string\n  /** WHERE clause predicate */\n  predicate: string\n  /** Order by clause (for batch processing) */\n  orderBy?: string\n}\n\n/**\n * Result of a purge operation\n */\nexport interface PurgeResult {\n  /** Whether the operation succeeded */\n  success: boolean\n  /** Request that was executed */\n  request: PurgeRequest\n  /** When the purge started */\n  startedAt: Date\n  /** When the purge completed */\n  completedAt: Date\n  /** Total records purged */\n  totalPurged: number\n  /** Records purged per table */\n  purgedByTable: Record<string, number>\n  /** Any errors encountered */\n  errors: PurgeError[]\n  /** Audit entries created */\n  auditEntries: AuditEntry[]\n  /** Whether this was a dry run */\n  wasDryRun: boolean\n}\n\n/**\n * Error during purge operation\n */\nexport interface PurgeError {\n  /** Table where error occurred */\n  table: string\n  /** Error message */\n  message: string\n  /** Error code (PostgreSQL error code if applicable) */\n  code?: string | undefined\n  /** Whether the operation was rolled back */\n  rolledBack: boolean\n}\n\n/**\n * Configuration for the retention manager\n */\nexport interface RetentionManagerConfig {\n  /** SQL client for executing queries */\n  sql: RetentionSqlClient\n  /** Table to store policies (default: _retention_policies) */\n  policiesTable?: string\n  /** Table to store audit entries (default: _retention_audit) */\n  auditTable?: string\n  /** Table to store legal holds (default: _legal_holds) */\n  legalHoldsTable?: string\n  /** Table to store GDPR requests (default: _gdpr_requests) */\n  gdprRequestsTable?: string\n  /** Schema for retention tables (default: postgres_do) */\n  schema?: string\n  /** Default batch size for purge operations */\n  defaultBatchSize?: number\n  /** Enable automatic scheduling */\n  enableScheduler?: boolean\n  /** Callback for audit events */\n  onAuditEvent?: (entry: AuditEntry) => void | Promise<void>\n}\n\n/**\n * Minimal SQL client interface for retention operations\n */\nexport interface RetentionSqlClient {\n  unsafe<T extends Record<string, unknown> = Record<string, unknown>>(\n    query: string,\n    params?: unknown[]\n  ): Promise<T[]>\n}\n\n/**\n * Convert time value to milliseconds\n */\nexport function timeToMs(value: number, unit: TimeUnit): number {\n  const multipliers: Record<TimeUnit, number> = {\n    seconds: 1000,\n    minutes: 60 * 1000,\n    hours: 60 * 60 * 1000,\n    days: 24 * 60 * 60 * 1000,\n    weeks: 7 * 24 * 60 * 60 * 1000,\n    months: 30 * 24 * 60 * 60 * 1000, // Approximate\n    years: 365 * 24 * 60 * 60 * 1000, // Approximate\n  }\n  return value * multipliers[unit]\n}\n\n/**\n * Convert size value to bytes\n */\nexport function sizeToBytes(value: number, unit: SizeUnit): number {\n  const multipliers: Record<SizeUnit, number> = {\n    bytes: 1,\n    kb: 1024,\n    mb: 1024 * 1024,\n    gb: 1024 * 1024 * 1024,\n    tb: 1024 * 1024 * 1024 * 1024,\n  }\n  return value * multipliers[unit]\n}\n\n/**\n * Convert PostgreSQL interval string from time value and unit\n */\nexport function toPgInterval(value: number, unit: TimeUnit): string {\n  const pgUnits: Record<TimeUnit, string> = {\n    seconds: 'seconds',\n    minutes: 'minutes',\n    hours: 'hours',\n    days: 'days',\n    weeks: 'weeks',\n    months: 'months',\n    years: 'years',\n  }\n  return `${value} ${pgUnits[unit]}`\n}\n\n/**\n * GDPR deadline: 72 hours from request time\n */\nconst GDPR_DEADLINE_MS = 72 * 60 * 60 * 1000\n\n/**\n * Generate a unique ID with a given prefix\n */\nfunction generateId(prefix: string = 'ret'): string {\n  return `${prefix}_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 8)}`\n}\n\n/**\n * Retention Manager\n *\n * Main class for managing retention policies, legal holds, and GDPR requests.\n *\n * @example\n * ```typescript\n * import { RetentionManager } from 'postgres.do/retention'\n *\n * const retention = new RetentionManager({ sql })\n *\n * // Create a time-based retention policy\n * const policy = await retention.createPolicy({\n *   name: 'Delete old logs',\n *   type: 'time',\n *   value: 30,\n *   unit: 'days',\n *   targets: [{ table: 'logs', timestampColumn: 'created_at' }],\n *   action: 'hard_delete',\n * })\n *\n * // Execute the policy\n * const result = await retention.executePolicy(policy.id)\n * console.log(`Deleted ${result.totalPurged} records`)\n * ```\n */\nexport class RetentionManager {\n  private config: Required<RetentionManagerConfig>\n  private scheduler: RetentionScheduler | null = null\n\n  constructor(config: RetentionManagerConfig) {\n    this.config = {\n      sql: config.sql,\n      policiesTable: config.policiesTable ?? '_retention_policies',\n      auditTable: config.auditTable ?? '_retention_audit',\n      legalHoldsTable: config.legalHoldsTable ?? '_legal_holds',\n      gdprRequestsTable: config.gdprRequestsTable ?? '_gdpr_requests',\n      schema: config.schema ?? 'postgres_do',\n      defaultBatchSize: config.defaultBatchSize ?? 1000,\n      enableScheduler: config.enableScheduler ?? false,\n      onAuditEvent: config.onAuditEvent ?? (async () => {}),\n    }\n  }\n\n  /**\n   * Initialize the retention system (creates required tables)\n   */\n  async initialize(): Promise<void> {\n    const { sql, schema, policiesTable, auditTable, legalHoldsTable, gdprRequestsTable } = this.config\n\n    // Create schema if it doesn't exist\n    await sql.unsafe(`CREATE SCHEMA IF NOT EXISTS ${schema}`)\n\n    // Create policies table\n    await sql.unsafe(`\n      CREATE TABLE IF NOT EXISTS ${schema}.${policiesTable} (\n        id TEXT PRIMARY KEY,\n        name TEXT NOT NULL,\n        description TEXT,\n        type TEXT NOT NULL,\n        value NUMERIC NOT NULL,\n        unit TEXT,\n        targets JSONB NOT NULL,\n        status TEXT NOT NULL DEFAULT 'active',\n        priority INTEGER NOT NULL DEFAULT 100,\n        compliance JSONB,\n        schedule JSONB,\n        action TEXT NOT NULL,\n        cascade_policy JSONB,\n        created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),\n        updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()\n      )\n    `)\n\n    // Create audit table\n    await sql.unsafe(`\n      CREATE TABLE IF NOT EXISTS ${schema}.${auditTable} (\n        id SERIAL PRIMARY KEY,\n        timestamp TIMESTAMPTZ NOT NULL DEFAULT NOW(),\n        action TEXT NOT NULL,\n        actor TEXT NOT NULL,\n        details JSONB NOT NULL,\n        table_name TEXT,\n        record_count INTEGER,\n        data_hash TEXT,\n        policy_id TEXT REFERENCES ${schema}.${policiesTable}(id),\n        created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()\n      )\n    `)\n\n    // Create legal holds table\n    await sql.unsafe(`\n      CREATE TABLE IF NOT EXISTS ${schema}.${legalHoldsTable} (\n        id TEXT PRIMARY KEY,\n        name TEXT NOT NULL,\n        description TEXT NOT NULL,\n        scope JSONB NOT NULL,\n        created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),\n        created_by TEXT NOT NULL,\n        expected_release_date TIMESTAMPTZ,\n        active BOOLEAN NOT NULL DEFAULT TRUE,\n        case_reference TEXT,\n        metadata JSONB\n      )\n    `)\n\n    // Create GDPR requests table\n    await sql.unsafe(`\n      CREATE TABLE IF NOT EXISTS ${schema}.${gdprRequestsTable} (\n        id TEXT PRIMARY KEY,\n        subject_id TEXT NOT NULL,\n        subject_id_type TEXT NOT NULL,\n        requested_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),\n        deadline TIMESTAMPTZ NOT NULL,\n        status TEXT NOT NULL DEFAULT 'pending',\n        tables JSONB NOT NULL,\n        result JSONB,\n        blocked_reason TEXT,\n        audit_trail JSONB NOT NULL DEFAULT '[]'::JSONB\n      )\n    `)\n\n    // Create indexes for efficient queries\n    await sql.unsafe(`\n      CREATE INDEX IF NOT EXISTS idx_${policiesTable}_status\n      ON ${schema}.${policiesTable}(status)\n    `)\n    await sql.unsafe(`\n      CREATE INDEX IF NOT EXISTS idx_${auditTable}_timestamp\n      ON ${schema}.${auditTable}(timestamp)\n    `)\n    await sql.unsafe(`\n      CREATE INDEX IF NOT EXISTS idx_${legalHoldsTable}_active\n      ON ${schema}.${legalHoldsTable}(active)\n    `)\n    await sql.unsafe(`\n      CREATE INDEX IF NOT EXISTS idx_${gdprRequestsTable}_status\n      ON ${schema}.${gdprRequestsTable}(status)\n    `)\n    await sql.unsafe(`\n      CREATE INDEX IF NOT EXISTS idx_${gdprRequestsTable}_deadline\n      ON ${schema}.${gdprRequestsTable}(deadline)\n    `)\n  }\n\n  /**\n   * Create a new retention policy\n   */\n  async createPolicy(\n    options: Omit<RetentionPolicy, 'id' | 'createdAt' | 'updatedAt' | 'status'> & { status?: PolicyStatus }\n  ): Promise<RetentionPolicy> {\n    const { sql, schema, policiesTable } = this.config\n\n    const policy: RetentionPolicy = {\n      id: generateId(),\n      name: options.name,\n      type: options.type,\n      value: options.value,\n      targets: options.targets,\n      status: options.status ?? 'active',\n      priority: options.priority ?? 100,\n      action: options.action,\n      createdAt: new Date(),\n      updatedAt: new Date(),\n    }\n    if (options.description !== undefined) {\n      policy.description = options.description\n    }\n    if (options.unit !== undefined) {\n      policy.unit = options.unit\n    }\n    if (options.compliance !== undefined) {\n      policy.compliance = options.compliance\n    }\n    if (options.schedule !== undefined) {\n      policy.schedule = options.schedule\n    }\n    if (options.cascade !== undefined) {\n      policy.cascade = options.cascade\n    }\n\n    await sql.unsafe(\n      `\n      INSERT INTO ${schema}.${policiesTable}\n      (id, name, description, type, value, unit, targets, status, priority, compliance, schedule, action, cascade_policy, created_at, updated_at)\n      VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15)\n    `,\n      [\n        policy.id,\n        policy.name,\n        policy.description ?? null,\n        policy.type,\n        policy.value,\n        policy.unit ?? null,\n        JSON.stringify(policy.targets),\n        policy.status,\n        policy.priority,\n        policy.compliance ? JSON.stringify(policy.compliance) : null,\n        policy.schedule ? JSON.stringify(policy.schedule) : null,\n        policy.action,\n        policy.cascade ? JSON.stringify(policy.cascade) : null,\n        policy.createdAt.toISOString(),\n        policy.updatedAt.toISOString(),\n      ]\n    )\n\n    await this.audit({\n      action: 'policy_created',\n      actor: 'system',\n      details: { policyId: policy.id, policyName: policy.name, policyType: policy.type },\n    })\n\n    return policy\n  }\n\n  /**\n   * Get a policy by ID\n   */\n  async getPolicy(id: string): Promise<RetentionPolicy | null> {\n    const { sql, schema, policiesTable } = this.config\n\n    const rows = await sql.unsafe<Record<string, unknown>>(\n      `SELECT * FROM ${schema}.${policiesTable} WHERE id = $1`,\n      [id]\n    )\n\n    if (rows.length === 0) {\n      return null\n    }\n\n    return this.rowToPolicy(rows[0]!)\n  }\n\n  /**\n   * List all policies\n   */\n  async listPolicies(options?: {\n    status?: PolicyStatus\n    type?: RetentionType\n    compliance?: ComplianceFramework\n  }): Promise<RetentionPolicy[]> {\n    const { sql, schema, policiesTable } = this.config\n\n    let query = `SELECT * FROM ${schema}.${policiesTable} WHERE 1=1`\n    const params: unknown[] = []\n    let paramIndex = 1\n\n    if (options?.status) {\n      query += ` AND status = $${paramIndex++}`\n      params.push(options.status)\n    }\n    if (options?.type) {\n      query += ` AND type = $${paramIndex++}`\n      params.push(options.type)\n    }\n    if (options?.compliance) {\n      query += ` AND compliance ? $${paramIndex++}`\n      params.push(options.compliance)\n    }\n\n    query += ' ORDER BY priority ASC, created_at DESC'\n\n    const rows = await sql.unsafe<Record<string, unknown>>(query, params)\n    return rows.map((row) => this.rowToPolicy(row))\n  }\n\n  /**\n   * Update a policy\n   */\n  async updatePolicy(\n    id: string,\n    updates: Partial<Omit<RetentionPolicy, 'id' | 'createdAt'>>\n  ): Promise<RetentionPolicy | null> {\n    const { sql, schema, policiesTable } = this.config\n\n    const existing = await this.getPolicy(id)\n    if (!existing) {\n      return null\n    }\n\n    const updated: RetentionPolicy = {\n      ...existing,\n      ...updates,\n      updatedAt: new Date(),\n    }\n\n    await sql.unsafe(\n      `\n      UPDATE ${schema}.${policiesTable}\n      SET name = $1, description = $2, type = $3, value = $4, unit = $5,\n          targets = $6, status = $7, priority = $8, compliance = $9,\n          schedule = $10, action = $11, cascade_policy = $12, updated_at = $13\n      WHERE id = $14\n    `,\n      [\n        updated.name,\n        updated.description ?? null,\n        updated.type,\n        updated.value,\n        updated.unit ?? null,\n        JSON.stringify(updated.targets),\n        updated.status,\n        updated.priority,\n        updated.compliance ? JSON.stringify(updated.compliance) : null,\n        updated.schedule ? JSON.stringify(updated.schedule) : null,\n        updated.action,\n        updated.cascade ? JSON.stringify(updated.cascade) : null,\n        updated.updatedAt.toISOString(),\n        id,\n      ]\n    )\n\n    await this.audit({\n      action: 'policy_updated',\n      actor: 'system',\n      details: { policyId: id, updates: Object.keys(updates) },\n    })\n\n    return updated\n  }\n\n  /**\n   * Delete a policy\n   */\n  async deletePolicy(id: string): Promise<boolean> {\n    const { sql, schema, policiesTable } = this.config\n\n    const result = await sql.unsafe(`DELETE FROM ${schema}.${policiesTable} WHERE id = $1 RETURNING id`, [id])\n\n    if (result.length > 0) {\n      await this.audit({\n        action: 'policy_deleted',\n        actor: 'system',\n        details: { policyId: id },\n      })\n      return true\n    }\n\n    return false\n  }\n\n  /**\n   * Execute a retention policy\n   */\n  async executePolicy(policyId: string, options?: { dryRun?: boolean; actor?: string }): Promise<PurgeResult> {\n    const policy = await this.getPolicy(policyId)\n    if (!policy) {\n      throw new Error(`Policy not found: ${policyId}`)\n    }\n\n    if (policy.status !== 'active') {\n      throw new Error(`Policy is not active: ${policyId}`)\n    }\n\n    // Check for legal holds\n    const blockedTables = await this.getBlockedTables(policy.targets.map((t) => t.table))\n    if (blockedTables.length > 0) {\n      throw new Error(`Cannot execute policy: tables under legal hold: ${blockedTables.join(', ')}`)\n    }\n\n    // Build purge request based on policy type\n    const purgeTargets = await this.buildPurgeTargets(policy)\n\n    const request: PurgeRequest = {\n      policyId,\n      targets: purgeTargets,\n      action: policy.action,\n      dryRun: options?.dryRun ?? false,\n      batchSize: this.config.defaultBatchSize,\n      reason: `Retention policy: ${policy.name}`,\n      actor: options?.actor ?? 'scheduler',\n    }\n\n    return this.purge(request)\n  }\n\n  /**\n   * Build purge targets based on policy type\n   */\n  private async buildPurgeTargets(policy: RetentionPolicy): Promise<PurgeTarget[]> {\n    const targets: PurgeTarget[] = []\n\n    for (const target of policy.targets) {\n      const schema = target.schema ?? 'public'\n      let predicate: string\n\n      switch (policy.type) {\n        case 'time': {\n          const timePolicy = policy as TimeRetentionPolicy\n          const column = target.timestampColumn ?? 'created_at'\n          const interval = toPgInterval(timePolicy.value, timePolicy.unit)\n          predicate = `${column} < NOW() - INTERVAL '${interval}'`\n          break\n        }\n\n        case 'count': {\n          const countPolicy = policy as CountRetentionPolicy\n          const orderColumn = countPolicy.orderBy?.column ?? 'id'\n          const orderDir = countPolicy.orderBy?.direction ?? 'desc'\n          // For count-based, we'll select IDs to keep and delete the rest\n          predicate = `id NOT IN (\n            SELECT id FROM ${schema}.${target.table}\n            ORDER BY ${orderColumn} ${orderDir}\n            LIMIT ${countPolicy.value}\n          )`\n          break\n        }\n\n        case 'size': {\n          // Size-based requires calculating table size and removing oldest records\n          const sizePolicy = policy as SizeRetentionPolicy\n          const targetBytes = sizeToBytes(sizePolicy.value, sizePolicy.unit)\n          const strategy = sizePolicy.strategy ?? 'oldest_first'\n          const orderColumn = strategy === 'oldest_first' ? (target.timestampColumn ?? 'created_at') : 'pg_column_size(t.*)'\n          const orderDir = strategy === 'oldest_first' ? 'ASC' : 'DESC'\n\n          // Get current size and calculate how much to remove\n          const sizeResult = await this.config.sql.unsafe<{ total_size: string }>(\n            `SELECT pg_total_relation_size('${schema}.${target.table}') as total_size`\n          )\n          const currentSize = parseInt(sizeResult[0]?.total_size ?? '0', 10)\n\n          if (currentSize <= targetBytes) {\n            continue // Skip this target, already under size limit\n          }\n\n          // Estimate rows to delete (rough estimate based on size ratio)\n          const countResult = await this.config.sql.unsafe<{ count: string }>(\n            `SELECT COUNT(*) as count FROM ${schema}.${target.table}`\n          )\n          const totalRows = parseInt(countResult[0]?.count ?? '0', 10)\n          const bytesPerRow = totalRows > 0 ? currentSize / totalRows : 0\n          const bytesToRemove = currentSize - targetBytes\n          const rowsToRemove = bytesPerRow > 0 ? Math.ceil(bytesToRemove / bytesPerRow) : 0\n\n          predicate = `id IN (\n            SELECT id FROM ${schema}.${target.table} t\n            ORDER BY ${orderColumn} ${orderDir}\n            LIMIT ${rowsToRemove}\n          )`\n          break\n        }\n\n        case 'custom': {\n          const customPolicy = policy as CustomRetentionPolicy\n          predicate = customPolicy.deletePredicate\n          break\n        }\n\n        default:\n          throw new Error(`Unknown policy type: ${policy.type}`)\n      }\n\n      // Add any additional predicate from target\n      if (target.predicate) {\n        predicate = `(${predicate}) AND (${target.predicate})`\n      }\n\n      targets.push({\n        table: target.table,\n        schema,\n        predicate,\n        orderBy: target.timestampColumn ?? 'id',\n      })\n    }\n\n    return targets\n  }\n\n  /**\n   * Execute a purge operation\n   */\n  async purge(request: PurgeRequest): Promise<PurgeResult> {\n    const { sql } = this.config\n    const startedAt = new Date()\n    const errors: PurgeError[] = []\n    const auditEntries: AuditEntry[] = []\n    const purgedByTable: Record<string, number> = {}\n    let totalPurged = 0\n\n    for (const target of request.targets) {\n      const schema = target.schema ?? 'public'\n      const fullTable = `${schema}.${target.table}`\n\n      try {\n        // Check for legal holds\n        const isBlocked = await this.isTableBlocked(target.table)\n        if (isBlocked) {\n          errors.push({\n            table: fullTable,\n            message: 'Table is under legal hold',\n            rolledBack: false,\n          })\n          continue\n        }\n\n        let deletedCount = 0\n\n        if (request.dryRun) {\n          // Dry run: just count matching records\n          const countResult = await sql.unsafe<{ count: string }>(\n            `SELECT COUNT(*) as count FROM ${fullTable} WHERE ${target.predicate}`\n          )\n          deletedCount = parseInt(countResult[0]?.count ?? '0', 10)\n        } else {\n          // Execute based on action type\n          switch (request.action) {\n            case 'soft_delete': {\n              // Soft delete: set deleted_at timestamp\n              const result = await sql.unsafe<{ id: unknown }>(\n                `UPDATE ${fullTable} SET deleted_at = NOW()\n                 WHERE ${target.predicate} AND deleted_at IS NULL\n                 RETURNING id`\n              )\n              deletedCount = result.length\n              break\n            }\n\n            case 'hard_delete': {\n              // Hard delete: actually remove records\n              const result = await sql.unsafe<{ id: unknown }>(\n                `DELETE FROM ${fullTable} WHERE ${target.predicate} RETURNING id`\n              )\n              deletedCount = result.length\n              break\n            }\n\n            case 'archive': {\n              // Archive: move to archive table\n              const archiveTable = `${fullTable}_archive`\n              await sql.unsafe(\n                `INSERT INTO ${archiveTable}\n                 SELECT *, NOW() as archived_at FROM ${fullTable}\n                 WHERE ${target.predicate}`\n              )\n              const result = await sql.unsafe<{ id: unknown }>(\n                `DELETE FROM ${fullTable} WHERE ${target.predicate} RETURNING id`\n              )\n              deletedCount = result.length\n              break\n            }\n\n            case 'cryptographic_erasure': {\n              // Cryptographic erasure: delete encryption keys\n              // This requires integration with key management - for now we hard delete\n              const result = await sql.unsafe<{ id: unknown }>(\n                `DELETE FROM ${fullTable} WHERE ${target.predicate} RETURNING id`\n              )\n              deletedCount = result.length\n              break\n            }\n          }\n        }\n\n        purgedByTable[fullTable] = deletedCount\n        totalPurged += deletedCount\n\n        // Create audit entry for this table\n        const auditEntry: AuditEntry = {\n          timestamp: new Date(),\n          action: request.dryRun ? 'purge_dry_run' : `purge_${request.action}`,\n          actor: request.actor ?? 'system',\n          details: {\n            policyId: request.policyId,\n            reason: request.reason,\n            predicate: target.predicate,\n          },\n          table: fullTable,\n          recordCount: deletedCount,\n        }\n        auditEntries.push(auditEntry)\n\n        if (!request.dryRun) {\n          await this.audit(auditEntry)\n        }\n      } catch (error) {\n        errors.push({\n          table: fullTable,\n          message: error instanceof Error ? error.message : String(error),\n          code: (error as { code?: string }).code,\n          rolledBack: true,\n        })\n      }\n    }\n\n    const result: PurgeResult = {\n      success: errors.length === 0,\n      request,\n      startedAt,\n      completedAt: new Date(),\n      totalPurged,\n      purgedByTable,\n      errors,\n      auditEntries,\n      wasDryRun: request.dryRun ?? false,\n    }\n\n    // Audit the overall operation\n    if (!request.dryRun) {\n      await this.audit({\n        timestamp: new Date(),\n        action: 'purge_completed',\n        actor: request.actor ?? 'system',\n        details: {\n          policyId: request.policyId,\n          success: result.success,\n          totalPurged,\n          errorCount: errors.length,\n          durationMs: result.completedAt.getTime() - result.startedAt.getTime(),\n        },\n      })\n    }\n\n    return result\n  }\n\n  /**\n   * Create a legal hold\n   */\n  async createLegalHold(\n    options: Omit<LegalHold, 'id' | 'createdAt' | 'active'> & { active?: boolean }\n  ): Promise<LegalHold> {\n    const { sql, schema, legalHoldsTable } = this.config\n\n    const hold: LegalHold = {\n      id: generateId('hold'),\n      name: options.name,\n      description: options.description,\n      scope: options.scope,\n      createdAt: new Date(),\n      createdBy: options.createdBy,\n      expectedReleaseDate: options.expectedReleaseDate,\n      active: options.active ?? true,\n      caseReference: options.caseReference,\n      metadata: options.metadata,\n    }\n\n    await sql.unsafe(\n      `\n      INSERT INTO ${schema}.${legalHoldsTable}\n      (id, name, description, scope, created_at, created_by, expected_release_date, active, case_reference, metadata)\n      VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)\n    `,\n      [\n        hold.id,\n        hold.name,\n        hold.description,\n        JSON.stringify(hold.scope),\n        hold.createdAt.toISOString(),\n        hold.createdBy,\n        hold.expectedReleaseDate?.toISOString() ?? null,\n        hold.active,\n        hold.caseReference ?? null,\n        hold.metadata ? JSON.stringify(hold.metadata) : null,\n      ]\n    )\n\n    await this.audit({\n      action: 'legal_hold_created',\n      actor: hold.createdBy,\n      details: {\n        holdId: hold.id,\n        holdName: hold.name,\n        scope: hold.scope.map((s) => s.table),\n      },\n    })\n\n    return hold\n  }\n\n  /**\n   * Release a legal hold\n   */\n  async releaseLegalHold(id: string, actor: string, reason?: string): Promise<boolean> {\n    const { sql, schema, legalHoldsTable } = this.config\n\n    const result = await sql.unsafe(\n      `UPDATE ${schema}.${legalHoldsTable} SET active = FALSE WHERE id = $1 AND active = TRUE RETURNING id`,\n      [id]\n    )\n\n    if (result.length > 0) {\n      await this.audit({\n        action: 'legal_hold_released',\n        actor,\n        details: { holdId: id, reason },\n      })\n      return true\n    }\n\n    return false\n  }\n\n  /**\n   * Check if a table is under legal hold\n   */\n  async isTableBlocked(table: string): Promise<boolean> {\n    const { sql, schema, legalHoldsTable } = this.config\n\n    const result = await sql.unsafe<{ count: string }>(\n      `\n      SELECT COUNT(*) as count FROM ${schema}.${legalHoldsTable}\n      WHERE active = TRUE AND scope @> $1::JSONB\n    `,\n      [JSON.stringify([{ table }])]\n    )\n\n    return parseInt(result[0]?.count ?? '0', 10) > 0\n  }\n\n  /**\n   * Get all tables currently under legal hold\n   */\n  private async getBlockedTables(tables: string[]): Promise<string[]> {\n    const blocked: string[] = []\n    for (const table of tables) {\n      if (await this.isTableBlocked(table)) {\n        blocked.push(table)\n      }\n    }\n    return blocked\n  }\n\n  /**\n   * List all active legal holds\n   */\n  async listLegalHolds(options?: { active?: boolean }): Promise<LegalHold[]> {\n    const { sql, schema, legalHoldsTable } = this.config\n\n    let query = `SELECT * FROM ${schema}.${legalHoldsTable}`\n    const params: unknown[] = []\n\n    if (options?.active !== undefined) {\n      query += ' WHERE active = $1'\n      params.push(options.active)\n    }\n\n    query += ' ORDER BY created_at DESC'\n\n    const rows = await sql.unsafe<Record<string, unknown>>(query, params)\n    return rows.map((row) => this.rowToLegalHold(row))\n  }\n\n  /**\n   * Create a GDPR erasure request\n   */\n  async createGdprErasureRequest(\n    options: Omit<GdprErasureRequest, 'id' | 'requestedAt' | 'deadline' | 'status' | 'auditTrail'>\n  ): Promise<GdprErasureRequest> {\n    const { sql, schema, gdprRequestsTable } = this.config\n\n    const requestedAt = new Date()\n    const deadline = new Date(requestedAt.getTime() + GDPR_DEADLINE_MS)\n\n    const request: GdprErasureRequest = {\n      id: generateId('gdpr'),\n      subjectId: options.subjectId,\n      subjectIdType: options.subjectIdType,\n      requestedAt,\n      deadline,\n      status: 'pending',\n      tables: options.tables,\n      auditTrail: [\n        {\n          timestamp: requestedAt,\n          action: 'request_created',\n          actor: 'system',\n          details: { subjectId: options.subjectId, subjectIdType: options.subjectIdType },\n        },\n      ],\n    }\n\n    await sql.unsafe(\n      `\n      INSERT INTO ${schema}.${gdprRequestsTable}\n      (id, subject_id, subject_id_type, requested_at, deadline, status, tables, audit_trail)\n      VALUES ($1, $2, $3, $4, $5, $6, $7, $8)\n    `,\n      [\n        request.id,\n        request.subjectId,\n        request.subjectIdType,\n        request.requestedAt.toISOString(),\n        request.deadline.toISOString(),\n        request.status,\n        JSON.stringify(request.tables),\n        JSON.stringify(request.auditTrail),\n      ]\n    )\n\n    await this.audit({\n      action: 'gdpr_request_created',\n      actor: 'system',\n      details: {\n        requestId: request.id,\n        subjectId: request.subjectId,\n        deadline: request.deadline.toISOString(),\n      },\n    })\n\n    return request\n  }\n\n  /**\n   * Execute a GDPR erasure request\n   */\n  async executeGdprErasureRequest(\n    requestId: string,\n    options?: { actor?: string }\n  ): Promise<GdprErasureResult> {\n    const { sql, schema, gdprRequestsTable } = this.config\n\n    // Get the request\n    const rows = await sql.unsafe<Record<string, unknown>>(\n      `SELECT * FROM ${schema}.${gdprRequestsTable} WHERE id = $1`,\n      [requestId]\n    )\n\n    if (rows.length === 0) {\n      throw new Error(`GDPR request not found: ${requestId}`)\n    }\n\n    const request = this.rowToGdprRequest(rows[0]!)\n\n    if (request.status === 'completed') {\n      throw new Error('Request already completed')\n    }\n\n    if (request.status === 'blocked') {\n      throw new Error(`Request is blocked: ${request.blockedReason}`)\n    }\n\n    // Check for legal holds\n    const blockedTables = await this.getBlockedTables(request.tables.map((t) => t.table))\n    if (blockedTables.length > 0) {\n      // Update status to blocked\n      await sql.unsafe(\n        `UPDATE ${schema}.${gdprRequestsTable} SET status = 'blocked', blocked_reason = $1 WHERE id = $2`,\n        [`Tables under legal hold: ${blockedTables.join(', ')}`, requestId]\n      )\n      throw new Error(`Cannot execute: tables under legal hold: ${blockedTables.join(', ')}`)\n    }\n\n    // Update status to in_progress\n    await sql.unsafe(`UPDATE ${schema}.${gdprRequestsTable} SET status = 'in_progress' WHERE id = $1`, [\n      requestId,\n    ])\n\n    const recordsByTable: Record<string, number> = {}\n    const errors: GdprErasureError[] = []\n    let totalRecords = 0\n\n    for (const tableMapping of request.tables) {\n      const tableSchema = tableMapping.schema ?? 'public'\n      const fullTable = `${tableSchema}.${tableMapping.table}`\n\n      try {\n        let affected = 0\n\n        switch (tableMapping.action) {\n          case 'delete': {\n            const result = await sql.unsafe<{ id: unknown }>(\n              `DELETE FROM ${fullTable} WHERE ${tableMapping.subjectColumn} = $1 RETURNING id`,\n              [request.subjectId]\n            )\n            affected = result.length\n            break\n          }\n\n          case 'anonymize': {\n            if (!tableMapping.anonymizeColumns || tableMapping.anonymizeColumns.length === 0) {\n              throw new Error('anonymizeColumns required for anonymize action')\n            }\n            const setClause = tableMapping.anonymizeColumns.map((col) => `${col} = NULL`).join(', ')\n            const result = await sql.unsafe<{ id: unknown }>(\n              `UPDATE ${fullTable} SET ${setClause} WHERE ${tableMapping.subjectColumn} = $1 RETURNING id`,\n              [request.subjectId]\n            )\n            affected = result.length\n            break\n          }\n\n          case 'pseudonymize': {\n            if (!tableMapping.pseudonymizeMap || Object.keys(tableMapping.pseudonymizeMap).length === 0) {\n              throw new Error('pseudonymizeMap required for pseudonymize action')\n            }\n            const setClause = Object.entries(tableMapping.pseudonymizeMap)\n              .map(([col, value]) => `${col} = '${value}'`)\n              .join(', ')\n            const result = await sql.unsafe<{ id: unknown }>(\n              `UPDATE ${fullTable} SET ${setClause} WHERE ${tableMapping.subjectColumn} = $1 RETURNING id`,\n              [request.subjectId]\n            )\n            affected = result.length\n            break\n          }\n        }\n\n        recordsByTable[fullTable] = affected\n        totalRecords += affected\n      } catch (error) {\n        errors.push({\n          table: fullTable,\n          message: error instanceof Error ? error.message : String(error),\n          recoverable: false,\n        })\n      }\n    }\n\n    const result: GdprErasureResult = {\n      completedAt: new Date(),\n      totalRecords,\n      recordsByTable,\n      errors,\n    }\n\n    // Update request with result\n    const finalStatus = errors.length === 0 ? 'completed' : 'failed'\n    const auditEntry: AuditEntry = {\n      timestamp: new Date(),\n      action: 'erasure_executed',\n      actor: options?.actor ?? 'system',\n      details: {\n        totalRecords,\n        errorCount: errors.length,\n        tables: Object.keys(recordsByTable),\n      },\n    }\n\n    request.auditTrail.push(auditEntry)\n\n    await sql.unsafe(\n      `UPDATE ${schema}.${gdprRequestsTable} SET status = $1, result = $2, audit_trail = $3 WHERE id = $4`,\n      [finalStatus, JSON.stringify(result), JSON.stringify(request.auditTrail), requestId]\n    )\n\n    await this.audit({\n      action: 'gdpr_erasure_completed',\n      actor: options?.actor ?? 'system',\n      details: {\n        requestId,\n        subjectId: request.subjectId,\n        status: finalStatus,\n        totalRecords,\n        errorCount: errors.length,\n      },\n    })\n\n    return result\n  }\n\n  /**\n   * Get pending GDPR requests approaching deadline\n   */\n  async getPendingGdprRequests(options?: { withinHours?: number }): Promise<GdprErasureRequest[]> {\n    const { sql, schema, gdprRequestsTable } = this.config\n\n    const withinHours = options?.withinHours ?? 24\n    const deadline = new Date(Date.now() + withinHours * 60 * 60 * 1000)\n\n    const rows = await sql.unsafe<Record<string, unknown>>(\n      `\n      SELECT * FROM ${schema}.${gdprRequestsTable}\n      WHERE status IN ('pending', 'in_progress')\n        AND deadline <= $1\n      ORDER BY deadline ASC\n    `,\n      [deadline.toISOString()]\n    )\n\n    return rows.map((row) => this.rowToGdprRequest(row))\n  }\n\n  /**\n   * Create an audit entry\n   */\n  private async audit(entry: Omit<AuditEntry, 'timestamp'> & { timestamp?: Date }): Promise<void> {\n    const { sql, schema, auditTable, onAuditEvent } = this.config\n\n    const fullEntry: AuditEntry = {\n      ...entry,\n      timestamp: entry.timestamp ?? new Date(),\n    }\n\n    await sql.unsafe(\n      `\n      INSERT INTO ${schema}.${auditTable}\n      (timestamp, action, actor, details, table_name, record_count, data_hash)\n      VALUES ($1, $2, $3, $4, $5, $6, $7)\n    `,\n      [\n        fullEntry.timestamp.toISOString(),\n        fullEntry.action,\n        fullEntry.actor,\n        JSON.stringify(fullEntry.details),\n        fullEntry.table ?? null,\n        fullEntry.recordCount ?? null,\n        fullEntry.dataHash ?? null,\n      ]\n    )\n\n    // Call the audit event callback\n    await onAuditEvent(fullEntry)\n  }\n\n  /**\n   * Get audit trail for a policy or table\n   */\n  async getAuditTrail(options?: {\n    policyId?: string\n    table?: string\n    since?: Date\n    limit?: number\n  }): Promise<AuditEntry[]> {\n    const { sql, schema, auditTable } = this.config\n\n    let query = `SELECT * FROM ${schema}.${auditTable} WHERE 1=1`\n    const params: unknown[] = []\n    let paramIndex = 1\n\n    if (options?.policyId) {\n      query += ` AND details->>'policyId' = $${paramIndex++}`\n      params.push(options.policyId)\n    }\n    if (options?.table) {\n      query += ` AND table_name = $${paramIndex++}`\n      params.push(options.table)\n    }\n    if (options?.since) {\n      query += ` AND timestamp >= $${paramIndex++}`\n      params.push(options.since.toISOString())\n    }\n\n    query += ' ORDER BY timestamp DESC'\n\n    if (options?.limit) {\n      query += ` LIMIT ${options.limit}`\n    }\n\n    const rows = await sql.unsafe<Record<string, unknown>>(query, params)\n    return rows.map((row) => {\n      const entry: AuditEntry = {\n        timestamp: new Date(row.timestamp as string),\n        action: row.action as string,\n        actor: row.actor as string,\n        details: row.details as Record<string, unknown>,\n      }\n      if (row.table_name !== undefined && row.table_name !== null) {\n        entry.table = row.table_name as string\n      }\n      if (row.record_count !== undefined && row.record_count !== null) {\n        entry.recordCount = row.record_count as number\n      }\n      if (row.data_hash !== undefined && row.data_hash !== null) {\n        entry.dataHash = row.data_hash as string\n      }\n      return entry\n    })\n  }\n\n  /**\n   * Start the scheduler for automatic policy execution\n   */\n  startScheduler(): void {\n    if (this.scheduler) {\n      return\n    }\n    this.scheduler = new RetentionScheduler(this)\n    this.scheduler.start()\n  }\n\n  /**\n   * Stop the scheduler\n   */\n  stopScheduler(): void {\n    if (this.scheduler) {\n      this.scheduler.stop()\n      this.scheduler = null\n    }\n  }\n\n  /**\n   * Safely parse JSON that may already be an object (from JSONB columns)\n   */\n  private parseJsonField<T>(value: unknown): T {\n    if (typeof value === 'string') {\n      return JSON.parse(value) as T\n    }\n    return value as T\n  }\n\n  /**\n   * Convert database row to RetentionPolicy\n   */\n  private rowToPolicy(row: Record<string, unknown>): RetentionPolicy {\n    const policy: RetentionPolicy = {\n      id: row.id as string,\n      name: row.name as string,\n      type: row.type as RetentionType,\n      value: Number(row.value),\n      targets: this.parseJsonField<RetentionTarget[]>(row.targets),\n      status: row.status as PolicyStatus,\n      priority: row.priority as number,\n      action: row.action as PurgeAction,\n      createdAt: new Date(row.created_at as string),\n      updatedAt: new Date(row.updated_at as string),\n    }\n\n    // Add optional fields only if present\n    if (row.description !== undefined && row.description !== null) {\n      policy.description = row.description as string\n    }\n    if (row.unit !== undefined && row.unit !== null) {\n      policy.unit = row.unit as TimeUnit | SizeUnit\n    }\n    if (row.compliance) {\n      policy.compliance = this.parseJsonField<ComplianceFramework[]>(row.compliance)\n    }\n    if (row.schedule) {\n      policy.schedule = this.parseJsonField<RetentionSchedule>(row.schedule)\n    }\n    if (row.cascade_policy) {\n      policy.cascade = this.parseJsonField<CascadePolicy>(row.cascade_policy)\n    }\n\n    // Add custom policy fields if present\n    if (row.type === 'custom') {\n      const customPolicy = policy as CustomRetentionPolicy\n      if (row.delete_predicate) {\n        customPolicy.deletePredicate = row.delete_predicate as string\n      }\n      if (row.pre_delete_hook) {\n        customPolicy.preDeleteHook = row.pre_delete_hook as string\n      }\n      if (row.post_delete_hook) {\n        customPolicy.postDeleteHook = row.post_delete_hook as string\n      }\n    }\n\n    return policy\n  }\n\n  /**\n   * Convert database row to LegalHold\n   */\n  private rowToLegalHold(row: Record<string, unknown>): LegalHold {\n    return {\n      id: row.id as string,\n      name: row.name as string,\n      description: row.description as string,\n      scope: this.parseJsonField<LegalHoldScope[]>(row.scope),\n      createdAt: new Date(row.created_at as string),\n      createdBy: row.created_by as string,\n      expectedReleaseDate: row.expected_release_date\n        ? new Date(row.expected_release_date as string)\n        : undefined,\n      active: row.active as boolean,\n      caseReference: row.case_reference as string | undefined,\n      metadata: row.metadata ? this.parseJsonField<Record<string, unknown>>(row.metadata) : undefined,\n    }\n  }\n\n  /**\n   * Convert database row to GdprErasureRequest\n   */\n  private rowToGdprRequest(row: Record<string, unknown>): GdprErasureRequest {\n    return {\n      id: row.id as string,\n      subjectId: row.subject_id as string,\n      subjectIdType: row.subject_id_type as 'user_id' | 'email' | 'phone' | 'custom',\n      requestedAt: new Date(row.requested_at as string),\n      deadline: new Date(row.deadline as string),\n      status: row.status as GdprErasureRequest['status'],\n      tables: this.parseJsonField<GdprTableMapping[]>(row.tables),\n      result: row.result ? this.parseJsonField<GdprErasureResult>(row.result) : undefined,\n      blockedReason: row.blocked_reason as string | undefined,\n      auditTrail: this.parseJsonField<AuditEntry[]>(row.audit_trail),\n    }\n  }\n}\n\n/**\n * Internal scheduler for automatic policy execution\n */\nclass RetentionScheduler {\n  private manager: RetentionManager\n  private intervalIds: Map<string, ReturnType<typeof setInterval>> = new Map()\n  private running = false\n\n  constructor(manager: RetentionManager) {\n    this.manager = manager\n  }\n\n  async start(): Promise<void> {\n    if (this.running) return\n    this.running = true\n\n    // Get all active policies with schedules\n    const policies = await this.manager.listPolicies({ status: 'active' })\n\n    for (const policy of policies) {\n      if (policy.schedule?.intervalMs) {\n        const intervalId = setInterval(async () => {\n          try {\n            await this.manager.executePolicy(policy.id, { actor: 'scheduler' })\n          } catch {\n            // Log error but don't stop the scheduler\n            console.error(`Failed to execute policy ${policy.id}`)\n          }\n        }, policy.schedule.intervalMs)\n\n        this.intervalIds.set(policy.id, intervalId)\n\n        // Run immediately if configured\n        if (policy.schedule.runOnActivation) {\n          try {\n            await this.manager.executePolicy(policy.id, { actor: 'scheduler' })\n          } catch {\n            console.error(`Failed to execute policy ${policy.id} on activation`)\n          }\n        }\n      }\n    }\n  }\n\n  stop(): void {\n    this.running = false\n    for (const intervalId of Array.from(this.intervalIds.values())) {\n      clearInterval(intervalId)\n    }\n    this.intervalIds.clear()\n  }\n}\n\n/**\n * Compliance policy templates\n */\nexport const ComplianceTemplates = {\n  /**\n   * GDPR-compliant retention policy template\n   */\n  gdpr: {\n    type: 'time' as const,\n    value: 365,\n    unit: 'days' as const,\n    compliance: ['gdpr' as const],\n    action: 'hard_delete' as const,\n    description: 'GDPR-compliant data retention (1 year)',\n  },\n\n  /**\n   * HIPAA-compliant retention policy template (6 years)\n   */\n  hipaa: {\n    type: 'time' as const,\n    value: 6,\n    unit: 'years' as const,\n    compliance: ['hipaa' as const],\n    action: 'archive' as const,\n    description: 'HIPAA-compliant medical record retention (6 years)',\n  },\n\n  /**\n   * SOX-compliant retention policy template (7 years)\n   */\n  sox: {\n    type: 'time' as const,\n    value: 7,\n    unit: 'years' as const,\n    compliance: ['sox' as const],\n    action: 'archive' as const,\n    description: 'SOX-compliant financial record retention (7 years)',\n  },\n\n  /**\n   * PCI-DSS compliant policy (1 year for logs)\n   */\n  pciDss: {\n    type: 'time' as const,\n    value: 1,\n    unit: 'years' as const,\n    compliance: ['pci_dss' as const],\n    action: 'hard_delete' as const,\n    description: 'PCI-DSS compliant audit log retention (1 year)',\n  },\n\n  /**\n   * CCPA-compliant policy (varies by data type)\n   */\n  ccpa: {\n    type: 'time' as const,\n    value: 365,\n    unit: 'days' as const,\n    compliance: ['ccpa' as const],\n    action: 'hard_delete' as const,\n    description: 'CCPA-compliant personal data retention',\n  },\n} as const\n\n/**\n * Create a pre-configured retention manager with compliance templates\n */\nexport function createRetentionManager(config: RetentionManagerConfig): RetentionManager {\n  return new RetentionManager(config)\n}\n"],"mappings":"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","names":["result"]}