All files / web/src/lib/abacus/print ring-emitter.ts

100% Statements 32/32
100% Branches 4/4
100% Functions 1/1
100% Lines 32/32

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 331x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 4x 4x 4x 4x 4x 4x 3x 3x 3x 3x 4x 1x 1x 4x  
/**
 * Doorbell → socket fan-out (Phase 2a, #8.4), modeled on
 * `lib/classroom/socket-emitter.ts`.
 *
 * TENANT SCOPING IS THE WHOLE POINT: a ring resolves to exactly one owning
 * user (the connection row's userId) and emits to that user's room only —
 * `io.to('user:' + userId)`, never a global `io.emit`. Another account's
 * browser must never observe that this user's printer did anything.
 */
 
import { getSocketIO } from '@/lib/socket-io'
import { PRINT_JOB_UPDATED_EVENT, type PrintJobUpdatedEvent } from './ring-events'
 
/**
 * Emit a print-job-updated hint to the owning user's room. Payload is
 * identifiers only (see ring-events.ts) — the client re-reads real state
 * through the authenticated proxy.
 */
export async function emitPrintJobUpdated(
  userId: string,
  payload: PrintJobUpdatedEvent
): Promise<void> {
  const io = await getSocketIO()
  if (!io) return
 
  try {
    io.to(`user:${userId}`).emit(PRINT_JOB_UPDATED_EVENT, payload)
    console.log(`[PrintRing] ${PRINT_JOB_UPDATED_EVENT} -> user:${userId}`)
  } catch (error) {
    console.error('[PrintRing] Failed to emit print-job-updated:', error)
  }
}