@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.
- package/README.md +654 -0
- package/STATE-PROTOCOL.md +502 -0
- package/dist/index.cjs +558 -0
- package/dist/index.d.cts +284 -0
- package/dist/index.d.ts +284 -0
- package/dist/index.js +530 -0
- package/package.json +48 -0
- package/src/index.ts +33 -0
- package/src/materialized-state.ts +93 -0
- package/src/stream-db.ts +934 -0
- package/src/types.ts +80 -0
- package/state-protocol.schema.json +186 -0
package/src/types.ts
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
// Based on Electric SQL's type definitions
|
|
2
|
+
// https://github.com/electric-sql/electric/blob/main/packages/typescript-client/src/types.ts
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Operation types for change events
|
|
6
|
+
*/
|
|
7
|
+
export type Operation = `insert` | `update` | `delete` | `upsert`
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* A generic value type supporting primitives, arrays, and objects
|
|
11
|
+
*/
|
|
12
|
+
export type Value<Extensions = never> =
|
|
13
|
+
| string
|
|
14
|
+
| number
|
|
15
|
+
| boolean
|
|
16
|
+
| bigint
|
|
17
|
+
| null
|
|
18
|
+
| Array<Value<Extensions>>
|
|
19
|
+
| { [key: string]: Value<Extensions> }
|
|
20
|
+
| Extensions
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* A row is a record of values
|
|
24
|
+
*/
|
|
25
|
+
export type Row<Extensions = never> = Record<string, Value<Extensions>>
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Headers for change messages
|
|
29
|
+
*/
|
|
30
|
+
export type ChangeHeaders = {
|
|
31
|
+
operation: Operation
|
|
32
|
+
txid?: string
|
|
33
|
+
timestamp?: string
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* A change event represents a state change event (insert/update/delete)
|
|
38
|
+
*/
|
|
39
|
+
export type ChangeEvent<T = unknown> = {
|
|
40
|
+
type: string
|
|
41
|
+
key: string
|
|
42
|
+
value?: T
|
|
43
|
+
old_value?: T
|
|
44
|
+
headers: ChangeHeaders
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Control event types for stream management
|
|
49
|
+
*/
|
|
50
|
+
export type ControlEvent = {
|
|
51
|
+
headers: {
|
|
52
|
+
control: `snapshot-start` | `snapshot-end` | `reset`
|
|
53
|
+
offset?: string
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* A state event is either a change event or a control event
|
|
59
|
+
*/
|
|
60
|
+
export type StateEvent<T = unknown> = ChangeEvent<T> | ControlEvent
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Type guard to check if an event is a change event
|
|
64
|
+
*/
|
|
65
|
+
export function isChangeEvent<T = unknown>(
|
|
66
|
+
event: StateEvent<T>
|
|
67
|
+
): event is ChangeEvent<T> {
|
|
68
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
69
|
+
return event != null && `operation` in event.headers
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Type guard to check if an event is a control event
|
|
74
|
+
*/
|
|
75
|
+
export function isControlEvent<T = unknown>(
|
|
76
|
+
event: StateEvent<T>
|
|
77
|
+
): event is ControlEvent {
|
|
78
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
79
|
+
return event != null && `control` in event.headers
|
|
80
|
+
}
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
+
"$id": "https://durable-streams.dev/state-protocol/v1",
|
|
4
|
+
"title": "Durable Streams State Protocol Schema",
|
|
5
|
+
"description": "JSON Schema for the Durable Streams State Protocol, an extension of the Durable Streams Protocol that defines change events and control messages for state synchronization",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"oneOf": [
|
|
8
|
+
{
|
|
9
|
+
"$ref": "#/definitions/ChangeMessage"
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
"$ref": "#/definitions/ControlMessage"
|
|
13
|
+
}
|
|
14
|
+
],
|
|
15
|
+
"definitions": {
|
|
16
|
+
"ChangeMessage": {
|
|
17
|
+
"type": "object",
|
|
18
|
+
"title": "Change Message",
|
|
19
|
+
"description": "A change event representing a state mutation (insert, update, or delete)",
|
|
20
|
+
"required": ["type", "key", "headers"],
|
|
21
|
+
"properties": {
|
|
22
|
+
"type": {
|
|
23
|
+
"type": "string",
|
|
24
|
+
"description": "Entity type discriminator. Used to route events to the correct collection or handler in multi-type streams",
|
|
25
|
+
"minLength": 1,
|
|
26
|
+
"examples": ["user", "message", "presence", "config"]
|
|
27
|
+
},
|
|
28
|
+
"key": {
|
|
29
|
+
"type": "string",
|
|
30
|
+
"description": "Entity identifier. Must be unique within a given type",
|
|
31
|
+
"minLength": 1,
|
|
32
|
+
"examples": ["user:123", "msg:456", "config:theme"]
|
|
33
|
+
},
|
|
34
|
+
"value": {
|
|
35
|
+
"description": "The new value for the entity. Required for insert and update operations. For delete operations, this field is typically omitted or set to null",
|
|
36
|
+
"oneOf": [
|
|
37
|
+
{ "type": "string" },
|
|
38
|
+
{ "type": "number" },
|
|
39
|
+
{ "type": "boolean" },
|
|
40
|
+
{ "type": "null" },
|
|
41
|
+
{ "type": "array" },
|
|
42
|
+
{ "type": "object" }
|
|
43
|
+
]
|
|
44
|
+
},
|
|
45
|
+
"old_value": {
|
|
46
|
+
"description": "Optional previous value. May be included in update or delete operations to enable conflict detection or audit logging",
|
|
47
|
+
"oneOf": [
|
|
48
|
+
{ "type": "string" },
|
|
49
|
+
{ "type": "number" },
|
|
50
|
+
{ "type": "boolean" },
|
|
51
|
+
{ "type": "null" },
|
|
52
|
+
{ "type": "array" },
|
|
53
|
+
{ "type": "object" }
|
|
54
|
+
]
|
|
55
|
+
},
|
|
56
|
+
"headers": {
|
|
57
|
+
"$ref": "#/definitions/ChangeHeaders"
|
|
58
|
+
}
|
|
59
|
+
},
|
|
60
|
+
"additionalProperties": false,
|
|
61
|
+
"allOf": [
|
|
62
|
+
{
|
|
63
|
+
"if": {
|
|
64
|
+
"properties": {
|
|
65
|
+
"headers": {
|
|
66
|
+
"properties": {
|
|
67
|
+
"operation": { "const": "insert" }
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
},
|
|
72
|
+
"then": {
|
|
73
|
+
"required": ["value"]
|
|
74
|
+
}
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
"if": {
|
|
78
|
+
"properties": {
|
|
79
|
+
"headers": {
|
|
80
|
+
"properties": {
|
|
81
|
+
"operation": { "const": "update" }
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
},
|
|
86
|
+
"then": {
|
|
87
|
+
"required": ["value"]
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
]
|
|
91
|
+
},
|
|
92
|
+
"ChangeHeaders": {
|
|
93
|
+
"type": "object",
|
|
94
|
+
"title": "Change Headers",
|
|
95
|
+
"description": "Headers for change messages containing operation metadata",
|
|
96
|
+
"required": ["operation"],
|
|
97
|
+
"properties": {
|
|
98
|
+
"operation": {
|
|
99
|
+
"type": "string",
|
|
100
|
+
"enum": ["insert", "update", "delete"],
|
|
101
|
+
"description": "The operation type. 'insert' creates a new entity, 'update' modifies an existing entity, 'delete' removes an entity"
|
|
102
|
+
},
|
|
103
|
+
"txid": {
|
|
104
|
+
"type": "string",
|
|
105
|
+
"description": "Optional transaction identifier. Used to group related changes or enable transactional semantics",
|
|
106
|
+
"minLength": 1
|
|
107
|
+
},
|
|
108
|
+
"timestamp": {
|
|
109
|
+
"type": "string",
|
|
110
|
+
"format": "date-time",
|
|
111
|
+
"description": "Optional RFC 3339 timestamp indicating when the change occurred. If omitted, servers may use the append timestamp"
|
|
112
|
+
}
|
|
113
|
+
},
|
|
114
|
+
"additionalProperties": false
|
|
115
|
+
},
|
|
116
|
+
"ControlMessage": {
|
|
117
|
+
"type": "object",
|
|
118
|
+
"title": "Control Message",
|
|
119
|
+
"description": "A control event for stream management, separate from data changes",
|
|
120
|
+
"required": ["headers"],
|
|
121
|
+
"properties": {
|
|
122
|
+
"headers": {
|
|
123
|
+
"$ref": "#/definitions/ControlHeaders"
|
|
124
|
+
}
|
|
125
|
+
},
|
|
126
|
+
"additionalProperties": false
|
|
127
|
+
},
|
|
128
|
+
"ControlHeaders": {
|
|
129
|
+
"type": "object",
|
|
130
|
+
"title": "Control Headers",
|
|
131
|
+
"description": "Headers for control messages",
|
|
132
|
+
"required": ["control"],
|
|
133
|
+
"properties": {
|
|
134
|
+
"control": {
|
|
135
|
+
"type": "string",
|
|
136
|
+
"enum": ["snapshot-start", "snapshot-end", "reset"],
|
|
137
|
+
"description": "Control event type. 'snapshot-start' and 'snapshot-end' delimit snapshot boundaries, 'reset' signals a state reset"
|
|
138
|
+
},
|
|
139
|
+
"offset": {
|
|
140
|
+
"type": "string",
|
|
141
|
+
"description": "Optional offset associated with the control event. Used to indicate the stream position at which the control event applies",
|
|
142
|
+
"minLength": 1
|
|
143
|
+
}
|
|
144
|
+
},
|
|
145
|
+
"additionalProperties": false
|
|
146
|
+
},
|
|
147
|
+
"Operation": {
|
|
148
|
+
"type": "string",
|
|
149
|
+
"enum": ["insert", "update", "delete"],
|
|
150
|
+
"description": "Operation type for change events"
|
|
151
|
+
},
|
|
152
|
+
"ControlType": {
|
|
153
|
+
"type": "string",
|
|
154
|
+
"enum": ["snapshot-start", "snapshot-end", "reset"],
|
|
155
|
+
"description": "Control event type"
|
|
156
|
+
},
|
|
157
|
+
"Value": {
|
|
158
|
+
"description": "A generic value type supporting primitives, arrays, and objects",
|
|
159
|
+
"oneOf": [
|
|
160
|
+
{ "type": "string" },
|
|
161
|
+
{ "type": "number" },
|
|
162
|
+
{ "type": "boolean" },
|
|
163
|
+
{ "type": "null" },
|
|
164
|
+
{
|
|
165
|
+
"type": "array",
|
|
166
|
+
"items": {
|
|
167
|
+
"$ref": "#/definitions/Value"
|
|
168
|
+
}
|
|
169
|
+
},
|
|
170
|
+
{
|
|
171
|
+
"type": "object",
|
|
172
|
+
"additionalProperties": {
|
|
173
|
+
"$ref": "#/definitions/Value"
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
]
|
|
177
|
+
},
|
|
178
|
+
"Row": {
|
|
179
|
+
"type": "object",
|
|
180
|
+
"description": "A row is a record of values",
|
|
181
|
+
"additionalProperties": {
|
|
182
|
+
"$ref": "#/definitions/Value"
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|