```ts
export function applyCharges(caps: Record<string, [number, number]>, charges: [string, string, number][]): { left: Record<string, number>; refused: [string, string][] } {
    const left: Record<string, number> = {};
    const refused: [string, string][] = [];

    for (const [id, bucket, amount] of charges) {
        if (typeof amount !== 'number' || amount <= 0 || !Number.isInteger(amount)) {
            throw new Error('Invalid charge amount');
        }

        if (!caps.hasOwnProperty(bucket)) {
            refused.push([id, 'unknown']);
            continue;
        }

        const [allowance, maxSingleCharge] = caps[bucket];
        if (amount > maxSingleCharge) {
            refused.push([id, 'single']);
            continue;
        }
        if (left[bucket] !== undefined && left[bucket] < amount) {
            refused.push([id, 'cap']);
            continue;
        }

        left[bucket] = (left[bucket] || allowance) - amount;
    }

    return { left, refused };
}
```