@durable-streams/state 0.1.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.
@@ -0,0 +1,284 @@
1
+ import { Collection, createOptimisticAction } from "@tanstack/db";
2
+ import { StandardSchemaV1 } from "@standard-schema/spec";
3
+ import { DurableStream, DurableStreamOptions } from "@durable-streams/client";
4
+
5
+ //#region src/types.d.ts
6
+ /**
7
+ * Operation types for change events
8
+ */
9
+ /**
10
+ * Operation types for change events
11
+ */
12
+ type Operation = `insert` | `update` | `delete` | `upsert`;
13
+ /**
14
+ * A generic value type supporting primitives, arrays, and objects
15
+ */
16
+ type Value<Extensions = never> = string | number | boolean | bigint | null | Array<Value<Extensions>> | {
17
+ [key: string]: Value<Extensions>;
18
+ } | Extensions;
19
+ /**
20
+ * A row is a record of values
21
+ */
22
+ type Row<Extensions = never> = Record<string, Value<Extensions>>;
23
+ /**
24
+ * Headers for change messages
25
+ */
26
+ type ChangeHeaders = {
27
+ operation: Operation;
28
+ txid?: string;
29
+ timestamp?: string;
30
+ };
31
+ /**
32
+ * A change event represents a state change event (insert/update/delete)
33
+ */
34
+ type ChangeEvent<T = unknown> = {
35
+ type: string;
36
+ key: string;
37
+ value?: T;
38
+ old_value?: T;
39
+ headers: ChangeHeaders;
40
+ };
41
+ /**
42
+ * Control event types for stream management
43
+ */
44
+ type ControlEvent = {
45
+ headers: {
46
+ control: `snapshot-start` | `snapshot-end` | `reset`;
47
+ offset?: string;
48
+ };
49
+ };
50
+ /**
51
+ * A state event is either a change event or a control event
52
+ */
53
+ type StateEvent<T = unknown> = ChangeEvent<T> | ControlEvent;
54
+ /**
55
+ * Type guard to check if an event is a change event
56
+ */
57
+ declare function isChangeEvent<T = unknown>(event: StateEvent<T>): event is ChangeEvent<T>;
58
+ /**
59
+ * Type guard to check if an event is a control event
60
+ */
61
+ declare function isControlEvent<T = unknown>(event: StateEvent<T>): event is ControlEvent;
62
+
63
+ //#endregion
64
+ //#region src/materialized-state.d.ts
65
+ /**
66
+ * MaterializedState maintains an in-memory view of state from change events.
67
+ *
68
+ * It organizes data by type, where each type contains a map of key -> value.
69
+ * This supports multi-type streams where different entity types can coexist.
70
+ */
71
+ declare class MaterializedState {
72
+ private data;
73
+ constructor();
74
+ /**
75
+ * Apply a single change event to update the materialized state
76
+ */
77
+ apply(event: ChangeEvent): void;
78
+ /**
79
+ * Apply a batch of change events
80
+ */
81
+ applyBatch(events: Array<ChangeEvent>): void;
82
+ /**
83
+ * Get a specific value by type and key
84
+ */
85
+ get<T = unknown>(type: string, key: string): T | undefined;
86
+ /**
87
+ * Get all entries for a specific type
88
+ */
89
+ getType(type: string): Map<string, unknown>;
90
+ /**
91
+ * Clear all state
92
+ */
93
+ clear(): void;
94
+ /**
95
+ * Get the number of types in the state
96
+ */
97
+ get typeCount(): number;
98
+ /**
99
+ * Get all type names
100
+ */
101
+ get types(): Array<string>;
102
+ }
103
+
104
+ //#endregion
105
+ //#region src/stream-db.d.ts
106
+ /**
107
+ * Definition for a single collection in the stream state
108
+ */
109
+ interface CollectionDefinition<T = unknown> {
110
+ /** Standard Schema for validating values */
111
+ schema: StandardSchemaV1<T>;
112
+ /** The type field value in change events that map to this collection */
113
+ type: string;
114
+ /** The property name in T that serves as the primary key */
115
+ primaryKey: string;
116
+ }
117
+ /**
118
+ * Helper methods for creating change events for a collection
119
+ */
120
+ interface CollectionEventHelpers<T> {
121
+ /**
122
+ * Create an insert change event
123
+ */
124
+ insert: (params: {
125
+ key?: string;
126
+ value: T;
127
+ headers?: Omit<Record<string, string>, `operation`>;
128
+ }) => ChangeEvent<T>;
129
+ /**
130
+ * Create an update change event
131
+ */
132
+ update: (params: {
133
+ key?: string;
134
+ value: T;
135
+ oldValue?: T;
136
+ headers?: Omit<Record<string, string>, `operation`>;
137
+ }) => ChangeEvent<T>;
138
+ /**
139
+ * Create a delete change event
140
+ */
141
+ delete: (params: {
142
+ key?: string;
143
+ oldValue?: T;
144
+ headers?: Omit<Record<string, string>, `operation`>;
145
+ }) => ChangeEvent<T>;
146
+ /**
147
+ * Create an upsert change event (insert or update)
148
+ */
149
+ upsert: (params: {
150
+ key?: string;
151
+ value: T;
152
+ headers?: Omit<Record<string, string>, `operation`>;
153
+ }) => ChangeEvent<T>;
154
+ }
155
+ /**
156
+ * Collection definition enhanced with event creation helpers
157
+ */
158
+ type CollectionWithHelpers<T = unknown> = CollectionDefinition<T> & CollectionEventHelpers<T>;
159
+ /**
160
+ * Stream state definition containing all collections
161
+ */
162
+ type StreamStateDefinition = Record<string, CollectionDefinition>;
163
+ /**
164
+ * Stream state schema with helper methods for creating change events
165
+ */
166
+ type StateSchema<T extends Record<string, CollectionDefinition>> = { [K in keyof T]: CollectionWithHelpers<T[K] extends CollectionDefinition<infer U> ? U : unknown> };
167
+ /**
168
+ * Definition for a single action that can be passed to createOptimisticAction
169
+ */
170
+ interface ActionDefinition<TParams = any, TContext = any> {
171
+ onMutate: (params: TParams) => void;
172
+ mutationFn: (params: TParams, context: TContext) => Promise<any>;
173
+ }
174
+ /**
175
+ * Factory function for creating actions with access to db and stream context
176
+ */
177
+ type ActionFactory<TDef extends StreamStateDefinition, TActions extends Record<string, ActionDefinition<any>>> = (context: {
178
+ db: StreamDB<TDef>;
179
+ stream: DurableStream;
180
+ }) => TActions;
181
+ /**
182
+ * Map action definitions to callable action functions
183
+ */
184
+ type ActionMap<TActions extends Record<string, ActionDefinition<any>>> = { [K in keyof TActions]: ReturnType<typeof createOptimisticAction<any>> };
185
+ /**
186
+ * Options for creating a stream DB
187
+ */
188
+ interface CreateStreamDBOptions<TDef extends StreamStateDefinition = StreamStateDefinition, TActions extends Record<string, ActionDefinition<any>> = Record<string, never>> {
189
+ /** Options for creating the durable stream (stream is created lazily on preload) */
190
+ streamOptions: DurableStreamOptions;
191
+ /** The stream state definition */
192
+ state: TDef;
193
+ /** Optional factory function to create actions with db and stream context */
194
+ actions?: ActionFactory<TDef, TActions>;
195
+ }
196
+ /**
197
+ * Extract the value type from a CollectionDefinition
198
+ */
199
+ type ExtractCollectionType<T extends CollectionDefinition> = T extends CollectionDefinition<infer U> ? U : unknown;
200
+ /**
201
+ * Map collection definitions to TanStack DB Collection types
202
+ */
203
+ type CollectionMap<TDef extends StreamStateDefinition> = { [K in keyof TDef]: Collection<ExtractCollectionType<TDef[K]> & object, string> };
204
+ /**
205
+ * The StreamDB interface - provides typed access to collections
206
+ */
207
+ type StreamDB<TDef extends StreamStateDefinition> = {
208
+ collections: CollectionMap<TDef>;
209
+ } & StreamDBMethods;
210
+ /**
211
+ * StreamDB with actions
212
+ */
213
+ type StreamDBWithActions<TDef extends StreamStateDefinition, TActions extends Record<string, ActionDefinition<any>>> = StreamDB<TDef> & {
214
+ actions: ActionMap<TActions>;
215
+ };
216
+ /**
217
+ * Utility methods available on StreamDB
218
+ */
219
+ interface StreamDBUtils {
220
+ /**
221
+ * Wait for a specific transaction ID to be synced through the stream
222
+ * @param txid The transaction ID to wait for (UUID string)
223
+ * @param timeout Optional timeout in milliseconds (defaults to 5000ms)
224
+ * @returns Promise that resolves when the txid is synced
225
+ */
226
+ awaitTxId: (txid: string, timeout?: number) => Promise<void>;
227
+ }
228
+ /**
229
+ * Methods available on a StreamDB instance
230
+ */
231
+ interface StreamDBMethods {
232
+ /**
233
+ * The underlying DurableStream instance
234
+ */
235
+ stream: DurableStream;
236
+ /**
237
+ * Preload all collections by consuming the stream until up-to-date
238
+ */
239
+ preload: () => Promise<void>;
240
+ /**
241
+ * Close the stream connection and cleanup
242
+ */
243
+ close: () => void;
244
+ /**
245
+ * Utility methods for advanced stream operations
246
+ */
247
+ utils: StreamDBUtils;
248
+ }
249
+ /**
250
+ * Create a state schema definition with typed collections and event helpers
251
+ */
252
+ declare function createStateSchema<T extends Record<string, CollectionDefinition>>(collections: T): StateSchema<T>;
253
+ /**
254
+ * Create a stream-backed database with TanStack DB collections
255
+ *
256
+ * This function is synchronous - it creates the stream handle and collections
257
+ * but does not start the stream connection. Call `db.preload()` to connect
258
+ * and sync initial data.
259
+ *
260
+ * @example
261
+ * ```typescript
262
+ * const stateSchema = createStateSchema({
263
+ * users: { schema: userSchema, type: "user", primaryKey: "id" },
264
+ * messages: { schema: messageSchema, type: "message", primaryKey: "id" },
265
+ * })
266
+ *
267
+ * // Create a stream DB (synchronous - stream is created lazily on preload)
268
+ * const db = createStreamDB({
269
+ * streamOptions: {
270
+ * url: "https://api.example.com/streams/my-stream",
271
+ * contentType: "application/json",
272
+ * },
273
+ * state: stateSchema,
274
+ * })
275
+ *
276
+ * // preload() creates the stream and loads initial data
277
+ * await db.preload()
278
+ * const user = await db.collections.users.get("123")
279
+ * ```
280
+ */
281
+ declare function createStreamDB<TDef extends StreamStateDefinition, TActions extends Record<string, ActionDefinition<any>> = Record<string, never>>(options: CreateStreamDBOptions<TDef, TActions>): TActions extends Record<string, never> ? StreamDB<TDef> : StreamDBWithActions<TDef, TActions>;
282
+
283
+ //#endregion
284
+ export { ActionDefinition, ActionFactory, ActionMap, ChangeEvent, ChangeHeaders, CollectionDefinition, CollectionEventHelpers, CollectionWithHelpers, ControlEvent, CreateStreamDBOptions, MaterializedState, Operation, Row, StateEvent, StateSchema, StreamDB, StreamDBMethods, StreamDBUtils, StreamDBWithActions, StreamStateDefinition, Value, createStateSchema, createStreamDB, isChangeEvent, isControlEvent };
@@ -0,0 +1,284 @@
1
+ import { Collection, createOptimisticAction } from "@tanstack/db";
2
+ import { DurableStream, DurableStreamOptions } from "@durable-streams/client";
3
+ import { StandardSchemaV1 } from "@standard-schema/spec";
4
+
5
+ //#region src/types.d.ts
6
+ /**
7
+ * Operation types for change events
8
+ */
9
+ /**
10
+ * Operation types for change events
11
+ */
12
+ type Operation = `insert` | `update` | `delete` | `upsert`;
13
+ /**
14
+ * A generic value type supporting primitives, arrays, and objects
15
+ */
16
+ type Value<Extensions = never> = string | number | boolean | bigint | null | Array<Value<Extensions>> | {
17
+ [key: string]: Value<Extensions>;
18
+ } | Extensions;
19
+ /**
20
+ * A row is a record of values
21
+ */
22
+ type Row<Extensions = never> = Record<string, Value<Extensions>>;
23
+ /**
24
+ * Headers for change messages
25
+ */
26
+ type ChangeHeaders = {
27
+ operation: Operation;
28
+ txid?: string;
29
+ timestamp?: string;
30
+ };
31
+ /**
32
+ * A change event represents a state change event (insert/update/delete)
33
+ */
34
+ type ChangeEvent<T = unknown> = {
35
+ type: string;
36
+ key: string;
37
+ value?: T;
38
+ old_value?: T;
39
+ headers: ChangeHeaders;
40
+ };
41
+ /**
42
+ * Control event types for stream management
43
+ */
44
+ type ControlEvent = {
45
+ headers: {
46
+ control: `snapshot-start` | `snapshot-end` | `reset`;
47
+ offset?: string;
48
+ };
49
+ };
50
+ /**
51
+ * A state event is either a change event or a control event
52
+ */
53
+ type StateEvent<T = unknown> = ChangeEvent<T> | ControlEvent;
54
+ /**
55
+ * Type guard to check if an event is a change event
56
+ */
57
+ declare function isChangeEvent<T = unknown>(event: StateEvent<T>): event is ChangeEvent<T>;
58
+ /**
59
+ * Type guard to check if an event is a control event
60
+ */
61
+ declare function isControlEvent<T = unknown>(event: StateEvent<T>): event is ControlEvent;
62
+
63
+ //#endregion
64
+ //#region src/materialized-state.d.ts
65
+ /**
66
+ * MaterializedState maintains an in-memory view of state from change events.
67
+ *
68
+ * It organizes data by type, where each type contains a map of key -> value.
69
+ * This supports multi-type streams where different entity types can coexist.
70
+ */
71
+ declare class MaterializedState {
72
+ private data;
73
+ constructor();
74
+ /**
75
+ * Apply a single change event to update the materialized state
76
+ */
77
+ apply(event: ChangeEvent): void;
78
+ /**
79
+ * Apply a batch of change events
80
+ */
81
+ applyBatch(events: Array<ChangeEvent>): void;
82
+ /**
83
+ * Get a specific value by type and key
84
+ */
85
+ get<T = unknown>(type: string, key: string): T | undefined;
86
+ /**
87
+ * Get all entries for a specific type
88
+ */
89
+ getType(type: string): Map<string, unknown>;
90
+ /**
91
+ * Clear all state
92
+ */
93
+ clear(): void;
94
+ /**
95
+ * Get the number of types in the state
96
+ */
97
+ get typeCount(): number;
98
+ /**
99
+ * Get all type names
100
+ */
101
+ get types(): Array<string>;
102
+ }
103
+
104
+ //#endregion
105
+ //#region src/stream-db.d.ts
106
+ /**
107
+ * Definition for a single collection in the stream state
108
+ */
109
+ interface CollectionDefinition<T = unknown> {
110
+ /** Standard Schema for validating values */
111
+ schema: StandardSchemaV1<T>;
112
+ /** The type field value in change events that map to this collection */
113
+ type: string;
114
+ /** The property name in T that serves as the primary key */
115
+ primaryKey: string;
116
+ }
117
+ /**
118
+ * Helper methods for creating change events for a collection
119
+ */
120
+ interface CollectionEventHelpers<T> {
121
+ /**
122
+ * Create an insert change event
123
+ */
124
+ insert: (params: {
125
+ key?: string;
126
+ value: T;
127
+ headers?: Omit<Record<string, string>, `operation`>;
128
+ }) => ChangeEvent<T>;
129
+ /**
130
+ * Create an update change event
131
+ */
132
+ update: (params: {
133
+ key?: string;
134
+ value: T;
135
+ oldValue?: T;
136
+ headers?: Omit<Record<string, string>, `operation`>;
137
+ }) => ChangeEvent<T>;
138
+ /**
139
+ * Create a delete change event
140
+ */
141
+ delete: (params: {
142
+ key?: string;
143
+ oldValue?: T;
144
+ headers?: Omit<Record<string, string>, `operation`>;
145
+ }) => ChangeEvent<T>;
146
+ /**
147
+ * Create an upsert change event (insert or update)
148
+ */
149
+ upsert: (params: {
150
+ key?: string;
151
+ value: T;
152
+ headers?: Omit<Record<string, string>, `operation`>;
153
+ }) => ChangeEvent<T>;
154
+ }
155
+ /**
156
+ * Collection definition enhanced with event creation helpers
157
+ */
158
+ type CollectionWithHelpers<T = unknown> = CollectionDefinition<T> & CollectionEventHelpers<T>;
159
+ /**
160
+ * Stream state definition containing all collections
161
+ */
162
+ type StreamStateDefinition = Record<string, CollectionDefinition>;
163
+ /**
164
+ * Stream state schema with helper methods for creating change events
165
+ */
166
+ type StateSchema<T extends Record<string, CollectionDefinition>> = { [K in keyof T]: CollectionWithHelpers<T[K] extends CollectionDefinition<infer U> ? U : unknown> };
167
+ /**
168
+ * Definition for a single action that can be passed to createOptimisticAction
169
+ */
170
+ interface ActionDefinition<TParams = any, TContext = any> {
171
+ onMutate: (params: TParams) => void;
172
+ mutationFn: (params: TParams, context: TContext) => Promise<any>;
173
+ }
174
+ /**
175
+ * Factory function for creating actions with access to db and stream context
176
+ */
177
+ type ActionFactory<TDef extends StreamStateDefinition, TActions extends Record<string, ActionDefinition<any>>> = (context: {
178
+ db: StreamDB<TDef>;
179
+ stream: DurableStream;
180
+ }) => TActions;
181
+ /**
182
+ * Map action definitions to callable action functions
183
+ */
184
+ type ActionMap<TActions extends Record<string, ActionDefinition<any>>> = { [K in keyof TActions]: ReturnType<typeof createOptimisticAction<any>> };
185
+ /**
186
+ * Options for creating a stream DB
187
+ */
188
+ interface CreateStreamDBOptions<TDef extends StreamStateDefinition = StreamStateDefinition, TActions extends Record<string, ActionDefinition<any>> = Record<string, never>> {
189
+ /** Options for creating the durable stream (stream is created lazily on preload) */
190
+ streamOptions: DurableStreamOptions;
191
+ /** The stream state definition */
192
+ state: TDef;
193
+ /** Optional factory function to create actions with db and stream context */
194
+ actions?: ActionFactory<TDef, TActions>;
195
+ }
196
+ /**
197
+ * Extract the value type from a CollectionDefinition
198
+ */
199
+ type ExtractCollectionType<T extends CollectionDefinition> = T extends CollectionDefinition<infer U> ? U : unknown;
200
+ /**
201
+ * Map collection definitions to TanStack DB Collection types
202
+ */
203
+ type CollectionMap<TDef extends StreamStateDefinition> = { [K in keyof TDef]: Collection<ExtractCollectionType<TDef[K]> & object, string> };
204
+ /**
205
+ * The StreamDB interface - provides typed access to collections
206
+ */
207
+ type StreamDB<TDef extends StreamStateDefinition> = {
208
+ collections: CollectionMap<TDef>;
209
+ } & StreamDBMethods;
210
+ /**
211
+ * StreamDB with actions
212
+ */
213
+ type StreamDBWithActions<TDef extends StreamStateDefinition, TActions extends Record<string, ActionDefinition<any>>> = StreamDB<TDef> & {
214
+ actions: ActionMap<TActions>;
215
+ };
216
+ /**
217
+ * Utility methods available on StreamDB
218
+ */
219
+ interface StreamDBUtils {
220
+ /**
221
+ * Wait for a specific transaction ID to be synced through the stream
222
+ * @param txid The transaction ID to wait for (UUID string)
223
+ * @param timeout Optional timeout in milliseconds (defaults to 5000ms)
224
+ * @returns Promise that resolves when the txid is synced
225
+ */
226
+ awaitTxId: (txid: string, timeout?: number) => Promise<void>;
227
+ }
228
+ /**
229
+ * Methods available on a StreamDB instance
230
+ */
231
+ interface StreamDBMethods {
232
+ /**
233
+ * The underlying DurableStream instance
234
+ */
235
+ stream: DurableStream;
236
+ /**
237
+ * Preload all collections by consuming the stream until up-to-date
238
+ */
239
+ preload: () => Promise<void>;
240
+ /**
241
+ * Close the stream connection and cleanup
242
+ */
243
+ close: () => void;
244
+ /**
245
+ * Utility methods for advanced stream operations
246
+ */
247
+ utils: StreamDBUtils;
248
+ }
249
+ /**
250
+ * Create a state schema definition with typed collections and event helpers
251
+ */
252
+ declare function createStateSchema<T extends Record<string, CollectionDefinition>>(collections: T): StateSchema<T>;
253
+ /**
254
+ * Create a stream-backed database with TanStack DB collections
255
+ *
256
+ * This function is synchronous - it creates the stream handle and collections
257
+ * but does not start the stream connection. Call `db.preload()` to connect
258
+ * and sync initial data.
259
+ *
260
+ * @example
261
+ * ```typescript
262
+ * const stateSchema = createStateSchema({
263
+ * users: { schema: userSchema, type: "user", primaryKey: "id" },
264
+ * messages: { schema: messageSchema, type: "message", primaryKey: "id" },
265
+ * })
266
+ *
267
+ * // Create a stream DB (synchronous - stream is created lazily on preload)
268
+ * const db = createStreamDB({
269
+ * streamOptions: {
270
+ * url: "https://api.example.com/streams/my-stream",
271
+ * contentType: "application/json",
272
+ * },
273
+ * state: stateSchema,
274
+ * })
275
+ *
276
+ * // preload() creates the stream and loads initial data
277
+ * await db.preload()
278
+ * const user = await db.collections.users.get("123")
279
+ * ```
280
+ */
281
+ declare function createStreamDB<TDef extends StreamStateDefinition, TActions extends Record<string, ActionDefinition<any>> = Record<string, never>>(options: CreateStreamDBOptions<TDef, TActions>): TActions extends Record<string, never> ? StreamDB<TDef> : StreamDBWithActions<TDef, TActions>;
282
+
283
+ //#endregion
284
+ export { ActionDefinition, ActionFactory, ActionMap, ChangeEvent, ChangeHeaders, CollectionDefinition, CollectionEventHelpers, CollectionWithHelpers, ControlEvent, CreateStreamDBOptions, MaterializedState, Operation, Row, StateEvent, StateSchema, StreamDB, StreamDBMethods, StreamDBUtils, StreamDBWithActions, StreamStateDefinition, Value, createStateSchema, createStreamDB, isChangeEvent, isControlEvent };