@ai-matrx/data 0.18.36 → 0.18.38

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/credentials.ts","../src/net/errors.ts","../src/net/retry.ts","../src/net/resilient-fetch.ts","../src/net/stream-monitor.ts"],"sourcesContent":["/**\n * @ai-matrx/data — the core module.\n *\n * The shared structural vocabulary every lane of the data layer speaks, plus\n * the lane-free net-resilience trio (S12 rides the core — re-exported from\n * `./net`, also importable as `@ai-matrx/data/net`). This entry point\n * otherwise deliberately stays tiny (the split-out law): the Supabase lane is\n * `@ai-matrx/data/db`; the API and files lanes arrive in later minors. Nothing\n * here performs network, storage, or global-state work at import time.\n *\n * The types are STRUCTURAL on purpose: the package never imports\n * `@supabase/supabase-js` — a real `PostgrestError` satisfies\n * `PostgrestLikeError` by shape, and so does any compatible client, so the\n * package works against whatever client the host injects.\n */\n\n/** The shape of a PostgREST error object (plain object, NOT an Error). */\nexport interface PostgrestLikeError {\n message: string;\n details?: string | null;\n hint?: string | null;\n code?: string | null;\n}\n\n/** The shape a `.maybeSingle()` call resolves to. */\nexport interface MaybeSingleResponse<Row> {\n data: Row | null;\n error: PostgrestLikeError | null;\n}\n\n/** A JSON value — the wire-safe subset every column and payload speaks. */\nexport type Json =\n | string\n | number\n | boolean\n | null\n | { [key: string]: Json | undefined }\n | Json[];\n\n/**\n * Converts any thrown value from a Supabase/PostgREST call into a proper Error\n * with a descriptive message. PostgREST errors are plain objects with `message`,\n * `code`, `details`, and `hint` — they are NOT `instanceof Error`, which causes\n * generic catch blocks to lose the real reason.\n */\nexport function pgErrorToError(err: unknown): Error {\n if (err instanceof Error) return err;\n if (err && typeof err === \"object\") {\n const e = err as Record<string, unknown>;\n const parts: string[] = [];\n if (typeof e.message === \"string\" && e.message) parts.push(e.message);\n if (typeof e.details === \"string\" && e.details)\n parts.push(`Details: ${e.details}`);\n if (typeof e.hint === \"string\" && e.hint) parts.push(`Hint: ${e.hint}`);\n if (typeof e.code === \"string\" && e.code) parts.push(`(code: ${e.code})`);\n if (parts.length > 0) return new Error(parts.join(\" — \"));\n }\n return new Error(String(err));\n}\n\nexport * from \"./credentials\";\nexport * from \"./net\";\n","/**\n * The credentials port — the ONE credential→header mapping for every client\n * package (DATA-PACKAGE-DESIGN.md \"The credentials port\"; API-key lane ratified\n * as C16, 2026-08-29).\n *\n * Three credential kinds, one mapping, in exactly one place:\n *\n * - `user` — a Supabase JWT: `Authorization: Bearer <jwt>` (both lanes)\n * - `guest` — a fingerprint: `X-Fingerprint-ID: <id>` (API lane only)\n * - `api_key` — an AI Matrx API key: `Authorization: Bearer mx_live_...`.\n * The server's ONE AuthMiddleware disambiguates by shape (`mx_` prefix vs\n * three-dot JWT), so both `user` and `api_key` ride the same header.\n *\n * No polling, no readiness loop: readiness is the port's promise.\n */\n\n/** A credential a Matrx client can present. */\nexport type MatrxCredential =\n | { kind: \"user\"; accessToken: string }\n | { kind: \"guest\"; fingerprintId: string }\n | { kind: \"api_key\"; apiKey: string };\n\n/**\n * The async source of the current credential. `get()` resolves when the host\n * knows its auth state (null = anonymous); `onChange` lets transports refresh\n * cached headers when the credential rotates.\n */\nexport interface CredentialsPort {\n get(): Promise<MatrxCredential | null>;\n onChange?(cb: () => void): () => void;\n}\n\n/** Prefix that marks a bearer token as an AI Matrx API key, never a JWT. */\nexport const API_KEY_PREFIX = \"mx_\";\n\n/**\n * Map a credential to the exact request headers the AI Matrx platform expects.\n * This is the single place that knowledge lives — transports spread the result\n * into their request headers and add `X-Organization-Id` from the org context\n * binding separately.\n */\nexport function credentialToHeaders(\n credential: MatrxCredential | null,\n): Record<string, string> {\n if (credential === null) return {};\n switch (credential.kind) {\n case \"user\":\n return { Authorization: `Bearer ${credential.accessToken}` };\n case \"guest\":\n return { \"X-Fingerprint-ID\": credential.fingerprintId };\n case \"api_key\": {\n if (!credential.apiKey.startsWith(API_KEY_PREFIX)) {\n throw new Error(\n `credentialToHeaders: an api_key credential must start with \"${API_KEY_PREFIX}\" ` +\n \"(AI Matrx API keys look like mx_live_...)\",\n );\n }\n return { Authorization: `Bearer ${credential.apiKey}` };\n }\n }\n}\n","/**\n * Structured errors for the resilience layer.\n *\n * Every network-layer failure resolves to one of these classes so callers\n * (and any recovery UI) can branch on error type, not on string matching.\n *\n * Ported verbatim from matrx-frontend `lib/net/errors.ts` — the origin of the\n * trio. The one coupling seam removed: `extractErrorMessage` (previously the\n * app's `utils/errors`) is pure logic and ships inside the package.\n */\n\nexport type NetErrorCode =\n | \"connect-timeout\"\n | \"total-timeout\"\n | \"heartbeat-timeout\"\n | \"network\"\n | \"http\"\n | \"aborted\"\n | \"offline\"\n | \"unknown\";\n\nexport class NetError extends Error {\n readonly code: NetErrorCode;\n override readonly cause?: unknown;\n readonly status?: number;\n readonly retryable: boolean;\n\n constructor(\n code: NetErrorCode,\n message: string,\n opts: { cause?: unknown; status?: number; retryable?: boolean } = {},\n ) {\n super(message);\n this.name = \"NetError\";\n this.code = code;\n this.cause = opts.cause;\n // Conditional only for `exactOptionalPropertyTypes` — same runtime shape.\n if (opts.status !== undefined) this.status = opts.status;\n this.retryable = opts.retryable ?? false;\n }\n}\n\nexport class ConnectTimeoutError extends NetError {\n constructor(ms: number, cause?: unknown) {\n super(\"connect-timeout\", `Connection timed out after ${ms}ms`, {\n cause,\n retryable: true,\n });\n this.name = \"ConnectTimeoutError\";\n }\n}\n\nexport class TotalTimeoutError extends NetError {\n constructor(ms: number, cause?: unknown) {\n super(\"total-timeout\", `Request exceeded ${ms}ms total budget`, {\n cause,\n retryable: false,\n });\n this.name = \"TotalTimeoutError\";\n }\n}\n\nexport class HeartbeatTimeoutError extends NetError {\n constructor(ms: number, cause?: unknown) {\n super(\n \"heartbeat-timeout\",\n `No server activity for ${ms}ms — stream considered dead`,\n { cause, retryable: false },\n );\n this.name = \"HeartbeatTimeoutError\";\n }\n}\n\nexport class NetworkError extends NetError {\n constructor(message: string, cause?: unknown) {\n super(\"network\", message, { cause, retryable: true });\n this.name = \"NetworkError\";\n }\n}\n\nexport class HttpError extends NetError {\n constructor(status: number, message: string, cause?: unknown) {\n // 408/429/5xx are generally safe to retry for idempotent ops.\n const retryable = status === 408 || status === 429 || status >= 500;\n super(\"http\", message, { cause, status, retryable });\n this.name = \"HttpError\";\n }\n}\n\nexport class AbortedError extends NetError {\n constructor(cause?: unknown) {\n super(\"aborted\", \"Request was aborted\", { cause, retryable: false });\n this.name = \"AbortedError\";\n }\n}\n\nexport class OfflineError extends NetError {\n constructor() {\n super(\"offline\", \"Browser is offline\", { retryable: true });\n this.name = \"OfflineError\";\n }\n}\n\nexport function isNetError(err: unknown): err is NetError {\n return err instanceof NetError;\n}\n\n/**\n * Folds any thrown value into a descriptive message string — THE one reader\n * for every failure shape this platform actually emits.\n *\n * 2026-09-07 (duplication census rows 9, 10, 21): this body had DRIFTED BEHIND\n * three host copies, which is the campaign's failure mode running in the\n * package's favour-direction. Each host had learned something the package\n * never did, so every non-frontend consumer of `@ai-matrx/data/net` was\n * flattening a server's own sentence into \"An unexpected error occurred\". All\n * three are folded in here, with a named regression test per shape:\n *\n * 1. **FastAPI's `detail`** (matrx-frontend `utils/errors.ts`, learned\n * 2026-08-31 as V2 finding G5). Our server refuses in `detail` — a string,\n * or an object carrying `message` — and every reader checked\n * `message`/`details` only. The server's sentence is the whole point of\n * catching this.\n * 2. **A `fallback` argument** (matrx-frontend `ShortcutForm.tsx`). A caller\n * who knows what it was doing can say \"Failed to save shortcut\" instead of\n * the generic sentence. Optional; the default is unchanged, so every\n * existing single-argument call behaves exactly as before.\n * 3. **Recursion into nested error objects** (aidream `workflow-studio`, two\n * byte-identical copies). The run pipeline nests: `{error: {type, message}}`\n * and `{error: {error: ...}}` and `{exception: {...}}` are all real shapes,\n * and a non-recursive reader answers \"An unexpected error occurred\" while\n * holding the actual reason two levels down. Depth-capped at 4 — a cyclic\n * or absurdly deep object must not hang a render.\n *\n * PostgREST-style errors are plain objects carrying\n * `message`/`details`/`hint`/`code`; a bare `String(err)` on those yields\n * \"[object Object]\", which is why this exists at all.\n */\nexport function extractErrorMessage(\n err: unknown,\n fallback = \"An unexpected error occurred\",\n): string {\n if (!err) return \"Unknown error\";\n if (typeof err === \"string\") return err;\n if (err instanceof Error) return err.message;\n if (typeof err === \"object\") {\n const e = err as Record<string, unknown>;\n const parts: string[] = [];\n if (typeof e.message === \"string\" && e.message) parts.push(e.message);\n // FastAPI's own word for the reason — only when nothing said `message`,\n // so a PostgREST error keeps its exact composed sentence.\n if (parts.length === 0) {\n if (typeof e.detail === \"string\" && e.detail) parts.push(e.detail);\n else if (e.detail && typeof e.detail === \"object\") {\n const d = e.detail as Record<string, unknown>;\n if (typeof d.message === \"string\" && d.message) parts.push(d.message);\n }\n }\n if (typeof e.details === \"string\" && e.details) parts.push(e.details);\n if (typeof e.hint === \"string\" && e.hint) parts.push(`Hint: ${e.hint}`);\n if (typeof e.code === \"string\" && e.code) parts.push(`Code: ${e.code}`);\n if (parts.length > 0) return parts.join(\" · \");\n\n const nested = extractNestedErrorMessage(e, 0);\n if (nested !== null) return nested;\n\n try {\n const s = JSON.stringify(err);\n if (s && s !== \"{}\") return s;\n } catch {\n /* ignore */\n }\n }\n return fallback;\n}\n\n/** Depth-capped walk into `error` / `exception` / `cause`. See above. */\nfunction extractNestedErrorMessage(\n e: Record<string, unknown>,\n depth: number,\n): string | null {\n if (depth > 4) return null;\n for (const key of [\"error\", \"exception\", \"cause\"] as const) {\n const value = e[key];\n if (typeof value === \"string\" && value) return value;\n if (value && typeof value === \"object\") {\n const inner = value as Record<string, unknown>;\n if (typeof inner.message === \"string\" && inner.message) return inner.message;\n if (typeof inner.detail === \"string\" && inner.detail) return inner.detail;\n const deeper = extractNestedErrorMessage(inner, depth + 1);\n if (deeper !== null) return deeper;\n }\n }\n return null;\n}\n\n/**\n * True when a failure is the TRANSPORT dying, not the server answering: the\n * browser is offline, asleep, mid-wifi-handoff, or DNS/TLS failed. It also\n * covers the bounded Envoy/Supabase gateway shape emitted when the upstream\n * database connection dies before response headers. `fetch`\n * rejects with a bare `TypeError: Failed to fetch` in every browser, and\n * supabase-js surfaces that verbatim (message only — no code, no hint).\n *\n * Use it to decide how LOUD a failure is, never whether to handle it. A\n * transport failure carries nothing an engineer can act on, so logging it at\n * error level buries the failures that do — that is how a notes surface filed\n * a system_error every time a laptop slept (2026-08-11). Anything with a\n * Postgres code, an HTTP status, or a server message is NOT this: keep those\n * loud.\n */\nexport function isTransportFailure(err: unknown): boolean {\n if (err instanceof NetworkError || err instanceof OfflineError) return true;\n if (typeof navigator !== \"undefined\" && navigator.onLine === false) return true;\n if (err instanceof TypeError) return true;\n if (err && typeof err === \"object\") {\n const e = err as { code?: unknown; status?: unknown; message?: unknown; name?: unknown };\n // A real PostgREST/Postgres failure always carries a code or status.\n if (e.code || e.status) return false;\n const text = `${typeof e.name === \"string\" ? e.name : \"\"} ${\n typeof e.message === \"string\" ? e.message : \"\"\n }`.toLowerCase();\n return (\n text.includes(\"failed to fetch\") ||\n text.includes(\"networkerror\") ||\n text.includes(\"network request failed\") ||\n text.includes(\"load failed\") || // Safari's wording for the same condition\n text.includes(\"upstream connect error or disconnect/reset before headers\")\n );\n }\n return false;\n}\n\nexport function toNetError(err: unknown): NetError {\n if (isNetError(err)) return err;\n if (err instanceof DOMException && err.name === \"AbortError\") {\n return new AbortedError(err);\n }\n if (err instanceof TypeError) {\n // fetch() throws TypeError on DNS/connection failure in every browser.\n return new NetworkError(err.message || \"Network request failed\", err);\n }\n return new NetError(\n \"unknown\",\n extractErrorMessage(err),\n { cause: err },\n );\n}\n","/**\n * withRetry — exponential backoff retry for idempotent operations.\n *\n * IMPORTANT: only wrap operations that are safe to repeat (GETs, cache\n * lookups, agent-definition fetches). Never wrap user-authored submissions —\n * recovery UX handles those via explicit user-initiated retry.\n *\n * Ported verbatim from matrx-frontend `lib/net/retry.ts`.\n */\n\nimport { isNetError, NetError } from \"./errors\";\n\nexport interface RetryOptions {\n /** Total attempts including the first. Default 3. */\n attempts?: number;\n /** Initial delay ms before the first retry. Default 250. */\n initialDelayMs?: number;\n /** Max delay ms cap between retries. Default 4_000. */\n maxDelayMs?: number;\n /** Multiplier applied after each failure. Default 2. */\n backoffFactor?: number;\n /** Jitter factor 0-1 applied to each delay. Default 0.25. */\n jitter?: number;\n /** Predicate deciding whether a given error should trigger a retry. Default: NetError.retryable. */\n retryOn?: (err: unknown) => boolean;\n /** Caller-provided signal. If aborted, no further retries are attempted. */\n signal?: AbortSignal;\n /** Fires before each retry; useful for logging/telemetry. */\n onRetry?: (attempt: number, err: unknown, delayMs: number) => void;\n}\n\nfunction defaultRetryOn(err: unknown): boolean {\n return isNetError(err) && (err as NetError).retryable;\n}\n\nfunction sleep(ms: number, signal?: AbortSignal): Promise<void> {\n return new Promise((resolve, reject) => {\n const timer = setTimeout(() => {\n resolve();\n if (signal) signal.removeEventListener(\"abort\", onAbort);\n }, ms);\n const onAbort = () => {\n clearTimeout(timer);\n reject(signal?.reason ?? new Error(\"Aborted\"));\n };\n if (signal) {\n if (signal.aborted) {\n clearTimeout(timer);\n reject(signal.reason);\n } else {\n signal.addEventListener(\"abort\", onAbort, { once: true });\n }\n }\n });\n}\n\nexport async function withRetry<T>(\n fn: (attempt: number) => Promise<T>,\n opts: RetryOptions = {},\n): Promise<T> {\n const attempts = Math.max(1, opts.attempts ?? 3);\n const initialDelayMs = opts.initialDelayMs ?? 250;\n const maxDelayMs = opts.maxDelayMs ?? 4_000;\n const factor = opts.backoffFactor ?? 2;\n const jitter = Math.max(0, Math.min(1, opts.jitter ?? 0.25));\n const retryOn = opts.retryOn ?? defaultRetryOn;\n\n let lastErr: unknown;\n for (let attempt = 1; attempt <= attempts; attempt++) {\n if (opts.signal?.aborted) throw opts.signal.reason;\n try {\n return await fn(attempt);\n } catch (err) {\n lastErr = err;\n if (attempt === attempts || !retryOn(err)) throw err;\n\n const base = Math.min(\n maxDelayMs,\n initialDelayMs * Math.pow(factor, attempt - 1),\n );\n const jitterAmount = base * jitter * (Math.random() * 2 - 1);\n const delayMs = Math.max(0, Math.round(base + jitterAmount));\n opts.onRetry?.(attempt, err, delayMs);\n await sleep(delayMs, opts.signal);\n }\n }\n\n // Unreachable — the loop always either returns or throws.\n throw lastErr;\n}\n","/**\n * resilientFetch — `fetch` with connect + total timeouts and composed abort.\n *\n * Every outbound HTTP call should go through this, not raw fetch. The\n * two-stage timeout (connect + total) lets callers allow long-streaming\n * responses without giving up the ability to fail fast when the server is\n * simply unreachable.\n *\n * Ported verbatim from matrx-frontend `lib/net/resilient-fetch.ts`. `fetch`\n * comes from the environment (Node 20+, browsers, React Native/Hermes) —\n * there is deliberately no injected transport here.\n */\n\nimport {\n AbortedError,\n ConnectTimeoutError,\n extractErrorMessage,\n HttpError,\n NetworkError,\n OfflineError,\n TotalTimeoutError,\n} from \"./errors\";\n\nexport interface ResilientFetchOptions {\n /** Max ms from request start → response headers received. Default 15_000. */\n connectTimeoutMs?: number;\n /**\n * Max ms from request start → fetch() promise resolves.\n * For streaming endpoints the response arrives quickly and the stream body\n * is read by the caller, so this bound only guards the handshake itself.\n * `null` disables the ceiling. Default 120_000.\n */\n totalTimeoutMs?: number | null;\n /** Caller-provided signal; composed with our internal timeout signals. */\n signal?: AbortSignal;\n /**\n * If true, throw on non-2xx responses instead of returning them. Default true.\n * Callers that need to read structured error bodies can set this to false.\n */\n throwOnHttpError?: boolean;\n}\n\nexport interface ResilientFetchResult {\n response: Response;\n /**\n * Composed AbortController tied to the fetch. Callers reading the body\n * (e.g. streaming) can call `.abort()` from this controller to cancel\n * in-flight reads. Already aborted on connect/total timeout.\n */\n controller: AbortController;\n}\n\nconst DEFAULTS = {\n connectTimeoutMs: 15_000,\n totalTimeoutMs: 120_000,\n} as const;\n\nfunction linkAbort(target: AbortController, signal: AbortSignal): () => void {\n if (signal.aborted) {\n target.abort(signal.reason);\n return () => {};\n }\n const onAbort = () => target.abort(signal.reason);\n signal.addEventListener(\"abort\", onAbort, { once: true });\n return () => signal.removeEventListener(\"abort\", onAbort);\n}\n\nexport async function resilientFetch(\n url: string,\n init: RequestInit = {},\n opts: ResilientFetchOptions = {},\n): Promise<ResilientFetchResult> {\n const connectTimeoutMs = opts.connectTimeoutMs ?? DEFAULTS.connectTimeoutMs;\n const totalTimeoutMs =\n opts.totalTimeoutMs === undefined\n ? DEFAULTS.totalTimeoutMs\n : opts.totalTimeoutMs;\n const throwOnHttpError = opts.throwOnHttpError ?? true;\n\n // NOTE: we deliberately do NOT hard-block on `navigator.onLine === false`\n // here. That flag is only a HINT — it is unreliable in both directions\n // (VPNs, virtual network adapters, browser network-service hiccups, sleep/\n // wake, and some corporate stacks leave it stuck at `false` while the\n // machine is fully online). Trusting it as a pre-flight gate produced a\n // real production outage: the Agent Builder (this path) threw OfflineError\n // on every submit while the Run path — which uses plain fetch() and never\n // consults the flag — worked fine on the same network. We now let the\n // fetch actually run and only classify the result as \"offline\" if a genuine\n // network failure ALSO corroborates the flag (see the catch below).\n\n const controller = new AbortController();\n const unlinkCaller = opts.signal\n ? linkAbort(controller, opts.signal)\n : () => {};\n\n let connectTimedOut = false;\n let totalTimedOut = false;\n\n const connectTimer = setTimeout(() => {\n connectTimedOut = true;\n controller.abort(\"connect-timeout\");\n }, connectTimeoutMs);\n\n const totalTimer =\n totalTimeoutMs !== null\n ? setTimeout(() => {\n totalTimedOut = true;\n controller.abort(\"total-timeout\");\n }, totalTimeoutMs)\n : null;\n\n try {\n const response = await fetch(url, { ...init, signal: controller.signal });\n clearTimeout(connectTimer);\n // Both timers end with the handshake: `totalTimeoutMs` bounds request\n // start → fetch() resolution (headers), exactly as documented on the\n // option. Body reads are guarded by the caller (stream monitor /\n // heartbeats), not by this timer — clearing it here is what lets a\n // long-streaming body outlive the handshake budget.\n if (totalTimer !== null) clearTimeout(totalTimer);\n\n if (throwOnHttpError && !response.ok) {\n throw new HttpError(\n response.status,\n `HTTP ${response.status} ${response.statusText}`,\n );\n }\n\n return { response, controller };\n } catch (err) {\n clearTimeout(connectTimer);\n if (totalTimer !== null) clearTimeout(totalTimer);\n\n if (connectTimedOut) throw new ConnectTimeoutError(connectTimeoutMs, err);\n if (totalTimedOut)\n throw new TotalTimeoutError(totalTimeoutMs as number, err);\n if (err instanceof DOMException && err.name === \"AbortError\") {\n throw new AbortedError(err);\n }\n if (opts.signal?.aborted) {\n // Caller aborted with a CUSTOM reason: fetch rejects with that reason\n // verbatim (not an AbortError DOMException). Without this check the\n // abort fell through to the generic classifiers and could surface as a\n // retryable NetworkError — an aborted request must never be retried.\n throw new AbortedError(err);\n }\n if (err instanceof TypeError) {\n // fetch() throws TypeError on DNS/connection failure. If the browser\n // ALSO reports offline, the flag is now corroborated by an actual\n // failure — surface the more specific OfflineError. Absent a real\n // failure, `navigator.onLine === false` is never allowed to block.\n if (typeof navigator !== \"undefined\" && navigator.onLine === false) {\n throw new OfflineError();\n }\n throw new NetworkError(err.message || \"Network request failed\", err);\n }\n if (err instanceof HttpError) throw err;\n throw new NetworkError(extractErrorMessage(err), err);\n } finally {\n unlinkCaller();\n }\n}\n","/**\n * Stream liveness monitor.\n *\n * Wraps an async iterable of server events and enforces two guarantees:\n * 1. Heartbeat deadline — fail if no event arrives within `heartbeatTimeoutMs`.\n * 2. Absolute ceiling — fail if total wall-clock time exceeds `maxLifetimeMs`.\n *\n * The wrapper is transparent: events pass through in order, with the same\n * shape. The only observable difference is that a stuck stream throws\n * `HeartbeatTimeoutError` or `TotalTimeoutError` instead of awaiting forever.\n *\n * The wrapped iterable should be driven by NDJSON parsing; the server MUST\n * emit `{kind:\"heartbeat\"}` at least every `heartbeatTimeoutMs / 2` ms for\n * long-running operations (the Matrx stream contract:\n * common-docs/systems/agents/execution-runtime/STREAM-CONTRACT.md).\n *\n * Ported verbatim from matrx-frontend `lib/net/stream-monitor.ts`, with one\n * defect fixed: cleanup of the underlying iterator after a watchdog timeout\n * is now fire-and-forget (see the note in the catch below).\n */\n\nimport { HeartbeatTimeoutError, TotalTimeoutError } from \"./errors\";\n\nexport interface StreamMonitorOptions {\n /** Max ms between any two events before the stream is considered dead. Default 30_000. */\n heartbeatTimeoutMs?: number;\n /** Max ms from wrap-start before throwing regardless of heartbeats. Default 600_000 (10min). */\n maxLifetimeMs?: number;\n /** Optional callback, fired every time a fresh event resets the deadline. */\n onHeartbeat?: (now: number) => void;\n /** Controller whose `.abort()` should be called on monitor-level timeout, so the underlying fetch is cancelled. */\n abortController?: AbortController;\n}\n\nconst DEFAULTS = {\n heartbeatTimeoutMs: 30_000,\n maxLifetimeMs: 600_000,\n} as const;\n\n/**\n * Wraps an async iterable so that it throws on stalled or overlong streams.\n *\n * Usage:\n * const live = monitorStream(events, { heartbeatTimeoutMs, abortController });\n * for await (const ev of live) { ... }\n */\nexport async function* monitorStream<T>(\n source: AsyncIterable<T>,\n opts: StreamMonitorOptions = {},\n): AsyncGenerator<T, void, undefined> {\n const heartbeatTimeoutMs =\n opts.heartbeatTimeoutMs ?? DEFAULTS.heartbeatTimeoutMs;\n const maxLifetimeMs = opts.maxLifetimeMs ?? DEFAULTS.maxLifetimeMs;\n const abort = opts.abortController;\n\n const startedAt = Date.now();\n const iter = source[Symbol.asyncIterator]() as AsyncIterator<T>;\n\n while (true) {\n const elapsed = Date.now() - startedAt;\n const remainingLifetime = maxLifetimeMs - elapsed;\n if (remainingLifetime <= 0) {\n abort?.abort(\"total-timeout\");\n throw new TotalTimeoutError(maxLifetimeMs);\n }\n\n const deadline = Math.min(heartbeatTimeoutMs, remainingLifetime);\n\n let timer: ReturnType<typeof setTimeout> | null = null;\n let timedOutKind: \"heartbeat\" | \"total\" | null = null;\n const timeoutPromise = new Promise<never>((_, reject) => {\n timer = setTimeout(() => {\n timedOutKind =\n deadline === remainingLifetime ? \"total\" : \"heartbeat\";\n abort?.abort(\n timedOutKind === \"total\" ? \"total-timeout\" : \"heartbeat-timeout\",\n );\n reject(\n timedOutKind === \"total\"\n ? new TotalTimeoutError(maxLifetimeMs)\n : new HeartbeatTimeoutError(heartbeatTimeoutMs),\n );\n }, deadline);\n });\n\n let result: IteratorResult<T>;\n try {\n result = await Promise.race([iter.next(), timeoutPromise]);\n } catch (err) {\n if (timer !== null) clearTimeout(timer);\n // Best-effort: let the underlying iterator clean up — WITHOUT awaiting\n // it. The original awaited `iter.return()`, but on an async generator\n // `return()` queues behind the still-pending `next()`; if the stream is\n // stuck and no abortController was provided to settle that read, the\n // await never resolves and the monitor hangs forever — the exact\n // failure mode this module exists to prevent. Fire-and-forget keeps\n // the timeout guarantee while still requesting cleanup.\n try {\n const cleanup = iter.return?.(undefined as never);\n if (cleanup) {\n void Promise.resolve(cleanup).catch(() => {\n /* ignore */\n });\n }\n } catch {\n /* ignore */\n }\n throw err;\n }\n\n if (timer !== null) clearTimeout(timer);\n if (result.done) return;\n\n opts.onHeartbeat?.(Date.now());\n yield result.value;\n }\n}\n"],"mappings":"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","names":["DEFAULTS"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/credentials.ts","../src/net/errors.ts","../src/net/retry.ts","../src/net/resilient-fetch.ts","../src/net/stream-monitor.ts"],"sourcesContent":["/**\n * @ai-matrx/data — the core module.\n *\n * The shared structural vocabulary every lane of the data layer speaks, plus\n * the lane-free net-resilience trio (S12 rides the core — re-exported from\n * `./net`, also importable as `@ai-matrx/data/net`). This entry point\n * otherwise deliberately stays tiny (the split-out law): the Supabase lane is\n * `@ai-matrx/data/db`; the API and files lanes arrive in later minors. Nothing\n * here performs network, storage, or global-state work at import time.\n *\n * The types are STRUCTURAL on purpose: the package never imports\n * `@supabase/supabase-js` — a real `PostgrestError` satisfies\n * `PostgrestLikeError` by shape, and so does any compatible client, so the\n * package works against whatever client the host injects.\n */\n\n/** The shape of a PostgREST error object (plain object, NOT an Error). */\nexport interface PostgrestLikeError {\n message: string;\n details?: string | null;\n hint?: string | null;\n code?: string | null;\n}\n\n/** The shape a `.maybeSingle()` call resolves to. */\nexport interface MaybeSingleResponse<Row> {\n data: Row | null;\n error: PostgrestLikeError | null;\n}\n\n/** A JSON value — the wire-safe subset every column and payload speaks. */\nexport type Json =\n | string\n | number\n | boolean\n | null\n | { [key: string]: Json | undefined }\n | Json[];\n\n/**\n * Converts any thrown value from a Supabase/PostgREST call into a proper Error\n * with a descriptive message. PostgREST errors are plain objects with `message`,\n * `code`, `details`, and `hint` — they are NOT `instanceof Error`, which causes\n * generic catch blocks to lose the real reason.\n */\nexport function pgErrorToError(err: unknown): Error {\n if (err instanceof Error) return err;\n if (err && typeof err === \"object\") {\n const e = err as Record<string, unknown>;\n const parts: string[] = [];\n if (typeof e.message === \"string\" && e.message) parts.push(e.message);\n if (typeof e.details === \"string\" && e.details)\n parts.push(`Details: ${e.details}`);\n if (typeof e.hint === \"string\" && e.hint) parts.push(`Hint: ${e.hint}`);\n if (typeof e.code === \"string\" && e.code) parts.push(`(code: ${e.code})`);\n if (parts.length > 0) return new Error(parts.join(\" — \"));\n }\n return new Error(String(err));\n}\n\nexport * from \"./credentials\";\nexport * from \"./net\";\n","/**\n * The credentials port — the ONE credential→header mapping for every client\n * package (DATA-PACKAGE-DESIGN.md \"The credentials port\"; API-key lane ratified\n * as C16, 2026-08-29).\n *\n * Three credential kinds, one mapping, in exactly one place:\n *\n * - `user` — a Supabase JWT: `Authorization: Bearer <jwt>` (both lanes)\n * - `guest` — a fingerprint: `X-Fingerprint-ID: <id>` (API lane only)\n * - `api_key` — an AI Matrx API key: `Authorization: Bearer mx_live_...`.\n * The server's ONE AuthMiddleware disambiguates by shape (`mx_` prefix vs\n * three-dot JWT), so both `user` and `api_key` ride the same header.\n *\n * No polling, no readiness loop: readiness is the port's promise.\n */\n\n/** A credential a Matrx client can present. */\nexport type MatrxCredential =\n | { kind: \"user\"; accessToken: string }\n | { kind: \"guest\"; fingerprintId: string }\n | { kind: \"api_key\"; apiKey: string };\n\n/**\n * The async source of the current credential. `get()` resolves when the host\n * knows its auth state (null = anonymous); `onChange` lets transports refresh\n * cached headers when the credential rotates.\n */\nexport interface CredentialsPort {\n get(): Promise<MatrxCredential | null>;\n onChange?(cb: () => void): () => void;\n}\n\n/** Prefix that marks a bearer token as an AI Matrx API key, never a JWT. */\nexport const API_KEY_PREFIX = \"mx_\";\n\n/**\n * Map a credential to the exact request headers the AI Matrx platform expects.\n * This is the single place that knowledge lives — transports spread the result\n * into their request headers and add `X-Organization-Id` from the org context\n * binding separately.\n */\nexport function credentialToHeaders(\n credential: MatrxCredential | null,\n): Record<string, string> {\n if (credential === null) return {};\n switch (credential.kind) {\n case \"user\":\n return { Authorization: `Bearer ${credential.accessToken}` };\n case \"guest\":\n return { \"X-Fingerprint-ID\": credential.fingerprintId };\n case \"api_key\": {\n if (!credential.apiKey.startsWith(API_KEY_PREFIX)) {\n throw new Error(\n `credentialToHeaders: an api_key credential must start with \"${API_KEY_PREFIX}\" ` +\n \"(AI Matrx API keys look like mx_live_...)\",\n );\n }\n return { Authorization: `Bearer ${credential.apiKey}` };\n }\n }\n}\n","/**\n * Structured errors for the resilience layer.\n *\n * Every network-layer failure resolves to one of these classes so callers\n * (and any recovery UI) can branch on error type, not on string matching.\n *\n * Ported verbatim from matrx-frontend `lib/net/errors.ts` — the origin of the\n * trio. The one coupling seam removed: `extractErrorMessage` (previously the\n * app's `utils/errors`) is pure logic and ships inside the package.\n */\n\nexport type NetErrorCode =\n | \"connect-timeout\"\n | \"total-timeout\"\n | \"heartbeat-timeout\"\n | \"network\"\n | \"http\"\n | \"aborted\"\n | \"offline\"\n | \"unknown\";\n\nexport class NetError extends Error {\n readonly code: NetErrorCode;\n override readonly cause?: unknown;\n readonly status?: number;\n readonly retryable: boolean;\n\n constructor(\n code: NetErrorCode,\n message: string,\n opts: { cause?: unknown; status?: number; retryable?: boolean } = {},\n ) {\n super(message);\n this.name = \"NetError\";\n this.code = code;\n this.cause = opts.cause;\n // Conditional only for `exactOptionalPropertyTypes` — same runtime shape.\n if (opts.status !== undefined) this.status = opts.status;\n this.retryable = opts.retryable ?? false;\n }\n}\n\nexport class ConnectTimeoutError extends NetError {\n constructor(ms: number, cause?: unknown) {\n super(\"connect-timeout\", `Connection timed out after ${ms}ms`, {\n cause,\n retryable: true,\n });\n this.name = \"ConnectTimeoutError\";\n }\n}\n\nexport class TotalTimeoutError extends NetError {\n constructor(ms: number, cause?: unknown) {\n super(\"total-timeout\", `Request exceeded ${ms}ms total budget`, {\n cause,\n retryable: false,\n });\n this.name = \"TotalTimeoutError\";\n }\n}\n\nexport class HeartbeatTimeoutError extends NetError {\n constructor(ms: number, cause?: unknown) {\n super(\n \"heartbeat-timeout\",\n `No server activity for ${ms}ms — stream considered dead`,\n { cause, retryable: false },\n );\n this.name = \"HeartbeatTimeoutError\";\n }\n}\n\nexport class NetworkError extends NetError {\n constructor(message: string, cause?: unknown) {\n super(\"network\", message, { cause, retryable: true });\n this.name = \"NetworkError\";\n }\n}\n\nexport class HttpError extends NetError {\n constructor(status: number, message: string, cause?: unknown) {\n // 408/429/5xx are generally safe to retry for idempotent ops.\n const retryable = status === 408 || status === 429 || status >= 500;\n super(\"http\", message, { cause, status, retryable });\n this.name = \"HttpError\";\n }\n}\n\nexport class AbortedError extends NetError {\n constructor(cause?: unknown) {\n super(\"aborted\", \"Request was aborted\", { cause, retryable: false });\n this.name = \"AbortedError\";\n }\n}\n\nexport class OfflineError extends NetError {\n constructor() {\n super(\"offline\", \"Browser is offline\", { retryable: true });\n this.name = \"OfflineError\";\n }\n}\n\nexport function isNetError(err: unknown): err is NetError {\n return err instanceof NetError;\n}\n\n/**\n * Folds any thrown value into a descriptive message string — THE one reader\n * for every failure shape this platform actually emits.\n *\n * 2026-09-07 (duplication census rows 9, 10, 21): this body had DRIFTED BEHIND\n * three host copies, which is the campaign's failure mode running in the\n * package's favour-direction. Each host had learned something the package\n * never did, so every non-frontend consumer of `@ai-matrx/data/net` was\n * flattening a server's own sentence into \"An unexpected error occurred\". All\n * three are folded in here, with a named regression test per shape:\n *\n * 1. **FastAPI's `detail`** (matrx-frontend `utils/errors.ts`, learned\n * 2026-08-31 as V2 finding G5). Our server refuses in `detail` — a string,\n * or an object carrying `message` — and every reader checked\n * `message`/`details` only. The server's sentence is the whole point of\n * catching this.\n * 2. **A `fallback` argument** (matrx-frontend `ShortcutForm.tsx`). A caller\n * who knows what it was doing can say \"Failed to save shortcut\" instead of\n * the generic sentence. Optional; the default is unchanged, so every\n * existing single-argument call behaves exactly as before.\n * 3. **Recursion into nested error objects** (aidream `workflow-studio`, two\n * byte-identical copies). The run pipeline nests: `{error: {type, message}}`\n * and `{error: {error: ...}}` and `{exception: {...}}` are all real shapes,\n * and a non-recursive reader answers \"An unexpected error occurred\" while\n * holding the actual reason two levels down. Depth-capped at 4 — a cyclic\n * or absurdly deep object must not hang a render.\n *\n * PostgREST-style errors are plain objects carrying\n * `message`/`details`/`hint`/`code`; a bare `String(err)` on those yields\n * \"[object Object]\", which is why this exists at all.\n */\nexport function extractErrorMessage(\n err: unknown,\n fallback = \"An unexpected error occurred\",\n): string {\n if (!err) return \"Unknown error\";\n if (typeof err === \"string\") return err;\n if (err instanceof Error) return err.message;\n if (typeof err === \"object\") {\n const e = err as Record<string, unknown>;\n const parts: string[] = [];\n if (typeof e.message === \"string\" && e.message) parts.push(e.message);\n // FastAPI's own word for the reason — only when nothing said `message`,\n // so a PostgREST error keeps its exact composed sentence.\n if (parts.length === 0) {\n if (typeof e.detail === \"string\" && e.detail) parts.push(e.detail);\n else if (e.detail && typeof e.detail === \"object\") {\n const d = e.detail as Record<string, unknown>;\n if (typeof d.message === \"string\" && d.message) parts.push(d.message);\n }\n }\n if (typeof e.details === \"string\" && e.details) parts.push(e.details);\n if (typeof e.hint === \"string\" && e.hint) parts.push(`Hint: ${e.hint}`);\n if (typeof e.code === \"string\" && e.code) parts.push(`Code: ${e.code}`);\n if (parts.length > 0) return parts.join(\" · \");\n\n const nested = extractNestedErrorMessage(e, 0);\n if (nested !== null) return nested;\n\n try {\n const s = JSON.stringify(err);\n if (s && s !== \"{}\") return s;\n } catch {\n /* ignore */\n }\n }\n return fallback;\n}\n\n/** Depth-capped walk into `error` / `exception` / `cause`. See above. */\nfunction extractNestedErrorMessage(\n e: Record<string, unknown>,\n depth: number,\n): string | null {\n if (depth > 4) return null;\n for (const key of [\"error\", \"exception\", \"cause\"] as const) {\n const value = e[key];\n if (typeof value === \"string\" && value) return value;\n if (value && typeof value === \"object\") {\n const inner = value as Record<string, unknown>;\n if (typeof inner.message === \"string\" && inner.message) return inner.message;\n if (typeof inner.detail === \"string\" && inner.detail) return inner.detail;\n const deeper = extractNestedErrorMessage(inner, depth + 1);\n if (deeper !== null) return deeper;\n }\n }\n return null;\n}\n\n/**\n * True when a failure is the TRANSPORT dying, not the server answering: the\n * browser is offline, asleep, mid-wifi-handoff, or DNS/TLS failed. It also\n * covers the bounded Envoy/Supabase gateway shape emitted when the upstream\n * database connection dies before response headers. `fetch`\n * rejects with a bare `TypeError: Failed to fetch` in every browser, and\n * supabase-js surfaces that verbatim (message only — no code, no hint).\n *\n * Use it to decide how LOUD a failure is, never whether to handle it. A\n * transport failure carries nothing an engineer can act on, so logging it at\n * error level buries the failures that do — that is how a notes surface filed\n * a system_error every time a laptop slept (2026-08-11). Anything with a\n * Postgres code, an HTTP status, or a server message is NOT this: keep those\n * loud.\n */\nexport function isTransportFailure(err: unknown): boolean {\n if (err instanceof NetworkError || err instanceof OfflineError) return true;\n if (typeof navigator !== \"undefined\" && navigator.onLine === false) return true;\n if (err instanceof TypeError) return true;\n if (err && typeof err === \"object\") {\n const e = err as { code?: unknown; status?: unknown; message?: unknown; name?: unknown };\n // A real PostgREST/Postgres failure always carries a code or status.\n if (e.code || e.status) return false;\n const text = `${typeof e.name === \"string\" ? e.name : \"\"} ${\n typeof e.message === \"string\" ? e.message : \"\"\n }`.toLowerCase();\n return (\n text.includes(\"failed to fetch\") ||\n text.includes(\"networkerror\") ||\n text.includes(\"network request failed\") ||\n text.includes(\"load failed\") || // Safari's wording for the same condition\n text.includes(\"upstream connect error or disconnect/reset before headers\")\n );\n }\n return false;\n}\n\nexport function toNetError(err: unknown): NetError {\n if (isNetError(err)) return err;\n if (err instanceof DOMException && err.name === \"AbortError\") {\n return new AbortedError(err);\n }\n if (err instanceof TypeError) {\n // fetch() throws TypeError on DNS/connection failure in every browser.\n return new NetworkError(err.message || \"Network request failed\", err);\n }\n return new NetError(\n \"unknown\",\n extractErrorMessage(err),\n { cause: err },\n );\n}\n","/**\n * withRetry — exponential backoff retry for idempotent operations.\n *\n * IMPORTANT: only wrap operations that are safe to repeat (GETs, cache\n * lookups, agent-definition fetches). Never wrap user-authored submissions —\n * recovery UX handles those via explicit user-initiated retry.\n *\n * Ported verbatim from matrx-frontend `lib/net/retry.ts`.\n */\n\nimport { isNetError, NetError } from \"./errors\";\n\nexport interface RetryOptions {\n /** Total attempts including the first. Default 3. */\n attempts?: number;\n /** Initial delay ms before the first retry. Default 250. */\n initialDelayMs?: number;\n /** Max delay ms cap between retries. Default 4_000. */\n maxDelayMs?: number;\n /** Multiplier applied after each failure. Default 2. */\n backoffFactor?: number;\n /** Jitter factor 0-1 applied to each delay. Default 0.25. */\n jitter?: number;\n /** Predicate deciding whether a given error should trigger a retry. Default: NetError.retryable. */\n retryOn?: (err: unknown) => boolean;\n /** Caller-provided signal. If aborted, no further retries are attempted. */\n signal?: AbortSignal;\n /** Fires before each retry; useful for logging/telemetry. */\n onRetry?: (attempt: number, err: unknown, delayMs: number) => void;\n}\n\nfunction defaultRetryOn(err: unknown): boolean {\n return isNetError(err) && (err as NetError).retryable;\n}\n\nfunction sleep(ms: number, signal?: AbortSignal): Promise<void> {\n return new Promise((resolve, reject) => {\n const timer = setTimeout(() => {\n resolve();\n if (signal) signal.removeEventListener(\"abort\", onAbort);\n }, ms);\n const onAbort = () => {\n clearTimeout(timer);\n reject(signal?.reason ?? new Error(\"Aborted\"));\n };\n if (signal) {\n if (signal.aborted) {\n clearTimeout(timer);\n reject(signal.reason);\n } else {\n signal.addEventListener(\"abort\", onAbort, { once: true });\n }\n }\n });\n}\n\nexport async function withRetry<T>(\n fn: (attempt: number) => Promise<T>,\n opts: RetryOptions = {},\n): Promise<T> {\n const attempts = Math.max(1, opts.attempts ?? 3);\n const initialDelayMs = opts.initialDelayMs ?? 250;\n const maxDelayMs = opts.maxDelayMs ?? 4_000;\n const factor = opts.backoffFactor ?? 2;\n const jitter = Math.max(0, Math.min(1, opts.jitter ?? 0.25));\n const retryOn = opts.retryOn ?? defaultRetryOn;\n\n let lastErr: unknown;\n for (let attempt = 1; attempt <= attempts; attempt++) {\n if (opts.signal?.aborted) throw opts.signal.reason;\n try {\n return await fn(attempt);\n } catch (err) {\n lastErr = err;\n if (attempt === attempts || !retryOn(err)) throw err;\n\n const base = Math.min(\n maxDelayMs,\n initialDelayMs * Math.pow(factor, attempt - 1),\n );\n const jitterAmount = base * jitter * (Math.random() * 2 - 1);\n const delayMs = Math.max(0, Math.round(base + jitterAmount));\n opts.onRetry?.(attempt, err, delayMs);\n await sleep(delayMs, opts.signal);\n }\n }\n\n // Unreachable — the loop always either returns or throws.\n throw lastErr;\n}\n","/**\n * resilientFetch — `fetch` with connect + total timeouts and composed abort.\n *\n * Every outbound HTTP call should go through this, not raw fetch. The\n * two-stage timeout (connect + total) lets callers allow long-streaming\n * responses without giving up the ability to fail fast when the server is\n * simply unreachable.\n *\n * Ported verbatim from matrx-frontend `lib/net/resilient-fetch.ts`. `fetch`\n * comes from the environment (Node 20+, browsers, React Native/Hermes) —\n * there is deliberately no injected transport here.\n */\n\nimport {\n AbortedError,\n ConnectTimeoutError,\n extractErrorMessage,\n HttpError,\n NetworkError,\n OfflineError,\n TotalTimeoutError,\n} from \"./errors\";\n\nexport interface ResilientFetchOptions {\n /** Max ms from request start → response headers received. Default 15_000. */\n connectTimeoutMs?: number;\n /**\n * Max ms from request start → fetch() promise resolves.\n * For streaming endpoints the response arrives quickly and the stream body\n * is read by the caller, so this bound only guards the handshake itself.\n * `null` disables the ceiling. Default 120_000.\n */\n totalTimeoutMs?: number | null;\n /** Caller-provided signal; composed with our internal timeout signals. */\n signal?: AbortSignal;\n /**\n * If true, throw on non-2xx responses instead of returning them. Default true.\n * Callers that need to read structured error bodies can set this to false.\n */\n throwOnHttpError?: boolean;\n}\n\nexport interface ResilientFetchResult {\n response: Response;\n /**\n * Composed AbortController tied to the fetch. Callers reading the body\n * (e.g. streaming) can call `.abort()` from this controller to cancel\n * in-flight reads. Already aborted on connect/total timeout.\n */\n controller: AbortController;\n}\n\nconst DEFAULTS = {\n connectTimeoutMs: 15_000,\n totalTimeoutMs: 120_000,\n} as const;\n\nfunction linkAbort(target: AbortController, signal: AbortSignal): () => void {\n if (signal.aborted) {\n target.abort(signal.reason);\n return () => {};\n }\n const onAbort = () => target.abort(signal.reason);\n signal.addEventListener(\"abort\", onAbort, { once: true });\n return () => signal.removeEventListener(\"abort\", onAbort);\n}\n\nexport async function resilientFetch(\n url: string,\n init: RequestInit = {},\n opts: ResilientFetchOptions = {},\n): Promise<ResilientFetchResult> {\n const connectTimeoutMs = opts.connectTimeoutMs ?? DEFAULTS.connectTimeoutMs;\n const totalTimeoutMs =\n opts.totalTimeoutMs === undefined\n ? DEFAULTS.totalTimeoutMs\n : opts.totalTimeoutMs;\n const throwOnHttpError = opts.throwOnHttpError ?? true;\n\n // NOTE: we deliberately do NOT hard-block on `navigator.onLine === false`\n // here. That flag is only a HINT — it is unreliable in both directions\n // (VPNs, virtual network adapters, browser network-service hiccups, sleep/\n // wake, and some corporate stacks leave it stuck at `false` while the\n // machine is fully online). Trusting it as a pre-flight gate produced a\n // real production outage: the Agent Builder (this path) threw OfflineError\n // on every submit while the Run path — which uses plain fetch() and never\n // consults the flag — worked fine on the same network. We now let the\n // fetch actually run and only classify the result as \"offline\" if a genuine\n // network failure ALSO corroborates the flag (see the catch below).\n\n const controller = new AbortController();\n const unlinkCaller = opts.signal\n ? linkAbort(controller, opts.signal)\n : () => {};\n\n let connectTimedOut = false;\n let totalTimedOut = false;\n\n const connectTimer = setTimeout(() => {\n connectTimedOut = true;\n controller.abort(\"connect-timeout\");\n }, connectTimeoutMs);\n\n const totalTimer =\n totalTimeoutMs !== null\n ? setTimeout(() => {\n totalTimedOut = true;\n controller.abort(\"total-timeout\");\n }, totalTimeoutMs)\n : null;\n\n try {\n const response = await fetch(url, { ...init, signal: controller.signal });\n clearTimeout(connectTimer);\n // Both timers end with the handshake: `totalTimeoutMs` bounds request\n // start → fetch() resolution (headers), exactly as documented on the\n // option. Body reads are guarded by the caller (stream monitor /\n // heartbeats), not by this timer — clearing it here is what lets a\n // long-streaming body outlive the handshake budget.\n if (totalTimer !== null) clearTimeout(totalTimer);\n\n if (throwOnHttpError && !response.ok) {\n throw new HttpError(\n response.status,\n `HTTP ${response.status} ${response.statusText}`,\n );\n }\n\n return { response, controller };\n } catch (err) {\n clearTimeout(connectTimer);\n if (totalTimer !== null) clearTimeout(totalTimer);\n\n if (connectTimedOut) throw new ConnectTimeoutError(connectTimeoutMs, err);\n if (totalTimedOut)\n throw new TotalTimeoutError(totalTimeoutMs as number, err);\n if (err instanceof DOMException && err.name === \"AbortError\") {\n throw new AbortedError(err);\n }\n if (opts.signal?.aborted) {\n // Caller aborted with a CUSTOM reason: fetch rejects with that reason\n // verbatim (not an AbortError DOMException). Without this check the\n // abort fell through to the generic classifiers and could surface as a\n // retryable NetworkError — an aborted request must never be retried.\n throw new AbortedError(err);\n }\n if (err instanceof TypeError) {\n // fetch() throws TypeError on DNS/connection failure. If the browser\n // ALSO reports offline, the flag is now corroborated by an actual\n // failure — surface the more specific OfflineError. Absent a real\n // failure, `navigator.onLine === false` is never allowed to block.\n if (typeof navigator !== \"undefined\" && navigator.onLine === false) {\n throw new OfflineError();\n }\n throw new NetworkError(err.message || \"Network request failed\", err);\n }\n if (err instanceof HttpError) throw err;\n throw new NetworkError(extractErrorMessage(err), err);\n } finally {\n unlinkCaller();\n }\n}\n","/**\n * Stream liveness monitor.\n *\n * Wraps an async iterable of server events and enforces two guarantees:\n * 1. Heartbeat deadline — fail if no event arrives within `heartbeatTimeoutMs`.\n * 2. Absolute ceiling — fail if total wall-clock time exceeds `maxLifetimeMs`.\n *\n * The wrapper is transparent: events pass through in order, with the same\n * shape. The only observable difference is that a stuck stream throws\n * `HeartbeatTimeoutError` or `TotalTimeoutError` instead of awaiting forever.\n *\n * The wrapped iterable should be driven by NDJSON parsing; the server MUST\n * emit `{kind:\"heartbeat\"}` at least every `heartbeatTimeoutMs / 2` ms for\n * long-running operations (the Matrx stream contract:\n * common-docs/systems/architecture/execution-runtime/STREAM-CONTRACT.md).\n *\n * Ported verbatim from matrx-frontend `lib/net/stream-monitor.ts`, with one\n * defect fixed: cleanup of the underlying iterator after a watchdog timeout\n * is now fire-and-forget (see the note in the catch below).\n */\n\nimport { HeartbeatTimeoutError, TotalTimeoutError } from \"./errors\";\n\nexport interface StreamMonitorOptions {\n /** Max ms between any two events before the stream is considered dead. Default 30_000. */\n heartbeatTimeoutMs?: number;\n /** Max ms from wrap-start before throwing regardless of heartbeats. Default 600_000 (10min). */\n maxLifetimeMs?: number;\n /** Optional callback, fired every time a fresh event resets the deadline. */\n onHeartbeat?: (now: number) => void;\n /** Controller whose `.abort()` should be called on monitor-level timeout, so the underlying fetch is cancelled. */\n abortController?: AbortController;\n}\n\nconst DEFAULTS = {\n heartbeatTimeoutMs: 30_000,\n maxLifetimeMs: 600_000,\n} as const;\n\n/**\n * Wraps an async iterable so that it throws on stalled or overlong streams.\n *\n * Usage:\n * const live = monitorStream(events, { heartbeatTimeoutMs, abortController });\n * for await (const ev of live) { ... }\n */\nexport async function* monitorStream<T>(\n source: AsyncIterable<T>,\n opts: StreamMonitorOptions = {},\n): AsyncGenerator<T, void, undefined> {\n const heartbeatTimeoutMs =\n opts.heartbeatTimeoutMs ?? DEFAULTS.heartbeatTimeoutMs;\n const maxLifetimeMs = opts.maxLifetimeMs ?? DEFAULTS.maxLifetimeMs;\n const abort = opts.abortController;\n\n const startedAt = Date.now();\n const iter = source[Symbol.asyncIterator]() as AsyncIterator<T>;\n\n while (true) {\n const elapsed = Date.now() - startedAt;\n const remainingLifetime = maxLifetimeMs - elapsed;\n if (remainingLifetime <= 0) {\n abort?.abort(\"total-timeout\");\n throw new TotalTimeoutError(maxLifetimeMs);\n }\n\n const deadline = Math.min(heartbeatTimeoutMs, remainingLifetime);\n\n let timer: ReturnType<typeof setTimeout> | null = null;\n let timedOutKind: \"heartbeat\" | \"total\" | null = null;\n const timeoutPromise = new Promise<never>((_, reject) => {\n timer = setTimeout(() => {\n timedOutKind =\n deadline === remainingLifetime ? \"total\" : \"heartbeat\";\n abort?.abort(\n timedOutKind === \"total\" ? \"total-timeout\" : \"heartbeat-timeout\",\n );\n reject(\n timedOutKind === \"total\"\n ? new TotalTimeoutError(maxLifetimeMs)\n : new HeartbeatTimeoutError(heartbeatTimeoutMs),\n );\n }, deadline);\n });\n\n let result: IteratorResult<T>;\n try {\n result = await Promise.race([iter.next(), timeoutPromise]);\n } catch (err) {\n if (timer !== null) clearTimeout(timer);\n // Best-effort: let the underlying iterator clean up — WITHOUT awaiting\n // it. The original awaited `iter.return()`, but on an async generator\n // `return()` queues behind the still-pending `next()`; if the stream is\n // stuck and no abortController was provided to settle that read, the\n // await never resolves and the monitor hangs forever — the exact\n // failure mode this module exists to prevent. Fire-and-forget keeps\n // the timeout guarantee while still requesting cleanup.\n try {\n const cleanup = iter.return?.(undefined as never);\n if (cleanup) {\n void Promise.resolve(cleanup).catch(() => {\n /* ignore */\n });\n }\n } catch {\n /* ignore */\n }\n throw err;\n }\n\n if (timer !== null) clearTimeout(timer);\n if (result.done) return;\n\n opts.onHeartbeat?.(Date.now());\n yield result.value;\n }\n}\n"],"mappings":"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","names":["DEFAULTS"]}
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/credentials.ts","../src/net/errors.ts","../src/net/retry.ts","../src/net/resilient-fetch.ts","../src/net/stream-monitor.ts","../src/index.ts"],"sourcesContent":["/**\n * The credentials port — the ONE credential→header mapping for every client\n * package (DATA-PACKAGE-DESIGN.md \"The credentials port\"; API-key lane ratified\n * as C16, 2026-08-29).\n *\n * Three credential kinds, one mapping, in exactly one place:\n *\n * - `user` — a Supabase JWT: `Authorization: Bearer <jwt>` (both lanes)\n * - `guest` — a fingerprint: `X-Fingerprint-ID: <id>` (API lane only)\n * - `api_key` — an AI Matrx API key: `Authorization: Bearer mx_live_...`.\n * The server's ONE AuthMiddleware disambiguates by shape (`mx_` prefix vs\n * three-dot JWT), so both `user` and `api_key` ride the same header.\n *\n * No polling, no readiness loop: readiness is the port's promise.\n */\n\n/** A credential a Matrx client can present. */\nexport type MatrxCredential =\n | { kind: \"user\"; accessToken: string }\n | { kind: \"guest\"; fingerprintId: string }\n | { kind: \"api_key\"; apiKey: string };\n\n/**\n * The async source of the current credential. `get()` resolves when the host\n * knows its auth state (null = anonymous); `onChange` lets transports refresh\n * cached headers when the credential rotates.\n */\nexport interface CredentialsPort {\n get(): Promise<MatrxCredential | null>;\n onChange?(cb: () => void): () => void;\n}\n\n/** Prefix that marks a bearer token as an AI Matrx API key, never a JWT. */\nexport const API_KEY_PREFIX = \"mx_\";\n\n/**\n * Map a credential to the exact request headers the AI Matrx platform expects.\n * This is the single place that knowledge lives — transports spread the result\n * into their request headers and add `X-Organization-Id` from the org context\n * binding separately.\n */\nexport function credentialToHeaders(\n credential: MatrxCredential | null,\n): Record<string, string> {\n if (credential === null) return {};\n switch (credential.kind) {\n case \"user\":\n return { Authorization: `Bearer ${credential.accessToken}` };\n case \"guest\":\n return { \"X-Fingerprint-ID\": credential.fingerprintId };\n case \"api_key\": {\n if (!credential.apiKey.startsWith(API_KEY_PREFIX)) {\n throw new Error(\n `credentialToHeaders: an api_key credential must start with \"${API_KEY_PREFIX}\" ` +\n \"(AI Matrx API keys look like mx_live_...)\",\n );\n }\n return { Authorization: `Bearer ${credential.apiKey}` };\n }\n }\n}\n","/**\n * Structured errors for the resilience layer.\n *\n * Every network-layer failure resolves to one of these classes so callers\n * (and any recovery UI) can branch on error type, not on string matching.\n *\n * Ported verbatim from matrx-frontend `lib/net/errors.ts` — the origin of the\n * trio. The one coupling seam removed: `extractErrorMessage` (previously the\n * app's `utils/errors`) is pure logic and ships inside the package.\n */\n\nexport type NetErrorCode =\n | \"connect-timeout\"\n | \"total-timeout\"\n | \"heartbeat-timeout\"\n | \"network\"\n | \"http\"\n | \"aborted\"\n | \"offline\"\n | \"unknown\";\n\nexport class NetError extends Error {\n readonly code: NetErrorCode;\n override readonly cause?: unknown;\n readonly status?: number;\n readonly retryable: boolean;\n\n constructor(\n code: NetErrorCode,\n message: string,\n opts: { cause?: unknown; status?: number; retryable?: boolean } = {},\n ) {\n super(message);\n this.name = \"NetError\";\n this.code = code;\n this.cause = opts.cause;\n // Conditional only for `exactOptionalPropertyTypes` — same runtime shape.\n if (opts.status !== undefined) this.status = opts.status;\n this.retryable = opts.retryable ?? false;\n }\n}\n\nexport class ConnectTimeoutError extends NetError {\n constructor(ms: number, cause?: unknown) {\n super(\"connect-timeout\", `Connection timed out after ${ms}ms`, {\n cause,\n retryable: true,\n });\n this.name = \"ConnectTimeoutError\";\n }\n}\n\nexport class TotalTimeoutError extends NetError {\n constructor(ms: number, cause?: unknown) {\n super(\"total-timeout\", `Request exceeded ${ms}ms total budget`, {\n cause,\n retryable: false,\n });\n this.name = \"TotalTimeoutError\";\n }\n}\n\nexport class HeartbeatTimeoutError extends NetError {\n constructor(ms: number, cause?: unknown) {\n super(\n \"heartbeat-timeout\",\n `No server activity for ${ms}ms — stream considered dead`,\n { cause, retryable: false },\n );\n this.name = \"HeartbeatTimeoutError\";\n }\n}\n\nexport class NetworkError extends NetError {\n constructor(message: string, cause?: unknown) {\n super(\"network\", message, { cause, retryable: true });\n this.name = \"NetworkError\";\n }\n}\n\nexport class HttpError extends NetError {\n constructor(status: number, message: string, cause?: unknown) {\n // 408/429/5xx are generally safe to retry for idempotent ops.\n const retryable = status === 408 || status === 429 || status >= 500;\n super(\"http\", message, { cause, status, retryable });\n this.name = \"HttpError\";\n }\n}\n\nexport class AbortedError extends NetError {\n constructor(cause?: unknown) {\n super(\"aborted\", \"Request was aborted\", { cause, retryable: false });\n this.name = \"AbortedError\";\n }\n}\n\nexport class OfflineError extends NetError {\n constructor() {\n super(\"offline\", \"Browser is offline\", { retryable: true });\n this.name = \"OfflineError\";\n }\n}\n\nexport function isNetError(err: unknown): err is NetError {\n return err instanceof NetError;\n}\n\n/**\n * Folds any thrown value into a descriptive message string — THE one reader\n * for every failure shape this platform actually emits.\n *\n * 2026-09-07 (duplication census rows 9, 10, 21): this body had DRIFTED BEHIND\n * three host copies, which is the campaign's failure mode running in the\n * package's favour-direction. Each host had learned something the package\n * never did, so every non-frontend consumer of `@ai-matrx/data/net` was\n * flattening a server's own sentence into \"An unexpected error occurred\". All\n * three are folded in here, with a named regression test per shape:\n *\n * 1. **FastAPI's `detail`** (matrx-frontend `utils/errors.ts`, learned\n * 2026-08-31 as V2 finding G5). Our server refuses in `detail` — a string,\n * or an object carrying `message` — and every reader checked\n * `message`/`details` only. The server's sentence is the whole point of\n * catching this.\n * 2. **A `fallback` argument** (matrx-frontend `ShortcutForm.tsx`). A caller\n * who knows what it was doing can say \"Failed to save shortcut\" instead of\n * the generic sentence. Optional; the default is unchanged, so every\n * existing single-argument call behaves exactly as before.\n * 3. **Recursion into nested error objects** (aidream `workflow-studio`, two\n * byte-identical copies). The run pipeline nests: `{error: {type, message}}`\n * and `{error: {error: ...}}` and `{exception: {...}}` are all real shapes,\n * and a non-recursive reader answers \"An unexpected error occurred\" while\n * holding the actual reason two levels down. Depth-capped at 4 — a cyclic\n * or absurdly deep object must not hang a render.\n *\n * PostgREST-style errors are plain objects carrying\n * `message`/`details`/`hint`/`code`; a bare `String(err)` on those yields\n * \"[object Object]\", which is why this exists at all.\n */\nexport function extractErrorMessage(\n err: unknown,\n fallback = \"An unexpected error occurred\",\n): string {\n if (!err) return \"Unknown error\";\n if (typeof err === \"string\") return err;\n if (err instanceof Error) return err.message;\n if (typeof err === \"object\") {\n const e = err as Record<string, unknown>;\n const parts: string[] = [];\n if (typeof e.message === \"string\" && e.message) parts.push(e.message);\n // FastAPI's own word for the reason — only when nothing said `message`,\n // so a PostgREST error keeps its exact composed sentence.\n if (parts.length === 0) {\n if (typeof e.detail === \"string\" && e.detail) parts.push(e.detail);\n else if (e.detail && typeof e.detail === \"object\") {\n const d = e.detail as Record<string, unknown>;\n if (typeof d.message === \"string\" && d.message) parts.push(d.message);\n }\n }\n if (typeof e.details === \"string\" && e.details) parts.push(e.details);\n if (typeof e.hint === \"string\" && e.hint) parts.push(`Hint: ${e.hint}`);\n if (typeof e.code === \"string\" && e.code) parts.push(`Code: ${e.code}`);\n if (parts.length > 0) return parts.join(\" · \");\n\n const nested = extractNestedErrorMessage(e, 0);\n if (nested !== null) return nested;\n\n try {\n const s = JSON.stringify(err);\n if (s && s !== \"{}\") return s;\n } catch {\n /* ignore */\n }\n }\n return fallback;\n}\n\n/** Depth-capped walk into `error` / `exception` / `cause`. See above. */\nfunction extractNestedErrorMessage(\n e: Record<string, unknown>,\n depth: number,\n): string | null {\n if (depth > 4) return null;\n for (const key of [\"error\", \"exception\", \"cause\"] as const) {\n const value = e[key];\n if (typeof value === \"string\" && value) return value;\n if (value && typeof value === \"object\") {\n const inner = value as Record<string, unknown>;\n if (typeof inner.message === \"string\" && inner.message) return inner.message;\n if (typeof inner.detail === \"string\" && inner.detail) return inner.detail;\n const deeper = extractNestedErrorMessage(inner, depth + 1);\n if (deeper !== null) return deeper;\n }\n }\n return null;\n}\n\n/**\n * True when a failure is the TRANSPORT dying, not the server answering: the\n * browser is offline, asleep, mid-wifi-handoff, or DNS/TLS failed. It also\n * covers the bounded Envoy/Supabase gateway shape emitted when the upstream\n * database connection dies before response headers. `fetch`\n * rejects with a bare `TypeError: Failed to fetch` in every browser, and\n * supabase-js surfaces that verbatim (message only — no code, no hint).\n *\n * Use it to decide how LOUD a failure is, never whether to handle it. A\n * transport failure carries nothing an engineer can act on, so logging it at\n * error level buries the failures that do — that is how a notes surface filed\n * a system_error every time a laptop slept (2026-08-11). Anything with a\n * Postgres code, an HTTP status, or a server message is NOT this: keep those\n * loud.\n */\nexport function isTransportFailure(err: unknown): boolean {\n if (err instanceof NetworkError || err instanceof OfflineError) return true;\n if (typeof navigator !== \"undefined\" && navigator.onLine === false) return true;\n if (err instanceof TypeError) return true;\n if (err && typeof err === \"object\") {\n const e = err as { code?: unknown; status?: unknown; message?: unknown; name?: unknown };\n // A real PostgREST/Postgres failure always carries a code or status.\n if (e.code || e.status) return false;\n const text = `${typeof e.name === \"string\" ? e.name : \"\"} ${\n typeof e.message === \"string\" ? e.message : \"\"\n }`.toLowerCase();\n return (\n text.includes(\"failed to fetch\") ||\n text.includes(\"networkerror\") ||\n text.includes(\"network request failed\") ||\n text.includes(\"load failed\") || // Safari's wording for the same condition\n text.includes(\"upstream connect error or disconnect/reset before headers\")\n );\n }\n return false;\n}\n\nexport function toNetError(err: unknown): NetError {\n if (isNetError(err)) return err;\n if (err instanceof DOMException && err.name === \"AbortError\") {\n return new AbortedError(err);\n }\n if (err instanceof TypeError) {\n // fetch() throws TypeError on DNS/connection failure in every browser.\n return new NetworkError(err.message || \"Network request failed\", err);\n }\n return new NetError(\n \"unknown\",\n extractErrorMessage(err),\n { cause: err },\n );\n}\n","/**\n * withRetry — exponential backoff retry for idempotent operations.\n *\n * IMPORTANT: only wrap operations that are safe to repeat (GETs, cache\n * lookups, agent-definition fetches). Never wrap user-authored submissions —\n * recovery UX handles those via explicit user-initiated retry.\n *\n * Ported verbatim from matrx-frontend `lib/net/retry.ts`.\n */\n\nimport { isNetError, NetError } from \"./errors\";\n\nexport interface RetryOptions {\n /** Total attempts including the first. Default 3. */\n attempts?: number;\n /** Initial delay ms before the first retry. Default 250. */\n initialDelayMs?: number;\n /** Max delay ms cap between retries. Default 4_000. */\n maxDelayMs?: number;\n /** Multiplier applied after each failure. Default 2. */\n backoffFactor?: number;\n /** Jitter factor 0-1 applied to each delay. Default 0.25. */\n jitter?: number;\n /** Predicate deciding whether a given error should trigger a retry. Default: NetError.retryable. */\n retryOn?: (err: unknown) => boolean;\n /** Caller-provided signal. If aborted, no further retries are attempted. */\n signal?: AbortSignal;\n /** Fires before each retry; useful for logging/telemetry. */\n onRetry?: (attempt: number, err: unknown, delayMs: number) => void;\n}\n\nfunction defaultRetryOn(err: unknown): boolean {\n return isNetError(err) && (err as NetError).retryable;\n}\n\nfunction sleep(ms: number, signal?: AbortSignal): Promise<void> {\n return new Promise((resolve, reject) => {\n const timer = setTimeout(() => {\n resolve();\n if (signal) signal.removeEventListener(\"abort\", onAbort);\n }, ms);\n const onAbort = () => {\n clearTimeout(timer);\n reject(signal?.reason ?? new Error(\"Aborted\"));\n };\n if (signal) {\n if (signal.aborted) {\n clearTimeout(timer);\n reject(signal.reason);\n } else {\n signal.addEventListener(\"abort\", onAbort, { once: true });\n }\n }\n });\n}\n\nexport async function withRetry<T>(\n fn: (attempt: number) => Promise<T>,\n opts: RetryOptions = {},\n): Promise<T> {\n const attempts = Math.max(1, opts.attempts ?? 3);\n const initialDelayMs = opts.initialDelayMs ?? 250;\n const maxDelayMs = opts.maxDelayMs ?? 4_000;\n const factor = opts.backoffFactor ?? 2;\n const jitter = Math.max(0, Math.min(1, opts.jitter ?? 0.25));\n const retryOn = opts.retryOn ?? defaultRetryOn;\n\n let lastErr: unknown;\n for (let attempt = 1; attempt <= attempts; attempt++) {\n if (opts.signal?.aborted) throw opts.signal.reason;\n try {\n return await fn(attempt);\n } catch (err) {\n lastErr = err;\n if (attempt === attempts || !retryOn(err)) throw err;\n\n const base = Math.min(\n maxDelayMs,\n initialDelayMs * Math.pow(factor, attempt - 1),\n );\n const jitterAmount = base * jitter * (Math.random() * 2 - 1);\n const delayMs = Math.max(0, Math.round(base + jitterAmount));\n opts.onRetry?.(attempt, err, delayMs);\n await sleep(delayMs, opts.signal);\n }\n }\n\n // Unreachable — the loop always either returns or throws.\n throw lastErr;\n}\n","/**\n * resilientFetch — `fetch` with connect + total timeouts and composed abort.\n *\n * Every outbound HTTP call should go through this, not raw fetch. The\n * two-stage timeout (connect + total) lets callers allow long-streaming\n * responses without giving up the ability to fail fast when the server is\n * simply unreachable.\n *\n * Ported verbatim from matrx-frontend `lib/net/resilient-fetch.ts`. `fetch`\n * comes from the environment (Node 20+, browsers, React Native/Hermes) —\n * there is deliberately no injected transport here.\n */\n\nimport {\n AbortedError,\n ConnectTimeoutError,\n extractErrorMessage,\n HttpError,\n NetworkError,\n OfflineError,\n TotalTimeoutError,\n} from \"./errors\";\n\nexport interface ResilientFetchOptions {\n /** Max ms from request start → response headers received. Default 15_000. */\n connectTimeoutMs?: number;\n /**\n * Max ms from request start → fetch() promise resolves.\n * For streaming endpoints the response arrives quickly and the stream body\n * is read by the caller, so this bound only guards the handshake itself.\n * `null` disables the ceiling. Default 120_000.\n */\n totalTimeoutMs?: number | null;\n /** Caller-provided signal; composed with our internal timeout signals. */\n signal?: AbortSignal;\n /**\n * If true, throw on non-2xx responses instead of returning them. Default true.\n * Callers that need to read structured error bodies can set this to false.\n */\n throwOnHttpError?: boolean;\n}\n\nexport interface ResilientFetchResult {\n response: Response;\n /**\n * Composed AbortController tied to the fetch. Callers reading the body\n * (e.g. streaming) can call `.abort()` from this controller to cancel\n * in-flight reads. Already aborted on connect/total timeout.\n */\n controller: AbortController;\n}\n\nconst DEFAULTS = {\n connectTimeoutMs: 15_000,\n totalTimeoutMs: 120_000,\n} as const;\n\nfunction linkAbort(target: AbortController, signal: AbortSignal): () => void {\n if (signal.aborted) {\n target.abort(signal.reason);\n return () => {};\n }\n const onAbort = () => target.abort(signal.reason);\n signal.addEventListener(\"abort\", onAbort, { once: true });\n return () => signal.removeEventListener(\"abort\", onAbort);\n}\n\nexport async function resilientFetch(\n url: string,\n init: RequestInit = {},\n opts: ResilientFetchOptions = {},\n): Promise<ResilientFetchResult> {\n const connectTimeoutMs = opts.connectTimeoutMs ?? DEFAULTS.connectTimeoutMs;\n const totalTimeoutMs =\n opts.totalTimeoutMs === undefined\n ? DEFAULTS.totalTimeoutMs\n : opts.totalTimeoutMs;\n const throwOnHttpError = opts.throwOnHttpError ?? true;\n\n // NOTE: we deliberately do NOT hard-block on `navigator.onLine === false`\n // here. That flag is only a HINT — it is unreliable in both directions\n // (VPNs, virtual network adapters, browser network-service hiccups, sleep/\n // wake, and some corporate stacks leave it stuck at `false` while the\n // machine is fully online). Trusting it as a pre-flight gate produced a\n // real production outage: the Agent Builder (this path) threw OfflineError\n // on every submit while the Run path — which uses plain fetch() and never\n // consults the flag — worked fine on the same network. We now let the\n // fetch actually run and only classify the result as \"offline\" if a genuine\n // network failure ALSO corroborates the flag (see the catch below).\n\n const controller = new AbortController();\n const unlinkCaller = opts.signal\n ? linkAbort(controller, opts.signal)\n : () => {};\n\n let connectTimedOut = false;\n let totalTimedOut = false;\n\n const connectTimer = setTimeout(() => {\n connectTimedOut = true;\n controller.abort(\"connect-timeout\");\n }, connectTimeoutMs);\n\n const totalTimer =\n totalTimeoutMs !== null\n ? setTimeout(() => {\n totalTimedOut = true;\n controller.abort(\"total-timeout\");\n }, totalTimeoutMs)\n : null;\n\n try {\n const response = await fetch(url, { ...init, signal: controller.signal });\n clearTimeout(connectTimer);\n // Both timers end with the handshake: `totalTimeoutMs` bounds request\n // start → fetch() resolution (headers), exactly as documented on the\n // option. Body reads are guarded by the caller (stream monitor /\n // heartbeats), not by this timer — clearing it here is what lets a\n // long-streaming body outlive the handshake budget.\n if (totalTimer !== null) clearTimeout(totalTimer);\n\n if (throwOnHttpError && !response.ok) {\n throw new HttpError(\n response.status,\n `HTTP ${response.status} ${response.statusText}`,\n );\n }\n\n return { response, controller };\n } catch (err) {\n clearTimeout(connectTimer);\n if (totalTimer !== null) clearTimeout(totalTimer);\n\n if (connectTimedOut) throw new ConnectTimeoutError(connectTimeoutMs, err);\n if (totalTimedOut)\n throw new TotalTimeoutError(totalTimeoutMs as number, err);\n if (err instanceof DOMException && err.name === \"AbortError\") {\n throw new AbortedError(err);\n }\n if (opts.signal?.aborted) {\n // Caller aborted with a CUSTOM reason: fetch rejects with that reason\n // verbatim (not an AbortError DOMException). Without this check the\n // abort fell through to the generic classifiers and could surface as a\n // retryable NetworkError — an aborted request must never be retried.\n throw new AbortedError(err);\n }\n if (err instanceof TypeError) {\n // fetch() throws TypeError on DNS/connection failure. If the browser\n // ALSO reports offline, the flag is now corroborated by an actual\n // failure — surface the more specific OfflineError. Absent a real\n // failure, `navigator.onLine === false` is never allowed to block.\n if (typeof navigator !== \"undefined\" && navigator.onLine === false) {\n throw new OfflineError();\n }\n throw new NetworkError(err.message || \"Network request failed\", err);\n }\n if (err instanceof HttpError) throw err;\n throw new NetworkError(extractErrorMessage(err), err);\n } finally {\n unlinkCaller();\n }\n}\n","/**\n * Stream liveness monitor.\n *\n * Wraps an async iterable of server events and enforces two guarantees:\n * 1. Heartbeat deadline — fail if no event arrives within `heartbeatTimeoutMs`.\n * 2. Absolute ceiling — fail if total wall-clock time exceeds `maxLifetimeMs`.\n *\n * The wrapper is transparent: events pass through in order, with the same\n * shape. The only observable difference is that a stuck stream throws\n * `HeartbeatTimeoutError` or `TotalTimeoutError` instead of awaiting forever.\n *\n * The wrapped iterable should be driven by NDJSON parsing; the server MUST\n * emit `{kind:\"heartbeat\"}` at least every `heartbeatTimeoutMs / 2` ms for\n * long-running operations (the Matrx stream contract:\n * common-docs/systems/agents/execution-runtime/STREAM-CONTRACT.md).\n *\n * Ported verbatim from matrx-frontend `lib/net/stream-monitor.ts`, with one\n * defect fixed: cleanup of the underlying iterator after a watchdog timeout\n * is now fire-and-forget (see the note in the catch below).\n */\n\nimport { HeartbeatTimeoutError, TotalTimeoutError } from \"./errors\";\n\nexport interface StreamMonitorOptions {\n /** Max ms between any two events before the stream is considered dead. Default 30_000. */\n heartbeatTimeoutMs?: number;\n /** Max ms from wrap-start before throwing regardless of heartbeats. Default 600_000 (10min). */\n maxLifetimeMs?: number;\n /** Optional callback, fired every time a fresh event resets the deadline. */\n onHeartbeat?: (now: number) => void;\n /** Controller whose `.abort()` should be called on monitor-level timeout, so the underlying fetch is cancelled. */\n abortController?: AbortController;\n}\n\nconst DEFAULTS = {\n heartbeatTimeoutMs: 30_000,\n maxLifetimeMs: 600_000,\n} as const;\n\n/**\n * Wraps an async iterable so that it throws on stalled or overlong streams.\n *\n * Usage:\n * const live = monitorStream(events, { heartbeatTimeoutMs, abortController });\n * for await (const ev of live) { ... }\n */\nexport async function* monitorStream<T>(\n source: AsyncIterable<T>,\n opts: StreamMonitorOptions = {},\n): AsyncGenerator<T, void, undefined> {\n const heartbeatTimeoutMs =\n opts.heartbeatTimeoutMs ?? DEFAULTS.heartbeatTimeoutMs;\n const maxLifetimeMs = opts.maxLifetimeMs ?? DEFAULTS.maxLifetimeMs;\n const abort = opts.abortController;\n\n const startedAt = Date.now();\n const iter = source[Symbol.asyncIterator]() as AsyncIterator<T>;\n\n while (true) {\n const elapsed = Date.now() - startedAt;\n const remainingLifetime = maxLifetimeMs - elapsed;\n if (remainingLifetime <= 0) {\n abort?.abort(\"total-timeout\");\n throw new TotalTimeoutError(maxLifetimeMs);\n }\n\n const deadline = Math.min(heartbeatTimeoutMs, remainingLifetime);\n\n let timer: ReturnType<typeof setTimeout> | null = null;\n let timedOutKind: \"heartbeat\" | \"total\" | null = null;\n const timeoutPromise = new Promise<never>((_, reject) => {\n timer = setTimeout(() => {\n timedOutKind =\n deadline === remainingLifetime ? \"total\" : \"heartbeat\";\n abort?.abort(\n timedOutKind === \"total\" ? \"total-timeout\" : \"heartbeat-timeout\",\n );\n reject(\n timedOutKind === \"total\"\n ? new TotalTimeoutError(maxLifetimeMs)\n : new HeartbeatTimeoutError(heartbeatTimeoutMs),\n );\n }, deadline);\n });\n\n let result: IteratorResult<T>;\n try {\n result = await Promise.race([iter.next(), timeoutPromise]);\n } catch (err) {\n if (timer !== null) clearTimeout(timer);\n // Best-effort: let the underlying iterator clean up — WITHOUT awaiting\n // it. The original awaited `iter.return()`, but on an async generator\n // `return()` queues behind the still-pending `next()`; if the stream is\n // stuck and no abortController was provided to settle that read, the\n // await never resolves and the monitor hangs forever — the exact\n // failure mode this module exists to prevent. Fire-and-forget keeps\n // the timeout guarantee while still requesting cleanup.\n try {\n const cleanup = iter.return?.(undefined as never);\n if (cleanup) {\n void Promise.resolve(cleanup).catch(() => {\n /* ignore */\n });\n }\n } catch {\n /* ignore */\n }\n throw err;\n }\n\n if (timer !== null) clearTimeout(timer);\n if (result.done) return;\n\n opts.onHeartbeat?.(Date.now());\n yield result.value;\n }\n}\n","/**\n * @ai-matrx/data — the core module.\n *\n * The shared structural vocabulary every lane of the data layer speaks, plus\n * the lane-free net-resilience trio (S12 rides the core — re-exported from\n * `./net`, also importable as `@ai-matrx/data/net`). This entry point\n * otherwise deliberately stays tiny (the split-out law): the Supabase lane is\n * `@ai-matrx/data/db`; the API and files lanes arrive in later minors. Nothing\n * here performs network, storage, or global-state work at import time.\n *\n * The types are STRUCTURAL on purpose: the package never imports\n * `@supabase/supabase-js` — a real `PostgrestError` satisfies\n * `PostgrestLikeError` by shape, and so does any compatible client, so the\n * package works against whatever client the host injects.\n */\n\n/** The shape of a PostgREST error object (plain object, NOT an Error). */\nexport interface PostgrestLikeError {\n message: string;\n details?: string | null;\n hint?: string | null;\n code?: string | null;\n}\n\n/** The shape a `.maybeSingle()` call resolves to. */\nexport interface MaybeSingleResponse<Row> {\n data: Row | null;\n error: PostgrestLikeError | null;\n}\n\n/** A JSON value — the wire-safe subset every column and payload speaks. */\nexport type Json =\n | string\n | number\n | boolean\n | null\n | { [key: string]: Json | undefined }\n | Json[];\n\n/**\n * Converts any thrown value from a Supabase/PostgREST call into a proper Error\n * with a descriptive message. PostgREST errors are plain objects with `message`,\n * `code`, `details`, and `hint` — they are NOT `instanceof Error`, which causes\n * generic catch blocks to lose the real reason.\n */\nexport function pgErrorToError(err: unknown): Error {\n if (err instanceof Error) return err;\n if (err && typeof err === \"object\") {\n const e = err as Record<string, unknown>;\n const parts: string[] = [];\n if (typeof e.message === \"string\" && e.message) parts.push(e.message);\n if (typeof e.details === \"string\" && e.details)\n parts.push(`Details: ${e.details}`);\n if (typeof e.hint === \"string\" && e.hint) parts.push(`Hint: ${e.hint}`);\n if (typeof e.code === \"string\" && e.code) parts.push(`(code: ${e.code})`);\n if (parts.length > 0) return new Error(parts.join(\" — \"));\n }\n return new Error(String(err));\n}\n\nexport * from \"./credentials\";\nexport * from \"./net\";\n"],"mappings":";AAiCO,IAAM,iBAAiB;AAQvB,SAAS,oBACd,YACwB;AACxB,MAAI,eAAe,KAAM,QAAO,CAAC;AACjC,UAAQ,WAAW,MAAM;AAAA,IACvB,KAAK;AACH,aAAO,EAAE,eAAe,UAAU,WAAW,WAAW,GAAG;AAAA,IAC7D,KAAK;AACH,aAAO,EAAE,oBAAoB,WAAW,cAAc;AAAA,IACxD,KAAK,WAAW;AACd,UAAI,CAAC,WAAW,OAAO,WAAW,cAAc,GAAG;AACjD,cAAM,IAAI;AAAA,UACR,+DAA+D,cAAc;AAAA,QAE/E;AAAA,MACF;AACA,aAAO,EAAE,eAAe,UAAU,WAAW,MAAM,GAAG;AAAA,IACxD;AAAA,EACF;AACF;;;ACvCO,IAAM,WAAN,cAAuB,MAAM;AAAA,EACzB;AAAA,EACS;AAAA,EACT;AAAA,EACA;AAAA,EAET,YACE,MACA,SACA,OAAkE,CAAC,GACnE;AACA,UAAM,OAAO;AACb,SAAK,OAAO;AACZ,SAAK,OAAO;AACZ,SAAK,QAAQ,KAAK;AAElB,QAAI,KAAK,WAAW,OAAW,MAAK,SAAS,KAAK;AAClD,SAAK,YAAY,KAAK,aAAa;AAAA,EACrC;AACF;AAEO,IAAM,sBAAN,cAAkC,SAAS;AAAA,EAChD,YAAY,IAAY,OAAiB;AACvC,UAAM,mBAAmB,8BAA8B,EAAE,MAAM;AAAA,MAC7D;AAAA,MACA,WAAW;AAAA,IACb,CAAC;AACD,SAAK,OAAO;AAAA,EACd;AACF;AAEO,IAAM,oBAAN,cAAgC,SAAS;AAAA,EAC9C,YAAY,IAAY,OAAiB;AACvC,UAAM,iBAAiB,oBAAoB,EAAE,mBAAmB;AAAA,MAC9D;AAAA,MACA,WAAW;AAAA,IACb,CAAC;AACD,SAAK,OAAO;AAAA,EACd;AACF;AAEO,IAAM,wBAAN,cAAoC,SAAS;AAAA,EAClD,YAAY,IAAY,OAAiB;AACvC;AAAA,MACE;AAAA,MACA,0BAA0B,EAAE;AAAA,MAC5B,EAAE,OAAO,WAAW,MAAM;AAAA,IAC5B;AACA,SAAK,OAAO;AAAA,EACd;AACF;AAEO,IAAM,eAAN,cAA2B,SAAS;AAAA,EACzC,YAAY,SAAiB,OAAiB;AAC5C,UAAM,WAAW,SAAS,EAAE,OAAO,WAAW,KAAK,CAAC;AACpD,SAAK,OAAO;AAAA,EACd;AACF;AAEO,IAAM,YAAN,cAAwB,SAAS;AAAA,EACtC,YAAY,QAAgB,SAAiB,OAAiB;AAE5D,UAAM,YAAY,WAAW,OAAO,WAAW,OAAO,UAAU;AAChE,UAAM,QAAQ,SAAS,EAAE,OAAO,QAAQ,UAAU,CAAC;AACnD,SAAK,OAAO;AAAA,EACd;AACF;AAEO,IAAM,eAAN,cAA2B,SAAS;AAAA,EACzC,YAAY,OAAiB;AAC3B,UAAM,WAAW,uBAAuB,EAAE,OAAO,WAAW,MAAM,CAAC;AACnE,SAAK,OAAO;AAAA,EACd;AACF;AAEO,IAAM,eAAN,cAA2B,SAAS;AAAA,EACzC,cAAc;AACZ,UAAM,WAAW,sBAAsB,EAAE,WAAW,KAAK,CAAC;AAC1D,SAAK,OAAO;AAAA,EACd;AACF;AAEO,SAAS,WAAW,KAA+B;AACxD,SAAO,eAAe;AACxB;AAiCO,SAAS,oBACd,KACA,WAAW,gCACH;AACR,MAAI,CAAC,IAAK,QAAO;AACjB,MAAI,OAAO,QAAQ,SAAU,QAAO;AACpC,MAAI,eAAe,MAAO,QAAO,IAAI;AACrC,MAAI,OAAO,QAAQ,UAAU;AAC3B,UAAM,IAAI;AACV,UAAM,QAAkB,CAAC;AACzB,QAAI,OAAO,EAAE,YAAY,YAAY,EAAE,QAAS,OAAM,KAAK,EAAE,OAAO;AAGpE,QAAI,MAAM,WAAW,GAAG;AACtB,UAAI,OAAO,EAAE,WAAW,YAAY,EAAE,OAAQ,OAAM,KAAK,EAAE,MAAM;AAAA,eACxD,EAAE,UAAU,OAAO,EAAE,WAAW,UAAU;AACjD,cAAM,IAAI,EAAE;AACZ,YAAI,OAAO,EAAE,YAAY,YAAY,EAAE,QAAS,OAAM,KAAK,EAAE,OAAO;AAAA,MACtE;AAAA,IACF;AACA,QAAI,OAAO,EAAE,YAAY,YAAY,EAAE,QAAS,OAAM,KAAK,EAAE,OAAO;AACpE,QAAI,OAAO,EAAE,SAAS,YAAY,EAAE,KAAM,OAAM,KAAK,SAAS,EAAE,IAAI,EAAE;AACtE,QAAI,OAAO,EAAE,SAAS,YAAY,EAAE,KAAM,OAAM,KAAK,SAAS,EAAE,IAAI,EAAE;AACtE,QAAI,MAAM,SAAS,EAAG,QAAO,MAAM,KAAK,QAAK;AAE7C,UAAM,SAAS,0BAA0B,GAAG,CAAC;AAC7C,QAAI,WAAW,KAAM,QAAO;AAE5B,QAAI;AACF,YAAM,IAAI,KAAK,UAAU,GAAG;AAC5B,UAAI,KAAK,MAAM,KAAM,QAAO;AAAA,IAC9B,QAAQ;AAAA,IAER;AAAA,EACF;AACA,SAAO;AACT;AAGA,SAAS,0BACP,GACA,OACe;AACf,MAAI,QAAQ,EAAG,QAAO;AACtB,aAAW,OAAO,CAAC,SAAS,aAAa,OAAO,GAAY;AAC1D,UAAM,QAAQ,EAAE,GAAG;AACnB,QAAI,OAAO,UAAU,YAAY,MAAO,QAAO;AAC/C,QAAI,SAAS,OAAO,UAAU,UAAU;AACtC,YAAM,QAAQ;AACd,UAAI,OAAO,MAAM,YAAY,YAAY,MAAM,QAAS,QAAO,MAAM;AACrE,UAAI,OAAO,MAAM,WAAW,YAAY,MAAM,OAAQ,QAAO,MAAM;AACnE,YAAM,SAAS,0BAA0B,OAAO,QAAQ,CAAC;AACzD,UAAI,WAAW,KAAM,QAAO;AAAA,IAC9B;AAAA,EACF;AACA,SAAO;AACT;AAiBO,SAAS,mBAAmB,KAAuB;AACxD,MAAI,eAAe,gBAAgB,eAAe,aAAc,QAAO;AACvE,MAAI,OAAO,cAAc,eAAe,UAAU,WAAW,MAAO,QAAO;AAC3E,MAAI,eAAe,UAAW,QAAO;AACrC,MAAI,OAAO,OAAO,QAAQ,UAAU;AAClC,UAAM,IAAI;AAEV,QAAI,EAAE,QAAQ,EAAE,OAAQ,QAAO;AAC/B,UAAM,OAAO,GAAG,OAAO,EAAE,SAAS,WAAW,EAAE,OAAO,EAAE,IACtD,OAAO,EAAE,YAAY,WAAW,EAAE,UAAU,EAC9C,GAAG,YAAY;AACf,WACE,KAAK,SAAS,iBAAiB,KAC/B,KAAK,SAAS,cAAc,KAC5B,KAAK,SAAS,wBAAwB,KACtC,KAAK,SAAS,aAAa;AAAA,IAC3B,KAAK,SAAS,2DAA2D;AAAA,EAE7E;AACA,SAAO;AACT;AAEO,SAAS,WAAW,KAAwB;AACjD,MAAI,WAAW,GAAG,EAAG,QAAO;AAC5B,MAAI,eAAe,gBAAgB,IAAI,SAAS,cAAc;AAC5D,WAAO,IAAI,aAAa,GAAG;AAAA,EAC7B;AACA,MAAI,eAAe,WAAW;AAE5B,WAAO,IAAI,aAAa,IAAI,WAAW,0BAA0B,GAAG;AAAA,EACtE;AACA,SAAO,IAAI;AAAA,IACT;AAAA,IACA,oBAAoB,GAAG;AAAA,IACvB,EAAE,OAAO,IAAI;AAAA,EACf;AACF;;;ACxNA,SAAS,eAAe,KAAuB;AAC7C,SAAO,WAAW,GAAG,KAAM,IAAiB;AAC9C;AAEA,SAAS,MAAM,IAAY,QAAqC;AAC9D,SAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,UAAM,QAAQ,WAAW,MAAM;AAC7B,cAAQ;AACR,UAAI,OAAQ,QAAO,oBAAoB,SAAS,OAAO;AAAA,IACzD,GAAG,EAAE;AACL,UAAM,UAAU,MAAM;AACpB,mBAAa,KAAK;AAClB,aAAO,QAAQ,UAAU,IAAI,MAAM,SAAS,CAAC;AAAA,IAC/C;AACA,QAAI,QAAQ;AACV,UAAI,OAAO,SAAS;AAClB,qBAAa,KAAK;AAClB,eAAO,OAAO,MAAM;AAAA,MACtB,OAAO;AACL,eAAO,iBAAiB,SAAS,SAAS,EAAE,MAAM,KAAK,CAAC;AAAA,MAC1D;AAAA,IACF;AAAA,EACF,CAAC;AACH;AAEA,eAAsB,UACpB,IACA,OAAqB,CAAC,GACV;AACZ,QAAM,WAAW,KAAK,IAAI,GAAG,KAAK,YAAY,CAAC;AAC/C,QAAM,iBAAiB,KAAK,kBAAkB;AAC9C,QAAM,aAAa,KAAK,cAAc;AACtC,QAAM,SAAS,KAAK,iBAAiB;AACrC,QAAM,SAAS,KAAK,IAAI,GAAG,KAAK,IAAI,GAAG,KAAK,UAAU,IAAI,CAAC;AAC3D,QAAM,UAAU,KAAK,WAAW;AAEhC,MAAI;AACJ,WAAS,UAAU,GAAG,WAAW,UAAU,WAAW;AACpD,QAAI,KAAK,QAAQ,QAAS,OAAM,KAAK,OAAO;AAC5C,QAAI;AACF,aAAO,MAAM,GAAG,OAAO;AAAA,IACzB,SAAS,KAAK;AACZ,gBAAU;AACV,UAAI,YAAY,YAAY,CAAC,QAAQ,GAAG,EAAG,OAAM;AAEjD,YAAM,OAAO,KAAK;AAAA,QAChB;AAAA,QACA,iBAAiB,KAAK,IAAI,QAAQ,UAAU,CAAC;AAAA,MAC/C;AACA,YAAM,eAAe,OAAO,UAAU,KAAK,OAAO,IAAI,IAAI;AAC1D,YAAM,UAAU,KAAK,IAAI,GAAG,KAAK,MAAM,OAAO,YAAY,CAAC;AAC3D,WAAK,UAAU,SAAS,KAAK,OAAO;AACpC,YAAM,MAAM,SAAS,KAAK,MAAM;AAAA,IAClC;AAAA,EACF;AAGA,QAAM;AACR;;;ACrCA,IAAM,WAAW;AAAA,EACf,kBAAkB;AAAA,EAClB,gBAAgB;AAClB;AAEA,SAAS,UAAU,QAAyB,QAAiC;AAC3E,MAAI,OAAO,SAAS;AAClB,WAAO,MAAM,OAAO,MAAM;AAC1B,WAAO,MAAM;AAAA,IAAC;AAAA,EAChB;AACA,QAAM,UAAU,MAAM,OAAO,MAAM,OAAO,MAAM;AAChD,SAAO,iBAAiB,SAAS,SAAS,EAAE,MAAM,KAAK,CAAC;AACxD,SAAO,MAAM,OAAO,oBAAoB,SAAS,OAAO;AAC1D;AAEA,eAAsB,eACpB,KACA,OAAoB,CAAC,GACrB,OAA8B,CAAC,GACA;AAC/B,QAAM,mBAAmB,KAAK,oBAAoB,SAAS;AAC3D,QAAM,iBACJ,KAAK,mBAAmB,SACpB,SAAS,iBACT,KAAK;AACX,QAAM,mBAAmB,KAAK,oBAAoB;AAalD,QAAM,aAAa,IAAI,gBAAgB;AACvC,QAAM,eAAe,KAAK,SACtB,UAAU,YAAY,KAAK,MAAM,IACjC,MAAM;AAAA,EAAC;AAEX,MAAI,kBAAkB;AACtB,MAAI,gBAAgB;AAEpB,QAAM,eAAe,WAAW,MAAM;AACpC,sBAAkB;AAClB,eAAW,MAAM,iBAAiB;AAAA,EACpC,GAAG,gBAAgB;AAEnB,QAAM,aACJ,mBAAmB,OACf,WAAW,MAAM;AACf,oBAAgB;AAChB,eAAW,MAAM,eAAe;AAAA,EAClC,GAAG,cAAc,IACjB;AAEN,MAAI;AACF,UAAM,WAAW,MAAM,MAAM,KAAK,EAAE,GAAG,MAAM,QAAQ,WAAW,OAAO,CAAC;AACxE,iBAAa,YAAY;AAMzB,QAAI,eAAe,KAAM,cAAa,UAAU;AAEhD,QAAI,oBAAoB,CAAC,SAAS,IAAI;AACpC,YAAM,IAAI;AAAA,QACR,SAAS;AAAA,QACT,QAAQ,SAAS,MAAM,IAAI,SAAS,UAAU;AAAA,MAChD;AAAA,IACF;AAEA,WAAO,EAAE,UAAU,WAAW;AAAA,EAChC,SAAS,KAAK;AACZ,iBAAa,YAAY;AACzB,QAAI,eAAe,KAAM,cAAa,UAAU;AAEhD,QAAI,gBAAiB,OAAM,IAAI,oBAAoB,kBAAkB,GAAG;AACxE,QAAI;AACF,YAAM,IAAI,kBAAkB,gBAA0B,GAAG;AAC3D,QAAI,eAAe,gBAAgB,IAAI,SAAS,cAAc;AAC5D,YAAM,IAAI,aAAa,GAAG;AAAA,IAC5B;AACA,QAAI,KAAK,QAAQ,SAAS;AAKxB,YAAM,IAAI,aAAa,GAAG;AAAA,IAC5B;AACA,QAAI,eAAe,WAAW;AAK5B,UAAI,OAAO,cAAc,eAAe,UAAU,WAAW,OAAO;AAClE,cAAM,IAAI,aAAa;AAAA,MACzB;AACA,YAAM,IAAI,aAAa,IAAI,WAAW,0BAA0B,GAAG;AAAA,IACrE;AACA,QAAI,eAAe,UAAW,OAAM;AACpC,UAAM,IAAI,aAAa,oBAAoB,GAAG,GAAG,GAAG;AAAA,EACtD,UAAE;AACA,iBAAa;AAAA,EACf;AACF;;;AC/HA,IAAMA,YAAW;AAAA,EACf,oBAAoB;AAAA,EACpB,eAAe;AACjB;AASA,gBAAuB,cACrB,QACA,OAA6B,CAAC,GACM;AACpC,QAAM,qBACJ,KAAK,sBAAsBA,UAAS;AACtC,QAAM,gBAAgB,KAAK,iBAAiBA,UAAS;AACrD,QAAM,QAAQ,KAAK;AAEnB,QAAM,YAAY,KAAK,IAAI;AAC3B,QAAM,OAAO,OAAO,OAAO,aAAa,EAAE;AAE1C,SAAO,MAAM;AACX,UAAM,UAAU,KAAK,IAAI,IAAI;AAC7B,UAAM,oBAAoB,gBAAgB;AAC1C,QAAI,qBAAqB,GAAG;AAC1B,aAAO,MAAM,eAAe;AAC5B,YAAM,IAAI,kBAAkB,aAAa;AAAA,IAC3C;AAEA,UAAM,WAAW,KAAK,IAAI,oBAAoB,iBAAiB;AAE/D,QAAI,QAA8C;AAClD,QAAI,eAA6C;AACjD,UAAM,iBAAiB,IAAI,QAAe,CAAC,GAAG,WAAW;AACvD,cAAQ,WAAW,MAAM;AACvB,uBACE,aAAa,oBAAoB,UAAU;AAC7C,eAAO;AAAA,UACL,iBAAiB,UAAU,kBAAkB;AAAA,QAC/C;AACA;AAAA,UACE,iBAAiB,UACb,IAAI,kBAAkB,aAAa,IACnC,IAAI,sBAAsB,kBAAkB;AAAA,QAClD;AAAA,MACF,GAAG,QAAQ;AAAA,IACb,CAAC;AAED,QAAI;AACJ,QAAI;AACF,eAAS,MAAM,QAAQ,KAAK,CAAC,KAAK,KAAK,GAAG,cAAc,CAAC;AAAA,IAC3D,SAAS,KAAK;AACZ,UAAI,UAAU,KAAM,cAAa,KAAK;AAQtC,UAAI;AACF,cAAM,UAAU,KAAK,SAAS,MAAkB;AAChD,YAAI,SAAS;AACX,eAAK,QAAQ,QAAQ,OAAO,EAAE,MAAM,MAAM;AAAA,UAE1C,CAAC;AAAA,QACH;AAAA,MACF,QAAQ;AAAA,MAER;AACA,YAAM;AAAA,IACR;AAEA,QAAI,UAAU,KAAM,cAAa,KAAK;AACtC,QAAI,OAAO,KAAM;AAEjB,SAAK,cAAc,KAAK,IAAI,CAAC;AAC7B,UAAM,OAAO;AAAA,EACf;AACF;;;ACvEO,SAAS,eAAe,KAAqB;AAClD,MAAI,eAAe,MAAO,QAAO;AACjC,MAAI,OAAO,OAAO,QAAQ,UAAU;AAClC,UAAM,IAAI;AACV,UAAM,QAAkB,CAAC;AACzB,QAAI,OAAO,EAAE,YAAY,YAAY,EAAE,QAAS,OAAM,KAAK,EAAE,OAAO;AACpE,QAAI,OAAO,EAAE,YAAY,YAAY,EAAE;AACrC,YAAM,KAAK,YAAY,EAAE,OAAO,EAAE;AACpC,QAAI,OAAO,EAAE,SAAS,YAAY,EAAE,KAAM,OAAM,KAAK,SAAS,EAAE,IAAI,EAAE;AACtE,QAAI,OAAO,EAAE,SAAS,YAAY,EAAE,KAAM,OAAM,KAAK,UAAU,EAAE,IAAI,GAAG;AACxE,QAAI,MAAM,SAAS,EAAG,QAAO,IAAI,MAAM,MAAM,KAAK,UAAK,CAAC;AAAA,EAC1D;AACA,SAAO,IAAI,MAAM,OAAO,GAAG,CAAC;AAC9B;","names":["DEFAULTS"]}
1
+ {"version":3,"sources":["../src/credentials.ts","../src/net/errors.ts","../src/net/retry.ts","../src/net/resilient-fetch.ts","../src/net/stream-monitor.ts","../src/index.ts"],"sourcesContent":["/**\n * The credentials port — the ONE credential→header mapping for every client\n * package (DATA-PACKAGE-DESIGN.md \"The credentials port\"; API-key lane ratified\n * as C16, 2026-08-29).\n *\n * Three credential kinds, one mapping, in exactly one place:\n *\n * - `user` — a Supabase JWT: `Authorization: Bearer <jwt>` (both lanes)\n * - `guest` — a fingerprint: `X-Fingerprint-ID: <id>` (API lane only)\n * - `api_key` — an AI Matrx API key: `Authorization: Bearer mx_live_...`.\n * The server's ONE AuthMiddleware disambiguates by shape (`mx_` prefix vs\n * three-dot JWT), so both `user` and `api_key` ride the same header.\n *\n * No polling, no readiness loop: readiness is the port's promise.\n */\n\n/** A credential a Matrx client can present. */\nexport type MatrxCredential =\n | { kind: \"user\"; accessToken: string }\n | { kind: \"guest\"; fingerprintId: string }\n | { kind: \"api_key\"; apiKey: string };\n\n/**\n * The async source of the current credential. `get()` resolves when the host\n * knows its auth state (null = anonymous); `onChange` lets transports refresh\n * cached headers when the credential rotates.\n */\nexport interface CredentialsPort {\n get(): Promise<MatrxCredential | null>;\n onChange?(cb: () => void): () => void;\n}\n\n/** Prefix that marks a bearer token as an AI Matrx API key, never a JWT. */\nexport const API_KEY_PREFIX = \"mx_\";\n\n/**\n * Map a credential to the exact request headers the AI Matrx platform expects.\n * This is the single place that knowledge lives — transports spread the result\n * into their request headers and add `X-Organization-Id` from the org context\n * binding separately.\n */\nexport function credentialToHeaders(\n credential: MatrxCredential | null,\n): Record<string, string> {\n if (credential === null) return {};\n switch (credential.kind) {\n case \"user\":\n return { Authorization: `Bearer ${credential.accessToken}` };\n case \"guest\":\n return { \"X-Fingerprint-ID\": credential.fingerprintId };\n case \"api_key\": {\n if (!credential.apiKey.startsWith(API_KEY_PREFIX)) {\n throw new Error(\n `credentialToHeaders: an api_key credential must start with \"${API_KEY_PREFIX}\" ` +\n \"(AI Matrx API keys look like mx_live_...)\",\n );\n }\n return { Authorization: `Bearer ${credential.apiKey}` };\n }\n }\n}\n","/**\n * Structured errors for the resilience layer.\n *\n * Every network-layer failure resolves to one of these classes so callers\n * (and any recovery UI) can branch on error type, not on string matching.\n *\n * Ported verbatim from matrx-frontend `lib/net/errors.ts` — the origin of the\n * trio. The one coupling seam removed: `extractErrorMessage` (previously the\n * app's `utils/errors`) is pure logic and ships inside the package.\n */\n\nexport type NetErrorCode =\n | \"connect-timeout\"\n | \"total-timeout\"\n | \"heartbeat-timeout\"\n | \"network\"\n | \"http\"\n | \"aborted\"\n | \"offline\"\n | \"unknown\";\n\nexport class NetError extends Error {\n readonly code: NetErrorCode;\n override readonly cause?: unknown;\n readonly status?: number;\n readonly retryable: boolean;\n\n constructor(\n code: NetErrorCode,\n message: string,\n opts: { cause?: unknown; status?: number; retryable?: boolean } = {},\n ) {\n super(message);\n this.name = \"NetError\";\n this.code = code;\n this.cause = opts.cause;\n // Conditional only for `exactOptionalPropertyTypes` — same runtime shape.\n if (opts.status !== undefined) this.status = opts.status;\n this.retryable = opts.retryable ?? false;\n }\n}\n\nexport class ConnectTimeoutError extends NetError {\n constructor(ms: number, cause?: unknown) {\n super(\"connect-timeout\", `Connection timed out after ${ms}ms`, {\n cause,\n retryable: true,\n });\n this.name = \"ConnectTimeoutError\";\n }\n}\n\nexport class TotalTimeoutError extends NetError {\n constructor(ms: number, cause?: unknown) {\n super(\"total-timeout\", `Request exceeded ${ms}ms total budget`, {\n cause,\n retryable: false,\n });\n this.name = \"TotalTimeoutError\";\n }\n}\n\nexport class HeartbeatTimeoutError extends NetError {\n constructor(ms: number, cause?: unknown) {\n super(\n \"heartbeat-timeout\",\n `No server activity for ${ms}ms — stream considered dead`,\n { cause, retryable: false },\n );\n this.name = \"HeartbeatTimeoutError\";\n }\n}\n\nexport class NetworkError extends NetError {\n constructor(message: string, cause?: unknown) {\n super(\"network\", message, { cause, retryable: true });\n this.name = \"NetworkError\";\n }\n}\n\nexport class HttpError extends NetError {\n constructor(status: number, message: string, cause?: unknown) {\n // 408/429/5xx are generally safe to retry for idempotent ops.\n const retryable = status === 408 || status === 429 || status >= 500;\n super(\"http\", message, { cause, status, retryable });\n this.name = \"HttpError\";\n }\n}\n\nexport class AbortedError extends NetError {\n constructor(cause?: unknown) {\n super(\"aborted\", \"Request was aborted\", { cause, retryable: false });\n this.name = \"AbortedError\";\n }\n}\n\nexport class OfflineError extends NetError {\n constructor() {\n super(\"offline\", \"Browser is offline\", { retryable: true });\n this.name = \"OfflineError\";\n }\n}\n\nexport function isNetError(err: unknown): err is NetError {\n return err instanceof NetError;\n}\n\n/**\n * Folds any thrown value into a descriptive message string — THE one reader\n * for every failure shape this platform actually emits.\n *\n * 2026-09-07 (duplication census rows 9, 10, 21): this body had DRIFTED BEHIND\n * three host copies, which is the campaign's failure mode running in the\n * package's favour-direction. Each host had learned something the package\n * never did, so every non-frontend consumer of `@ai-matrx/data/net` was\n * flattening a server's own sentence into \"An unexpected error occurred\". All\n * three are folded in here, with a named regression test per shape:\n *\n * 1. **FastAPI's `detail`** (matrx-frontend `utils/errors.ts`, learned\n * 2026-08-31 as V2 finding G5). Our server refuses in `detail` — a string,\n * or an object carrying `message` — and every reader checked\n * `message`/`details` only. The server's sentence is the whole point of\n * catching this.\n * 2. **A `fallback` argument** (matrx-frontend `ShortcutForm.tsx`). A caller\n * who knows what it was doing can say \"Failed to save shortcut\" instead of\n * the generic sentence. Optional; the default is unchanged, so every\n * existing single-argument call behaves exactly as before.\n * 3. **Recursion into nested error objects** (aidream `workflow-studio`, two\n * byte-identical copies). The run pipeline nests: `{error: {type, message}}`\n * and `{error: {error: ...}}` and `{exception: {...}}` are all real shapes,\n * and a non-recursive reader answers \"An unexpected error occurred\" while\n * holding the actual reason two levels down. Depth-capped at 4 — a cyclic\n * or absurdly deep object must not hang a render.\n *\n * PostgREST-style errors are plain objects carrying\n * `message`/`details`/`hint`/`code`; a bare `String(err)` on those yields\n * \"[object Object]\", which is why this exists at all.\n */\nexport function extractErrorMessage(\n err: unknown,\n fallback = \"An unexpected error occurred\",\n): string {\n if (!err) return \"Unknown error\";\n if (typeof err === \"string\") return err;\n if (err instanceof Error) return err.message;\n if (typeof err === \"object\") {\n const e = err as Record<string, unknown>;\n const parts: string[] = [];\n if (typeof e.message === \"string\" && e.message) parts.push(e.message);\n // FastAPI's own word for the reason — only when nothing said `message`,\n // so a PostgREST error keeps its exact composed sentence.\n if (parts.length === 0) {\n if (typeof e.detail === \"string\" && e.detail) parts.push(e.detail);\n else if (e.detail && typeof e.detail === \"object\") {\n const d = e.detail as Record<string, unknown>;\n if (typeof d.message === \"string\" && d.message) parts.push(d.message);\n }\n }\n if (typeof e.details === \"string\" && e.details) parts.push(e.details);\n if (typeof e.hint === \"string\" && e.hint) parts.push(`Hint: ${e.hint}`);\n if (typeof e.code === \"string\" && e.code) parts.push(`Code: ${e.code}`);\n if (parts.length > 0) return parts.join(\" · \");\n\n const nested = extractNestedErrorMessage(e, 0);\n if (nested !== null) return nested;\n\n try {\n const s = JSON.stringify(err);\n if (s && s !== \"{}\") return s;\n } catch {\n /* ignore */\n }\n }\n return fallback;\n}\n\n/** Depth-capped walk into `error` / `exception` / `cause`. See above. */\nfunction extractNestedErrorMessage(\n e: Record<string, unknown>,\n depth: number,\n): string | null {\n if (depth > 4) return null;\n for (const key of [\"error\", \"exception\", \"cause\"] as const) {\n const value = e[key];\n if (typeof value === \"string\" && value) return value;\n if (value && typeof value === \"object\") {\n const inner = value as Record<string, unknown>;\n if (typeof inner.message === \"string\" && inner.message) return inner.message;\n if (typeof inner.detail === \"string\" && inner.detail) return inner.detail;\n const deeper = extractNestedErrorMessage(inner, depth + 1);\n if (deeper !== null) return deeper;\n }\n }\n return null;\n}\n\n/**\n * True when a failure is the TRANSPORT dying, not the server answering: the\n * browser is offline, asleep, mid-wifi-handoff, or DNS/TLS failed. It also\n * covers the bounded Envoy/Supabase gateway shape emitted when the upstream\n * database connection dies before response headers. `fetch`\n * rejects with a bare `TypeError: Failed to fetch` in every browser, and\n * supabase-js surfaces that verbatim (message only — no code, no hint).\n *\n * Use it to decide how LOUD a failure is, never whether to handle it. A\n * transport failure carries nothing an engineer can act on, so logging it at\n * error level buries the failures that do — that is how a notes surface filed\n * a system_error every time a laptop slept (2026-08-11). Anything with a\n * Postgres code, an HTTP status, or a server message is NOT this: keep those\n * loud.\n */\nexport function isTransportFailure(err: unknown): boolean {\n if (err instanceof NetworkError || err instanceof OfflineError) return true;\n if (typeof navigator !== \"undefined\" && navigator.onLine === false) return true;\n if (err instanceof TypeError) return true;\n if (err && typeof err === \"object\") {\n const e = err as { code?: unknown; status?: unknown; message?: unknown; name?: unknown };\n // A real PostgREST/Postgres failure always carries a code or status.\n if (e.code || e.status) return false;\n const text = `${typeof e.name === \"string\" ? e.name : \"\"} ${\n typeof e.message === \"string\" ? e.message : \"\"\n }`.toLowerCase();\n return (\n text.includes(\"failed to fetch\") ||\n text.includes(\"networkerror\") ||\n text.includes(\"network request failed\") ||\n text.includes(\"load failed\") || // Safari's wording for the same condition\n text.includes(\"upstream connect error or disconnect/reset before headers\")\n );\n }\n return false;\n}\n\nexport function toNetError(err: unknown): NetError {\n if (isNetError(err)) return err;\n if (err instanceof DOMException && err.name === \"AbortError\") {\n return new AbortedError(err);\n }\n if (err instanceof TypeError) {\n // fetch() throws TypeError on DNS/connection failure in every browser.\n return new NetworkError(err.message || \"Network request failed\", err);\n }\n return new NetError(\n \"unknown\",\n extractErrorMessage(err),\n { cause: err },\n );\n}\n","/**\n * withRetry — exponential backoff retry for idempotent operations.\n *\n * IMPORTANT: only wrap operations that are safe to repeat (GETs, cache\n * lookups, agent-definition fetches). Never wrap user-authored submissions —\n * recovery UX handles those via explicit user-initiated retry.\n *\n * Ported verbatim from matrx-frontend `lib/net/retry.ts`.\n */\n\nimport { isNetError, NetError } from \"./errors\";\n\nexport interface RetryOptions {\n /** Total attempts including the first. Default 3. */\n attempts?: number;\n /** Initial delay ms before the first retry. Default 250. */\n initialDelayMs?: number;\n /** Max delay ms cap between retries. Default 4_000. */\n maxDelayMs?: number;\n /** Multiplier applied after each failure. Default 2. */\n backoffFactor?: number;\n /** Jitter factor 0-1 applied to each delay. Default 0.25. */\n jitter?: number;\n /** Predicate deciding whether a given error should trigger a retry. Default: NetError.retryable. */\n retryOn?: (err: unknown) => boolean;\n /** Caller-provided signal. If aborted, no further retries are attempted. */\n signal?: AbortSignal;\n /** Fires before each retry; useful for logging/telemetry. */\n onRetry?: (attempt: number, err: unknown, delayMs: number) => void;\n}\n\nfunction defaultRetryOn(err: unknown): boolean {\n return isNetError(err) && (err as NetError).retryable;\n}\n\nfunction sleep(ms: number, signal?: AbortSignal): Promise<void> {\n return new Promise((resolve, reject) => {\n const timer = setTimeout(() => {\n resolve();\n if (signal) signal.removeEventListener(\"abort\", onAbort);\n }, ms);\n const onAbort = () => {\n clearTimeout(timer);\n reject(signal?.reason ?? new Error(\"Aborted\"));\n };\n if (signal) {\n if (signal.aborted) {\n clearTimeout(timer);\n reject(signal.reason);\n } else {\n signal.addEventListener(\"abort\", onAbort, { once: true });\n }\n }\n });\n}\n\nexport async function withRetry<T>(\n fn: (attempt: number) => Promise<T>,\n opts: RetryOptions = {},\n): Promise<T> {\n const attempts = Math.max(1, opts.attempts ?? 3);\n const initialDelayMs = opts.initialDelayMs ?? 250;\n const maxDelayMs = opts.maxDelayMs ?? 4_000;\n const factor = opts.backoffFactor ?? 2;\n const jitter = Math.max(0, Math.min(1, opts.jitter ?? 0.25));\n const retryOn = opts.retryOn ?? defaultRetryOn;\n\n let lastErr: unknown;\n for (let attempt = 1; attempt <= attempts; attempt++) {\n if (opts.signal?.aborted) throw opts.signal.reason;\n try {\n return await fn(attempt);\n } catch (err) {\n lastErr = err;\n if (attempt === attempts || !retryOn(err)) throw err;\n\n const base = Math.min(\n maxDelayMs,\n initialDelayMs * Math.pow(factor, attempt - 1),\n );\n const jitterAmount = base * jitter * (Math.random() * 2 - 1);\n const delayMs = Math.max(0, Math.round(base + jitterAmount));\n opts.onRetry?.(attempt, err, delayMs);\n await sleep(delayMs, opts.signal);\n }\n }\n\n // Unreachable — the loop always either returns or throws.\n throw lastErr;\n}\n","/**\n * resilientFetch — `fetch` with connect + total timeouts and composed abort.\n *\n * Every outbound HTTP call should go through this, not raw fetch. The\n * two-stage timeout (connect + total) lets callers allow long-streaming\n * responses without giving up the ability to fail fast when the server is\n * simply unreachable.\n *\n * Ported verbatim from matrx-frontend `lib/net/resilient-fetch.ts`. `fetch`\n * comes from the environment (Node 20+, browsers, React Native/Hermes) —\n * there is deliberately no injected transport here.\n */\n\nimport {\n AbortedError,\n ConnectTimeoutError,\n extractErrorMessage,\n HttpError,\n NetworkError,\n OfflineError,\n TotalTimeoutError,\n} from \"./errors\";\n\nexport interface ResilientFetchOptions {\n /** Max ms from request start → response headers received. Default 15_000. */\n connectTimeoutMs?: number;\n /**\n * Max ms from request start → fetch() promise resolves.\n * For streaming endpoints the response arrives quickly and the stream body\n * is read by the caller, so this bound only guards the handshake itself.\n * `null` disables the ceiling. Default 120_000.\n */\n totalTimeoutMs?: number | null;\n /** Caller-provided signal; composed with our internal timeout signals. */\n signal?: AbortSignal;\n /**\n * If true, throw on non-2xx responses instead of returning them. Default true.\n * Callers that need to read structured error bodies can set this to false.\n */\n throwOnHttpError?: boolean;\n}\n\nexport interface ResilientFetchResult {\n response: Response;\n /**\n * Composed AbortController tied to the fetch. Callers reading the body\n * (e.g. streaming) can call `.abort()` from this controller to cancel\n * in-flight reads. Already aborted on connect/total timeout.\n */\n controller: AbortController;\n}\n\nconst DEFAULTS = {\n connectTimeoutMs: 15_000,\n totalTimeoutMs: 120_000,\n} as const;\n\nfunction linkAbort(target: AbortController, signal: AbortSignal): () => void {\n if (signal.aborted) {\n target.abort(signal.reason);\n return () => {};\n }\n const onAbort = () => target.abort(signal.reason);\n signal.addEventListener(\"abort\", onAbort, { once: true });\n return () => signal.removeEventListener(\"abort\", onAbort);\n}\n\nexport async function resilientFetch(\n url: string,\n init: RequestInit = {},\n opts: ResilientFetchOptions = {},\n): Promise<ResilientFetchResult> {\n const connectTimeoutMs = opts.connectTimeoutMs ?? DEFAULTS.connectTimeoutMs;\n const totalTimeoutMs =\n opts.totalTimeoutMs === undefined\n ? DEFAULTS.totalTimeoutMs\n : opts.totalTimeoutMs;\n const throwOnHttpError = opts.throwOnHttpError ?? true;\n\n // NOTE: we deliberately do NOT hard-block on `navigator.onLine === false`\n // here. That flag is only a HINT — it is unreliable in both directions\n // (VPNs, virtual network adapters, browser network-service hiccups, sleep/\n // wake, and some corporate stacks leave it stuck at `false` while the\n // machine is fully online). Trusting it as a pre-flight gate produced a\n // real production outage: the Agent Builder (this path) threw OfflineError\n // on every submit while the Run path — which uses plain fetch() and never\n // consults the flag — worked fine on the same network. We now let the\n // fetch actually run and only classify the result as \"offline\" if a genuine\n // network failure ALSO corroborates the flag (see the catch below).\n\n const controller = new AbortController();\n const unlinkCaller = opts.signal\n ? linkAbort(controller, opts.signal)\n : () => {};\n\n let connectTimedOut = false;\n let totalTimedOut = false;\n\n const connectTimer = setTimeout(() => {\n connectTimedOut = true;\n controller.abort(\"connect-timeout\");\n }, connectTimeoutMs);\n\n const totalTimer =\n totalTimeoutMs !== null\n ? setTimeout(() => {\n totalTimedOut = true;\n controller.abort(\"total-timeout\");\n }, totalTimeoutMs)\n : null;\n\n try {\n const response = await fetch(url, { ...init, signal: controller.signal });\n clearTimeout(connectTimer);\n // Both timers end with the handshake: `totalTimeoutMs` bounds request\n // start → fetch() resolution (headers), exactly as documented on the\n // option. Body reads are guarded by the caller (stream monitor /\n // heartbeats), not by this timer — clearing it here is what lets a\n // long-streaming body outlive the handshake budget.\n if (totalTimer !== null) clearTimeout(totalTimer);\n\n if (throwOnHttpError && !response.ok) {\n throw new HttpError(\n response.status,\n `HTTP ${response.status} ${response.statusText}`,\n );\n }\n\n return { response, controller };\n } catch (err) {\n clearTimeout(connectTimer);\n if (totalTimer !== null) clearTimeout(totalTimer);\n\n if (connectTimedOut) throw new ConnectTimeoutError(connectTimeoutMs, err);\n if (totalTimedOut)\n throw new TotalTimeoutError(totalTimeoutMs as number, err);\n if (err instanceof DOMException && err.name === \"AbortError\") {\n throw new AbortedError(err);\n }\n if (opts.signal?.aborted) {\n // Caller aborted with a CUSTOM reason: fetch rejects with that reason\n // verbatim (not an AbortError DOMException). Without this check the\n // abort fell through to the generic classifiers and could surface as a\n // retryable NetworkError — an aborted request must never be retried.\n throw new AbortedError(err);\n }\n if (err instanceof TypeError) {\n // fetch() throws TypeError on DNS/connection failure. If the browser\n // ALSO reports offline, the flag is now corroborated by an actual\n // failure — surface the more specific OfflineError. Absent a real\n // failure, `navigator.onLine === false` is never allowed to block.\n if (typeof navigator !== \"undefined\" && navigator.onLine === false) {\n throw new OfflineError();\n }\n throw new NetworkError(err.message || \"Network request failed\", err);\n }\n if (err instanceof HttpError) throw err;\n throw new NetworkError(extractErrorMessage(err), err);\n } finally {\n unlinkCaller();\n }\n}\n","/**\n * Stream liveness monitor.\n *\n * Wraps an async iterable of server events and enforces two guarantees:\n * 1. Heartbeat deadline — fail if no event arrives within `heartbeatTimeoutMs`.\n * 2. Absolute ceiling — fail if total wall-clock time exceeds `maxLifetimeMs`.\n *\n * The wrapper is transparent: events pass through in order, with the same\n * shape. The only observable difference is that a stuck stream throws\n * `HeartbeatTimeoutError` or `TotalTimeoutError` instead of awaiting forever.\n *\n * The wrapped iterable should be driven by NDJSON parsing; the server MUST\n * emit `{kind:\"heartbeat\"}` at least every `heartbeatTimeoutMs / 2` ms for\n * long-running operations (the Matrx stream contract:\n * common-docs/systems/architecture/execution-runtime/STREAM-CONTRACT.md).\n *\n * Ported verbatim from matrx-frontend `lib/net/stream-monitor.ts`, with one\n * defect fixed: cleanup of the underlying iterator after a watchdog timeout\n * is now fire-and-forget (see the note in the catch below).\n */\n\nimport { HeartbeatTimeoutError, TotalTimeoutError } from \"./errors\";\n\nexport interface StreamMonitorOptions {\n /** Max ms between any two events before the stream is considered dead. Default 30_000. */\n heartbeatTimeoutMs?: number;\n /** Max ms from wrap-start before throwing regardless of heartbeats. Default 600_000 (10min). */\n maxLifetimeMs?: number;\n /** Optional callback, fired every time a fresh event resets the deadline. */\n onHeartbeat?: (now: number) => void;\n /** Controller whose `.abort()` should be called on monitor-level timeout, so the underlying fetch is cancelled. */\n abortController?: AbortController;\n}\n\nconst DEFAULTS = {\n heartbeatTimeoutMs: 30_000,\n maxLifetimeMs: 600_000,\n} as const;\n\n/**\n * Wraps an async iterable so that it throws on stalled or overlong streams.\n *\n * Usage:\n * const live = monitorStream(events, { heartbeatTimeoutMs, abortController });\n * for await (const ev of live) { ... }\n */\nexport async function* monitorStream<T>(\n source: AsyncIterable<T>,\n opts: StreamMonitorOptions = {},\n): AsyncGenerator<T, void, undefined> {\n const heartbeatTimeoutMs =\n opts.heartbeatTimeoutMs ?? DEFAULTS.heartbeatTimeoutMs;\n const maxLifetimeMs = opts.maxLifetimeMs ?? DEFAULTS.maxLifetimeMs;\n const abort = opts.abortController;\n\n const startedAt = Date.now();\n const iter = source[Symbol.asyncIterator]() as AsyncIterator<T>;\n\n while (true) {\n const elapsed = Date.now() - startedAt;\n const remainingLifetime = maxLifetimeMs - elapsed;\n if (remainingLifetime <= 0) {\n abort?.abort(\"total-timeout\");\n throw new TotalTimeoutError(maxLifetimeMs);\n }\n\n const deadline = Math.min(heartbeatTimeoutMs, remainingLifetime);\n\n let timer: ReturnType<typeof setTimeout> | null = null;\n let timedOutKind: \"heartbeat\" | \"total\" | null = null;\n const timeoutPromise = new Promise<never>((_, reject) => {\n timer = setTimeout(() => {\n timedOutKind =\n deadline === remainingLifetime ? \"total\" : \"heartbeat\";\n abort?.abort(\n timedOutKind === \"total\" ? \"total-timeout\" : \"heartbeat-timeout\",\n );\n reject(\n timedOutKind === \"total\"\n ? new TotalTimeoutError(maxLifetimeMs)\n : new HeartbeatTimeoutError(heartbeatTimeoutMs),\n );\n }, deadline);\n });\n\n let result: IteratorResult<T>;\n try {\n result = await Promise.race([iter.next(), timeoutPromise]);\n } catch (err) {\n if (timer !== null) clearTimeout(timer);\n // Best-effort: let the underlying iterator clean up — WITHOUT awaiting\n // it. The original awaited `iter.return()`, but on an async generator\n // `return()` queues behind the still-pending `next()`; if the stream is\n // stuck and no abortController was provided to settle that read, the\n // await never resolves and the monitor hangs forever — the exact\n // failure mode this module exists to prevent. Fire-and-forget keeps\n // the timeout guarantee while still requesting cleanup.\n try {\n const cleanup = iter.return?.(undefined as never);\n if (cleanup) {\n void Promise.resolve(cleanup).catch(() => {\n /* ignore */\n });\n }\n } catch {\n /* ignore */\n }\n throw err;\n }\n\n if (timer !== null) clearTimeout(timer);\n if (result.done) return;\n\n opts.onHeartbeat?.(Date.now());\n yield result.value;\n }\n}\n","/**\n * @ai-matrx/data — the core module.\n *\n * The shared structural vocabulary every lane of the data layer speaks, plus\n * the lane-free net-resilience trio (S12 rides the core — re-exported from\n * `./net`, also importable as `@ai-matrx/data/net`). This entry point\n * otherwise deliberately stays tiny (the split-out law): the Supabase lane is\n * `@ai-matrx/data/db`; the API and files lanes arrive in later minors. Nothing\n * here performs network, storage, or global-state work at import time.\n *\n * The types are STRUCTURAL on purpose: the package never imports\n * `@supabase/supabase-js` — a real `PostgrestError` satisfies\n * `PostgrestLikeError` by shape, and so does any compatible client, so the\n * package works against whatever client the host injects.\n */\n\n/** The shape of a PostgREST error object (plain object, NOT an Error). */\nexport interface PostgrestLikeError {\n message: string;\n details?: string | null;\n hint?: string | null;\n code?: string | null;\n}\n\n/** The shape a `.maybeSingle()` call resolves to. */\nexport interface MaybeSingleResponse<Row> {\n data: Row | null;\n error: PostgrestLikeError | null;\n}\n\n/** A JSON value — the wire-safe subset every column and payload speaks. */\nexport type Json =\n | string\n | number\n | boolean\n | null\n | { [key: string]: Json | undefined }\n | Json[];\n\n/**\n * Converts any thrown value from a Supabase/PostgREST call into a proper Error\n * with a descriptive message. PostgREST errors are plain objects with `message`,\n * `code`, `details`, and `hint` — they are NOT `instanceof Error`, which causes\n * generic catch blocks to lose the real reason.\n */\nexport function pgErrorToError(err: unknown): Error {\n if (err instanceof Error) return err;\n if (err && typeof err === \"object\") {\n const e = err as Record<string, unknown>;\n const parts: string[] = [];\n if (typeof e.message === \"string\" && e.message) parts.push(e.message);\n if (typeof e.details === \"string\" && e.details)\n parts.push(`Details: ${e.details}`);\n if (typeof e.hint === \"string\" && e.hint) parts.push(`Hint: ${e.hint}`);\n if (typeof e.code === \"string\" && e.code) parts.push(`(code: ${e.code})`);\n if (parts.length > 0) return new Error(parts.join(\" — \"));\n }\n return new Error(String(err));\n}\n\nexport * from \"./credentials\";\nexport * from \"./net\";\n"],"mappings":"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","names":["DEFAULTS"]}
package/dist/net.cjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/net.ts","../src/net/errors.ts","../src/net/retry.ts","../src/net/resilient-fetch.ts","../src/net/stream-monitor.ts"],"sourcesContent":["/**\n * @ai-matrx/data/net — the net-resilience trio.\n *\n * Transport-adjacent and lane-free: `fetch` with connect/total timeouts and\n * composed abort, exponential-backoff retry for idempotent operations, the\n * stream stall watchdog, and the structured error vocabulary they all speak.\n * Ported behavior-verbatim from matrx-frontend `lib/net/` (the origin of the\n * trio); each module's header carries the incident that forged it.\n *\n * This tier depends on NOTHING — no Supabase, no org context, no API lane.\n * `fetch` and timers come from the environment; there are no injected seams\n * because the originals carried no app coupling beyond one pure message\n * helper, which now ships inside the package (`extractErrorMessage`).\n */\nexport {\n AbortedError,\n ConnectTimeoutError,\n extractErrorMessage,\n HeartbeatTimeoutError,\n HttpError,\n isNetError,\n isTransportFailure,\n NetError,\n NetworkError,\n OfflineError,\n toNetError,\n TotalTimeoutError,\n type NetErrorCode,\n} from \"./net/errors\";\nexport { withRetry, type RetryOptions } from \"./net/retry\";\nexport {\n resilientFetch,\n type ResilientFetchOptions,\n type ResilientFetchResult,\n} from \"./net/resilient-fetch\";\nexport { monitorStream, type StreamMonitorOptions } from \"./net/stream-monitor\";\n","/**\n * Structured errors for the resilience layer.\n *\n * Every network-layer failure resolves to one of these classes so callers\n * (and any recovery UI) can branch on error type, not on string matching.\n *\n * Ported verbatim from matrx-frontend `lib/net/errors.ts` — the origin of the\n * trio. The one coupling seam removed: `extractErrorMessage` (previously the\n * app's `utils/errors`) is pure logic and ships inside the package.\n */\n\nexport type NetErrorCode =\n | \"connect-timeout\"\n | \"total-timeout\"\n | \"heartbeat-timeout\"\n | \"network\"\n | \"http\"\n | \"aborted\"\n | \"offline\"\n | \"unknown\";\n\nexport class NetError extends Error {\n readonly code: NetErrorCode;\n override readonly cause?: unknown;\n readonly status?: number;\n readonly retryable: boolean;\n\n constructor(\n code: NetErrorCode,\n message: string,\n opts: { cause?: unknown; status?: number; retryable?: boolean } = {},\n ) {\n super(message);\n this.name = \"NetError\";\n this.code = code;\n this.cause = opts.cause;\n // Conditional only for `exactOptionalPropertyTypes` — same runtime shape.\n if (opts.status !== undefined) this.status = opts.status;\n this.retryable = opts.retryable ?? false;\n }\n}\n\nexport class ConnectTimeoutError extends NetError {\n constructor(ms: number, cause?: unknown) {\n super(\"connect-timeout\", `Connection timed out after ${ms}ms`, {\n cause,\n retryable: true,\n });\n this.name = \"ConnectTimeoutError\";\n }\n}\n\nexport class TotalTimeoutError extends NetError {\n constructor(ms: number, cause?: unknown) {\n super(\"total-timeout\", `Request exceeded ${ms}ms total budget`, {\n cause,\n retryable: false,\n });\n this.name = \"TotalTimeoutError\";\n }\n}\n\nexport class HeartbeatTimeoutError extends NetError {\n constructor(ms: number, cause?: unknown) {\n super(\n \"heartbeat-timeout\",\n `No server activity for ${ms}ms — stream considered dead`,\n { cause, retryable: false },\n );\n this.name = \"HeartbeatTimeoutError\";\n }\n}\n\nexport class NetworkError extends NetError {\n constructor(message: string, cause?: unknown) {\n super(\"network\", message, { cause, retryable: true });\n this.name = \"NetworkError\";\n }\n}\n\nexport class HttpError extends NetError {\n constructor(status: number, message: string, cause?: unknown) {\n // 408/429/5xx are generally safe to retry for idempotent ops.\n const retryable = status === 408 || status === 429 || status >= 500;\n super(\"http\", message, { cause, status, retryable });\n this.name = \"HttpError\";\n }\n}\n\nexport class AbortedError extends NetError {\n constructor(cause?: unknown) {\n super(\"aborted\", \"Request was aborted\", { cause, retryable: false });\n this.name = \"AbortedError\";\n }\n}\n\nexport class OfflineError extends NetError {\n constructor() {\n super(\"offline\", \"Browser is offline\", { retryable: true });\n this.name = \"OfflineError\";\n }\n}\n\nexport function isNetError(err: unknown): err is NetError {\n return err instanceof NetError;\n}\n\n/**\n * Folds any thrown value into a descriptive message string — THE one reader\n * for every failure shape this platform actually emits.\n *\n * 2026-09-07 (duplication census rows 9, 10, 21): this body had DRIFTED BEHIND\n * three host copies, which is the campaign's failure mode running in the\n * package's favour-direction. Each host had learned something the package\n * never did, so every non-frontend consumer of `@ai-matrx/data/net` was\n * flattening a server's own sentence into \"An unexpected error occurred\". All\n * three are folded in here, with a named regression test per shape:\n *\n * 1. **FastAPI's `detail`** (matrx-frontend `utils/errors.ts`, learned\n * 2026-08-31 as V2 finding G5). Our server refuses in `detail` — a string,\n * or an object carrying `message` — and every reader checked\n * `message`/`details` only. The server's sentence is the whole point of\n * catching this.\n * 2. **A `fallback` argument** (matrx-frontend `ShortcutForm.tsx`). A caller\n * who knows what it was doing can say \"Failed to save shortcut\" instead of\n * the generic sentence. Optional; the default is unchanged, so every\n * existing single-argument call behaves exactly as before.\n * 3. **Recursion into nested error objects** (aidream `workflow-studio`, two\n * byte-identical copies). The run pipeline nests: `{error: {type, message}}`\n * and `{error: {error: ...}}` and `{exception: {...}}` are all real shapes,\n * and a non-recursive reader answers \"An unexpected error occurred\" while\n * holding the actual reason two levels down. Depth-capped at 4 — a cyclic\n * or absurdly deep object must not hang a render.\n *\n * PostgREST-style errors are plain objects carrying\n * `message`/`details`/`hint`/`code`; a bare `String(err)` on those yields\n * \"[object Object]\", which is why this exists at all.\n */\nexport function extractErrorMessage(\n err: unknown,\n fallback = \"An unexpected error occurred\",\n): string {\n if (!err) return \"Unknown error\";\n if (typeof err === \"string\") return err;\n if (err instanceof Error) return err.message;\n if (typeof err === \"object\") {\n const e = err as Record<string, unknown>;\n const parts: string[] = [];\n if (typeof e.message === \"string\" && e.message) parts.push(e.message);\n // FastAPI's own word for the reason — only when nothing said `message`,\n // so a PostgREST error keeps its exact composed sentence.\n if (parts.length === 0) {\n if (typeof e.detail === \"string\" && e.detail) parts.push(e.detail);\n else if (e.detail && typeof e.detail === \"object\") {\n const d = e.detail as Record<string, unknown>;\n if (typeof d.message === \"string\" && d.message) parts.push(d.message);\n }\n }\n if (typeof e.details === \"string\" && e.details) parts.push(e.details);\n if (typeof e.hint === \"string\" && e.hint) parts.push(`Hint: ${e.hint}`);\n if (typeof e.code === \"string\" && e.code) parts.push(`Code: ${e.code}`);\n if (parts.length > 0) return parts.join(\" · \");\n\n const nested = extractNestedErrorMessage(e, 0);\n if (nested !== null) return nested;\n\n try {\n const s = JSON.stringify(err);\n if (s && s !== \"{}\") return s;\n } catch {\n /* ignore */\n }\n }\n return fallback;\n}\n\n/** Depth-capped walk into `error` / `exception` / `cause`. See above. */\nfunction extractNestedErrorMessage(\n e: Record<string, unknown>,\n depth: number,\n): string | null {\n if (depth > 4) return null;\n for (const key of [\"error\", \"exception\", \"cause\"] as const) {\n const value = e[key];\n if (typeof value === \"string\" && value) return value;\n if (value && typeof value === \"object\") {\n const inner = value as Record<string, unknown>;\n if (typeof inner.message === \"string\" && inner.message) return inner.message;\n if (typeof inner.detail === \"string\" && inner.detail) return inner.detail;\n const deeper = extractNestedErrorMessage(inner, depth + 1);\n if (deeper !== null) return deeper;\n }\n }\n return null;\n}\n\n/**\n * True when a failure is the TRANSPORT dying, not the server answering: the\n * browser is offline, asleep, mid-wifi-handoff, or DNS/TLS failed. It also\n * covers the bounded Envoy/Supabase gateway shape emitted when the upstream\n * database connection dies before response headers. `fetch`\n * rejects with a bare `TypeError: Failed to fetch` in every browser, and\n * supabase-js surfaces that verbatim (message only — no code, no hint).\n *\n * Use it to decide how LOUD a failure is, never whether to handle it. A\n * transport failure carries nothing an engineer can act on, so logging it at\n * error level buries the failures that do — that is how a notes surface filed\n * a system_error every time a laptop slept (2026-08-11). Anything with a\n * Postgres code, an HTTP status, or a server message is NOT this: keep those\n * loud.\n */\nexport function isTransportFailure(err: unknown): boolean {\n if (err instanceof NetworkError || err instanceof OfflineError) return true;\n if (typeof navigator !== \"undefined\" && navigator.onLine === false) return true;\n if (err instanceof TypeError) return true;\n if (err && typeof err === \"object\") {\n const e = err as { code?: unknown; status?: unknown; message?: unknown; name?: unknown };\n // A real PostgREST/Postgres failure always carries a code or status.\n if (e.code || e.status) return false;\n const text = `${typeof e.name === \"string\" ? e.name : \"\"} ${\n typeof e.message === \"string\" ? e.message : \"\"\n }`.toLowerCase();\n return (\n text.includes(\"failed to fetch\") ||\n text.includes(\"networkerror\") ||\n text.includes(\"network request failed\") ||\n text.includes(\"load failed\") || // Safari's wording for the same condition\n text.includes(\"upstream connect error or disconnect/reset before headers\")\n );\n }\n return false;\n}\n\nexport function toNetError(err: unknown): NetError {\n if (isNetError(err)) return err;\n if (err instanceof DOMException && err.name === \"AbortError\") {\n return new AbortedError(err);\n }\n if (err instanceof TypeError) {\n // fetch() throws TypeError on DNS/connection failure in every browser.\n return new NetworkError(err.message || \"Network request failed\", err);\n }\n return new NetError(\n \"unknown\",\n extractErrorMessage(err),\n { cause: err },\n );\n}\n","/**\n * withRetry — exponential backoff retry for idempotent operations.\n *\n * IMPORTANT: only wrap operations that are safe to repeat (GETs, cache\n * lookups, agent-definition fetches). Never wrap user-authored submissions —\n * recovery UX handles those via explicit user-initiated retry.\n *\n * Ported verbatim from matrx-frontend `lib/net/retry.ts`.\n */\n\nimport { isNetError, NetError } from \"./errors\";\n\nexport interface RetryOptions {\n /** Total attempts including the first. Default 3. */\n attempts?: number;\n /** Initial delay ms before the first retry. Default 250. */\n initialDelayMs?: number;\n /** Max delay ms cap between retries. Default 4_000. */\n maxDelayMs?: number;\n /** Multiplier applied after each failure. Default 2. */\n backoffFactor?: number;\n /** Jitter factor 0-1 applied to each delay. Default 0.25. */\n jitter?: number;\n /** Predicate deciding whether a given error should trigger a retry. Default: NetError.retryable. */\n retryOn?: (err: unknown) => boolean;\n /** Caller-provided signal. If aborted, no further retries are attempted. */\n signal?: AbortSignal;\n /** Fires before each retry; useful for logging/telemetry. */\n onRetry?: (attempt: number, err: unknown, delayMs: number) => void;\n}\n\nfunction defaultRetryOn(err: unknown): boolean {\n return isNetError(err) && (err as NetError).retryable;\n}\n\nfunction sleep(ms: number, signal?: AbortSignal): Promise<void> {\n return new Promise((resolve, reject) => {\n const timer = setTimeout(() => {\n resolve();\n if (signal) signal.removeEventListener(\"abort\", onAbort);\n }, ms);\n const onAbort = () => {\n clearTimeout(timer);\n reject(signal?.reason ?? new Error(\"Aborted\"));\n };\n if (signal) {\n if (signal.aborted) {\n clearTimeout(timer);\n reject(signal.reason);\n } else {\n signal.addEventListener(\"abort\", onAbort, { once: true });\n }\n }\n });\n}\n\nexport async function withRetry<T>(\n fn: (attempt: number) => Promise<T>,\n opts: RetryOptions = {},\n): Promise<T> {\n const attempts = Math.max(1, opts.attempts ?? 3);\n const initialDelayMs = opts.initialDelayMs ?? 250;\n const maxDelayMs = opts.maxDelayMs ?? 4_000;\n const factor = opts.backoffFactor ?? 2;\n const jitter = Math.max(0, Math.min(1, opts.jitter ?? 0.25));\n const retryOn = opts.retryOn ?? defaultRetryOn;\n\n let lastErr: unknown;\n for (let attempt = 1; attempt <= attempts; attempt++) {\n if (opts.signal?.aborted) throw opts.signal.reason;\n try {\n return await fn(attempt);\n } catch (err) {\n lastErr = err;\n if (attempt === attempts || !retryOn(err)) throw err;\n\n const base = Math.min(\n maxDelayMs,\n initialDelayMs * Math.pow(factor, attempt - 1),\n );\n const jitterAmount = base * jitter * (Math.random() * 2 - 1);\n const delayMs = Math.max(0, Math.round(base + jitterAmount));\n opts.onRetry?.(attempt, err, delayMs);\n await sleep(delayMs, opts.signal);\n }\n }\n\n // Unreachable — the loop always either returns or throws.\n throw lastErr;\n}\n","/**\n * resilientFetch — `fetch` with connect + total timeouts and composed abort.\n *\n * Every outbound HTTP call should go through this, not raw fetch. The\n * two-stage timeout (connect + total) lets callers allow long-streaming\n * responses without giving up the ability to fail fast when the server is\n * simply unreachable.\n *\n * Ported verbatim from matrx-frontend `lib/net/resilient-fetch.ts`. `fetch`\n * comes from the environment (Node 20+, browsers, React Native/Hermes) —\n * there is deliberately no injected transport here.\n */\n\nimport {\n AbortedError,\n ConnectTimeoutError,\n extractErrorMessage,\n HttpError,\n NetworkError,\n OfflineError,\n TotalTimeoutError,\n} from \"./errors\";\n\nexport interface ResilientFetchOptions {\n /** Max ms from request start → response headers received. Default 15_000. */\n connectTimeoutMs?: number;\n /**\n * Max ms from request start → fetch() promise resolves.\n * For streaming endpoints the response arrives quickly and the stream body\n * is read by the caller, so this bound only guards the handshake itself.\n * `null` disables the ceiling. Default 120_000.\n */\n totalTimeoutMs?: number | null;\n /** Caller-provided signal; composed with our internal timeout signals. */\n signal?: AbortSignal;\n /**\n * If true, throw on non-2xx responses instead of returning them. Default true.\n * Callers that need to read structured error bodies can set this to false.\n */\n throwOnHttpError?: boolean;\n}\n\nexport interface ResilientFetchResult {\n response: Response;\n /**\n * Composed AbortController tied to the fetch. Callers reading the body\n * (e.g. streaming) can call `.abort()` from this controller to cancel\n * in-flight reads. Already aborted on connect/total timeout.\n */\n controller: AbortController;\n}\n\nconst DEFAULTS = {\n connectTimeoutMs: 15_000,\n totalTimeoutMs: 120_000,\n} as const;\n\nfunction linkAbort(target: AbortController, signal: AbortSignal): () => void {\n if (signal.aborted) {\n target.abort(signal.reason);\n return () => {};\n }\n const onAbort = () => target.abort(signal.reason);\n signal.addEventListener(\"abort\", onAbort, { once: true });\n return () => signal.removeEventListener(\"abort\", onAbort);\n}\n\nexport async function resilientFetch(\n url: string,\n init: RequestInit = {},\n opts: ResilientFetchOptions = {},\n): Promise<ResilientFetchResult> {\n const connectTimeoutMs = opts.connectTimeoutMs ?? DEFAULTS.connectTimeoutMs;\n const totalTimeoutMs =\n opts.totalTimeoutMs === undefined\n ? DEFAULTS.totalTimeoutMs\n : opts.totalTimeoutMs;\n const throwOnHttpError = opts.throwOnHttpError ?? true;\n\n // NOTE: we deliberately do NOT hard-block on `navigator.onLine === false`\n // here. That flag is only a HINT — it is unreliable in both directions\n // (VPNs, virtual network adapters, browser network-service hiccups, sleep/\n // wake, and some corporate stacks leave it stuck at `false` while the\n // machine is fully online). Trusting it as a pre-flight gate produced a\n // real production outage: the Agent Builder (this path) threw OfflineError\n // on every submit while the Run path — which uses plain fetch() and never\n // consults the flag — worked fine on the same network. We now let the\n // fetch actually run and only classify the result as \"offline\" if a genuine\n // network failure ALSO corroborates the flag (see the catch below).\n\n const controller = new AbortController();\n const unlinkCaller = opts.signal\n ? linkAbort(controller, opts.signal)\n : () => {};\n\n let connectTimedOut = false;\n let totalTimedOut = false;\n\n const connectTimer = setTimeout(() => {\n connectTimedOut = true;\n controller.abort(\"connect-timeout\");\n }, connectTimeoutMs);\n\n const totalTimer =\n totalTimeoutMs !== null\n ? setTimeout(() => {\n totalTimedOut = true;\n controller.abort(\"total-timeout\");\n }, totalTimeoutMs)\n : null;\n\n try {\n const response = await fetch(url, { ...init, signal: controller.signal });\n clearTimeout(connectTimer);\n // Both timers end with the handshake: `totalTimeoutMs` bounds request\n // start → fetch() resolution (headers), exactly as documented on the\n // option. Body reads are guarded by the caller (stream monitor /\n // heartbeats), not by this timer — clearing it here is what lets a\n // long-streaming body outlive the handshake budget.\n if (totalTimer !== null) clearTimeout(totalTimer);\n\n if (throwOnHttpError && !response.ok) {\n throw new HttpError(\n response.status,\n `HTTP ${response.status} ${response.statusText}`,\n );\n }\n\n return { response, controller };\n } catch (err) {\n clearTimeout(connectTimer);\n if (totalTimer !== null) clearTimeout(totalTimer);\n\n if (connectTimedOut) throw new ConnectTimeoutError(connectTimeoutMs, err);\n if (totalTimedOut)\n throw new TotalTimeoutError(totalTimeoutMs as number, err);\n if (err instanceof DOMException && err.name === \"AbortError\") {\n throw new AbortedError(err);\n }\n if (opts.signal?.aborted) {\n // Caller aborted with a CUSTOM reason: fetch rejects with that reason\n // verbatim (not an AbortError DOMException). Without this check the\n // abort fell through to the generic classifiers and could surface as a\n // retryable NetworkError — an aborted request must never be retried.\n throw new AbortedError(err);\n }\n if (err instanceof TypeError) {\n // fetch() throws TypeError on DNS/connection failure. If the browser\n // ALSO reports offline, the flag is now corroborated by an actual\n // failure — surface the more specific OfflineError. Absent a real\n // failure, `navigator.onLine === false` is never allowed to block.\n if (typeof navigator !== \"undefined\" && navigator.onLine === false) {\n throw new OfflineError();\n }\n throw new NetworkError(err.message || \"Network request failed\", err);\n }\n if (err instanceof HttpError) throw err;\n throw new NetworkError(extractErrorMessage(err), err);\n } finally {\n unlinkCaller();\n }\n}\n","/**\n * Stream liveness monitor.\n *\n * Wraps an async iterable of server events and enforces two guarantees:\n * 1. Heartbeat deadline — fail if no event arrives within `heartbeatTimeoutMs`.\n * 2. Absolute ceiling — fail if total wall-clock time exceeds `maxLifetimeMs`.\n *\n * The wrapper is transparent: events pass through in order, with the same\n * shape. The only observable difference is that a stuck stream throws\n * `HeartbeatTimeoutError` or `TotalTimeoutError` instead of awaiting forever.\n *\n * The wrapped iterable should be driven by NDJSON parsing; the server MUST\n * emit `{kind:\"heartbeat\"}` at least every `heartbeatTimeoutMs / 2` ms for\n * long-running operations (the Matrx stream contract:\n * common-docs/systems/agents/execution-runtime/STREAM-CONTRACT.md).\n *\n * Ported verbatim from matrx-frontend `lib/net/stream-monitor.ts`, with one\n * defect fixed: cleanup of the underlying iterator after a watchdog timeout\n * is now fire-and-forget (see the note in the catch below).\n */\n\nimport { HeartbeatTimeoutError, TotalTimeoutError } from \"./errors\";\n\nexport interface StreamMonitorOptions {\n /** Max ms between any two events before the stream is considered dead. Default 30_000. */\n heartbeatTimeoutMs?: number;\n /** Max ms from wrap-start before throwing regardless of heartbeats. Default 600_000 (10min). */\n maxLifetimeMs?: number;\n /** Optional callback, fired every time a fresh event resets the deadline. */\n onHeartbeat?: (now: number) => void;\n /** Controller whose `.abort()` should be called on monitor-level timeout, so the underlying fetch is cancelled. */\n abortController?: AbortController;\n}\n\nconst DEFAULTS = {\n heartbeatTimeoutMs: 30_000,\n maxLifetimeMs: 600_000,\n} as const;\n\n/**\n * Wraps an async iterable so that it throws on stalled or overlong streams.\n *\n * Usage:\n * const live = monitorStream(events, { heartbeatTimeoutMs, abortController });\n * for await (const ev of live) { ... }\n */\nexport async function* monitorStream<T>(\n source: AsyncIterable<T>,\n opts: StreamMonitorOptions = {},\n): AsyncGenerator<T, void, undefined> {\n const heartbeatTimeoutMs =\n opts.heartbeatTimeoutMs ?? DEFAULTS.heartbeatTimeoutMs;\n const maxLifetimeMs = opts.maxLifetimeMs ?? DEFAULTS.maxLifetimeMs;\n const abort = opts.abortController;\n\n const startedAt = Date.now();\n const iter = source[Symbol.asyncIterator]() as AsyncIterator<T>;\n\n while (true) {\n const elapsed = Date.now() - startedAt;\n const remainingLifetime = maxLifetimeMs - elapsed;\n if (remainingLifetime <= 0) {\n abort?.abort(\"total-timeout\");\n throw new TotalTimeoutError(maxLifetimeMs);\n }\n\n const deadline = Math.min(heartbeatTimeoutMs, remainingLifetime);\n\n let timer: ReturnType<typeof setTimeout> | null = null;\n let timedOutKind: \"heartbeat\" | \"total\" | null = null;\n const timeoutPromise = new Promise<never>((_, reject) => {\n timer = setTimeout(() => {\n timedOutKind =\n deadline === remainingLifetime ? \"total\" : \"heartbeat\";\n abort?.abort(\n timedOutKind === \"total\" ? \"total-timeout\" : \"heartbeat-timeout\",\n );\n reject(\n timedOutKind === \"total\"\n ? new TotalTimeoutError(maxLifetimeMs)\n : new HeartbeatTimeoutError(heartbeatTimeoutMs),\n );\n }, deadline);\n });\n\n let result: IteratorResult<T>;\n try {\n result = await Promise.race([iter.next(), timeoutPromise]);\n } catch (err) {\n if (timer !== null) clearTimeout(timer);\n // Best-effort: let the underlying iterator clean up — WITHOUT awaiting\n // it. The original awaited `iter.return()`, but on an async generator\n // `return()` queues behind the still-pending `next()`; if the stream is\n // stuck and no abortController was provided to settle that read, the\n // await never resolves and the monitor hangs forever — the exact\n // failure mode this module exists to prevent. Fire-and-forget keeps\n // the timeout guarantee while still requesting cleanup.\n try {\n const cleanup = iter.return?.(undefined as never);\n if (cleanup) {\n void Promise.resolve(cleanup).catch(() => {\n /* ignore */\n });\n }\n } catch {\n /* ignore */\n }\n throw err;\n }\n\n if (timer !== null) clearTimeout(timer);\n if (result.done) return;\n\n opts.onHeartbeat?.(Date.now());\n yield result.value;\n }\n}\n"],"mappings":"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","names":["DEFAULTS"]}
1
+ {"version":3,"sources":["../src/net.ts","../src/net/errors.ts","../src/net/retry.ts","../src/net/resilient-fetch.ts","../src/net/stream-monitor.ts"],"sourcesContent":["/**\n * @ai-matrx/data/net — the net-resilience trio.\n *\n * Transport-adjacent and lane-free: `fetch` with connect/total timeouts and\n * composed abort, exponential-backoff retry for idempotent operations, the\n * stream stall watchdog, and the structured error vocabulary they all speak.\n * Ported behavior-verbatim from matrx-frontend `lib/net/` (the origin of the\n * trio); each module's header carries the incident that forged it.\n *\n * This tier depends on NOTHING — no Supabase, no org context, no API lane.\n * `fetch` and timers come from the environment; there are no injected seams\n * because the originals carried no app coupling beyond one pure message\n * helper, which now ships inside the package (`extractErrorMessage`).\n */\nexport {\n AbortedError,\n ConnectTimeoutError,\n extractErrorMessage,\n HeartbeatTimeoutError,\n HttpError,\n isNetError,\n isTransportFailure,\n NetError,\n NetworkError,\n OfflineError,\n toNetError,\n TotalTimeoutError,\n type NetErrorCode,\n} from \"./net/errors\";\nexport { withRetry, type RetryOptions } from \"./net/retry\";\nexport {\n resilientFetch,\n type ResilientFetchOptions,\n type ResilientFetchResult,\n} from \"./net/resilient-fetch\";\nexport { monitorStream, type StreamMonitorOptions } from \"./net/stream-monitor\";\n","/**\n * Structured errors for the resilience layer.\n *\n * Every network-layer failure resolves to one of these classes so callers\n * (and any recovery UI) can branch on error type, not on string matching.\n *\n * Ported verbatim from matrx-frontend `lib/net/errors.ts` — the origin of the\n * trio. The one coupling seam removed: `extractErrorMessage` (previously the\n * app's `utils/errors`) is pure logic and ships inside the package.\n */\n\nexport type NetErrorCode =\n | \"connect-timeout\"\n | \"total-timeout\"\n | \"heartbeat-timeout\"\n | \"network\"\n | \"http\"\n | \"aborted\"\n | \"offline\"\n | \"unknown\";\n\nexport class NetError extends Error {\n readonly code: NetErrorCode;\n override readonly cause?: unknown;\n readonly status?: number;\n readonly retryable: boolean;\n\n constructor(\n code: NetErrorCode,\n message: string,\n opts: { cause?: unknown; status?: number; retryable?: boolean } = {},\n ) {\n super(message);\n this.name = \"NetError\";\n this.code = code;\n this.cause = opts.cause;\n // Conditional only for `exactOptionalPropertyTypes` — same runtime shape.\n if (opts.status !== undefined) this.status = opts.status;\n this.retryable = opts.retryable ?? false;\n }\n}\n\nexport class ConnectTimeoutError extends NetError {\n constructor(ms: number, cause?: unknown) {\n super(\"connect-timeout\", `Connection timed out after ${ms}ms`, {\n cause,\n retryable: true,\n });\n this.name = \"ConnectTimeoutError\";\n }\n}\n\nexport class TotalTimeoutError extends NetError {\n constructor(ms: number, cause?: unknown) {\n super(\"total-timeout\", `Request exceeded ${ms}ms total budget`, {\n cause,\n retryable: false,\n });\n this.name = \"TotalTimeoutError\";\n }\n}\n\nexport class HeartbeatTimeoutError extends NetError {\n constructor(ms: number, cause?: unknown) {\n super(\n \"heartbeat-timeout\",\n `No server activity for ${ms}ms — stream considered dead`,\n { cause, retryable: false },\n );\n this.name = \"HeartbeatTimeoutError\";\n }\n}\n\nexport class NetworkError extends NetError {\n constructor(message: string, cause?: unknown) {\n super(\"network\", message, { cause, retryable: true });\n this.name = \"NetworkError\";\n }\n}\n\nexport class HttpError extends NetError {\n constructor(status: number, message: string, cause?: unknown) {\n // 408/429/5xx are generally safe to retry for idempotent ops.\n const retryable = status === 408 || status === 429 || status >= 500;\n super(\"http\", message, { cause, status, retryable });\n this.name = \"HttpError\";\n }\n}\n\nexport class AbortedError extends NetError {\n constructor(cause?: unknown) {\n super(\"aborted\", \"Request was aborted\", { cause, retryable: false });\n this.name = \"AbortedError\";\n }\n}\n\nexport class OfflineError extends NetError {\n constructor() {\n super(\"offline\", \"Browser is offline\", { retryable: true });\n this.name = \"OfflineError\";\n }\n}\n\nexport function isNetError(err: unknown): err is NetError {\n return err instanceof NetError;\n}\n\n/**\n * Folds any thrown value into a descriptive message string — THE one reader\n * for every failure shape this platform actually emits.\n *\n * 2026-09-07 (duplication census rows 9, 10, 21): this body had DRIFTED BEHIND\n * three host copies, which is the campaign's failure mode running in the\n * package's favour-direction. Each host had learned something the package\n * never did, so every non-frontend consumer of `@ai-matrx/data/net` was\n * flattening a server's own sentence into \"An unexpected error occurred\". All\n * three are folded in here, with a named regression test per shape:\n *\n * 1. **FastAPI's `detail`** (matrx-frontend `utils/errors.ts`, learned\n * 2026-08-31 as V2 finding G5). Our server refuses in `detail` — a string,\n * or an object carrying `message` — and every reader checked\n * `message`/`details` only. The server's sentence is the whole point of\n * catching this.\n * 2. **A `fallback` argument** (matrx-frontend `ShortcutForm.tsx`). A caller\n * who knows what it was doing can say \"Failed to save shortcut\" instead of\n * the generic sentence. Optional; the default is unchanged, so every\n * existing single-argument call behaves exactly as before.\n * 3. **Recursion into nested error objects** (aidream `workflow-studio`, two\n * byte-identical copies). The run pipeline nests: `{error: {type, message}}`\n * and `{error: {error: ...}}` and `{exception: {...}}` are all real shapes,\n * and a non-recursive reader answers \"An unexpected error occurred\" while\n * holding the actual reason two levels down. Depth-capped at 4 — a cyclic\n * or absurdly deep object must not hang a render.\n *\n * PostgREST-style errors are plain objects carrying\n * `message`/`details`/`hint`/`code`; a bare `String(err)` on those yields\n * \"[object Object]\", which is why this exists at all.\n */\nexport function extractErrorMessage(\n err: unknown,\n fallback = \"An unexpected error occurred\",\n): string {\n if (!err) return \"Unknown error\";\n if (typeof err === \"string\") return err;\n if (err instanceof Error) return err.message;\n if (typeof err === \"object\") {\n const e = err as Record<string, unknown>;\n const parts: string[] = [];\n if (typeof e.message === \"string\" && e.message) parts.push(e.message);\n // FastAPI's own word for the reason — only when nothing said `message`,\n // so a PostgREST error keeps its exact composed sentence.\n if (parts.length === 0) {\n if (typeof e.detail === \"string\" && e.detail) parts.push(e.detail);\n else if (e.detail && typeof e.detail === \"object\") {\n const d = e.detail as Record<string, unknown>;\n if (typeof d.message === \"string\" && d.message) parts.push(d.message);\n }\n }\n if (typeof e.details === \"string\" && e.details) parts.push(e.details);\n if (typeof e.hint === \"string\" && e.hint) parts.push(`Hint: ${e.hint}`);\n if (typeof e.code === \"string\" && e.code) parts.push(`Code: ${e.code}`);\n if (parts.length > 0) return parts.join(\" · \");\n\n const nested = extractNestedErrorMessage(e, 0);\n if (nested !== null) return nested;\n\n try {\n const s = JSON.stringify(err);\n if (s && s !== \"{}\") return s;\n } catch {\n /* ignore */\n }\n }\n return fallback;\n}\n\n/** Depth-capped walk into `error` / `exception` / `cause`. See above. */\nfunction extractNestedErrorMessage(\n e: Record<string, unknown>,\n depth: number,\n): string | null {\n if (depth > 4) return null;\n for (const key of [\"error\", \"exception\", \"cause\"] as const) {\n const value = e[key];\n if (typeof value === \"string\" && value) return value;\n if (value && typeof value === \"object\") {\n const inner = value as Record<string, unknown>;\n if (typeof inner.message === \"string\" && inner.message) return inner.message;\n if (typeof inner.detail === \"string\" && inner.detail) return inner.detail;\n const deeper = extractNestedErrorMessage(inner, depth + 1);\n if (deeper !== null) return deeper;\n }\n }\n return null;\n}\n\n/**\n * True when a failure is the TRANSPORT dying, not the server answering: the\n * browser is offline, asleep, mid-wifi-handoff, or DNS/TLS failed. It also\n * covers the bounded Envoy/Supabase gateway shape emitted when the upstream\n * database connection dies before response headers. `fetch`\n * rejects with a bare `TypeError: Failed to fetch` in every browser, and\n * supabase-js surfaces that verbatim (message only — no code, no hint).\n *\n * Use it to decide how LOUD a failure is, never whether to handle it. A\n * transport failure carries nothing an engineer can act on, so logging it at\n * error level buries the failures that do — that is how a notes surface filed\n * a system_error every time a laptop slept (2026-08-11). Anything with a\n * Postgres code, an HTTP status, or a server message is NOT this: keep those\n * loud.\n */\nexport function isTransportFailure(err: unknown): boolean {\n if (err instanceof NetworkError || err instanceof OfflineError) return true;\n if (typeof navigator !== \"undefined\" && navigator.onLine === false) return true;\n if (err instanceof TypeError) return true;\n if (err && typeof err === \"object\") {\n const e = err as { code?: unknown; status?: unknown; message?: unknown; name?: unknown };\n // A real PostgREST/Postgres failure always carries a code or status.\n if (e.code || e.status) return false;\n const text = `${typeof e.name === \"string\" ? e.name : \"\"} ${\n typeof e.message === \"string\" ? e.message : \"\"\n }`.toLowerCase();\n return (\n text.includes(\"failed to fetch\") ||\n text.includes(\"networkerror\") ||\n text.includes(\"network request failed\") ||\n text.includes(\"load failed\") || // Safari's wording for the same condition\n text.includes(\"upstream connect error or disconnect/reset before headers\")\n );\n }\n return false;\n}\n\nexport function toNetError(err: unknown): NetError {\n if (isNetError(err)) return err;\n if (err instanceof DOMException && err.name === \"AbortError\") {\n return new AbortedError(err);\n }\n if (err instanceof TypeError) {\n // fetch() throws TypeError on DNS/connection failure in every browser.\n return new NetworkError(err.message || \"Network request failed\", err);\n }\n return new NetError(\n \"unknown\",\n extractErrorMessage(err),\n { cause: err },\n );\n}\n","/**\n * withRetry — exponential backoff retry for idempotent operations.\n *\n * IMPORTANT: only wrap operations that are safe to repeat (GETs, cache\n * lookups, agent-definition fetches). Never wrap user-authored submissions —\n * recovery UX handles those via explicit user-initiated retry.\n *\n * Ported verbatim from matrx-frontend `lib/net/retry.ts`.\n */\n\nimport { isNetError, NetError } from \"./errors\";\n\nexport interface RetryOptions {\n /** Total attempts including the first. Default 3. */\n attempts?: number;\n /** Initial delay ms before the first retry. Default 250. */\n initialDelayMs?: number;\n /** Max delay ms cap between retries. Default 4_000. */\n maxDelayMs?: number;\n /** Multiplier applied after each failure. Default 2. */\n backoffFactor?: number;\n /** Jitter factor 0-1 applied to each delay. Default 0.25. */\n jitter?: number;\n /** Predicate deciding whether a given error should trigger a retry. Default: NetError.retryable. */\n retryOn?: (err: unknown) => boolean;\n /** Caller-provided signal. If aborted, no further retries are attempted. */\n signal?: AbortSignal;\n /** Fires before each retry; useful for logging/telemetry. */\n onRetry?: (attempt: number, err: unknown, delayMs: number) => void;\n}\n\nfunction defaultRetryOn(err: unknown): boolean {\n return isNetError(err) && (err as NetError).retryable;\n}\n\nfunction sleep(ms: number, signal?: AbortSignal): Promise<void> {\n return new Promise((resolve, reject) => {\n const timer = setTimeout(() => {\n resolve();\n if (signal) signal.removeEventListener(\"abort\", onAbort);\n }, ms);\n const onAbort = () => {\n clearTimeout(timer);\n reject(signal?.reason ?? new Error(\"Aborted\"));\n };\n if (signal) {\n if (signal.aborted) {\n clearTimeout(timer);\n reject(signal.reason);\n } else {\n signal.addEventListener(\"abort\", onAbort, { once: true });\n }\n }\n });\n}\n\nexport async function withRetry<T>(\n fn: (attempt: number) => Promise<T>,\n opts: RetryOptions = {},\n): Promise<T> {\n const attempts = Math.max(1, opts.attempts ?? 3);\n const initialDelayMs = opts.initialDelayMs ?? 250;\n const maxDelayMs = opts.maxDelayMs ?? 4_000;\n const factor = opts.backoffFactor ?? 2;\n const jitter = Math.max(0, Math.min(1, opts.jitter ?? 0.25));\n const retryOn = opts.retryOn ?? defaultRetryOn;\n\n let lastErr: unknown;\n for (let attempt = 1; attempt <= attempts; attempt++) {\n if (opts.signal?.aborted) throw opts.signal.reason;\n try {\n return await fn(attempt);\n } catch (err) {\n lastErr = err;\n if (attempt === attempts || !retryOn(err)) throw err;\n\n const base = Math.min(\n maxDelayMs,\n initialDelayMs * Math.pow(factor, attempt - 1),\n );\n const jitterAmount = base * jitter * (Math.random() * 2 - 1);\n const delayMs = Math.max(0, Math.round(base + jitterAmount));\n opts.onRetry?.(attempt, err, delayMs);\n await sleep(delayMs, opts.signal);\n }\n }\n\n // Unreachable — the loop always either returns or throws.\n throw lastErr;\n}\n","/**\n * resilientFetch — `fetch` with connect + total timeouts and composed abort.\n *\n * Every outbound HTTP call should go through this, not raw fetch. The\n * two-stage timeout (connect + total) lets callers allow long-streaming\n * responses without giving up the ability to fail fast when the server is\n * simply unreachable.\n *\n * Ported verbatim from matrx-frontend `lib/net/resilient-fetch.ts`. `fetch`\n * comes from the environment (Node 20+, browsers, React Native/Hermes) —\n * there is deliberately no injected transport here.\n */\n\nimport {\n AbortedError,\n ConnectTimeoutError,\n extractErrorMessage,\n HttpError,\n NetworkError,\n OfflineError,\n TotalTimeoutError,\n} from \"./errors\";\n\nexport interface ResilientFetchOptions {\n /** Max ms from request start → response headers received. Default 15_000. */\n connectTimeoutMs?: number;\n /**\n * Max ms from request start → fetch() promise resolves.\n * For streaming endpoints the response arrives quickly and the stream body\n * is read by the caller, so this bound only guards the handshake itself.\n * `null` disables the ceiling. Default 120_000.\n */\n totalTimeoutMs?: number | null;\n /** Caller-provided signal; composed with our internal timeout signals. */\n signal?: AbortSignal;\n /**\n * If true, throw on non-2xx responses instead of returning them. Default true.\n * Callers that need to read structured error bodies can set this to false.\n */\n throwOnHttpError?: boolean;\n}\n\nexport interface ResilientFetchResult {\n response: Response;\n /**\n * Composed AbortController tied to the fetch. Callers reading the body\n * (e.g. streaming) can call `.abort()` from this controller to cancel\n * in-flight reads. Already aborted on connect/total timeout.\n */\n controller: AbortController;\n}\n\nconst DEFAULTS = {\n connectTimeoutMs: 15_000,\n totalTimeoutMs: 120_000,\n} as const;\n\nfunction linkAbort(target: AbortController, signal: AbortSignal): () => void {\n if (signal.aborted) {\n target.abort(signal.reason);\n return () => {};\n }\n const onAbort = () => target.abort(signal.reason);\n signal.addEventListener(\"abort\", onAbort, { once: true });\n return () => signal.removeEventListener(\"abort\", onAbort);\n}\n\nexport async function resilientFetch(\n url: string,\n init: RequestInit = {},\n opts: ResilientFetchOptions = {},\n): Promise<ResilientFetchResult> {\n const connectTimeoutMs = opts.connectTimeoutMs ?? DEFAULTS.connectTimeoutMs;\n const totalTimeoutMs =\n opts.totalTimeoutMs === undefined\n ? DEFAULTS.totalTimeoutMs\n : opts.totalTimeoutMs;\n const throwOnHttpError = opts.throwOnHttpError ?? true;\n\n // NOTE: we deliberately do NOT hard-block on `navigator.onLine === false`\n // here. That flag is only a HINT — it is unreliable in both directions\n // (VPNs, virtual network adapters, browser network-service hiccups, sleep/\n // wake, and some corporate stacks leave it stuck at `false` while the\n // machine is fully online). Trusting it as a pre-flight gate produced a\n // real production outage: the Agent Builder (this path) threw OfflineError\n // on every submit while the Run path — which uses plain fetch() and never\n // consults the flag — worked fine on the same network. We now let the\n // fetch actually run and only classify the result as \"offline\" if a genuine\n // network failure ALSO corroborates the flag (see the catch below).\n\n const controller = new AbortController();\n const unlinkCaller = opts.signal\n ? linkAbort(controller, opts.signal)\n : () => {};\n\n let connectTimedOut = false;\n let totalTimedOut = false;\n\n const connectTimer = setTimeout(() => {\n connectTimedOut = true;\n controller.abort(\"connect-timeout\");\n }, connectTimeoutMs);\n\n const totalTimer =\n totalTimeoutMs !== null\n ? setTimeout(() => {\n totalTimedOut = true;\n controller.abort(\"total-timeout\");\n }, totalTimeoutMs)\n : null;\n\n try {\n const response = await fetch(url, { ...init, signal: controller.signal });\n clearTimeout(connectTimer);\n // Both timers end with the handshake: `totalTimeoutMs` bounds request\n // start → fetch() resolution (headers), exactly as documented on the\n // option. Body reads are guarded by the caller (stream monitor /\n // heartbeats), not by this timer — clearing it here is what lets a\n // long-streaming body outlive the handshake budget.\n if (totalTimer !== null) clearTimeout(totalTimer);\n\n if (throwOnHttpError && !response.ok) {\n throw new HttpError(\n response.status,\n `HTTP ${response.status} ${response.statusText}`,\n );\n }\n\n return { response, controller };\n } catch (err) {\n clearTimeout(connectTimer);\n if (totalTimer !== null) clearTimeout(totalTimer);\n\n if (connectTimedOut) throw new ConnectTimeoutError(connectTimeoutMs, err);\n if (totalTimedOut)\n throw new TotalTimeoutError(totalTimeoutMs as number, err);\n if (err instanceof DOMException && err.name === \"AbortError\") {\n throw new AbortedError(err);\n }\n if (opts.signal?.aborted) {\n // Caller aborted with a CUSTOM reason: fetch rejects with that reason\n // verbatim (not an AbortError DOMException). Without this check the\n // abort fell through to the generic classifiers and could surface as a\n // retryable NetworkError — an aborted request must never be retried.\n throw new AbortedError(err);\n }\n if (err instanceof TypeError) {\n // fetch() throws TypeError on DNS/connection failure. If the browser\n // ALSO reports offline, the flag is now corroborated by an actual\n // failure — surface the more specific OfflineError. Absent a real\n // failure, `navigator.onLine === false` is never allowed to block.\n if (typeof navigator !== \"undefined\" && navigator.onLine === false) {\n throw new OfflineError();\n }\n throw new NetworkError(err.message || \"Network request failed\", err);\n }\n if (err instanceof HttpError) throw err;\n throw new NetworkError(extractErrorMessage(err), err);\n } finally {\n unlinkCaller();\n }\n}\n","/**\n * Stream liveness monitor.\n *\n * Wraps an async iterable of server events and enforces two guarantees:\n * 1. Heartbeat deadline — fail if no event arrives within `heartbeatTimeoutMs`.\n * 2. Absolute ceiling — fail if total wall-clock time exceeds `maxLifetimeMs`.\n *\n * The wrapper is transparent: events pass through in order, with the same\n * shape. The only observable difference is that a stuck stream throws\n * `HeartbeatTimeoutError` or `TotalTimeoutError` instead of awaiting forever.\n *\n * The wrapped iterable should be driven by NDJSON parsing; the server MUST\n * emit `{kind:\"heartbeat\"}` at least every `heartbeatTimeoutMs / 2` ms for\n * long-running operations (the Matrx stream contract:\n * common-docs/systems/architecture/execution-runtime/STREAM-CONTRACT.md).\n *\n * Ported verbatim from matrx-frontend `lib/net/stream-monitor.ts`, with one\n * defect fixed: cleanup of the underlying iterator after a watchdog timeout\n * is now fire-and-forget (see the note in the catch below).\n */\n\nimport { HeartbeatTimeoutError, TotalTimeoutError } from \"./errors\";\n\nexport interface StreamMonitorOptions {\n /** Max ms between any two events before the stream is considered dead. Default 30_000. */\n heartbeatTimeoutMs?: number;\n /** Max ms from wrap-start before throwing regardless of heartbeats. Default 600_000 (10min). */\n maxLifetimeMs?: number;\n /** Optional callback, fired every time a fresh event resets the deadline. */\n onHeartbeat?: (now: number) => void;\n /** Controller whose `.abort()` should be called on monitor-level timeout, so the underlying fetch is cancelled. */\n abortController?: AbortController;\n}\n\nconst DEFAULTS = {\n heartbeatTimeoutMs: 30_000,\n maxLifetimeMs: 600_000,\n} as const;\n\n/**\n * Wraps an async iterable so that it throws on stalled or overlong streams.\n *\n * Usage:\n * const live = monitorStream(events, { heartbeatTimeoutMs, abortController });\n * for await (const ev of live) { ... }\n */\nexport async function* monitorStream<T>(\n source: AsyncIterable<T>,\n opts: StreamMonitorOptions = {},\n): AsyncGenerator<T, void, undefined> {\n const heartbeatTimeoutMs =\n opts.heartbeatTimeoutMs ?? DEFAULTS.heartbeatTimeoutMs;\n const maxLifetimeMs = opts.maxLifetimeMs ?? DEFAULTS.maxLifetimeMs;\n const abort = opts.abortController;\n\n const startedAt = Date.now();\n const iter = source[Symbol.asyncIterator]() as AsyncIterator<T>;\n\n while (true) {\n const elapsed = Date.now() - startedAt;\n const remainingLifetime = maxLifetimeMs - elapsed;\n if (remainingLifetime <= 0) {\n abort?.abort(\"total-timeout\");\n throw new TotalTimeoutError(maxLifetimeMs);\n }\n\n const deadline = Math.min(heartbeatTimeoutMs, remainingLifetime);\n\n let timer: ReturnType<typeof setTimeout> | null = null;\n let timedOutKind: \"heartbeat\" | \"total\" | null = null;\n const timeoutPromise = new Promise<never>((_, reject) => {\n timer = setTimeout(() => {\n timedOutKind =\n deadline === remainingLifetime ? \"total\" : \"heartbeat\";\n abort?.abort(\n timedOutKind === \"total\" ? \"total-timeout\" : \"heartbeat-timeout\",\n );\n reject(\n timedOutKind === \"total\"\n ? new TotalTimeoutError(maxLifetimeMs)\n : new HeartbeatTimeoutError(heartbeatTimeoutMs),\n );\n }, deadline);\n });\n\n let result: IteratorResult<T>;\n try {\n result = await Promise.race([iter.next(), timeoutPromise]);\n } catch (err) {\n if (timer !== null) clearTimeout(timer);\n // Best-effort: let the underlying iterator clean up — WITHOUT awaiting\n // it. The original awaited `iter.return()`, but on an async generator\n // `return()` queues behind the still-pending `next()`; if the stream is\n // stuck and no abortController was provided to settle that read, the\n // await never resolves and the monitor hangs forever — the exact\n // failure mode this module exists to prevent. Fire-and-forget keeps\n // the timeout guarantee while still requesting cleanup.\n try {\n const cleanup = iter.return?.(undefined as never);\n if (cleanup) {\n void Promise.resolve(cleanup).catch(() => {\n /* ignore */\n });\n }\n } catch {\n /* ignore */\n }\n throw err;\n }\n\n if (timer !== null) clearTimeout(timer);\n if (result.done) return;\n\n opts.onHeartbeat?.(Date.now());\n yield result.value;\n }\n}\n"],"mappings":"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","names":["DEFAULTS"]}
package/dist/net.d.cts CHANGED
@@ -176,7 +176,7 @@ declare function resilientFetch(url: string, init?: RequestInit, opts?: Resilien
176
176
  * The wrapped iterable should be driven by NDJSON parsing; the server MUST
177
177
  * emit `{kind:"heartbeat"}` at least every `heartbeatTimeoutMs / 2` ms for
178
178
  * long-running operations (the Matrx stream contract:
179
- * common-docs/systems/agents/execution-runtime/STREAM-CONTRACT.md).
179
+ * common-docs/systems/architecture/execution-runtime/STREAM-CONTRACT.md).
180
180
  *
181
181
  * Ported verbatim from matrx-frontend `lib/net/stream-monitor.ts`, with one
182
182
  * defect fixed: cleanup of the underlying iterator after a watchdog timeout
package/dist/net.d.ts CHANGED
@@ -176,7 +176,7 @@ declare function resilientFetch(url: string, init?: RequestInit, opts?: Resilien
176
176
  * The wrapped iterable should be driven by NDJSON parsing; the server MUST
177
177
  * emit `{kind:"heartbeat"}` at least every `heartbeatTimeoutMs / 2` ms for
178
178
  * long-running operations (the Matrx stream contract:
179
- * common-docs/systems/agents/execution-runtime/STREAM-CONTRACT.md).
179
+ * common-docs/systems/architecture/execution-runtime/STREAM-CONTRACT.md).
180
180
  *
181
181
  * Ported verbatim from matrx-frontend `lib/net/stream-monitor.ts`, with one
182
182
  * defect fixed: cleanup of the underlying iterator after a watchdog timeout