All files / web/src/lib/kid-songs atomicWriteSong.ts

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

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 33 341x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 1x 1x 1x 2x 2x 2x 2x 2x  
import { randomBytes } from 'node:crypto'
import { open, rename, unlink } from 'node:fs/promises'
import { basename, dirname, join } from 'node:path'
 
interface AtomicWriteOperations {
  open: typeof open
  rename: typeof rename
  unlink: typeof unlink
}
 
const defaultOperations: AtomicWriteOperations = { open, rename, unlink }
 
export async function atomicWriteSong(
  path: string,
  bytes: Buffer,
  operations: AtomicWriteOperations = defaultOperations
): Promise<void> {
  const tempPath = join(
    dirname(path),
    `.${basename(path)}.${process.pid}.${randomBytes(8).toString('hex')}.tmp`
  )
  let handle: Awaited<ReturnType<typeof open>> | null = null
  try {
    handle = await operations.open(tempPath, 'wx', 0o600)
    await handle.writeFile(bytes)
    await handle.close()
    handle = null
    await operations.rename(tempPath, path)
  } finally {
    await handle?.close().catch(() => undefined)
    await operations.unlink(tempPath).catch(() => undefined)
  }
}