@kyneta/bridge-transport 1.7.0 → 2.0.0

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.
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { GeneratedChannel, Transport, TransportFactory } from "@kyneta/transport";
1
+ import { GeneratedChannel, Transport } from "@kyneta/transport";
2
2
 
3
3
  //#region src/bridge.d.ts
4
4
  /**
@@ -82,7 +82,7 @@ declare class BridgeTransport extends Transport<BridgeTransportContext> {
82
82
  * })
83
83
  * ```
84
84
  */
85
- declare function createBridgeTransport(params: BridgeTransportParams): TransportFactory;
85
+ declare function createBridgeTransport(params: BridgeTransportParams): BridgeTransport;
86
86
  //#endregion
87
87
  export { Bridge, BridgeTransport, type BridgeTransportParams, createBridgeTransport };
88
88
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","names":[],"sources":["../src/bridge.ts"],"mappings":";;;;AAgCA;;;;;;cAAa,MAAA;EAAA,SACF,UAAA,EAAU,GAAA,SAAA,eAAA;EAEnB,YAAA,CAAa,SAAA,EAAW,eAAA;EAOxB,eAAA,CAAgB,WAAA;EAqBO;;;;;;;EAVvB,UAAA,CACE,eAAA,UACA,aAAA,UACA,KAAA,EAAO,UAAA,CAAW,WAAA;EAAA,IAOhB,YAAA,CAAA,GAAgB,GAAA;AAAA;AAAA,KASjB,sBAAA;EACH,iBAAiB;AAAA;AAAA,KAGP,qBAAA;EApBR,+EAsBF,WAAA;EAfoB;;AAAG;AAGxB;EAiBC,aAAA;EACA,MAAA,EAAQ,MAAM;AAAA;AAXG;AAGnB;;;;;;;;;AAQgB;AAgBhB;;AA3BmB,cA2BN,eAAA,SAAwB,SAAA,CAAU,sBAAA;EAAA,SACpC,MAAA,EAAQ,MAAA;EAAA,QAGT,gBAAA;EAAA,QACA,gBAAA;EAAA,QAIA,iBAAA;;IAEM,WAAA;IAAa,aAAA;IAAe;EAAA,GAAU,qBAAA;EAKpD,QAAA,CAAS,OAAA,EAAS,sBAAA,GAAyB,gBAAA;EAwBrC,OAAA,CAAA,GAAW,OAAA;EA4BX,MAAA,CAAA,GAAU,OAAA;EAmBhB,eAAA,CAAgB,iBAAA;EAkBhB,eAAA,CAAgB,iBAAA;EAkBwC;;;;;;;EAAxD,YAAA,CAAa,eAAA,UAAyB,KAAA,EAAO,UAAA,CAAW,WAAA;AAAA;;;;;;;;;;;;iBAiC1C,qBAAA,CACd,MAAA,EAAQ,qBAAA,GACP,gBAAgB"}
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../src/bridge.ts"],"mappings":";;;;AA4BA;;;;;;cAAa,MAAA;EAAA,SACF,UAAA,EAAU,GAAA,SAAA,eAAA;EAEnB,YAAA,CAAa,SAAA,EAAW,eAAA;EAOxB,eAAA,CAAgB,WAAA;EAqBO;;;;;;;EAVvB,UAAA,CACE,eAAA,UACA,aAAA,UACA,KAAA,EAAO,UAAA,CAAW,WAAA;EAAA,IAOhB,YAAA,CAAA,GAAgB,GAAA;AAAA;AAAA,KASjB,sBAAA;EACH,iBAAiB;AAAA;AAAA,KAGP,qBAAA;EApBR,+EAsBF,WAAA;EAfoB;;AAAG;AAGxB;EAiBC,aAAA;EACA,MAAA,EAAQ,MAAM;AAAA;AAXG;AAGnB;;;;;;;;;AAQgB;AAgBhB;;AA3BmB,cA2BN,eAAA,SAAwB,SAAA,CAAU,sBAAA;EAAA,SACpC,MAAA,EAAQ,MAAA;EAAA,QAGT,gBAAA;EAAA,QACA,gBAAA;EAAA,QAIA,iBAAA;;IAEM,WAAA;IAAa,aAAA;IAAe;EAAA,GAAU,qBAAA;EAKpD,QAAA,CAAS,OAAA,EAAS,sBAAA,GAAyB,gBAAA;EAwBrC,OAAA,CAAA,GAAW,OAAA;EA4BX,MAAA,CAAA,GAAU,OAAA;EAmBhB,eAAA,CAAgB,iBAAA;EAmBhB,eAAA,CAAgB,iBAAA;EAkBwC;;;;;;;EAAxD,YAAA,CAAa,eAAA,UAAyB,KAAA,EAAO,UAAA,CAAW,WAAA;AAAA;;;;;;;;;;;;iBAiC1C,qBAAA,CACd,MAAA,EAAQ,qBAAA,GACP,eAAe"}
package/dist/index.js CHANGED
@@ -94,7 +94,8 @@ var BridgeTransport = class extends Transport {
94
94
  send: "binary",
95
95
  opts: {
96
96
  threshold: 100 * 1024,
97
- onError: (e, dir) => console.warn(`[BridgeTransport] wire error (${dir}):`, e)
97
+ onError: (e, dir) => console.warn(`[BridgeTransport] wire error (${dir}):`, e),
98
+ onFrame: this.frameObserver
98
99
  }
99
100
  }));
100
101
  }
@@ -142,7 +143,7 @@ var BridgeTransport = class extends Transport {
142
143
  * ```
143
144
  */
144
145
  function createBridgeTransport(params) {
145
- return () => new BridgeTransport(params);
146
+ return new BridgeTransport(params);
146
147
  }
147
148
  //#endregion
148
149
  export { Bridge, BridgeTransport, createBridgeTransport };
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../src/bridge.ts"],"sourcesContent":["// bridge-adapter — in-process transport with Pipeline-based delivery.\n//\n// BridgeTransport is a real transport that runs the full wire pipeline\n// (aliasing, framing, fragmentation) end-to-end via per-channel\n// Pipeline<\"binary\"> instances — exactly like every other binary\n// transport. Async delivery is preserved via `queueMicrotask()` to keep\n// test behavior representative of real network adapters.\n//\n// Usage:\n// const bridge = new Bridge()\n// const exchangeA = new Exchange({\n// transports: [createBridgeTransport({ transportId: \"peer-a\", bridge })],\n// })\n\nimport type {\n ChannelId,\n GeneratedChannel,\n TransportFactory,\n} from \"@kyneta/transport\"\nimport { Pipeline, Transport } from \"@kyneta/transport\"\n\n// ---------------------------------------------------------------------------\n// Bridge — message router connecting multiple BridgeTransports in-process\n// ---------------------------------------------------------------------------\n\n/**\n * In-process byte router connecting multiple `BridgeTransport`s,\n * keyed by each transport's unique `transportId`.\n *\n * Channel-level sends route through per-channel `Pipeline<\"binary\">`\n * instances and call `routeBytes`.\n */\nexport class Bridge {\n readonly transports = new Map<string, BridgeTransport>()\n\n addTransport(transport: BridgeTransport): void {\n if (!transport.transportId) {\n throw new Error(\"can't add transport without transport id\")\n }\n this.transports.set(transport.transportId, transport)\n }\n\n removeTransport(transportId: string): void {\n this.transports.delete(transportId)\n }\n\n /**\n * Route already-encoded bytes from one transport to another. The\n * receiving transport's `deliverBytes` is responsible for decoding\n * and applying the inbound alias transformer.\n *\n * Used by `BridgeTransport`'s channel send path.\n */\n routeBytes(\n fromTransportId: string,\n toTransportId: string,\n bytes: Uint8Array<ArrayBuffer>,\n ): void {\n const toTransport = this.transports.get(toTransportId)\n if (!toTransport) return\n toTransport.deliverBytes(fromTransportId, bytes)\n }\n\n get transportIds(): Set<string> {\n return new Set(this.transports.keys())\n }\n}\n\n// ---------------------------------------------------------------------------\n// BridgeTransport — in-process network adapter for testing\n// ---------------------------------------------------------------------------\n\ntype BridgeTransportContext = {\n targetTransportId: string\n}\n\nexport type BridgeTransportParams = {\n /** Unique identifier for this transport instance (e.g. \"peer-a\", \"server\"). */\n transportId: string\n /**\n * Transport type category. Defaults to \"bridge\".\n * Stored in ChannelMeta for informational purposes.\n */\n transportType?: string\n bridge: Bridge\n}\n\n/**\n * In-memory transport that runs the full wire pipeline end-to-end\n * via per-channel `Pipeline<\"binary\">` instances. Tests that use this\n * transport exercise the same wire path as production transports.\n *\n * @example\n * ```typescript\n * const bridge = new Bridge()\n * const exchangeA = new Exchange({\n * transports: [createBridgeTransport({ transportId: \"peer-a\", bridge })],\n * })\n * ```\n */\nexport class BridgeTransport extends Transport<BridgeTransportContext> {\n readonly bridge: Bridge\n\n // Track which remote transport each channel connects to.\n private channelToAdapter = new Map<ChannelId, string>()\n private adapterToChannel = new Map<string, ChannelId>()\n\n // Per-channel pipeline. Created with the channel; lives until removal.\n // Keyed by channelId.\n private pipelineByChannel = new Map<ChannelId, Pipeline<\"binary\">>()\n\n constructor({ transportId, transportType, bridge }: BridgeTransportParams) {\n super({ transportType: transportType ?? \"bridge\", transportId })\n this.bridge = bridge\n }\n\n generate(context: BridgeTransportContext): GeneratedChannel {\n return {\n transportType: this.transportType,\n send: msg => {\n const channelId = this.adapterToChannel.get(context.targetTransportId)\n if (channelId === undefined) return\n const pipeline = this.pipelineByChannel.get(channelId)\n if (!pipeline) return\n for (const r of pipeline.send(msg)) {\n if (r.ok) {\n this.bridge.routeBytes(\n this.transportId,\n context.targetTransportId,\n r.value,\n )\n }\n }\n },\n stop: () => {\n // Cleanup handled by removeChannel.\n },\n }\n }\n\n async onStart(): Promise<void> {\n this.bridge.addTransport(this)\n\n // Phase 1: create channels on both sides (no establish yet).\n // Doing remote-side and local-side creation separately ensures both\n // peers' `adapterToChannel` maps are populated before the joining\n // side initiates the handshake — otherwise the joining side's\n // establish message would arrive at a remote that hasn't routed\n // bytes back yet.\n for (const [transportId, adapter] of this.bridge.transports) {\n if (transportId !== this.transportId) {\n adapter.createChannelTo(this.transportId)\n }\n }\n for (const transportId of this.bridge.transports.keys()) {\n if (transportId !== this.transportId) {\n this.createChannelTo(transportId)\n }\n }\n\n // Phase 2: only the joining transport initiates establish. The\n // already-started side learns the joining peer's identity from\n // the establish handshake it echoes back.\n for (const channelId of this.adapterToChannel.values()) {\n this.establishChannel(channelId)\n }\n }\n\n async onStop(): Promise<void> {\n for (const [transportId, adapter] of this.bridge.transports) {\n if (transportId !== this.transportId) {\n adapter.removeChannelTo(this.transportId)\n }\n }\n this.bridge.removeTransport(this.transportId)\n\n for (const channelId of this.channelToAdapter.keys()) {\n this.removeChannel(channelId)\n }\n for (const pipeline of this.pipelineByChannel.values()) {\n pipeline.dispose()\n }\n this.pipelineByChannel.clear()\n this.channelToAdapter.clear()\n this.adapterToChannel.clear()\n }\n\n createChannelTo(targetTransportId: string): void {\n if (this.adapterToChannel.has(targetTransportId)) return\n const channel = this.addChannel({ targetTransportId })\n this.channelToAdapter.set(channel.channelId, targetTransportId)\n this.adapterToChannel.set(targetTransportId, channel.channelId)\n this.pipelineByChannel.set(\n channel.channelId,\n new Pipeline({\n send: \"binary\",\n opts: {\n threshold: 100 * 1024,\n onError: (e, dir) =>\n console.warn(`[BridgeTransport] wire error (${dir}):`, e),\n },\n }),\n )\n }\n\n removeChannelTo(targetTransportId: string): void {\n const channelId = this.adapterToChannel.get(targetTransportId)\n if (channelId !== undefined) {\n this.removeChannel(channelId)\n this.channelToAdapter.delete(channelId)\n this.adapterToChannel.delete(targetTransportId)\n this.pipelineByChannel.get(channelId)?.dispose()\n this.pipelineByChannel.delete(channelId)\n }\n }\n\n /**\n * Deliver encoded bytes to the appropriate channel.\n *\n * Routes through the per-channel `Pipeline.receive()` which handles\n * decoding, deframing, reassembly, and alias resolution. Delivers\n * each resolved message asynchronously via `queueMicrotask()`.\n */\n deliverBytes(fromTransportId: string, bytes: Uint8Array<ArrayBuffer>): void {\n const channelId = this.adapterToChannel.get(fromTransportId)\n if (channelId === undefined) return\n const channel = this.channels.get(channelId)\n if (!channel) return\n const pipeline = this.pipelineByChannel.get(channelId)\n if (!pipeline) return\n for (const r of pipeline.receive(bytes)) {\n if (r.ok) {\n const msg = r.value\n queueMicrotask(() => {\n channel.onReceive(msg)\n })\n }\n }\n }\n}\n\n// ---------------------------------------------------------------------------\n// Factory function\n// ---------------------------------------------------------------------------\n\n/**\n * Create a BridgeTransport factory for in-process testing.\n *\n * @example\n * ```typescript\n * const bridge = new Bridge()\n * const exchangeA = new Exchange({\n * transports: [createBridgeTransport({ transportId: \"peer-a\", bridge })],\n * })\n * ```\n */\nexport function createBridgeTransport(\n params: BridgeTransportParams,\n): TransportFactory {\n return () => new BridgeTransport(params)\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../src/bridge.ts"],"sourcesContent":["// bridge-adapter — in-process transport with Pipeline-based delivery.\n//\n// BridgeTransport is a real transport that runs the full wire pipeline\n// (aliasing, framing, fragmentation) end-to-end via per-channel\n// Pipeline<\"binary\"> instances — exactly like every other binary\n// transport. Async delivery is preserved via `queueMicrotask()` to keep\n// test behavior representative of real network adapters.\n//\n// Usage:\n// const bridge = new Bridge()\n// const exchangeA = new Exchange({\n// transports: [createBridgeTransport({ transportId: \"peer-a\", bridge })],\n// })\n\nimport type { ChannelId, GeneratedChannel } from \"@kyneta/transport\"\nimport { Pipeline, Transport } from \"@kyneta/transport\"\n\n// ---------------------------------------------------------------------------\n// Bridge — message router connecting multiple BridgeTransports in-process\n// ---------------------------------------------------------------------------\n\n/**\n * In-process byte router connecting multiple `BridgeTransport`s,\n * keyed by each transport's unique `transportId`.\n *\n * Channel-level sends route through per-channel `Pipeline<\"binary\">`\n * instances and call `routeBytes`.\n */\nexport class Bridge {\n readonly transports = new Map<string, BridgeTransport>()\n\n addTransport(transport: BridgeTransport): void {\n if (!transport.transportId) {\n throw new Error(\"can't add transport without transport id\")\n }\n this.transports.set(transport.transportId, transport)\n }\n\n removeTransport(transportId: string): void {\n this.transports.delete(transportId)\n }\n\n /**\n * Route already-encoded bytes from one transport to another. The\n * receiving transport's `deliverBytes` is responsible for decoding\n * and applying the inbound alias transformer.\n *\n * Used by `BridgeTransport`'s channel send path.\n */\n routeBytes(\n fromTransportId: string,\n toTransportId: string,\n bytes: Uint8Array<ArrayBuffer>,\n ): void {\n const toTransport = this.transports.get(toTransportId)\n if (!toTransport) return\n toTransport.deliverBytes(fromTransportId, bytes)\n }\n\n get transportIds(): Set<string> {\n return new Set(this.transports.keys())\n }\n}\n\n// ---------------------------------------------------------------------------\n// BridgeTransport — in-process network adapter for testing\n// ---------------------------------------------------------------------------\n\ntype BridgeTransportContext = {\n targetTransportId: string\n}\n\nexport type BridgeTransportParams = {\n /** Unique identifier for this transport instance (e.g. \"peer-a\", \"server\"). */\n transportId: string\n /**\n * Transport type category. Defaults to \"bridge\".\n * Stored in ChannelMeta for informational purposes.\n */\n transportType?: string\n bridge: Bridge\n}\n\n/**\n * In-memory transport that runs the full wire pipeline end-to-end\n * via per-channel `Pipeline<\"binary\">` instances. Tests that use this\n * transport exercise the same wire path as production transports.\n *\n * @example\n * ```typescript\n * const bridge = new Bridge()\n * const exchangeA = new Exchange({\n * transports: [createBridgeTransport({ transportId: \"peer-a\", bridge })],\n * })\n * ```\n */\nexport class BridgeTransport extends Transport<BridgeTransportContext> {\n readonly bridge: Bridge\n\n // Track which remote transport each channel connects to.\n private channelToAdapter = new Map<ChannelId, string>()\n private adapterToChannel = new Map<string, ChannelId>()\n\n // Per-channel pipeline. Created with the channel; lives until removal.\n // Keyed by channelId.\n private pipelineByChannel = new Map<ChannelId, Pipeline<\"binary\">>()\n\n constructor({ transportId, transportType, bridge }: BridgeTransportParams) {\n super({ transportType: transportType ?? \"bridge\", transportId })\n this.bridge = bridge\n }\n\n generate(context: BridgeTransportContext): GeneratedChannel {\n return {\n transportType: this.transportType,\n send: msg => {\n const channelId = this.adapterToChannel.get(context.targetTransportId)\n if (channelId === undefined) return\n const pipeline = this.pipelineByChannel.get(channelId)\n if (!pipeline) return\n for (const r of pipeline.send(msg)) {\n if (r.ok) {\n this.bridge.routeBytes(\n this.transportId,\n context.targetTransportId,\n r.value,\n )\n }\n }\n },\n stop: () => {\n // Cleanup handled by removeChannel.\n },\n }\n }\n\n async onStart(): Promise<void> {\n this.bridge.addTransport(this)\n\n // Phase 1: create channels on both sides (no establish yet).\n // Doing remote-side and local-side creation separately ensures both\n // peers' `adapterToChannel` maps are populated before the joining\n // side initiates the handshake — otherwise the joining side's\n // establish message would arrive at a remote that hasn't routed\n // bytes back yet.\n for (const [transportId, adapter] of this.bridge.transports) {\n if (transportId !== this.transportId) {\n adapter.createChannelTo(this.transportId)\n }\n }\n for (const transportId of this.bridge.transports.keys()) {\n if (transportId !== this.transportId) {\n this.createChannelTo(transportId)\n }\n }\n\n // Phase 2: only the joining transport initiates establish. The\n // already-started side learns the joining peer's identity from\n // the establish handshake it echoes back.\n for (const channelId of this.adapterToChannel.values()) {\n this.establishChannel(channelId)\n }\n }\n\n async onStop(): Promise<void> {\n for (const [transportId, adapter] of this.bridge.transports) {\n if (transportId !== this.transportId) {\n adapter.removeChannelTo(this.transportId)\n }\n }\n this.bridge.removeTransport(this.transportId)\n\n for (const channelId of this.channelToAdapter.keys()) {\n this.removeChannel(channelId)\n }\n for (const pipeline of this.pipelineByChannel.values()) {\n pipeline.dispose()\n }\n this.pipelineByChannel.clear()\n this.channelToAdapter.clear()\n this.adapterToChannel.clear()\n }\n\n createChannelTo(targetTransportId: string): void {\n if (this.adapterToChannel.has(targetTransportId)) return\n const channel = this.addChannel({ targetTransportId })\n this.channelToAdapter.set(channel.channelId, targetTransportId)\n this.adapterToChannel.set(targetTransportId, channel.channelId)\n this.pipelineByChannel.set(\n channel.channelId,\n new Pipeline({\n send: \"binary\",\n opts: {\n threshold: 100 * 1024,\n onError: (e, dir) =>\n console.warn(`[BridgeTransport] wire error (${dir}):`, e),\n onFrame: this.frameObserver,\n },\n }),\n )\n }\n\n removeChannelTo(targetTransportId: string): void {\n const channelId = this.adapterToChannel.get(targetTransportId)\n if (channelId !== undefined) {\n this.removeChannel(channelId)\n this.channelToAdapter.delete(channelId)\n this.adapterToChannel.delete(targetTransportId)\n this.pipelineByChannel.get(channelId)?.dispose()\n this.pipelineByChannel.delete(channelId)\n }\n }\n\n /**\n * Deliver encoded bytes to the appropriate channel.\n *\n * Routes through the per-channel `Pipeline.receive()` which handles\n * decoding, deframing, reassembly, and alias resolution. Delivers\n * each resolved message asynchronously via `queueMicrotask()`.\n */\n deliverBytes(fromTransportId: string, bytes: Uint8Array<ArrayBuffer>): void {\n const channelId = this.adapterToChannel.get(fromTransportId)\n if (channelId === undefined) return\n const channel = this.channels.get(channelId)\n if (!channel) return\n const pipeline = this.pipelineByChannel.get(channelId)\n if (!pipeline) return\n for (const r of pipeline.receive(bytes)) {\n if (r.ok) {\n const msg = r.value\n queueMicrotask(() => {\n channel.onReceive(msg)\n })\n }\n }\n }\n}\n\n// ---------------------------------------------------------------------------\n// Factory function\n// ---------------------------------------------------------------------------\n\n/**\n * Create a BridgeTransport factory for in-process testing.\n *\n * @example\n * ```typescript\n * const bridge = new Bridge()\n * const exchangeA = new Exchange({\n * transports: [createBridgeTransport({ transportId: \"peer-a\", bridge })],\n * })\n * ```\n */\nexport function createBridgeTransport(\n params: BridgeTransportParams,\n): BridgeTransport {\n return new BridgeTransport(params)\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kyneta/bridge-transport",
3
- "version": "1.7.0",
3
+ "version": "2.0.0",
4
4
  "description": "In-process transport for @kyneta/exchange — codec-faithful + alias-aware delivery for testing multi-peer scenarios in a single process",
5
5
  "author": "Duane Johnson",
6
6
  "license": "MIT",
@@ -24,15 +24,15 @@
24
24
  }
25
25
  },
26
26
  "peerDependencies": {
27
- "@kyneta/transport": "^1.7.0"
27
+ "@kyneta/transport": "^2.0.0"
28
28
  },
29
29
  "devDependencies": {
30
30
  "tsdown": "^0.22.0",
31
31
  "typescript": "^5.9.2",
32
32
  "vitest": "^4.0.17",
33
- "@kyneta/transport": "^1.7.0",
34
- "@kyneta/schema": "^1.7.0",
35
- "@kyneta/wire": "^1.7.0"
33
+ "@kyneta/wire": "^2.0.0",
34
+ "@kyneta/schema": "^2.0.0",
35
+ "@kyneta/transport": "^2.0.0"
36
36
  },
37
37
  "scripts": {
38
38
  "build": "tsdown",
package/src/bridge.ts CHANGED
@@ -12,11 +12,7 @@
12
12
  // transports: [createBridgeTransport({ transportId: "peer-a", bridge })],
13
13
  // })
14
14
 
15
- import type {
16
- ChannelId,
17
- GeneratedChannel,
18
- TransportFactory,
19
- } from "@kyneta/transport"
15
+ import type { ChannelId, GeneratedChannel } from "@kyneta/transport"
20
16
  import { Pipeline, Transport } from "@kyneta/transport"
21
17
 
22
18
  // ---------------------------------------------------------------------------
@@ -198,6 +194,7 @@ export class BridgeTransport extends Transport<BridgeTransportContext> {
198
194
  threshold: 100 * 1024,
199
195
  onError: (e, dir) =>
200
196
  console.warn(`[BridgeTransport] wire error (${dir}):`, e),
197
+ onFrame: this.frameObserver,
201
198
  },
202
199
  }),
203
200
  )
@@ -256,6 +253,6 @@ export class BridgeTransport extends Transport<BridgeTransportContext> {
256
253
  */
257
254
  export function createBridgeTransport(
258
255
  params: BridgeTransportParams,
259
- ): TransportFactory {
260
- return () => new BridgeTransport(params)
256
+ ): BridgeTransport {
257
+ return new BridgeTransport(params)
261
258
  }