| 82 | * Only called when USER_TYPE === 'ant' — zero overhead in external builds. |
| 83 | */ |
| 84 | export function wrapApiForFaultInjection( |
| 85 | api: BridgeApiClient, |
| 86 | ): BridgeApiClient { |
| 87 | function consume(method: BridgeFault['method']): BridgeFault | null { |
| 88 | const idx = faultQueue.findIndex(f => f.method === method) |
| 89 | if (idx === -1) return null |
| 90 | const fault = faultQueue[idx]! |
| 91 | fault.count-- |
| 92 | if (fault.count <= 0) faultQueue.splice(idx, 1) |
| 93 | return fault |
| 94 | } |
| 95 | |
| 96 | function throwFault(fault: BridgeFault, context: string): never { |
| 97 | logForDebugging( |
| 98 | `[bridge:debug] Injecting ${fault.kind} fault into ${context}: status=${fault.status} errorType=${fault.errorType ?? 'none'}`, |
| 99 | ) |
| 100 | if (fault.kind === 'fatal') { |
| 101 | throw new BridgeFatalError( |
| 102 | `[injected] ${context} ${fault.status}`, |
| 103 | fault.status, |
| 104 | fault.errorType, |
| 105 | ) |
| 106 | } |
| 107 | // Transient: mimic an axios rejection (5xx / network). No .status on |
| 108 | // the error itself — that's how the catch blocks distinguish. |
| 109 | throw new Error(`[injected transient] ${context} ${fault.status}`) |
| 110 | } |
| 111 | |
| 112 | return { |
| 113 | ...api, |
| 114 | async pollForWork(envId, secret, signal, reclaimMs) { |
| 115 | const f = consume('pollForWork') |
| 116 | if (f) throwFault(f, 'Poll') |
| 117 | return api.pollForWork(envId, secret, signal, reclaimMs) |
| 118 | }, |
| 119 | async registerBridgeEnvironment(config) { |
| 120 | const f = consume('registerBridgeEnvironment') |
| 121 | if (f) throwFault(f, 'Registration') |
| 122 | return api.registerBridgeEnvironment(config) |
| 123 | }, |
| 124 | async reconnectSession(envId, sessionId) { |
| 125 | const f = consume('reconnectSession') |
| 126 | if (f) throwFault(f, 'ReconnectSession') |
| 127 | return api.reconnectSession(envId, sessionId) |
| 128 | }, |
| 129 | async heartbeatWork(envId, workId, token) { |
| 130 | const f = consume('heartbeatWork') |
| 131 | if (f) throwFault(f, 'Heartbeat') |
| 132 | return api.heartbeatWork(envId, workId, token) |
| 133 | }, |
| 134 | } |
| 135 | } |