Skip to content
markpaper

src/transport/envelope.ts

v0.2.0 · 2 KB

Download file
// Envelope parsing: `{status:'success', data, request_type}` | `{status:'failure', error, error_code}`.

import { NadoEnvelopeError, NadoRejection } from './errors.js';
import type { Envelope } from './types.js';

/** Result of {@link parseEnvelope}. */
export type ParsedEnvelope<T = unknown> =
  | { readonly ok: true; readonly data: T; readonly requestType: string | undefined }
  | { readonly ok: false; readonly code: number; readonly error: string; readonly requestType: string | undefined };

/**
 * Classifies a parsed JSON body. Any other shape (no `status`, not an object) is a transport failure
 * with an unknown outcome, never "the venue said no".
 *
 * @throws NadoEnvelopeError when the body is not an envelope.
 */
export function parseEnvelope<T = unknown>(body: unknown, requestType?: string): ParsedEnvelope<T> {
  if (body === null || typeof body !== 'object' || Array.isArray(body))
    throw new NadoEnvelopeError({ body, requestType });
  const env = body as Partial<Envelope<T>> & { request_type?: unknown; error?: unknown; error_code?: unknown };
  const rt = typeof env.request_type === 'string' ? env.request_type : requestType;
  if (env.status === 'success') return { ok: true, data: env.data as T, requestType: rt };
  if (env.status === 'failure') {
    const raw = Number(env.error_code);
    return {
      ok: false,
      code: Number.isFinite(raw) ? raw : -1,
      error: typeof env.error === 'string' ? env.error : String(env.error ?? 'unknown error'),
      requestType: rt,
    };
  }
  throw new NadoEnvelopeError({ body, requestType });
}

/**
 * Unwraps an envelope: returns `data` on success, throws {@link NadoRejection} on failure.
 *
 * @throws NadoRejection | NadoEnvelopeError
 */
export function unwrapEnvelope<T = unknown>(body: unknown, path: 'query' | 'execute', requestType?: string): T {
  const parsed = parseEnvelope<T>(body, requestType);
  if (parsed.ok) return parsed.data;
  throw new NadoRejection({ code: parsed.code, error: parsed.error, requestType: parsed.requestType, path });
}
All files