hyperstack-typescript 0.2.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/README.md ADDED
@@ -0,0 +1,111 @@
1
+ # Hyperstack TypeScript SDK
2
+
3
+ Pure TypeScript SDK for the Hyperstack Solana streaming platform. Framework-agnostic core with AsyncIterable-based streaming.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install hyperstack-typescript
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ ### Setup
14
+
15
+ ```typescript
16
+ import { HyperStack } from 'hyperstack-typescript';
17
+ import { SETTLEMENT_GAME_STACK } from './generated/settlement-game-stack';
18
+
19
+ const hs = await HyperStack.connect('wss://mainnet.hyperstack.xyz', {
20
+ stack: SETTLEMENT_GAME_STACK,
21
+ });
22
+ ```
23
+
24
+ ### Streaming with AsyncIterable
25
+
26
+ ```typescript
27
+ for await (const update of hs.views.settlementGame.list.watch()) {
28
+ if (update.type === 'upsert') {
29
+ console.log('Game updated:', update.key, update.data);
30
+ } else if (update.type === 'delete') {
31
+ console.log('Game deleted:', update.key);
32
+ }
33
+ }
34
+
35
+ for await (const update of hs.views.settlementGame.state.watch('game-123')) {
36
+ console.log('Game 123 updated:', update.data);
37
+ }
38
+
39
+ for await (const update of hs.views.settlementGame.list.watchRich()) {
40
+ if (update.type === 'updated') {
41
+ console.log('Changed from:', update.before);
42
+ console.log('Changed to:', update.after);
43
+ }
44
+ }
45
+ ```
46
+
47
+ ### One-Shot Queries
48
+
49
+ ```typescript
50
+ const games = await hs.views.settlementGame.list.get();
51
+
52
+ const game = await hs.views.settlementGame.state.get('game-123');
53
+ ```
54
+
55
+ ### Connection Management
56
+
57
+ ```typescript
58
+ console.log(hs.connectionState);
59
+
60
+ const unsubscribe = hs.onConnectionStateChange((state) => {
61
+ console.log('Connection state:', state);
62
+ });
63
+
64
+ await hs.disconnect();
65
+ ```
66
+
67
+ ## API
68
+
69
+ ### HyperStack
70
+
71
+ Main client class with typed view accessors.
72
+
73
+ - `HyperStack.connect(url, options)` - Connect to a HyperStack server
74
+ - `views` - Typed view accessors based on your stack definition
75
+ - `connectionState` - Current connection state
76
+ - `onConnectionStateChange(callback)` - Listen for connection changes
77
+ - `disconnect()` - Disconnect from the server
78
+
79
+ ### Views
80
+
81
+ Each view provides:
82
+
83
+ **StateView (keyed entities)**
84
+ - `watch(key)` - AsyncIterable of updates for a specific key
85
+ - `watchRich(key)` - AsyncIterable with before/after diffs
86
+ - `get(key)` - Get entity by key
87
+ - `getSync(key)` - Get entity synchronously (if available)
88
+
89
+ **ListView (collections)**
90
+ - `watch()` - AsyncIterable of all updates
91
+ - `watchRich()` - AsyncIterable with before/after diffs
92
+ - `get()` - Get all entities
93
+ - `getSync()` - Get all entities synchronously (if available)
94
+
95
+ ### Update Types
96
+
97
+ ```typescript
98
+ type Update<T> =
99
+ | { type: 'upsert'; key: string; data: T }
100
+ | { type: 'patch'; key: string; data: Partial<T> }
101
+ | { type: 'delete'; key: string };
102
+
103
+ type RichUpdate<T> =
104
+ | { type: 'created'; key: string; data: T }
105
+ | { type: 'updated'; key: string; before: T; after: T; patch?: unknown }
106
+ | { type: 'deleted'; key: string; lastKnown?: T };
107
+ ```
108
+
109
+ ## License
110
+
111
+ MIT
@@ -0,0 +1,206 @@
1
+ type ConnectionState = 'disconnected' | 'connecting' | 'connected' | 'reconnecting' | 'error';
2
+ type Update<T> = {
3
+ type: 'upsert';
4
+ key: string;
5
+ data: T;
6
+ } | {
7
+ type: 'patch';
8
+ key: string;
9
+ data: Partial<T>;
10
+ } | {
11
+ type: 'delete';
12
+ key: string;
13
+ };
14
+ type RichUpdate<T> = {
15
+ type: 'created';
16
+ key: string;
17
+ data: T;
18
+ } | {
19
+ type: 'updated';
20
+ key: string;
21
+ before: T;
22
+ after: T;
23
+ patch?: unknown;
24
+ } | {
25
+ type: 'deleted';
26
+ key: string;
27
+ lastKnown?: T;
28
+ };
29
+ interface ViewDef<T, TMode extends 'state' | 'list'> {
30
+ readonly mode: TMode;
31
+ readonly view: string;
32
+ readonly _entity?: T;
33
+ }
34
+ interface StackDefinition {
35
+ readonly name: string;
36
+ readonly views: Record<string, ViewGroup>;
37
+ }
38
+ interface ViewGroup {
39
+ state?: ViewDef<unknown, 'state'>;
40
+ list?: ViewDef<unknown, 'list'>;
41
+ }
42
+ interface Subscription {
43
+ view: string;
44
+ key?: string;
45
+ partition?: string;
46
+ filters?: Record<string, string>;
47
+ }
48
+ interface HyperStackOptions<TStack extends StackDefinition> {
49
+ stack: TStack;
50
+ autoReconnect?: boolean;
51
+ reconnectIntervals?: number[];
52
+ maxReconnectAttempts?: number;
53
+ }
54
+ interface HyperStackConfig {
55
+ websocketUrl?: string;
56
+ reconnectIntervals?: number[];
57
+ maxReconnectAttempts?: number;
58
+ initialSubscriptions?: Subscription[];
59
+ }
60
+ declare const DEFAULT_CONFIG: Required<Pick<HyperStackConfig, 'reconnectIntervals' | 'maxReconnectAttempts'>>;
61
+ declare class HyperStackError extends Error {
62
+ code: string;
63
+ details?: unknown | undefined;
64
+ constructor(message: string, code: string, details?: unknown | undefined);
65
+ }
66
+ type TypedViews<TViews extends StackDefinition['views']> = {
67
+ [K in keyof TViews]: TypedViewGroup<TViews[K]>;
68
+ };
69
+ type TypedViewGroup<TGroup> = {
70
+ state: TGroup extends {
71
+ state: ViewDef<infer T, 'state'>;
72
+ } ? TypedStateView<T> : never;
73
+ list: TGroup extends {
74
+ list: ViewDef<infer T, 'list'>;
75
+ } ? TypedListView<T> : never;
76
+ };
77
+ interface TypedStateView<T> {
78
+ watch(key: string): AsyncIterable<Update<T>>;
79
+ watchRich(key: string): AsyncIterable<RichUpdate<T>>;
80
+ get(key: string): Promise<T | null>;
81
+ getSync(key: string): T | null | undefined;
82
+ }
83
+ interface TypedListView<T> {
84
+ watch(): AsyncIterable<Update<T>>;
85
+ watchRich(): AsyncIterable<RichUpdate<T>>;
86
+ get(): Promise<T[]>;
87
+ getSync(): T[] | undefined;
88
+ }
89
+ type SubscribeCallback<T> = (update: Update<T>) => void;
90
+ type UnsubscribeFn = () => void;
91
+ type ConnectionStateCallback = (state: ConnectionState, error?: string) => void;
92
+
93
+ type FrameMode = 'state' | 'append' | 'list';
94
+ type FrameOp = 'create' | 'upsert' | 'patch' | 'delete';
95
+ interface EntityFrame<T = unknown> {
96
+ mode: FrameMode;
97
+ entity: string;
98
+ op: FrameOp;
99
+ key: string;
100
+ data: T;
101
+ }
102
+ declare function parseFrame(data: ArrayBuffer | string): EntityFrame;
103
+ declare function parseFrameFromBlob(blob: Blob): Promise<EntityFrame>;
104
+ declare function isValidFrame(frame: unknown): frame is EntityFrame;
105
+
106
+ type FrameHandler = <T>(frame: EntityFrame<T>) => void;
107
+ declare class ConnectionManager {
108
+ private ws;
109
+ private websocketUrl;
110
+ private reconnectIntervals;
111
+ private maxReconnectAttempts;
112
+ private reconnectAttempts;
113
+ private reconnectTimeout;
114
+ private pingInterval;
115
+ private currentState;
116
+ private subscriptionQueue;
117
+ private activeSubscriptions;
118
+ private frameHandlers;
119
+ private stateHandlers;
120
+ constructor(config: HyperStackConfig);
121
+ getState(): ConnectionState;
122
+ onFrame(handler: FrameHandler): () => void;
123
+ onStateChange(handler: ConnectionStateCallback): () => void;
124
+ connect(): Promise<void>;
125
+ disconnect(): void;
126
+ subscribe(subscription: Subscription): void;
127
+ unsubscribe(view: string, key?: string): void;
128
+ isConnected(): boolean;
129
+ private makeSubKey;
130
+ private flushSubscriptionQueue;
131
+ private resubscribeActive;
132
+ private updateState;
133
+ private notifyFrameHandlers;
134
+ private handleReconnect;
135
+ private clearReconnectTimeout;
136
+ private startPingInterval;
137
+ private stopPingInterval;
138
+ }
139
+
140
+ type EntityUpdateCallback = (viewPath: string, key: string, update: Update<unknown>) => void;
141
+ type RichUpdateCallback = (viewPath: string, key: string, update: RichUpdate<unknown>) => void;
142
+ declare class EntityStore {
143
+ private entities;
144
+ private updateCallbacks;
145
+ private richUpdateCallbacks;
146
+ handleFrame<T>(frame: EntityFrame<T>): void;
147
+ getAll<T>(viewPath: string): T[];
148
+ get<T>(viewPath: string, key: string): T | null;
149
+ getAllSync<T>(viewPath: string): T[] | undefined;
150
+ getSync<T>(viewPath: string, key: string): T | null | undefined;
151
+ keys(viewPath: string): string[];
152
+ size(viewPath: string): number;
153
+ clear(): void;
154
+ clearView(viewPath: string): void;
155
+ onUpdate(callback: EntityUpdateCallback): UnsubscribeFn;
156
+ onRichUpdate(callback: RichUpdateCallback): UnsubscribeFn;
157
+ subscribe<T>(viewPath: string, callback: SubscribeCallback<T>): UnsubscribeFn;
158
+ subscribeToKey<T>(viewPath: string, key: string, callback: SubscribeCallback<T>): UnsubscribeFn;
159
+ private notifyUpdate;
160
+ private notifyRichUpdate;
161
+ private notifyRichDelete;
162
+ }
163
+
164
+ declare class SubscriptionRegistry {
165
+ private subscriptions;
166
+ private connection;
167
+ constructor(connection: ConnectionManager);
168
+ subscribe(subscription: Subscription): UnsubscribeFn;
169
+ unsubscribe(subscription: Subscription): void;
170
+ getRefCount(subscription: Subscription): number;
171
+ getActiveSubscriptions(): Subscription[];
172
+ clear(): void;
173
+ private makeSubKey;
174
+ }
175
+
176
+ declare class HyperStack<TStack extends StackDefinition> {
177
+ private readonly connection;
178
+ private readonly store;
179
+ private readonly subscriptionRegistry;
180
+ private readonly _views;
181
+ private readonly stack;
182
+ private constructor();
183
+ static connect<T extends StackDefinition>(url: string, options: HyperStackOptions<T>): Promise<HyperStack<T>>;
184
+ get views(): TypedViews<TStack['views']>;
185
+ get connectionState(): ConnectionState;
186
+ get stackName(): string;
187
+ onConnectionStateChange(callback: ConnectionStateCallback): UnsubscribeFn;
188
+ onFrame(callback: (frame: EntityFrame) => void): UnsubscribeFn;
189
+ connect(): Promise<void>;
190
+ disconnect(): void;
191
+ isConnected(): boolean;
192
+ clearStore(): void;
193
+ getStore(): EntityStore;
194
+ getConnection(): ConnectionManager;
195
+ getSubscriptionRegistry(): SubscriptionRegistry;
196
+ }
197
+
198
+ declare function createUpdateStream<T>(store: EntityStore, subscriptionRegistry: SubscriptionRegistry, subscription: Subscription, keyFilter?: string): AsyncIterable<Update<T>>;
199
+ declare function createRichUpdateStream<T>(store: EntityStore, subscriptionRegistry: SubscriptionRegistry, subscription: Subscription, keyFilter?: string): AsyncIterable<RichUpdate<T>>;
200
+
201
+ declare function createTypedStateView<T>(viewDef: ViewDef<T, 'state'>, store: EntityStore, subscriptionRegistry: SubscriptionRegistry): TypedStateView<T>;
202
+ declare function createTypedListView<T>(viewDef: ViewDef<T, 'list'>, store: EntityStore, subscriptionRegistry: SubscriptionRegistry): TypedListView<T>;
203
+ declare function createTypedViews<TStack extends StackDefinition>(stack: TStack, store: EntityStore, subscriptionRegistry: SubscriptionRegistry): TypedViews<TStack['views']>;
204
+
205
+ export { ConnectionManager, DEFAULT_CONFIG, EntityStore, HyperStack, HyperStackError, SubscriptionRegistry, createRichUpdateStream, createTypedListView, createTypedStateView, createTypedViews, createUpdateStream, isValidFrame, parseFrame, parseFrameFromBlob };
206
+ export type { ConnectionState, ConnectionStateCallback, EntityFrame, FrameMode, FrameOp, HyperStackConfig, HyperStackOptions, RichUpdate, StackDefinition, SubscribeCallback, Subscription, TypedListView, TypedStateView, TypedViewGroup, TypedViews, UnsubscribeFn, Update, ViewDef, ViewGroup };