• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

clippingkk / web / #1242

01 Aug 2026 12:11PM UTC coverage: 6.352% (+0.8%) from 5.551%
#1242

push

web-flow
Merge 671ee868b into d28b0f250

164 of 3201 branches covered (5.12%)

Branch coverage included in aggregate %.

64 of 106 new or added lines in 8 files covered. (60.38%)

2 existing lines in 2 files now uncovered.

376 of 5300 relevant lines covered (7.09%)

33.9 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

66.67
/src/server/http.ts
1
import { metrics, SpanStatusCode, trace } from '@opentelemetry/api'
2
import { logs, SeverityNumber } from '@opentelemetry/api-logs'
3
import { withSpan } from '@superlog/otel-helpers'
4
import { connection } from 'next/server'
5

6
import type { ApiErrorResponse, ApiSuccessResponse } from '@/contracts/http'
7

8
import { getServerEnv } from './env'
9
import { ApiError } from './errors'
10

11
type Handler = (request: Request, context?: unknown) => Promise<Response>
12

13
const tracer = trace.getTracer('clippingkk.web.http')
1✔
14
const meter = metrics.getMeter('clippingkk.web.http')
1✔
15
const logger = logs.getLogger('clippingkk.web.http')
1✔
16
const routeOperations = meter.createCounter('app.http.server.operations', {
1✔
17
  description: 'Completed application HTTP operations',
18
})
19
const routeDuration = meter.createHistogram(
1✔
20
  'app.http.server.operation.duration',
21
  {
22
    description: 'Application HTTP operation duration',
23
    unit: 'ms',
24
  }
25
)
26

27
export function json<T>(data: T, status = 200, msg = '') {
4✔
28
  const payload: ApiSuccessResponse<T> = { status, msg, data }
2✔
29
  return Response.json(payload, { status })
2✔
30
}
31

32
export function errorJson(error: unknown) {
33
  const status = error instanceof ApiError ? error.status : 500
1!
34
  const message =
35
    error instanceof Error ? error.message : 'internal server error'
1!
36
  const payload: ApiErrorResponse = {
1✔
37
    status,
38
    msg: message,
39
    error: message,
40
  }
41
  return Response.json(payload, { status })
1✔
42
}
43

44
function corsHeaders(request: Request) {
45
  const env = getServerEnv()
2✔
46
  const origin = request.headers.get('origin')
2✔
47
  const allowed =
48
    origin && env.corsAllowedOrigins.has(origin) ? origin : env.APP_ORIGIN
2!
49
  return {
2✔
50
    'Access-Control-Allow-Origin': allowed,
51
    'Access-Control-Allow-Credentials': 'true',
52
    'Access-Control-Allow-Headers':
53
      'Authorization, Content-Type, X-Basic, X-Accept-Language, Sentry-Trace, Baggage',
54
    'Access-Control-Allow-Methods': 'GET, POST, DELETE, OPTIONS',
55
    Vary: 'Origin',
56
  }
57
}
58

59
function withCors(response: Response, request: Request) {
60
  const headers = new Headers(response.headers)
2✔
61
  for (const [key, value] of Object.entries(corsHeaders(request))) {
2✔
62
    headers.set(key, value)
10✔
63
  }
64
  return new Response(response.body, {
2✔
65
    status: response.status,
66
    statusText: response.statusText,
67
    headers,
68
  })
69
}
70

71
export function route(handler: Handler, operation = 'http.route'): Handler {
3✔
72
  return async (request, context) => {
3✔
73
    await connection()
2✔
74
    return withSpan(
2✔
75
      operation,
76
      async (span) => {
77
        const startedAt = performance.now()
2✔
78
        const method = request.method
2✔
79
        try {
2✔
80
          const response = withCors(await handler(request, context), request)
2✔
81
          const outcome = response.ok ? 'success' : 'error'
2✔
82
          const attributes = {
2✔
83
            operation,
84
            'http.request.method': method,
85
            'http.response.status_code': response.status,
86
            outcome,
87
          }
88
          span.setAttributes(attributes)
2✔
89
          if (!response.ok) span.setStatus({ code: SpanStatusCode.ERROR })
2✔
90
          routeOperations.add(1, attributes)
2✔
91
          logger.emit({
2✔
92
            severityNumber: response.ok
2✔
93
              ? SeverityNumber.INFO
94
              : SeverityNumber.WARN,
95
            severityText: response.ok ? 'INFO' : 'WARN',
2✔
96
            body: 'HTTP route completed',
97
            attributes,
98
          })
99
          return response
2✔
100
        } catch (error) {
NEW
101
          const response = withCors(errorJson(error), request)
×
NEW
102
          const errorType = error instanceof ApiError ? 'api_error' : 'internal'
×
NEW
103
          const attributes = {
×
104
            operation,
105
            'http.request.method': method,
106
            'http.response.status_code': response.status,
107
            'error.type': errorType,
108
            outcome: 'error',
109
          }
NEW
110
          span.recordException(
×
111
            error instanceof Error ? error : new Error(String(error))
×
112
          )
NEW
113
          span.setStatus({ code: SpanStatusCode.ERROR })
×
NEW
114
          span.setAttributes(attributes)
×
NEW
115
          routeOperations.add(1, attributes)
×
NEW
116
          logger.emit({
×
117
            severityNumber:
118
              response.status >= 500
×
119
                ? SeverityNumber.ERROR
120
                : SeverityNumber.WARN,
121
            severityText: response.status >= 500 ? 'ERROR' : 'WARN',
×
122
            body: 'HTTP route failed',
123
            attributes,
124
          })
NEW
125
          return response
×
126
        } finally {
127
          routeDuration.record(performance.now() - startedAt, {
2✔
128
            operation,
129
            'http.request.method': method,
130
          })
131
        }
132
      },
133
      { tracer }
134
    )
135
  }
136
}
137

138
export const options = route(
1✔
NEW
139
  async () => new Response(null, { status: 204 }),
×
140
  'http.options'
141
)
142

143
export async function body<T>(request: Request): Promise<T> {
144
  try {
×
145
    return (await request.json()) as T
×
146
  } catch {
147
    throw new ApiError('invalid JSON body')
×
148
  }
149
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc