{"version":3,"file":"index.cjs","names":["buildActionRouteDictionary","encodeReliabilitySlot","PayloadTypeToInt","extractWirePayload","ReversePayloadType","assembleWireJson","decodeReliabilitySlot"],"sources":["../../src/ActionRuntime/Transport/codec/createBinaryWireAdapter.ts"],"sourcesContent":["import { pack, unpack } from \"msgpackr\";\nimport type { ActionDomain } from \"../../../ActionDefinition/Domain/ActionDomain\";\nimport type { ITransportRouteActionParams } from \"../Transport.types\";\nimport {\n  assembleWireJson,\n  buildActionRouteDictionary,\n  decodeReliabilitySlot,\n  encodeReliabilitySlot,\n  extractWirePayload,\n  type IActionWireFormat,\n  type IFrameReliabilityWire,\n  PayloadTypeToInt,\n  ReversePayloadType,\n} from \"./actionWireCodec\";\n\n/**\n * Positional layout of the stateless binary envelope. A flat tuple (rather than an object) strips the\n * repeated `domain`/`id`/`form`/`type` and context key names from every frame, and we carry only the\n * context fields the receiver can't recompute: `cuid` (correlation) and `originClient` (return\n * routing).\n *\n *   [ routeInt, typeInt, time, cuid, originClient, payloadData ]\n *\n * Dropped vs the JSON wire: `form`/`type` strings, `inputHash`/`outputHash` (recomputed on hydrate),\n * `context.timeCreated` (reconstructed from `time`) and `context.routing` (rebuilt empty — the\n * receiver re-stamps its own route items as it handles the action). For the leanest possible frames\n * (integer correlation, identity dropped after a handshake), use `createBinaryWireSessionFactory`.\n */\nconst ENVELOPE = {\n  route: 0,\n  type: 1,\n  time: 2,\n  cuid: 3,\n  originClient: 4,\n  payload: 5,\n  /** Optional trailing slot — present only on a reliable frame (see {@link encodeReliabilitySlot}). */\n  reliability: 6,\n  /** Optional streamKey slot (E3) — present only on a *keyed* reliable frame; makes the frame length 8. */\n  streamKey: 7,\n} as const;\n/**\n * Best-effort frame length. A reliable frame appends the seq/ack slot → {@link ENVELOPE_LENGTH_RELIABLE};\n * a keyed reliable frame appends the streamKey too → {@link ENVELOPE_LENGTH_RELIABLE_KEYED}. Distinct\n * lengths self-discriminate, so keyless reliable frames stay byte-identical and an old peer drops a keyed one.\n */\nconst ENVELOPE_LENGTH = 6;\nconst ENVELOPE_LENGTH_RELIABLE = 7;\nconst ENVELOPE_LENGTH_RELIABLE_KEYED = 8;\n\n/**\n * Builds a *stateless* `formatMessage` pipeline for {@link LinkTransport}, packing action\n * payloads into a compact msgpackr binary frame instead of JSON. The `domain`/`id` route collapses to\n * a single integer drawn from a shared dictionary; `form`/`type`, the recomputable\n * `inputHash`/`outputHash`, and the per-frame `context.routing`/`context.timeCreated` are all dropped\n * (see {@link ENVELOPE}).\n *\n * No validation runs here: `incoming` blindly reconstructs the wire JSON shape and hands it back to\n * the connection. `ActionRuntime` hydrates the request and validates its input at the universal execution\n * boundary, before any local handler sees it, exactly as it does for a JSON frame.\n *\n * Both ends of the socket MUST construct the adapter with the same domains in the same order — the\n * integer dictionary is positional. Mismatched dictionaries will route to the wrong action.\n *\n * Because `incoming` returns `undefined` for text frames, a binary server can still serve plain-JSON\n * clients on the same runtime (the connection falls back to its built-in JSON parser).\n */\nexport function createBinaryWireAdapter(domains: ActionDomain<any>[]): IActionWireFormat {\n  const { routeToInt, intToRoute } = buildActionRouteDictionary(domains);\n\n  return {\n    outgoing: (input: ITransportRouteActionParams): Uint8Array => {\n      const json = input.action.toJsonObject();\n      const routeKey = `${json.domain}:${json.id}`;\n      const routeInt = routeToInt.get(routeKey);\n\n      if (routeInt == null) {\n        throw new Error(`[binary-wire] Cannot pack unregistered action route: ${routeKey}`);\n      }\n\n      // A reliable frame appends the seq/ack slot; a keyed reliable frame appends the streamKey too; a\n      // best-effort frame stays at ENVELOPE_LENGTH, so its packed bytes are unchanged.\n      const reliabilitySlot = encodeReliabilitySlot(input.reliability, json.type);\n      const streamKey = input.reliability?.streamKey;\n      const length =\n        streamKey != null\n          ? ENVELOPE_LENGTH_RELIABLE_KEYED\n          : reliabilitySlot == null\n            ? ENVELOPE_LENGTH\n            : ENVELOPE_LENGTH_RELIABLE;\n      const envelope = new Array(length);\n      envelope[ENVELOPE.route] = routeInt;\n      envelope[ENVELOPE.type] = PayloadTypeToInt[json.type];\n      envelope[ENVELOPE.time] = json.time;\n      envelope[ENVELOPE.cuid] = json.context.cuid;\n      envelope[ENVELOPE.originClient] = json.context.originClient;\n      envelope[ENVELOPE.payload] = extractWirePayload(json);\n      if (reliabilitySlot != null) envelope[ENVELOPE.reliability] = reliabilitySlot;\n      if (streamKey != null) envelope[ENVELOPE.streamKey] = streamKey;\n\n      return pack(envelope);\n    },\n\n    incoming: (frame: string | ArrayBuffer | Uint8Array | Blob) => {\n      // Only binary frames are ours. Text frames fall through to the JSON parser; Blobs should have\n      // been converted to a buffer by the connection before reaching us — if not, we can't unpack\n      // them synchronously, so defer.\n      let buffer: Uint8Array;\n      if (frame instanceof ArrayBuffer) {\n        buffer = new Uint8Array(frame);\n      } else if (frame instanceof Uint8Array) {\n        buffer = frame;\n      } else {\n        return undefined;\n      }\n\n      try {\n        const envelope = unpack(buffer);\n\n        if (!isEnvelope(envelope)) return undefined;\n\n        const routeMeta = intToRoute[envelope[ENVELOPE.route]];\n        const payloadType = ReversePayloadType[envelope[ENVELOPE.type]];\n        if (routeMeta == null || payloadType == null) return undefined;\n\n        const time = envelope[ENVELOPE.time];\n        // Rebuild the context: `routing` starts empty (the receiver re-stamps its own hops) and\n        // `timeCreated` is approximated by the payload `time` — neither affects hydration/validation.\n        const context = {\n          cuid: envelope[ENVELOPE.cuid],\n          timeCreated: time,\n          routing: [],\n          originClient: envelope[ENVELOPE.originClient],\n        };\n\n        return assembleWireJson(routeMeta, payloadType, time, context, envelope[ENVELOPE.payload]);\n      } catch (e) {\n        console.error(\"[binary-wire] Failed to unpack binary action frame\", e);\n        return undefined;\n      }\n    },\n\n    incomingReliability: (frame): IFrameReliabilityWire | undefined => {\n      let buffer: Uint8Array;\n      if (frame instanceof ArrayBuffer) buffer = new Uint8Array(frame);\n      else if (frame instanceof Uint8Array) buffer = frame;\n      else return undefined;\n\n      try {\n        const envelope = unpack(buffer);\n        if (\n          !isEnvelope(envelope) ||\n          (envelope.length !== ENVELOPE_LENGTH_RELIABLE &&\n            envelope.length !== ENVELOPE_LENGTH_RELIABLE_KEYED)\n        ) {\n          return undefined;\n        }\n        const payloadType = ReversePayloadType[envelope[ENVELOPE.type]];\n        if (payloadType == null) return undefined;\n        const wire = decodeReliabilitySlot(envelope[ENVELOPE.reliability], payloadType);\n        if (wire == null) return undefined;\n        if (envelope.length === ENVELOPE_LENGTH_RELIABLE_KEYED) {\n          // A non-string streamKey off the wire (buggy/hostile peer) degrades the slot to best-effort.\n          const streamKey = envelope[ENVELOPE.streamKey];\n          if (typeof streamKey !== \"string\") return undefined;\n          wire.streamKey = streamKey;\n        }\n        return wire;\n      } catch {\n        return undefined;\n      }\n    },\n  };\n}\n\n/** A decoded frame is one of ours iff it's an array of best-effort or reliable length. */\n// msgpackr yields `any`; this matches the pre-existing `Array.isArray` narrowing the raw envelope indexing relied on.\nfunction isEnvelope(envelope: unknown): envelope is any[] {\n  return (\n    Array.isArray(envelope) &&\n    (envelope.length === ENVELOPE_LENGTH ||\n      envelope.length === ENVELOPE_LENGTH_RELIABLE ||\n      envelope.length === ENVELOPE_LENGTH_RELIABLE_KEYED)\n  );\n}\n"],"mappings":"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"}