Documentation Index Fetch the complete documentation index at: https://mintlify.com/hypertekorg/hyperstack/llms.txt
Use this file to discover all available pages before exploring further.
Overview
The SubscriptionRegistry manages WebSocket subscriptions with automatic reference counting and deduplication. Multiple consumers can subscribe to the same view, and the registry ensures only one WebSocket subscription is created.
Automatic Subscription Management
When you use view methods like use(), watch(), or watchRich(), subscriptions are automatically created and cleaned up.
const client = await HyperStack . connect ( stack );
// Subscription created automatically
for await ( const user of client . views . users . state . use ( 'user-123' )) {
console . log ( user );
}
// Subscription cleaned up when loop exits
SubscriptionRegistry
The registry is accessible via client.getSubscriptionRegistry() for advanced use cases.
subscribe()
Manually create a subscription.
subscribe ( subscription : Subscription ): UnsubscribeFn
Returns: Function to unsubscribe
const registry = client . getSubscriptionRegistry ();
const unsubscribe = registry . subscribe ({
view: 'users.state' ,
key: 'user-123' ,
});
// Later...
unsubscribe ();
unsubscribe()
Manually unsubscribe from a subscription.
unsubscribe ( subscription : Subscription ): void
getRefCount()
Get the current reference count for a subscription.
getRefCount ( subscription : Subscription ): number
const count = registry . getRefCount ({
view: 'users.state' ,
key: 'user-123' ,
});
console . log ( ` ${ count } active consumers` );
getActiveSubscriptions()
Get all active subscriptions.
getActiveSubscriptions (): Subscription []
const active = registry . getActiveSubscriptions ();
console . log ( ` ${ active . length } active subscriptions` );
active . forEach ( sub => {
console . log ( `- ${ sub . view } : ${ sub . key } ` );
});
clear()
Clear all subscriptions.
Subscription Type
interface Subscription {
view : string ;
key ?: string ;
partition ?: string ;
filters ?: Record < string , string >;
take ?: number ;
skip ?: number ;
}
View path to subscribe to (e.g., 'users.state')
Entity key for state views. Omit for list views
Partition identifier for multi-tenant systems
Filter criteria for the subscription
Maximum number of entities to receive
Number of entities to skip (pagination)
Reference Counting
The registry uses reference counting to deduplicate subscriptions.
// First subscription - creates WebSocket subscription
const unsub1 = registry . subscribe ({ view: 'users.state' , key: 'user-123' });
console . log ( registry . getRefCount ({ view: 'users.state' , key: 'user-123' }));
// Output: 1
// Second subscription to same entity - reuses existing subscription
const unsub2 = registry . subscribe ({ view: 'users.state' , key: 'user-123' });
console . log ( registry . getRefCount ({ view: 'users.state' , key: 'user-123' }));
// Output: 2
// First unsubscribe - keeps subscription alive
unsub1 ();
console . log ( registry . getRefCount ({ view: 'users.state' , key: 'user-123' }));
// Output: 1
// Second unsubscribe - removes WebSocket subscription
unsub2 ();
console . log ( registry . getRefCount ({ view: 'users.state' , key: 'user-123' }));
// Output: 0
Connection Management
The ConnectionManager handles WebSocket connection lifecycle and is accessible via client.getConnection().
Connection States
type ConnectionState =
| 'disconnected'
| 'connecting'
| 'connected'
| 'reconnecting'
| 'error' ;
Monitoring Connection State
const unsubscribe = client . onConnectionStateChange (( state , error ) => {
switch ( state ) {
case 'connecting' :
console . log ( 'Establishing connection...' );
break ;
case 'connected' :
console . log ( 'Connected successfully' );
break ;
case 'reconnecting' :
console . log ( 'Connection lost, reconnecting...' );
break ;
case 'error' :
console . error ( 'Connection error:' , error );
break ;
case 'disconnected' :
console . log ( 'Disconnected' );
break ;
}
});
Manual Connection Control
// Connect without auto-connect
const client = await HyperStack . connect ( stack , {
autoReconnect: false ,
});
// Manually connect
await client . connect ();
// Check connection status
if ( client . isConnected ()) {
console . log ( 'Ready to use' );
}
// Manually disconnect
client . disconnect ();
Subscription Lifecycle
View Subscriptions
When using typed views, subscriptions follow this lifecycle:
// 1. Create subscription
const iterator = client . views . users . state . use ( 'user-123' );
// 2. Registry increments ref count and subscribes to WebSocket
for await ( const user of iterator ) {
console . log ( user );
// 3. Break to unsubscribe
if ( someCondition ) break ;
}
// 4. Registry decrements ref count and unsubscribes if count reaches 0
Manual Cleanup
const iterator = client . views . users . state . use ( 'user-123' );
// Force cleanup
await iterator . return ?.();
Stream Creation
Low-level stream creation functions for advanced use cases.
createUpdateStream()
function createUpdateStream < T >(
storage : StorageAdapter ,
subscriptionRegistry : SubscriptionRegistry ,
subscription : Subscription ,
key ?: string
) : AsyncIterable < Update < T >>
Creates an async iterable that yields Update<T> objects.
createRichUpdateStream()
function createRichUpdateStream < T >(
storage : StorageAdapter ,
subscriptionRegistry : SubscriptionRegistry ,
subscription : Subscription ,
key ?: string
) : AsyncIterable < RichUpdate < T >>
Creates an async iterable that yields RichUpdate<T> objects with before/after snapshots.
createEntityStream()
function createEntityStream < T >(
storage : StorageAdapter ,
subscriptionRegistry : SubscriptionRegistry ,
subscription : Subscription ,
options ?: WatchOptions ,
key ?: string
) : AsyncIterable < T >
Creates an async iterable that yields entity objects directly.
Reconnection Behavior
HyperStack automatically reconnects with exponential backoff when the connection is lost.
Default Configuration
const DEFAULT_CONFIG = {
reconnectIntervals: [ 1000 , 2000 , 4000 , 8000 , 16000 ], // ms
maxReconnectAttempts: 5 ,
};
Custom Reconnection
const client = await HyperStack . connect ( stack , {
autoReconnect: true ,
reconnectIntervals: [ 500 , 1000 , 2000 , 5000 ],
maxReconnectAttempts: 10 ,
});
Disable Reconnection
const client = await HyperStack . connect ( stack , {
autoReconnect: false ,
});
// Handle reconnection manually
client . onConnectionStateChange ( async ( state ) => {
if ( state === 'error' ) {
await new Promise ( resolve => setTimeout ( resolve , 3000 ));
await client . connect ();
}
});
Subscription Reactivation
When reconnecting, HyperStack automatically resubscribes to all active subscriptions.
const client = await HyperStack . connect ( stack );
// Create subscriptions
const stream1 = client . views . users . state . use ( 'user-123' );
const stream2 = client . views . posts . list . use ();
// Connection drops and reconnects...
// Both subscriptions are automatically reactivated
Advanced Patterns
Shared Subscriptions
Share a single subscription across multiple components.
class SubscriptionManager {
private subscriptions = new Map < string , AsyncIterable < any >>();
subscribe < T >( view : string , key : string ) : AsyncIterable < T > {
const id = ` ${ view } : ${ key } ` ;
if ( ! this . subscriptions . has ( id )) {
const stream = client . views [ view ]. state . use ( key );
this . subscriptions . set ( id , stream );
}
return this . subscriptions . get ( id ) ! ;
}
}
Conditional Subscriptions
function useConditionalSubscription ( userId : string | null ) {
const [ user , setUser ] = useState ( null );
useEffect (() => {
if ( ! userId ) return ;
const controller = new AbortController ();
( async () => {
try {
for await ( const u of client . views . users . state . use ( userId )) {
if ( controller . signal . aborted ) break ;
setUser ( u );
}
} catch ( err ) {
if ( ! controller . signal . aborted ) throw err ;
}
})();
return () => controller . abort ();
}, [ userId ]);
return user ;
}
Type Exports
import type {
Subscription ,
UnsubscribeFn ,
ConnectionState ,
ConnectionStateCallback ,
} from '@hyperstack/core' ;
import {
SubscriptionRegistry ,
ConnectionManager ,
} from '@hyperstack/core' ;
Best Practices
Let the framework manage subscriptions - Use view methods instead of manual registry calls
Clean up properly - Always unsubscribe when components unmount
Monitor connection state - Show loading/offline UI based on connection state
Handle reconnection gracefully - Don’t assume subscriptions remain active forever
Use reference counting - Let multiple components subscribe to the same data safely
Next Steps
Working with Views Learn about view subscription methods
Storage Adapters Understand how data is stored and updated