-
Notifications
You must be signed in to change notification settings - Fork 12
kiit.codes
Status is a platform-agnostic status type that lives in src/lib/codes/. It describes the status of any operation — a service call, a background job step, using a consistent shape rather than throwing raw errors or returning ad-hoc booleans.
Sample JSON representation, which can be used as ApiError response.
{
"name" : "TOKEN_EXPIRED",
"type" : "denied" // denied | ignored | invalid | errored | unknown
"code" : 403,
"success": false,
"message": "Session token expired"
}Logical heirarchy and relationship of the status types.
- Status = Passed | Failed
- Passed = Succeeded | Pending
- Failed = Denied | Ignored | Invalid | Errored | Unknown
graph TD
classDef statusNode fill:#3b82f6,stroke:#1d4ed8,color:#ffffff,font-weight:bold
classDef passedNode fill:#86efac,stroke:#16a34a,color:#14532d,font-weight:bold
classDef succeededNode fill:#22c55e,stroke:#15803d,color:#ffffff,font-weight:bold
classDef pendingNode fill:#fde047,stroke:#ca8a04,color:#713f12,font-weight:bold
classDef failedNode fill:#fca5a5,stroke:#f87171,color:#7f1d1d,font-weight:bold
classDef deniedNode fill:#111827,stroke:#000000,color:#ffffff,font-weight:bold
classDef ignoredNode fill:#9ca3af,stroke:#6b7280,color:#ffffff,font-weight:bold
classDef invalidNode fill:#f97316,stroke:#c2410c,color:#ffffff,font-weight:bold
classDef erroredNode fill:#dc2626,stroke:#b91c1c,color:#ffffff,font-weight:bold
classDef unknownNode fill:#7f1d1d,stroke:#450a0a,color:#ffffff,font-weight:bold
Status["Status<br/>name / type / code<br/>message / success"]
Passed["Passed<br/>success: true"]
Failed["Failed<br/>success: false"]
Succeeded["Succeeded<br/>type: succeeded"]
Pending["Pending<br/>type: pending"]
Denied["Denied<br/>type: denied"]
Ignored["Ignored<br/>type: ignored"]
Invalid["Invalid<br/>type: invalid"]
Errored["Errored<br/>type: errored"]
Unknown["Unknown<br/>type: unknown"]
Status --> Passed
Status --> Failed
Passed --> Succeeded
Passed --> Pending
Failed --> Denied
Failed --> Ignored
Failed --> Invalid
Failed --> Errored
Failed --> Unknown
class Status statusNode
class Passed passedNode
class Succeeded succeededNode
class Pending pendingNode
class Failed failedNode
class Denied deniedNode
class Ignored ignoredNode
class Invalid invalidNode
class Errored erroredNode
class Unknown unknownNode
Three problems this solves:
- Universal: Can be used universally in service layers ( XService.ts ), background jobs, routes.
- Hierarchy: Status has a logical group and heirarchy for successes and failures.
- Standard : Establishes an precise status type and representation across layers.
-
Compliant: Convertible to Http status codes via
toHttpStatus()insrc/lib/codes/utils.ts - Reusable : Can be reused across multiple operations that can yield the same status/error.
- Extensible: Easily extensible by creating new Status Codes for specific domains / features.
- Searchable: The name and type are unique and easily searchable in logs.
- Aggregated: Because of the hierarchy, the type, or name can be aggregated in logs/records.
-
Exceptions: Compatible with exceptions via the
StatusErrorsubtype of Error to store it.
Similar definitions exist else where.
GraphQL
The closest representation of this is MutationError
https://cosmo.wundergraph.com/4f7ac3fd/staging/graph/main/schema?category=interfaces&typename=MutationError&fieldName=
{
code: number,
message: string
}| Parent | Type | Level | Purpose |
|---|---|---|---|
| Status | Passed |
Parent | Parent of successes |
| Passed | Succeeded |
Child | Successful operation |
| Passed | Pending |
Child | Pending processing |
| Status | Failed |
Parent | Parent for any failure |
| Failed | Denied |
Child | Security related |
| Failed | Ignored |
Child | Safely ignored |
| Failed | Invalid |
Child | Invalid data |
| Failed | Errored |
Child | Failure of known business rule |
| Failed | Unknown |
Child | Unhandled failure |
Every Status has the following fields and carries the info:
| Field | Purpose |
|---|---|
type |
Discriminant of ('denied', 'invalid', 'ignored', 'errored', 'unknown', ) |
name |
Unique domain label of status, e.g. TOKEN_EXPIRED, RATE_LIMITED, both are Denied
|
code |
Numeric code — defaults align with HTTP status codes, flexible for other runtimes |
message |
Human-readable description — must be a constant, never constructed from runtime data |
success |
Boolean shortcut for callers that don't need to narrow the type |
Setting up domain specific status codes. There are also general purpose ones available.
export class AuthCodes {
static readonly unsealFailed: Denied = {
success: false,
name: 'UnsealFailed',
type: 'denied',
code: 401,
message: 'Token unseal failed',
}
static readonly tokenExpired: Denied = {
success: false,
name: 'TokenExpired',
type: 'denied',
code: 401,
message: 'Token has expired',
}
}A subtype of Error is available to store the Status, refer to StatusError .
Related: https://gitlab.com/khealth/platform-team/chimera/-/blob/main/web-app/src/lib/http/browser-api-client.ts#L220
/** Native Error subclass that carries the originating Status */
export class StatusError extends Error {
constructor(public readonly status: Status) {
super(status.message)
this.name = status.name
}
}attempt(fn, status, options) is the canonical entry point for any service-layer call that can throw (Apollo, Prisma, fetch, third-party SDKs). On throw, attempt calls record() (which sanitizes via sanitizeError) and returns the supplied Status. On success, it returns T unwrapped.
import logging from '@khealth/k-common-logging'
import { attempt, isStatus } from '@/lib/codes'
const logger = logging.getLogger('fetch-visit-header')
const data = await attempt(
async () => {
const client = createUserGraphqlClient()
const { data } = await client.query({ query: GET_VISIT_HEADER, variables: { sessionId } })
return data
},
GraphCodes.queryFailed,
{ logger, action: 'fetch-visit-header' },
)
if (isStatus(data)) return { kind: 'error' }
// data is narrowed to the query result typeSanitization is built in. record() runs every caught error through sanitizeError (src/lib/codes/utils.ts:46). Don't hand-roll a try/catch + sanitizeError — that's strictly redundant. Only drop down to a manual try/catch when you need to discriminate thrown error classes into different Status values (e.g. ExpiredTokenError → tokenExpired vs other → tokenInvalid). See docs/code-standards.md § "Error handling" for the full rationale and anti-pattern callout.
Existing in-tree precedent: src/lib/auth/session.ts:95 (the unseal path).
When you need branching on thrown error classes — i.e. the carve-out where attempt doesn't fit — call record() from inside your own try/catch:
import { record } from '@/lib/codes'
try {
return await verifyAuthToken(token)
} catch (err) {
if (err instanceof ExpiredTokenError) {
record(logger, 'verify-token', AuthCodes.tokenExpired, err)
return AuthCodes.tokenExpired
}
record(logger, 'verify-token', AuthCodes.tokenInvalid, err)
return AuthCodes.tokenInvalid
}
// → logger.error('action:verify-token, status:denied', { status: {…}, error: {name, code} })import { toError } from '@/lib/codes'
throw toError(Codes.denied) // StatusError carrying the Statusimport { toHttpStatus } from '@/lib/codes'
return new Response(null, { status: toHttpStatus(status) })