{"version":3,"file":"aws-event-stream.cjs","names":["calculateDelay","delay"],"sources":["../src/aws-event-stream.ts"],"sourcesContent":["/**\n * AWS Event Stream binary frame encoder.\n *\n * Implements the AWS binary event stream framing protocol used by Bedrock's\n * streaming (invoke-with-response-stream) endpoint. Each frame carries a set of\n * string headers and a raw-bytes payload, wrapped in a prelude with CRC32\n * checksums for integrity.\n *\n * Binary frame layout:\n *   [total_length: 4B uint32-BE]\n *   [headers_length: 4B uint32-BE]\n *   [prelude_crc32: 4B CRC32 of first 8 bytes]\n *   [headers: variable]\n *   [payload: variable, raw JSON bytes]\n *   [message_crc32: 4B CRC32 of entire frame minus last 4 bytes]\n */\n\nimport { crc32 } from \"node:zlib\";\nimport type * as http from \"node:http\";\nimport type { StreamingProfile, RecordedTimings } from \"./types.js\";\nimport { delay, calculateDelay } from \"./sse-writer.js\";\n\n// ─── Header encoding ────────────────────────────────────────────────────────\n\nfunction encodeHeaders(headers: Record<string, string>): Buffer {\n  const parts: Buffer[] = [];\n  for (const [name, value] of Object.entries(headers)) {\n    const nameBytes = Buffer.from(name, \"utf8\");\n    const valueBytes = Buffer.from(value, \"utf8\");\n\n    // name_length (1 byte) + name + type (1 byte, 7 = STRING) +\n    // value_length (2 bytes BE) + value\n    const header = Buffer.alloc(1 + nameBytes.length + 1 + 2 + valueBytes.length);\n    let offset = 0;\n    header.writeUInt8(nameBytes.length, offset);\n    offset += 1;\n    nameBytes.copy(header, offset);\n    offset += nameBytes.length;\n    header.writeUInt8(7, offset); // STRING type\n    offset += 1;\n    header.writeUInt16BE(valueBytes.length, offset);\n    offset += 2;\n    valueBytes.copy(header, offset);\n\n    parts.push(header);\n  }\n  return Buffer.concat(parts);\n}\n\n// ─── Frame encoding ─────────────────────────────────────────────────────────\n\n/**\n * Encode a single AWS Event Stream binary frame with the given headers and\n * payload buffer.\n */\nexport function encodeEventStreamFrame(headers: Record<string, string>, payload: Buffer): Buffer {\n  const headersBuffer = encodeHeaders(headers);\n  const headersLength = headersBuffer.length;\n\n  // prelude (8) + prelude_crc (4) + headers + payload + message_crc (4)\n  const totalLength = 4 + 4 + 4 + headersLength + payload.length + 4;\n\n  const frame = Buffer.alloc(totalLength);\n  let offset = 0;\n\n  // Prelude\n  frame.writeUInt32BE(totalLength, offset);\n  offset += 4;\n  frame.writeUInt32BE(headersLength, offset);\n  offset += 4;\n\n  // Prelude CRC32 (covers first 8 bytes)\n  const preludeCrc = crc32(frame.subarray(0, 8));\n  frame.writeUInt32BE(preludeCrc >>> 0, offset);\n  offset += 4;\n\n  // Headers\n  headersBuffer.copy(frame, offset);\n  offset += headersLength;\n\n  // Payload\n  payload.copy(frame, offset);\n  offset += payload.length;\n\n  // Message CRC32 (covers entire frame minus last 4 bytes)\n  const messageCrc = crc32(frame.subarray(0, totalLength - 4));\n  frame.writeUInt32BE(messageCrc >>> 0, offset);\n\n  return frame;\n}\n\n// ─── Convenience wrappers ───────────────────────────────────────────────────\n\n/**\n * Encode an event-stream message with standard AWS headers for a JSON event.\n *\n * Sets `:content-type` = `application/json`, `:event-type` = eventType,\n * `:message-type` = `event`.\n */\nexport function encodeEventStreamMessage(eventType: string, jsonPayload: object): Buffer {\n  const headers: Record<string, string> = {\n    \":content-type\": \"application/json\",\n    \":event-type\": eventType,\n    \":message-type\": \"event\",\n  };\n  const payload = Buffer.from(JSON.stringify(jsonPayload), \"utf8\");\n  return encodeEventStreamFrame(headers, payload);\n}\n\n/**\n * Write a sequence of event-stream frames to an HTTP response with optional\n * timing control. Mirrors the writeSSEStream pattern from sse-writer.ts.\n *\n * Returns `true` when all events are written (including when the response\n * was already ended before writing began), or `false` if interrupted by\n * the provided abort signal.\n */\nexport async function writeEventStream(\n  res: http.ServerResponse,\n  events: Array<{ eventType: string; payload: object }>,\n  options?: {\n    latency?: number;\n    streamingProfile?: StreamingProfile;\n    recordedTimings?: RecordedTimings;\n    replaySpeed?: number;\n    signal?: AbortSignal;\n    onChunkSent?: () => void;\n  },\n): Promise<boolean> {\n  const opts = options ?? {};\n  const latency = opts.latency ?? 0;\n  const profile = opts.streamingProfile;\n  const { recordedTimings, replaySpeed } = opts;\n  const signal = opts.signal;\n  const onChunkSent = opts.onChunkSent;\n\n  if (res.writableEnded) return true;\n  res.setHeader(\"Content-Type\", \"application/vnd.amazon.eventstream\");\n  res.setHeader(\"Transfer-Encoding\", \"chunked\");\n\n  let chunkIndex = 0;\n  for (const event of events) {\n    const chunkDelay = calculateDelay(chunkIndex, profile, latency, recordedTimings, replaySpeed);\n    if (chunkDelay > 0) {\n      await delay(chunkDelay, signal);\n    }\n    if (signal?.aborted) return false;\n    if (res.writableEnded) return true;\n\n    const frame = encodeEventStreamMessage(event.eventType, event.payload);\n    res.write(frame);\n    onChunkSent?.();\n    if (signal?.aborted) return false;\n    chunkIndex++;\n  }\n\n  if (!res.writableEnded) {\n    res.end();\n  }\n  return true;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAwBA,SAAS,cAAc,SAAyC;CAC9D,MAAM,QAAkB,EAAE;AAC1B,MAAK,MAAM,CAAC,MAAM,UAAU,OAAO,QAAQ,QAAQ,EAAE;EACnD,MAAM,YAAY,OAAO,KAAK,MAAM,OAAO;EAC3C,MAAM,aAAa,OAAO,KAAK,OAAO,OAAO;EAI7C,MAAM,SAAS,OAAO,MAAM,IAAI,UAAU,SAAS,IAAI,IAAI,WAAW,OAAO;EAC7E,IAAI,SAAS;AACb,SAAO,WAAW,UAAU,QAAQ,OAAO;AAC3C,YAAU;AACV,YAAU,KAAK,QAAQ,OAAO;AAC9B,YAAU,UAAU;AACpB,SAAO,WAAW,GAAG,OAAO;AAC5B,YAAU;AACV,SAAO,cAAc,WAAW,QAAQ,OAAO;AAC/C,YAAU;AACV,aAAW,KAAK,QAAQ,OAAO;AAE/B,QAAM,KAAK,OAAO;;AAEpB,QAAO,OAAO,OAAO,MAAM;;;;;;AAS7B,SAAgB,uBAAuB,SAAiC,SAAyB;CAC/F,MAAM,gBAAgB,cAAc,QAAQ;CAC5C,MAAM,gBAAgB,cAAc;CAGpC,MAAM,cAAc,KAAY,gBAAgB,QAAQ,SAAS;CAEjE,MAAM,QAAQ,OAAO,MAAM,YAAY;CACvC,IAAI,SAAS;AAGb,OAAM,cAAc,aAAa,OAAO;AACxC,WAAU;AACV,OAAM,cAAc,eAAe,OAAO;AAC1C,WAAU;CAGV,MAAM,kCAAmB,MAAM,SAAS,GAAG,EAAE,CAAC;AAC9C,OAAM,cAAc,eAAe,GAAG,OAAO;AAC7C,WAAU;AAGV,eAAc,KAAK,OAAO,OAAO;AACjC,WAAU;AAGV,SAAQ,KAAK,OAAO,OAAO;AAC3B,WAAU,QAAQ;CAGlB,MAAM,kCAAmB,MAAM,SAAS,GAAG,cAAc,EAAE,CAAC;AAC5D,OAAM,cAAc,eAAe,GAAG,OAAO;AAE7C,QAAO;;;;;;;;AAWT,SAAgB,yBAAyB,WAAmB,aAA6B;AAOvF,QAAO,uBANiC;EACtC,iBAAiB;EACjB,eAAe;EACf,iBAAiB;EAClB,EACe,OAAO,KAAK,KAAK,UAAU,YAAY,EAAE,OAAO,CACjB;;;;;;;;;;AAWjD,eAAsB,iBACpB,KACA,QACA,SAQkB;CAClB,MAAM,OAAO,WAAW,EAAE;CAC1B,MAAM,UAAU,KAAK,WAAW;CAChC,MAAM,UAAU,KAAK;CACrB,MAAM,EAAE,iBAAiB,gBAAgB;CACzC,MAAM,SAAS,KAAK;CACpB,MAAM,cAAc,KAAK;AAEzB,KAAI,IAAI,cAAe,QAAO;AAC9B,KAAI,UAAU,gBAAgB,qCAAqC;AACnE,KAAI,UAAU,qBAAqB,UAAU;CAE7C,IAAI,aAAa;AACjB,MAAK,MAAM,SAAS,QAAQ;EAC1B,MAAM,aAAaA,kCAAe,YAAY,SAAS,SAAS,iBAAiB,YAAY;AAC7F,MAAI,aAAa,EACf,OAAMC,yBAAM,YAAY,OAAO;AAEjC,MAAI,QAAQ,QAAS,QAAO;AAC5B,MAAI,IAAI,cAAe,QAAO;EAE9B,MAAM,QAAQ,yBAAyB,MAAM,WAAW,MAAM,QAAQ;AACtE,MAAI,MAAM,MAAM;AAChB,iBAAe;AACf,MAAI,QAAQ,QAAS,QAAO;AAC5B;;AAGF,KAAI,CAAC,IAAI,cACP,KAAI,KAAK;AAEX,QAAO"}