@hyperfrontend/network-protocol/routing

Routing

Purpose

The Routing module provides topic-based message routing across channels: routed-packet envelopes that pair a packet with a topic, and the subscription types a router uses to decide which channels receive a topic's messages.


Key Interfaces

RoutedPacket

interface RoutedPacket {
  topicId: string // Topic this packet is routed to (UUID v4)
  packet: unknown // The packet content
}

RoutedWirePacket

A routed packet carrying a sealed frame.

interface RoutedWirePacket extends RoutedPacket {
  packet: Uint8Array // The sealed frame
}

RoutedUnencryptedPacket<T>

A routed packet carrying a plaintext packet.

interface RoutedUnencryptedPacket<T = any> extends RoutedPacket {
  packet: UnencryptedPacket<T>
}

RoutingOptions

interface RoutingOptions {
  isDynamic: boolean // true: fetch subscriptions anew for each message; false: fetch once and cache
  subscriptions: Subscriptions // Maps channels to their subscribed topics
}

Subscriptions

type Subscriptions = WeakMap<Channel, Topic[]>

Router

Configures routing from the available channels and topics.

type Router = (channels: Channel[], topics: Topic[]) => RoutingOptions

Routing Flow


Factory Functions

createRoutedUnencryptedPacket

Builds the plaintext packet with createUnencryptedPacket and pairs it with a topic; the result is frozen.

function createRoutedUnencryptedPacket<T = any>(topicId: string, origin: string, target: string, data: Data<T>): RoutedUnencryptedPacket
import { createRoutedUnencryptedPacket } from '@hyperfrontend/network-protocol/routing'

const routed = createRoutedUnencryptedPacket(topic.id, originId, targetId, data)
// => { topicId, packet: { origin, target, data } }

createRoutedWirePacket

Pairs an already sealed frame with a topic; the result is frozen.

function createRoutedWirePacket(topicId: string, packet: WirePacket): RoutedWirePacket

Dynamic vs Static Routing

Static (isDynamic: false)

Subscriptions are resolved once; use when channels do not change subscriptions at runtime.

const router: Router = (channels, topics) => {
  const subscriptions = new WeakMap<Channel, Topic[]>()
  const userEvents = topics.find((t) => t.name === 'user-events')
  channels.forEach((channel) => subscriptions.set(channel, userEvents ? [userEvents] : []))
  return { isDynamic: false, subscriptions }
}

Dynamic (isDynamic: true)

Subscriptions are re-evaluated for each message; use when channels subscribe and unsubscribe at runtime.

const router: Router = (channels, topics) => ({ isDynamic: true, subscriptions: currentSubscriptions() })

Usage Example

import { createTopicStore } from '@hyperfrontend/network-protocol/topic'
import { createChannelStore } from '@hyperfrontend/network-protocol/browser/channel'

const topicStore = createTopicStore()
const channelStore = createChannelStore()

topicStore.create('user-events', 'system-events', 'notifications')

const first = channelStore.create('client-1', {
  send,
  receive,
  protocolProvider,
  session: firstSession,
})
const second = channelStore.create('client-2', {
  send,
  receive,
  protocolProvider,
  session: secondSession,
})

const router: Router = (channels, topics) => {
  const subscriptions = new WeakMap<Channel, Topic[]>()
  const userEvents = topics.find((t) => t.name === 'user-events')
  const notifications = topics.find((t) => t.name === 'notifications')
  subscriptions.set(first, [userEvents, notifications].filter(Boolean) as Topic[])
  subscriptions.set(second, notifications ? [notifications] : [])
  return { isDynamic: false, subscriptions }
}

const routingOptions = router(
  channelStore.list.map((entry) => entry.channel),
  topicStore.list
)

Error Handling

createRoutedUnencryptedPacket('not-a-uuid', originId, targetId, data)
// Error: 'Cannot create a routed unencrypted packet without a valid topic'

createRoutedWirePacket('not-a-uuid', frame)
// Error: 'Cannot create a routed wire packet without a valid topic'

createRoutedWirePacket(topic.id, new Uint8Array(0))
// Error: 'Cannot create a routed wire packet without a valid wire packet'

createRoutedUnencryptedPacket also throws the packet creator's errors for an invalid origin, target, or data envelope.


Validation Helpers

validations/ holds isValidRoutedUnencryptedPacket, isValidRoutedWirePacket, isValidRoutingOptions, isValidSubscriptions (a WeakMap), and isValidRouter (a function whose result for empty inputs is valid routing options). They are internal to the module.


Relationship to Other Modules


See Also

Related Modules

ModuleRelationship
topic/Topics used for subscription
channel/Channels that receive routed messages
packet/The packet shapes a routed packet carries

API Reference§

ƒ Functions

§function

createRoutedUnencryptedPacket<T>(topicId: string, origin: string, target: string, data: Data<T>): RoutedUnencryptedPacket

Creates a routed unencrypted packet with the specified topic, origin, target, and data. The packet is created using the unencrypted packet creator and frozen.
Parameters
NameTypeDescription
§topicId
stringThe topic identifier for routing the packet
§origin
stringThe origin URL of the packet sender
§target
stringThe target URL of the packet recipient
§data
Data<T>The data payload to include in the packet
Returns
RoutedUnencryptedPacket
A frozen RoutedUnencryptedPacket
Example

Creating a routed unencrypted packet

const routedPacket = createRoutedUnencryptedPacket(
  '550e8400-e29b-41d4-a716-446655440000',
  'origin-uuid',
  'target-uuid',
  { message: 'hello' }
)
// => { topicId: '550e8400-...', packet: { origin, target, data } }
§function

createRoutedWirePacket(topicId: string, packet: WirePacket): RoutedWirePacket

Creates a routed wire packet with the specified topic and packet data. The packet is frozen to prevent modifications after creation.
Parameters
NameTypeDescription
§topicId
stringThe topic identifier for routing the packet
§packet
WirePacketThe wire packet to route
Returns
RoutedWirePacket
A frozen RoutedWirePacket
Example

Creating a routed wire packet

const routedPacket = createRoutedWirePacket(
  '550e8400-e29b-41d4-a716-446655440000',
  wirePacket
)
// => { topicId: '550e8400-...', packet: wirePacket }

◈ Interfaces

§interface

RoutedPacket

A packet routed to a specific topic.
Properties
§packet:unknownThe packet payload
§topicId:stringTopic identifier for routing
§interface

RoutedUnencryptedPacket

A routed packet with unencrypted payload.
Properties
§packet:UnencryptedPacket<T>Unencrypted packet with typed payload
§topicId:stringTopic identifier for routing
§interface

RoutedWirePacket

A routed packet carrying sealed wire bytes.
Properties
§packet:Uint8ArrayThe sealed frame
§topicId:stringTopic identifier for routing
§interface

RoutingOptions

Configuration options for message routing.
Properties
§isDynamic:booleanControls subscription fetching behavior:
  • true: Fetches subscriptions anew for each message, useful for dynamic subscription setups.
  • false: Fetches subscriptions once and caches them, ideal for stable setups.
§subscriptions:SubscriptionsMaps channels to subscribed topics, determining message routing based on topic.

◆ Types

§type

Router

Defines a function type that configures routing based on channels and topics.
This function is invoked to determine routing configurations, including whether subscriptions are fetched dynamically for each message, and how messages are routed based on topics to specific channels.
type Router = (channels: Channel[], topics: Topic[]) => RoutingOptions
§type

Subscriptions

Maps channels to their subscribed topics
type Subscriptions = WeakMap<Channel, Topic[]>