Files
OmniRoute/open-sse/types.d.ts
backryun 8eebda13ca refactor(sse): resolve open-sse utils/translator type diagnostics for TS 7 (#8483)
First slice of the TypeScript 7 migration split requested on #7697: resolve the
type diagnostics under `open-sse/tsconfig.json` in the lowest-risk modules, with
no toolchain change. 12 diagnostics across 8 files, all outside the hot path —
`chatCore.ts` and `stream.ts` are deliberately left for a later, standalone slice.

Fixes, by cause:

* `Transformer.cancel` (progressTracker, sseHeartbeat, and stream.ts's existing
  handler) — the WHATWG Streams standard defines `transformer.cancel(reason)` and
  Node implements it (verified on v24: cancelling the readable side invokes it),
  but `lib.dom.d.ts` still omits it from `Transformer`, so every such handler was
  TS2353. These handlers clear the heartbeat/progress intervals when an SSE client
  disconnects, so deleting them to satisfy the checker would leak a timer per
  abandoned stream. The interface is patched in `open-sse/types.d.ts` instead.

* `earlyStreamKeepalive` — `SettledHandler` was discriminated by `ok: true | false`.
  This workspace compiles with `strictNullChecks: false`, where a boolean-literal
  discriminant narrows the positive branch but not the negative one, so reading
  `.error` off the rejected arm did not type-check (the two `.response` reads
  elsewhere in the file did, which is why only one site errored). Retagged with a
  string discriminant, which narrows both branches under the same settings.

* `toolCallShim` / `openai-responses` — assigning back to a property declared
  `unknown` resets the `typeof` narrowing, so the following comparison no longer
  saw a number/array. Both now read through a local. The `Read` limit clamp is
  behavior-identical: its two branches are mutually exclusive at READ_MAX_LIMIT 2000.

* `sanitizeToolResultId` — takes `unknown` but forwards to a `string` parameter; a
  non-string id previously reached `.replace()` and threw. Coerced instead.

* `openaiHelper` — `opts = {}` inferred `{}`; typed as `FilterToOpenAIFormatOptions`.

* `cursorAgentProtobuf` — `Buffer.alloc(0)` infers `Buffer<ArrayBuffer>` under
  @types/node 26 while the decoded field is `Buffer<ArrayBufferLike>`; the locals
  now use bare `Buffer`, matching `requestMetadata` a few lines above.

Validation: 335 -> 321 diagnostics with zero new errors (full tsc error-set diff
against the base config). typecheck:core clean, lint clean, check:type-coverage
92.17% -> 94.17%. All 114 existing test files that import a touched module pass;
`plan3-p0.test.ts` fails identically with and without this change (it reads the
developer's real ~/.omniroute DB instead of a test-scoped DATA_DIR).

The new test covers the three behavioral surfaces rather than the refactors the
existing keepalive/heartbeat suites already hold: that `transformer.cancel()`
really fires and can clear an interval, the id coercion, and the limit-clamp bounds.
2026-07-25 02:53:07 -03:00

161 lines
5.1 KiB
TypeScript

/**
* Core type definitions for omniroute.
*
* These types describe the main data structures flowing through the proxy
* pipeline: credentials, model info, executor results, and chat parameters.
*
* Usage (JSDoc reference):
* /** @type {import("./types").ProviderCredentials} *\/
*/
// ============ Provider & Auth ============
export interface ProviderCredentials {
/** OAuth access token (short-lived) */
accessToken: string;
/** OAuth refresh token (long-lived) */
refreshToken?: string;
/** Internal connection ID */
connectionId: string;
/** Optional per-account concurrency cap */
maxConcurrent?: number | null;
/** User email associated with the connection */
email?: string;
/** API key (for apikey auth type) */
apiKey?: string;
/** Token expiry timestamp */
expiresAt?: string;
/** Provider-specific extra data (e.g., AWS region, auth method) */
providerSpecificData?: Record<string, unknown>;
}
export interface ModelInfo {
/** Canonical provider ID (e.g., "claude", "antigravity") */
provider: string;
/** Model identifier (e.g., "claude-opus-4-6") */
model: string;
/** Optional original model string from client (e.g., "cc/opus-4-6") */
originalModel?: string;
}
// ============ Executor ============
export interface ExecutorResult {
/** Whether the upstream request succeeded (2xx) */
success: boolean;
/** The HTTP Response from the upstream provider */
response: Response;
/** HTTP status code (present on failure) */
status?: number;
/** Human-readable error message (present on failure) */
error?: string;
/** Suggested retry delay in ms (present on rate-limit) */
retryAfterMs?: number;
}
// ============ Chat Pipeline ============
export interface ChatCoreParams {
/** Parsed request body */
body: Record<string, unknown>;
/** Resolved model info */
modelInfo: ModelInfo;
/** Provider credentials to use */
credentials: ProviderCredentials;
/** Request-scoped logger */
log: Logger;
/** Raw client request body (before translation) */
clientRawRequest?: Record<string, unknown>;
/** Connection ID for usage tracking */
connectionId: string;
/** API key metadata for usage attribution */
apiKeyInfo?: { id?: string; name?: string } | null;
/** Client User-Agent header */
userAgent?: string;
/** Callback when credentials are refreshed mid-request */
onCredentialsRefreshed?: (creds: ProviderCredentials) => Promise<void>;
/** Callback on successful upstream response */
onRequestSuccess?: () => Promise<void>;
}
// ============ Logger ============
export interface Logger {
debug(tag: string, message: string, data?: Record<string, unknown>): void;
info(tag: string, message: string, data?: Record<string, unknown>): void;
warn(tag: string, message: string, data?: Record<string, unknown>): void;
error(tag: string, message: string, data?: Record<string, unknown>): void;
}
export interface TaggedLogger {
debug(message: string, meta?: Record<string, unknown>): void;
info(message: string, meta?: Record<string, unknown>): void;
warn(message: string, meta?: Record<string, unknown>): void;
error(message: string, meta?: Record<string, unknown>): void;
}
// ============ Configuration ============
export interface ProviderConfig {
/** Primary base URL */
baseUrl?: string;
/** Multiple base URLs for failover */
baseUrls?: string[];
/** API format identifier */
format: string;
/** Default HTTP headers */
headers?: Record<string, string>;
/** OAuth client ID */
clientId?: string;
/** OAuth client secret */
clientSecret?: string;
/** Token refresh endpoint */
tokenUrl?: string;
/** Authorization endpoint */
authUrl?: string;
}
// ============ Translator ============
export interface TranslationState {
/** Source format to translate TO */
sourceFormat: string;
/** Current accumulated content */
content?: string;
/** Provider that generated the response */
provider?: string;
/** Map of tool names for translation */
toolNameMap?: Record<string, string> | null;
/** Accumulated usage data */
usage?: UsageData | null;
/** Finish reason from provider */
finishReason?: string | null;
}
export interface UsageData {
prompt_tokens: number;
completion_tokens: number;
total_tokens: number;
}
// ============ Lib gap: Transformer.cancel ============
declare global {
/**
* The WHATWG Streams standard defines `transformer.cancel(reason)`, invoked when
* the readable side is cancelled (for us: an SSE client disconnecting). Node
* implements it — verified on v24 — but `lib.dom.d.ts` still omits it from
* `Transformer`, so every `new TransformStream({ ..., cancel() {} })` in the
* codebase fails with TS2353 ("'cancel' does not exist in type 'Transformer'").
*
* These `cancel` handlers are load-bearing: they clear heartbeat/progress
* intervals and idle timers on disconnect. Deleting them to satisfy the checker
* would leak a timer per abandoned stream, so the type is patched instead.
*
* Remove once the bundled lib declares it.
*/
interface Transformer<I = unknown, O = unknown> {
cancel?: (reason?: unknown) => void | PromiseLike<void>;
}
}