( op: string, fn: () => T | Promise<T>, caller: string = "unknown" )
| 430 | * On error: the error is RE-THROWN after logging — caller still owns flow. |
| 431 | */ |
| 432 | export async function withErrorContext<T>( |
| 433 | op: string, |
| 434 | fn: () => T | Promise<T>, |
| 435 | caller: string = "unknown" |
| 436 | ): Promise<T> { |
| 437 | const t0 = Date.now(); |
| 438 | try { |
| 439 | const result = await fn(); |
| 440 | logErrorContext({ |
| 441 | ts: new Date().toISOString(), |
| 442 | op, |
| 443 | duration_ms: Date.now() - t0, |
| 444 | outcome: "ok", |
| 445 | schema_version: 1, |
| 446 | last_writer: caller, |
| 447 | }); |
| 448 | return result; |
| 449 | } catch (err) { |
| 450 | logErrorContext({ |
| 451 | ts: new Date().toISOString(), |
| 452 | op, |
| 453 | duration_ms: Date.now() - t0, |
| 454 | outcome: "error", |
| 455 | error: err instanceof Error ? err.message : String(err), |
| 456 | schema_version: 1, |
| 457 | last_writer: caller, |
| 458 | }); |
| 459 | throw err; |
| 460 | } |
| 461 | } |
| 462 | |
| 463 | function logErrorContext(entry: ErrorContextEntry): void { |
| 464 | try { |
no test coverage detected