/** * [ref] A9 seam — response senders, extracted VERBATIM out of `http/server.ts` so the per-domain route * modules under `http/routes/` can call them without a value edge back into `server.ts` (that edge would close * a runtime import cycle — see `test/module-cycle-gate.test.ts`). * * Pure move: bodies are byte-identical to the ones that lived in the pre-A9 `server.ts` (that ~9.7k-line file * no longer exists, so quoting its line numbers here would point at nothing). `server.ts` * now imports them from here, so all ~500 existing `sendJson(...)` call sites are unchanged text. */ import type { ServerResponse } from "node:http"; /** * [ref](黑板 [ref]②)—— 一条响应「是 SSE 流」的**在场标记**。 * * 引擎附着租约把**打开着的 SSE 流**当作壳的附着租约来数,于是 `handle()` 需要判「这条响应是不是流」。 * 🔴 **不能靠读回 content-type**(本机 node v24.2.0 实测):`res.writeHead(status, headersObject)` 直接 * 把头写进网络缓冲,`res.getHeader("content-type")` / `res.hasHeader(...)` 在那之后**仍然**回 * `undefined` / `false`。照那条路写会得到一个恒为 0 的附着计数 —— 而恒 0 的方向恰好是「以为没人附着」 * ⇒ 在一只有人用的引擎底下触发自退。所以判据必须是我们自己在开流那一刻盖的这个章。 * * symbol 键:不上 wire、不进 `JSON.stringify`、不与任何应用属性撞名。 */ export declare const SSE_RESPONSE_MARK: unique symbol; /** 这条响应是否已开成 SSE 流(= 走过 {@link sseHeaders})。 */ export declare function isSseResponse(res: ServerResponse): boolean; export declare function sseHeaders(res: ServerResponse, extra?: Record): void; /** * 一条 SSE 连接的墙钟上限。到点发一帧 `error`/`STREAM_MAX_DURATION`(**不是**静默关闭 —— 客户端分不出 * 「到帽了请重连」和「跑完了」)然后收流。 * * 🔴 为什么共享(2026-08-08,[ref] 二轮扫描 finding):这个数与下面的心跳间隔此前在**三处**各手抄一份 * —— `http/sse-log.ts`(task_run + image_bake 共用的那台泵)、`http/routes/trace-usage.ts`、 * `http/routes/approvals-assistant.ts`(它的顶注还自称「parity with streamTaskTrace」)。三处各自有测试 * 钉着**自己那一份**,没有任何跨副本的一致性钉 ⇒ 单边调帽后「parity」只剩注释里那句话。 * * ⚠️ 改这个数请连**文案**一起改:三处的 `STREAM_MAX_DURATION` 消息里把「15-minute」写死在散文里 * (文案面另有 freeze 门,故此处不做插值——散文与常量的同步靠这行告诫,不靠机器)。 */ export declare const SSE_MAX_STREAM_MS: number; /** * 长时间无事件时的心跳间隔(防中继的 idle timeout 掐流)。与 {@link SSE_MAX_STREAM_MS} 同批收口。 * * ⚠️ 收口的只有**间隔**,**不含帧形**:`sse-log` / `trace-usage` 发 `data: {}`,`approvals-assistant` * 发 `data: {"type":"heartbeat"}`。这是真分歧不是笔误 —— trace-usage 自己的 meta 帧还引着 [ref]C-12 * 「每个 data payload 都带 type」的约定,而它的心跳帧没有 type(probe-sse-1 的注释把这条记成「按 * data.type 分派的客户端会拿到 undefined」)。统一帧形是**行为面**改动(要么给两处加 type=多发一个键, * 要么给 approvals 减 type=消费端破坏),属候裁,不在本批的零行为码范围内。 */ export declare const SSE_HEARTBEAT_IDLE_MS = 15000; export declare function sendJson(res: ServerResponse, status: number, body: unknown): void; /** A6([ref] lens1 §C3/lens3 §C3a): canonical error-response sender for sites that already carry a * machine `errorCode` — `errorCode` is a required positional arg (not optional-bag) so a call site * cannot regress to the "code but no errorCode" shape the error-code-key-gate forbids. Body shape is * `{ error: message, errorCode, ...extra }`; a site that used to ALSO emit a legacy `code` key keeps it * by passing `{ code: ... }` inside `extra` — this is a pure call-site migration (zero wire-contract * change), not a new response shape. Sites where the two machine-code channels genuinely disagree, or * where `error`/`errorCode` aren't cleanly separable from the rest of the body, are left on raw * `sendJson` (see the A6 migration notes for the callers still doing that on purpose). */ export declare function sendError(res: ServerResponse, status: number, errorCode: string, message: string, extra?: Record): void; /** B7([ref] lens1 §C3):`HttpError` 的兜底 catch-all 站点没有自己的语义分支——`err.code` 在 42 个 * throw 点里只有 3 个带。为了让「errorCode 恒在」在这些站点也成立(而不是靠豁免表放行),按**状态码** * 给一个诚实的粗码:它只声明「这类拒绝」,不冒充比 throw 点更细的语义。带 `code` 的 throw 点原样优先。 */ export declare function httpErrorCode(status: number, code?: string): string; /** * 🔴 B1 / S-535 —— `SessionPolicyError` → 应答,经 {@link sessionPolicyErrorHttp} 那**一只**映射 * (`src/session-policy-wire.ts`)。回 `true` = 已应答,调用点 `return`。 * * 为什么是一只共享腿而不是每个站点一句 `instanceof`:映射本身早就收成一处了,而**咨询它的站点**只有两条 * **写**腿 —— 偏偏 core 7.25.0 那个新词 `corrupt` 是在**读**上抛出来的(店读不出它自己写的那一行: * S-529 的两道读回拒 + C1 的体形拒)。于是 `GET /v1/sessions/:id/policy` 与 2c 的 manifest 导出腿把它交给了 * 域外通用臂 ⇒ `500 internal.error "internal error"`:码丢,连带**修复路径**也丢(同一行 `PUT` 却答 * `500 corrupt` 并点名那一列)。一只腿之后,新站点只会忘记调它,不会再各自发明一份映射。 */ export declare function sendSessionPolicyError(res: ServerResponse, e: unknown): boolean; export declare function msg(e: unknown): string; //# sourceMappingURL=send.d.ts.map