boundlessdb 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/CHANGELOG.md +89 -0
- package/LICENSE +21 -0
- package/README.md +545 -0
- package/dist/better-sqlite3-shim.d.ts +12 -0
- package/dist/better-sqlite3-shim.d.ts.map +1 -0
- package/dist/better-sqlite3-shim.js +18 -0
- package/dist/better-sqlite3-shim.js.map +1 -0
- package/dist/browser.d.ts +14 -0
- package/dist/browser.d.ts.map +1 -0
- package/dist/browser.js +14 -0
- package/dist/browser.js.map +1 -0
- package/dist/config/extractor.d.ts +22 -0
- package/dist/config/extractor.d.ts.map +1 -0
- package/dist/config/extractor.js +132 -0
- package/dist/config/extractor.js.map +1 -0
- package/dist/config/validator.d.ts +14 -0
- package/dist/config/validator.d.ts.map +1 -0
- package/dist/config/validator.js +94 -0
- package/dist/config/validator.js.map +1 -0
- package/dist/event-store.browser.d.ts +61 -0
- package/dist/event-store.browser.d.ts.map +1 -0
- package/dist/event-store.browser.js +323 -0
- package/dist/event-store.browser.js.map +1 -0
- package/dist/event-store.d.ts +101 -0
- package/dist/event-store.d.ts.map +1 -0
- package/dist/event-store.js +249 -0
- package/dist/event-store.js.map +1 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +16 -0
- package/dist/index.js.map +1 -0
- package/dist/query-builder.d.ts +72 -0
- package/dist/query-builder.d.ts.map +1 -0
- package/dist/query-builder.js +84 -0
- package/dist/query-builder.js.map +1 -0
- package/dist/storage/interface.d.ts +48 -0
- package/dist/storage/interface.d.ts.map +1 -0
- package/dist/storage/interface.js +5 -0
- package/dist/storage/interface.js.map +1 -0
- package/dist/storage/memory.d.ts +27 -0
- package/dist/storage/memory.d.ts.map +1 -0
- package/dist/storage/memory.js +94 -0
- package/dist/storage/memory.js.map +1 -0
- package/dist/storage/postgres.d.ts +76 -0
- package/dist/storage/postgres.d.ts.map +1 -0
- package/dist/storage/postgres.js +346 -0
- package/dist/storage/postgres.js.map +1 -0
- package/dist/storage/sqlite.d.ts +47 -0
- package/dist/storage/sqlite.d.ts.map +1 -0
- package/dist/storage/sqlite.js +249 -0
- package/dist/storage/sqlite.js.map +1 -0
- package/dist/storage/sqljs.d.ts +60 -0
- package/dist/storage/sqljs.d.ts.map +1 -0
- package/dist/storage/sqljs.js +354 -0
- package/dist/storage/sqljs.js.map +1 -0
- package/dist/types.d.ts +172 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +52 -0
- package/dist/types.js.map +1 -0
- package/package.json +75 -0
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Main EventStore class
|
|
3
|
+
*/
|
|
4
|
+
import { randomUUID, createHash } from 'node:crypto';
|
|
5
|
+
import { KeyExtractor } from './config/extractor.js';
|
|
6
|
+
import { validateConfig } from './config/validator.js';
|
|
7
|
+
import { SqliteStorage } from './storage/sqlite.js';
|
|
8
|
+
import { QueryResult, isConstrainedCondition, } from './types.js';
|
|
9
|
+
import { QueryBuilder } from './query-builder.js';
|
|
10
|
+
/**
|
|
11
|
+
* Recursively sort object keys for deterministic JSON
|
|
12
|
+
*/
|
|
13
|
+
function sortObjectKeys(obj) {
|
|
14
|
+
if (obj === null || typeof obj !== 'object') {
|
|
15
|
+
return obj;
|
|
16
|
+
}
|
|
17
|
+
if (Array.isArray(obj)) {
|
|
18
|
+
return obj.map(sortObjectKeys);
|
|
19
|
+
}
|
|
20
|
+
const sorted = {};
|
|
21
|
+
for (const key of Object.keys(obj).sort()) {
|
|
22
|
+
sorted[key] = sortObjectKeys(obj[key]);
|
|
23
|
+
}
|
|
24
|
+
return sorted;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Compute SHA256 hash of ConsistencyConfig
|
|
28
|
+
*/
|
|
29
|
+
function hashConfig(config) {
|
|
30
|
+
const normalized = JSON.stringify(sortObjectKeys(config));
|
|
31
|
+
return createHash('sha256').update(normalized).digest('hex');
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* DCB-native Event Store
|
|
35
|
+
*
|
|
36
|
+
* Implements Dynamic Consistency Boundaries for event sourcing.
|
|
37
|
+
* No cryptographic signing - tokens are Base64 encoded for convenience.
|
|
38
|
+
*/
|
|
39
|
+
export class EventStore {
|
|
40
|
+
storage;
|
|
41
|
+
keyExtractor;
|
|
42
|
+
config;
|
|
43
|
+
constructor(options) {
|
|
44
|
+
// Validate configuration
|
|
45
|
+
validateConfig(options.consistency);
|
|
46
|
+
this.storage = options.storage;
|
|
47
|
+
this.config = options.consistency;
|
|
48
|
+
this.keyExtractor = new KeyExtractor(this.config);
|
|
49
|
+
// Check config hash and reindex if needed (SqliteStorage only)
|
|
50
|
+
this.checkAndReindexIfNeeded();
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Check if config has changed since last run, reindex if needed
|
|
54
|
+
*/
|
|
55
|
+
checkAndReindexIfNeeded() {
|
|
56
|
+
// Only works with SqliteStorage (has metadata table)
|
|
57
|
+
if (!(this.storage instanceof SqliteStorage)) {
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
const currentHash = hashConfig(this.config);
|
|
61
|
+
const storedHash = this.storage.getConfigHash();
|
|
62
|
+
if (storedHash === null) {
|
|
63
|
+
// First run — just store the hash
|
|
64
|
+
console.log('[EventStore] First run, storing config hash:', currentHash.substring(0, 16) + '...');
|
|
65
|
+
this.storage.setConfigHash(currentHash);
|
|
66
|
+
}
|
|
67
|
+
else if (storedHash !== currentHash) {
|
|
68
|
+
// Config changed — reindex!
|
|
69
|
+
console.log('[EventStore] ⚠️ Config changed! Rebuilding key index...');
|
|
70
|
+
console.log(`[EventStore] Old hash: ${storedHash.substring(0, 16)}...`);
|
|
71
|
+
console.log(`[EventStore] New hash: ${currentHash.substring(0, 16)}...`);
|
|
72
|
+
const startTime = Date.now();
|
|
73
|
+
let eventCount = 0;
|
|
74
|
+
let keyCount = 0;
|
|
75
|
+
this.storage.reindex((event) => {
|
|
76
|
+
eventCount++;
|
|
77
|
+
// Convert StoredEvent to Event format for KeyExtractor
|
|
78
|
+
const keys = this.keyExtractor.extract({
|
|
79
|
+
type: event.type,
|
|
80
|
+
data: event.data,
|
|
81
|
+
metadata: event.metadata
|
|
82
|
+
});
|
|
83
|
+
keyCount += keys.length;
|
|
84
|
+
return keys;
|
|
85
|
+
});
|
|
86
|
+
// Update stored hash
|
|
87
|
+
this.storage.setConfigHash(currentHash);
|
|
88
|
+
const duration = Date.now() - startTime;
|
|
89
|
+
console.log(`[EventStore] ✅ Reindex complete: ${eventCount} events, ${keyCount} keys (${duration}ms)`);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Create a fluent query builder.
|
|
94
|
+
*
|
|
95
|
+
* @typeParam E - Event union type for typed results
|
|
96
|
+
* @returns QueryBuilder for chaining
|
|
97
|
+
*
|
|
98
|
+
* @example
|
|
99
|
+
* ```typescript
|
|
100
|
+
* const result = await store.query<CourseEvent>()
|
|
101
|
+
* .matchType('CourseCreated')
|
|
102
|
+
* .matchKey('StudentSubscribed', 'course', 'cs101')
|
|
103
|
+
* .fromPosition(100n)
|
|
104
|
+
* .limit(50)
|
|
105
|
+
* .read();
|
|
106
|
+
* ```
|
|
107
|
+
*/
|
|
108
|
+
query() {
|
|
109
|
+
return new QueryBuilder(this);
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Read events matching a query
|
|
113
|
+
*
|
|
114
|
+
* @typeParam E - Event union type for typed results
|
|
115
|
+
* @returns QueryResult with typed events and consistency token
|
|
116
|
+
*
|
|
117
|
+
* @example
|
|
118
|
+
* ```typescript
|
|
119
|
+
* // Typed read
|
|
120
|
+
* const result = await store.read<CartEvents>({
|
|
121
|
+
* conditions: [{ type: 'ProductItemAdded', key: 'cart', value: 'cart-123' }]
|
|
122
|
+
* });
|
|
123
|
+
*
|
|
124
|
+
* // Untyped read (all events of type)
|
|
125
|
+
* const result = await store.read({
|
|
126
|
+
* conditions: [{ type: 'ProductItemAdded' }]
|
|
127
|
+
* });
|
|
128
|
+
* ```
|
|
129
|
+
*/
|
|
130
|
+
async read(query) {
|
|
131
|
+
const events = await this.storage.query(query.conditions, query.fromPosition, query.limit);
|
|
132
|
+
// Get the position for the append condition
|
|
133
|
+
// If we have events, use the last event's position
|
|
134
|
+
// Otherwise, use the current latest position
|
|
135
|
+
const position = events.length > 0
|
|
136
|
+
? events[events.length - 1].position
|
|
137
|
+
: await this.storage.getLatestPosition();
|
|
138
|
+
return new QueryResult(events, position, query.conditions);
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Append events with optional consistency check
|
|
142
|
+
*
|
|
143
|
+
* @typeParam E - Event type for type checking
|
|
144
|
+
* @param events Events to append
|
|
145
|
+
* @param condition Consistency check - can be:
|
|
146
|
+
* - null: Skip consistency check (optimistic first write)
|
|
147
|
+
* - AppendCondition: { position, conditions } from a previous read
|
|
148
|
+
* @returns AppendResult on success, ConflictResult on conflict
|
|
149
|
+
*
|
|
150
|
+
* @example
|
|
151
|
+
* ```typescript
|
|
152
|
+
* // With appendCondition from read
|
|
153
|
+
* const result = await store.read<CartEvents>({ conditions: [...] });
|
|
154
|
+
* await store.append<CartEvents>([newEvent], result.appendCondition);
|
|
155
|
+
*
|
|
156
|
+
* // Without consistency check (first write)
|
|
157
|
+
* await store.append<CartEvents>([newEvent], null);
|
|
158
|
+
* ```
|
|
159
|
+
*/
|
|
160
|
+
async append(events, condition) {
|
|
161
|
+
if (events.length === 0) {
|
|
162
|
+
// Nothing to append
|
|
163
|
+
const position = await this.storage.getLatestPosition();
|
|
164
|
+
return {
|
|
165
|
+
conflict: false,
|
|
166
|
+
position,
|
|
167
|
+
appendCondition: { position, conditions: condition?.conditions ?? [] },
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
// Extract keys from all events
|
|
171
|
+
const keysPerEvent = events.map(event => this.keyExtractor.extract(event));
|
|
172
|
+
// Check for conflicts if condition provided
|
|
173
|
+
if (condition !== null) {
|
|
174
|
+
const conflictingEvents = await this.storage.getEventsSince(condition.conditions, condition.position);
|
|
175
|
+
if (conflictingEvents.length > 0) {
|
|
176
|
+
// Conflict detected — return delta
|
|
177
|
+
const latestPosition = conflictingEvents[conflictingEvents.length - 1].position;
|
|
178
|
+
return {
|
|
179
|
+
conflict: true,
|
|
180
|
+
conflictingEvents: conflictingEvents,
|
|
181
|
+
appendCondition: { position: latestPosition, conditions: condition.conditions },
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
// No conflict — prepare events for storage
|
|
186
|
+
const now = new Date();
|
|
187
|
+
const eventsToStore = events.map(event => ({
|
|
188
|
+
id: randomUUID(),
|
|
189
|
+
type: event.type,
|
|
190
|
+
data: event.data,
|
|
191
|
+
metadata: event.metadata,
|
|
192
|
+
timestamp: now,
|
|
193
|
+
}));
|
|
194
|
+
// Append atomically
|
|
195
|
+
const position = await this.storage.append(eventsToStore, keysPerEvent);
|
|
196
|
+
// Build new appendCondition
|
|
197
|
+
const newConditions = this.buildConditionsFromEvents(events, condition);
|
|
198
|
+
return {
|
|
199
|
+
conflict: false,
|
|
200
|
+
position,
|
|
201
|
+
appendCondition: { position, conditions: newConditions },
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Build conditions that cover the appended events
|
|
206
|
+
* Used to create the appendCondition returned after append
|
|
207
|
+
*/
|
|
208
|
+
buildConditionsFromEvents(events, originalCondition) {
|
|
209
|
+
// Start with original conditions if provided
|
|
210
|
+
const conditions = new Map();
|
|
211
|
+
if (originalCondition !== null) {
|
|
212
|
+
for (const cond of originalCondition.conditions) {
|
|
213
|
+
if (isConstrainedCondition(cond)) {
|
|
214
|
+
const key = `${cond.type}:${cond.key}:${cond.value}`;
|
|
215
|
+
conditions.set(key, cond);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
// Add conditions from the newly appended events
|
|
220
|
+
for (const event of events) {
|
|
221
|
+
const extractedKeys = this.keyExtractor.extract(event);
|
|
222
|
+
for (const extracted of extractedKeys) {
|
|
223
|
+
const cond = { type: event.type, key: extracted.name, value: extracted.value };
|
|
224
|
+
const key = `${cond.type}:${cond.key}:${cond.value}`;
|
|
225
|
+
conditions.set(key, cond);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
return Array.from(conditions.values());
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* Get the underlying storage (for advanced use cases)
|
|
232
|
+
*/
|
|
233
|
+
getStorage() {
|
|
234
|
+
return this.storage;
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* Close the event store
|
|
238
|
+
*/
|
|
239
|
+
async close() {
|
|
240
|
+
await this.storage.close();
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* Factory function to create an event store
|
|
245
|
+
*/
|
|
246
|
+
export function createEventStore(options) {
|
|
247
|
+
return new EventStore(options);
|
|
248
|
+
}
|
|
249
|
+
//# sourceMappingURL=event-store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"event-store.js","sourceRoot":"","sources":["../src/event-store.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACrD,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AACrD,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAEvD,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EACL,WAAW,EACX,sBAAsB,GAYvB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,YAAY,EAAsB,MAAM,oBAAoB,CAAC;AAEtE;;GAEG;AACH,SAAS,cAAc,CAAC,GAAY;IAClC,IAAI,GAAG,KAAK,IAAI,IAAI,OAAO,GAAG,KAAK,QAAQ,EAAE,CAAC;QAC5C,OAAO,GAAG,CAAC;IACb,CAAC;IACD,IAAI,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;QACvB,OAAO,GAAG,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;IACjC,CAAC;IACD,MAAM,MAAM,GAA4B,EAAE,CAAC;IAC3C,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC;QAC1C,MAAM,CAAC,GAAG,CAAC,GAAG,cAAc,CAAE,GAA+B,CAAC,GAAG,CAAC,CAAC,CAAC;IACtE,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;GAEG;AACH,SAAS,UAAU,CAAC,MAAyB;IAC3C,MAAM,UAAU,GAAG,IAAI,CAAC,SAAS,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC,CAAC;IAC1D,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AAC/D,CAAC;AAMD;;;;;GAKG;AACH,MAAM,OAAO,UAAU;IACJ,OAAO,CAAe;IACtB,YAAY,CAAe;IAC3B,MAAM,CAAoB;IAE3C,YAAY,OAAyB;QACnC,yBAAyB;QACzB,cAAc,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC;QAEpC,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC;QAC/B,IAAI,CAAC,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC;QAClC,IAAI,CAAC,YAAY,GAAG,IAAI,YAAY,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAElD,+DAA+D;QAC/D,IAAI,CAAC,uBAAuB,EAAE,CAAC;IACjC,CAAC;IAED;;OAEG;IACK,uBAAuB;QAC7B,qDAAqD;QACrD,IAAI,CAAC,CAAC,IAAI,CAAC,OAAO,YAAY,aAAa,CAAC,EAAE,CAAC;YAC7C,OAAO;QACT,CAAC;QAED,MAAM,WAAW,GAAG,UAAU,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAC5C,MAAM,UAAU,GAAG,IAAI,CAAC,OAAO,CAAC,aAAa,EAAE,CAAC;QAEhD,IAAI,UAAU,KAAK,IAAI,EAAE,CAAC;YACxB,kCAAkC;YAClC,OAAO,CAAC,GAAG,CAAC,8CAA8C,EAAE,WAAW,CAAC,SAAS,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,KAAK,CAAC,CAAC;YAClG,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,WAAW,CAAC,CAAC;QAC1C,CAAC;aAAM,IAAI,UAAU,KAAK,WAAW,EAAE,CAAC;YACtC,4BAA4B;YAC5B,OAAO,CAAC,GAAG,CAAC,0DAA0D,CAAC,CAAC;YACxE,OAAO,CAAC,GAAG,CAAC,6BAA6B,UAAU,CAAC,SAAS,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,CAAC;YAC3E,OAAO,CAAC,GAAG,CAAC,6BAA6B,WAAW,CAAC,SAAS,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,CAAC;YAC5E,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;YAE7B,IAAI,UAAU,GAAG,CAAC,CAAC;YACnB,IAAI,QAAQ,GAAG,CAAC,CAAC;YAEjB,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,EAAE;gBAC7B,UAAU,EAAE,CAAC;gBACb,uDAAuD;gBACvD,MAAM,IAAI,GAAG,IAAI,CAAC,YAAY,CAAC,OAAO,CAAC;oBACrC,IAAI,EAAE,KAAK,CAAC,IAAI;oBAChB,IAAI,EAAE,KAAK,CAAC,IAAI;oBAChB,QAAQ,EAAE,KAAK,CAAC,QAAQ;iBACzB,CAAC,CAAC;gBACH,QAAQ,IAAI,IAAI,CAAC,MAAM,CAAC;gBACxB,OAAO,IAAI,CAAC;YACd,CAAC,CAAC,CAAC;YAEH,qBAAqB;YACrB,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,WAAW,CAAC,CAAC;YAExC,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS,CAAC;YACxC,OAAO,CAAC,GAAG,CAAC,oCAAoC,UAAU,YAAY,QAAQ,UAAU,QAAQ,KAAK,CAAC,CAAC;QACzG,CAAC;IACH,CAAC;IAED;;;;;;;;;;;;;;;OAeG;IACH,KAAK;QACH,OAAO,IAAI,YAAY,CAAI,IAAmC,CAAC,CAAC;IAClE,CAAC;IAED;;;;;;;;;;;;;;;;;;OAkBG;IACH,KAAK,CAAC,IAAI,CAA0B,KAAY;QAC9C,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,KAAK,CACrC,KAAK,CAAC,UAAU,EAChB,KAAK,CAAC,YAAY,EAClB,KAAK,CAAC,KAAK,CACZ,CAAC;QAEF,4CAA4C;QAC5C,mDAAmD;QACnD,6CAA6C;QAC7C,MAAM,QAAQ,GAAG,MAAM,CAAC,MAAM,GAAG,CAAC;YAChC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,QAAQ;YACpC,CAAC,CAAC,MAAM,IAAI,CAAC,OAAO,CAAC,iBAAiB,EAAE,CAAC;QAE3C,OAAO,IAAI,WAAW,CACpB,MAA0B,EAC1B,QAAQ,EACR,KAAK,CAAC,UAAU,CACjB,CAAC;IACJ,CAAC;IAED;;;;;;;;;;;;;;;;;;;OAmBG;IACH,KAAK,CAAC,MAAM,CACV,MAA8B,EAC9B,SAAiC;QAEjC,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACxB,oBAAoB;YACpB,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,iBAAiB,EAAE,CAAC;YACxD,OAAO;gBACL,QAAQ,EAAE,KAAK;gBACf,QAAQ;gBACR,eAAe,EAAE,EAAE,QAAQ,EAAE,UAAU,EAAE,SAAS,EAAE,UAAU,IAAI,EAAE,EAAE;aACvE,CAAC;QACJ,CAAC;QAED,+BAA+B;QAC/B,MAAM,YAAY,GAAG,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,IAAI,CAAC,YAAY,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC;QAE3E,4CAA4C;QAC5C,IAAI,SAAS,KAAK,IAAI,EAAE,CAAC;YACvB,MAAM,iBAAiB,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,cAAc,CACzD,SAAS,CAAC,UAAU,EACpB,SAAS,CAAC,QAAQ,CACnB,CAAC;YAEF,IAAI,iBAAiB,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBACjC,mCAAmC;gBACnC,MAAM,cAAc,GAAG,iBAAiB,CAAC,iBAAiB,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC;gBAEhF,OAAO;oBACL,QAAQ,EAAE,IAAI;oBACd,iBAAiB,EAAE,iBAAqC;oBACxD,eAAe,EAAE,EAAE,QAAQ,EAAE,cAAc,EAAE,UAAU,EAAE,SAAS,CAAC,UAAU,EAAE;iBAChF,CAAC;YACJ,CAAC;QACH,CAAC;QAED,2CAA2C;QAC3C,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC;QACvB,MAAM,aAAa,GAAG,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;YACzC,EAAE,EAAE,UAAU,EAAE;YAChB,IAAI,EAAE,KAAK,CAAC,IAAI;YAChB,IAAI,EAAE,KAAK,CAAC,IAAI;YAChB,QAAQ,EAAE,KAAK,CAAC,QAAQ;YACxB,SAAS,EAAE,GAAG;SACf,CAAC,CAAC,CAAC;QAEJ,oBAAoB;QACpB,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,aAAa,EAAE,YAAY,CAAC,CAAC;QAExE,4BAA4B;QAC5B,MAAM,aAAa,GAAG,IAAI,CAAC,yBAAyB,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;QAExE,OAAO;YACL,QAAQ,EAAE,KAAK;YACf,QAAQ;YACR,eAAe,EAAE,EAAE,QAAQ,EAAE,UAAU,EAAE,aAAa,EAAE;SACzD,CAAC;IACJ,CAAC;IAED;;;OAGG;IACK,yBAAyB,CAC/B,MAA8B,EAC9B,iBAAyC;QAEzC,6CAA6C;QAC7C,MAAM,UAAU,GAAG,IAAI,GAAG,EAAgC,CAAC;QAE3D,IAAI,iBAAiB,KAAK,IAAI,EAAE,CAAC;YAC/B,KAAK,MAAM,IAAI,IAAI,iBAAiB,CAAC,UAAU,EAAE,CAAC;gBAChD,IAAI,sBAAsB,CAAC,IAAI,CAAC,EAAE,CAAC;oBACjC,MAAM,GAAG,GAAG,GAAG,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,GAAG,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;oBACrD,UAAU,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;gBAC5B,CAAC;YACH,CAAC;QACH,CAAC;QAED,gDAAgD;QAChD,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;YAC3B,MAAM,aAAa,GAAG,IAAI,CAAC,YAAY,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;YACvD,KAAK,MAAM,SAAS,IAAI,aAAa,EAAE,CAAC;gBACtC,MAAM,IAAI,GAAyB,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,GAAG,EAAE,SAAS,CAAC,IAAI,EAAE,KAAK,EAAE,SAAS,CAAC,KAAK,EAAE,CAAC;gBACrG,MAAM,GAAG,GAAG,GAAG,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,GAAG,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;gBACrD,UAAU,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;YAC5B,CAAC;QACH,CAAC;QAED,OAAO,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,MAAM,EAAE,CAAC,CAAC;IACzC,CAAC;IAED;;OAEG;IACH,UAAU;QACR,OAAO,IAAI,CAAC,OAAO,CAAC;IACtB,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,KAAK;QACT,MAAM,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;IAC7B,CAAC;CACF;AAED;;GAEG;AACH,MAAM,UAAU,gBAAgB,CAAC,OAAyB;IACxD,OAAO,IAAI,UAAU,CAAC,OAAO,CAAC,CAAC;AACjC,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DCB Event Store - Public API
|
|
3
|
+
*/
|
|
4
|
+
export type { Event, EventWithMetadata, StoredEvent, Query, QueryCondition, UnconstrainedCondition, ConstrainedCondition, ConsistencyConfig, ConsistencyKeyDef, EventTypeConfig, ExtractedKey, AppendResult, ConflictResult, AppendCondition, EventStoreOptions, } from './types.js';
|
|
5
|
+
export { QueryResult, isConflict, isConstrainedCondition } from './types.js';
|
|
6
|
+
export { EventStore, createEventStore, type EventStoreConfig } from './event-store.js';
|
|
7
|
+
export type { EventStorage, EventToStore } from './storage/interface.js';
|
|
8
|
+
export { InMemoryStorage } from './storage/memory.js';
|
|
9
|
+
export { SqliteStorage } from './storage/sqlite.js';
|
|
10
|
+
export { SqlJsStorage, type SqlJsStorageOptions } from './storage/sqljs.js';
|
|
11
|
+
export { PostgresStorage } from './storage/postgres.js';
|
|
12
|
+
export { KeyExtractor, KeyExtractionError } from './config/extractor.js';
|
|
13
|
+
export { validateConfig, ConfigValidationError } from './config/validator.js';
|
|
14
|
+
export { QueryBuilder } from './query-builder.js';
|
|
15
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,YAAY,EAEV,KAAK,EACL,iBAAiB,EACjB,WAAW,EAEX,KAAK,EACL,cAAc,EACd,sBAAsB,EACtB,oBAAoB,EAEpB,iBAAiB,EACjB,iBAAiB,EACjB,eAAe,EACf,YAAY,EAEZ,YAAY,EACZ,cAAc,EACd,eAAe,EACf,iBAAiB,GAClB,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAC;AAG7E,OAAO,EAAE,UAAU,EAAE,gBAAgB,EAAE,KAAK,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAGvF,YAAY,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AACzE,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AACtD,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,YAAY,EAAE,KAAK,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAC5E,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAGxD,OAAO,EAAE,YAAY,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AACzE,OAAO,EAAE,cAAc,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAG9E,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DCB Event Store - Public API
|
|
3
|
+
*/
|
|
4
|
+
export { QueryResult, isConflict, isConstrainedCondition } from './types.js';
|
|
5
|
+
// Event Store
|
|
6
|
+
export { EventStore, createEventStore } from './event-store.js';
|
|
7
|
+
export { InMemoryStorage } from './storage/memory.js';
|
|
8
|
+
export { SqliteStorage } from './storage/sqlite.js';
|
|
9
|
+
export { SqlJsStorage } from './storage/sqljs.js';
|
|
10
|
+
export { PostgresStorage } from './storage/postgres.js';
|
|
11
|
+
// Config
|
|
12
|
+
export { KeyExtractor, KeyExtractionError } from './config/extractor.js';
|
|
13
|
+
export { validateConfig, ConfigValidationError } from './config/validator.js';
|
|
14
|
+
// Query Builder
|
|
15
|
+
export { QueryBuilder } from './query-builder.js';
|
|
16
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAyBH,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAC;AAE7E,cAAc;AACd,OAAO,EAAE,UAAU,EAAE,gBAAgB,EAAyB,MAAM,kBAAkB,CAAC;AAIvF,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AACtD,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,YAAY,EAA4B,MAAM,oBAAoB,CAAC;AAC5E,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAExD,SAAS;AACT,OAAO,EAAE,YAAY,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AACzE,OAAO,EAAE,cAAc,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAE9E,gBAAgB;AAChB,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fluent Query Builder for BoundlessDB
|
|
3
|
+
*/
|
|
4
|
+
import type { Event, QueryCondition, QueryResult } from './types.js';
|
|
5
|
+
export interface QueryExecutor<E extends Event> {
|
|
6
|
+
read(query: {
|
|
7
|
+
conditions: QueryCondition[];
|
|
8
|
+
fromPosition?: bigint;
|
|
9
|
+
limit?: number;
|
|
10
|
+
}): Promise<QueryResult<E>>;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Fluent API for building queries.
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```typescript
|
|
17
|
+
* const result = await store.query<CourseEvent>()
|
|
18
|
+
* .matchType('CourseCreated')
|
|
19
|
+
* .matchKey('StudentSubscribed', 'course', 'cs101')
|
|
20
|
+
* .fromPosition(100n)
|
|
21
|
+
* .limit(50)
|
|
22
|
+
* .read();
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
export declare class QueryBuilder<E extends Event> {
|
|
26
|
+
private readonly executor;
|
|
27
|
+
private conditions;
|
|
28
|
+
private _fromPosition?;
|
|
29
|
+
private _limit?;
|
|
30
|
+
constructor(executor: QueryExecutor<E>);
|
|
31
|
+
/**
|
|
32
|
+
* Add an unconstrained condition (match all events of type).
|
|
33
|
+
*
|
|
34
|
+
* @example
|
|
35
|
+
* ```typescript
|
|
36
|
+
* .matchType('CourseCreated') // matches ALL CourseCreated events
|
|
37
|
+
* ```
|
|
38
|
+
*/
|
|
39
|
+
matchType(type: string): this;
|
|
40
|
+
/**
|
|
41
|
+
* Add a constrained condition (match events where key equals value).
|
|
42
|
+
*
|
|
43
|
+
* @example
|
|
44
|
+
* ```typescript
|
|
45
|
+
* .matchKey('StudentSubscribed', 'course', 'cs101')
|
|
46
|
+
* ```
|
|
47
|
+
*/
|
|
48
|
+
matchKey(type: string, key: string, value: string): this;
|
|
49
|
+
/**
|
|
50
|
+
* Start reading from a specific position.
|
|
51
|
+
*
|
|
52
|
+
* @example
|
|
53
|
+
* ```typescript
|
|
54
|
+
* .fromPosition(100n) // skip events before position 100
|
|
55
|
+
* ```
|
|
56
|
+
*/
|
|
57
|
+
fromPosition(position: bigint): this;
|
|
58
|
+
/**
|
|
59
|
+
* Limit the number of events returned.
|
|
60
|
+
*
|
|
61
|
+
* @example
|
|
62
|
+
* ```typescript
|
|
63
|
+
* .limit(50) // return at most 50 events
|
|
64
|
+
* ```
|
|
65
|
+
*/
|
|
66
|
+
limit(count: number): this;
|
|
67
|
+
/**
|
|
68
|
+
* Execute the query and return results.
|
|
69
|
+
*/
|
|
70
|
+
read(): Promise<QueryResult<E>>;
|
|
71
|
+
}
|
|
72
|
+
//# sourceMappingURL=query-builder.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"query-builder.d.ts","sourceRoot":"","sources":["../src/query-builder.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,KAAK,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAErE,MAAM,WAAW,aAAa,CAAC,CAAC,SAAS,KAAK;IAC5C,IAAI,CAAC,KAAK,EAAE;QACV,UAAU,EAAE,cAAc,EAAE,CAAC;QAC7B,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,KAAK,CAAC,EAAE,MAAM,CAAC;KAChB,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC;CAC7B;AAED;;;;;;;;;;;;GAYG;AACH,qBAAa,YAAY,CAAC,CAAC,SAAS,KAAK;IAK3B,OAAO,CAAC,QAAQ,CAAC,QAAQ;IAJrC,OAAO,CAAC,UAAU,CAAwB;IAC1C,OAAO,CAAC,aAAa,CAAC,CAAS;IAC/B,OAAO,CAAC,MAAM,CAAC,CAAS;gBAEK,QAAQ,EAAE,aAAa,CAAC,CAAC,CAAC;IAEvD;;;;;;;OAOG;IACH,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAK7B;;;;;;;OAOG;IACH,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI;IAKxD;;;;;;;OAOG;IACH,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IAKpC;;;;;;;OAOG;IACH,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAK1B;;OAEG;IACG,IAAI,IAAI,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC;CAOtC"}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fluent Query Builder for BoundlessDB
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Fluent API for building queries.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```typescript
|
|
9
|
+
* const result = await store.query<CourseEvent>()
|
|
10
|
+
* .matchType('CourseCreated')
|
|
11
|
+
* .matchKey('StudentSubscribed', 'course', 'cs101')
|
|
12
|
+
* .fromPosition(100n)
|
|
13
|
+
* .limit(50)
|
|
14
|
+
* .read();
|
|
15
|
+
* ```
|
|
16
|
+
*/
|
|
17
|
+
export class QueryBuilder {
|
|
18
|
+
executor;
|
|
19
|
+
conditions = [];
|
|
20
|
+
_fromPosition;
|
|
21
|
+
_limit;
|
|
22
|
+
constructor(executor) {
|
|
23
|
+
this.executor = executor;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Add an unconstrained condition (match all events of type).
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* ```typescript
|
|
30
|
+
* .matchType('CourseCreated') // matches ALL CourseCreated events
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
matchType(type) {
|
|
34
|
+
this.conditions.push({ type });
|
|
35
|
+
return this;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Add a constrained condition (match events where key equals value).
|
|
39
|
+
*
|
|
40
|
+
* @example
|
|
41
|
+
* ```typescript
|
|
42
|
+
* .matchKey('StudentSubscribed', 'course', 'cs101')
|
|
43
|
+
* ```
|
|
44
|
+
*/
|
|
45
|
+
matchKey(type, key, value) {
|
|
46
|
+
this.conditions.push({ type, key, value });
|
|
47
|
+
return this;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Start reading from a specific position.
|
|
51
|
+
*
|
|
52
|
+
* @example
|
|
53
|
+
* ```typescript
|
|
54
|
+
* .fromPosition(100n) // skip events before position 100
|
|
55
|
+
* ```
|
|
56
|
+
*/
|
|
57
|
+
fromPosition(position) {
|
|
58
|
+
this._fromPosition = position;
|
|
59
|
+
return this;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Limit the number of events returned.
|
|
63
|
+
*
|
|
64
|
+
* @example
|
|
65
|
+
* ```typescript
|
|
66
|
+
* .limit(50) // return at most 50 events
|
|
67
|
+
* ```
|
|
68
|
+
*/
|
|
69
|
+
limit(count) {
|
|
70
|
+
this._limit = count;
|
|
71
|
+
return this;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Execute the query and return results.
|
|
75
|
+
*/
|
|
76
|
+
async read() {
|
|
77
|
+
return this.executor.read({
|
|
78
|
+
conditions: this.conditions,
|
|
79
|
+
fromPosition: this._fromPosition,
|
|
80
|
+
limit: this._limit,
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
//# sourceMappingURL=query-builder.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"query-builder.js","sourceRoot":"","sources":["../src/query-builder.ts"],"names":[],"mappings":"AAAA;;GAEG;AAYH;;;;;;;;;;;;GAYG;AACH,MAAM,OAAO,YAAY;IAKM;IAJrB,UAAU,GAAqB,EAAE,CAAC;IAClC,aAAa,CAAU;IACvB,MAAM,CAAU;IAExB,YAA6B,QAA0B;QAA1B,aAAQ,GAAR,QAAQ,CAAkB;IAAG,CAAC;IAE3D;;;;;;;OAOG;IACH,SAAS,CAAC,IAAY;QACpB,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC;QAC/B,OAAO,IAAI,CAAC;IACd,CAAC;IAED;;;;;;;OAOG;IACH,QAAQ,CAAC,IAAY,EAAE,GAAW,EAAE,KAAa;QAC/C,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,GAAG,EAAE,KAAK,EAAE,CAAC,CAAC;QAC3C,OAAO,IAAI,CAAC;IACd,CAAC;IAED;;;;;;;OAOG;IACH,YAAY,CAAC,QAAgB;QAC3B,IAAI,CAAC,aAAa,GAAG,QAAQ,CAAC;QAC9B,OAAO,IAAI,CAAC;IACd,CAAC;IAED;;;;;;;OAOG;IACH,KAAK,CAAC,KAAa;QACjB,IAAI,CAAC,MAAM,GAAG,KAAK,CAAC;QACpB,OAAO,IAAI,CAAC;IACd,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,IAAI;QACR,OAAO,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC;YACxB,UAAU,EAAE,IAAI,CAAC,UAAU;YAC3B,YAAY,EAAE,IAAI,CAAC,aAAa;YAChC,KAAK,EAAE,IAAI,CAAC,MAAM;SACnB,CAAC,CAAC;IACL,CAAC;CACF"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Storage Interface for the Event Store
|
|
3
|
+
*/
|
|
4
|
+
import type { ExtractedKey, QueryCondition, StoredEvent } from '../types.js';
|
|
5
|
+
/**
|
|
6
|
+
* Event to be stored (before position assignment)
|
|
7
|
+
*/
|
|
8
|
+
export interface EventToStore {
|
|
9
|
+
id: string;
|
|
10
|
+
type: string;
|
|
11
|
+
data: unknown;
|
|
12
|
+
metadata?: Record<string, unknown>;
|
|
13
|
+
timestamp: Date;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Storage backend interface
|
|
17
|
+
*/
|
|
18
|
+
export interface EventStorage {
|
|
19
|
+
/**
|
|
20
|
+
* Append events to the store with their extracted keys
|
|
21
|
+
* Must be atomic: either all events are stored, or none
|
|
22
|
+
* @returns Position of the last appended event
|
|
23
|
+
*/
|
|
24
|
+
append(events: EventToStore[], keys: ExtractedKey[][]): Promise<bigint>;
|
|
25
|
+
/**
|
|
26
|
+
* Query events by conditions
|
|
27
|
+
* @param conditions Query conditions (type + key + value)
|
|
28
|
+
* @param fromPosition Start position (exclusive)
|
|
29
|
+
* @param limit Maximum number of events to return
|
|
30
|
+
*/
|
|
31
|
+
query(conditions: QueryCondition[], fromPosition?: bigint, limit?: number): Promise<StoredEvent[]>;
|
|
32
|
+
/**
|
|
33
|
+
* Get events since a given position that match the conditions
|
|
34
|
+
* Used for consistency checks (conflict detection)
|
|
35
|
+
* @returns Events that match AND have position > sincePosition
|
|
36
|
+
*/
|
|
37
|
+
getEventsSince(conditions: QueryCondition[], sincePosition: bigint): Promise<StoredEvent[]>;
|
|
38
|
+
/**
|
|
39
|
+
* Get the current highest position in the store
|
|
40
|
+
* @returns 0n if store is empty
|
|
41
|
+
*/
|
|
42
|
+
getLatestPosition(): Promise<bigint>;
|
|
43
|
+
/**
|
|
44
|
+
* Close the storage connection
|
|
45
|
+
*/
|
|
46
|
+
close(): Promise<void>;
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=interface.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"interface.d.ts","sourceRoot":"","sources":["../../src/storage/interface.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE7E;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,OAAO,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACnC,SAAS,EAAE,IAAI,CAAC;CACjB;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B;;;;OAIG;IACH,MAAM,CAAC,MAAM,EAAE,YAAY,EAAE,EAAE,IAAI,EAAE,YAAY,EAAE,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAExE;;;;;OAKG;IACH,KAAK,CACH,UAAU,EAAE,cAAc,EAAE,EAC5B,YAAY,CAAC,EAAE,MAAM,EACrB,KAAK,CAAC,EAAE,MAAM,GACb,OAAO,CAAC,WAAW,EAAE,CAAC,CAAC;IAE1B;;;;OAIG;IACH,cAAc,CACZ,UAAU,EAAE,cAAc,EAAE,EAC5B,aAAa,EAAE,MAAM,GACpB,OAAO,CAAC,WAAW,EAAE,CAAC,CAAC;IAE1B;;;OAGG;IACH,iBAAiB,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC;IAErC;;OAEG;IACH,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"interface.js","sourceRoot":"","sources":["../../src/storage/interface.ts"],"names":[],"mappings":"AAAA;;GAEG"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* In-Memory Storage implementation (for testing)
|
|
3
|
+
*/
|
|
4
|
+
import { type ExtractedKey, type QueryCondition, type StoredEvent } from '../types.js';
|
|
5
|
+
import type { EventStorage, EventToStore } from './interface.js';
|
|
6
|
+
/**
|
|
7
|
+
* In-memory event storage for testing purposes
|
|
8
|
+
* NOT suitable for production use
|
|
9
|
+
*/
|
|
10
|
+
export declare class InMemoryStorage implements EventStorage {
|
|
11
|
+
private events;
|
|
12
|
+
private nextPosition;
|
|
13
|
+
append(eventsToStore: EventToStore[], keys: ExtractedKey[][]): Promise<bigint>;
|
|
14
|
+
query(conditions: QueryCondition[], fromPosition?: bigint, limit?: number): Promise<StoredEvent[]>;
|
|
15
|
+
getEventsSince(conditions: QueryCondition[], sincePosition: bigint): Promise<StoredEvent[]>;
|
|
16
|
+
getLatestPosition(): Promise<bigint>;
|
|
17
|
+
close(): Promise<void>;
|
|
18
|
+
/**
|
|
19
|
+
* Get all events (for testing)
|
|
20
|
+
*/
|
|
21
|
+
getAllEvents(): StoredEvent[];
|
|
22
|
+
/**
|
|
23
|
+
* Clear all events (for testing)
|
|
24
|
+
*/
|
|
25
|
+
clear(): void;
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=memory.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"memory.d.ts","sourceRoot":"","sources":["../../src/storage/memory.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAA0B,KAAK,YAAY,EAAE,KAAK,cAAc,EAAE,KAAK,WAAW,EAAE,MAAM,aAAa,CAAC;AAC/G,OAAO,KAAK,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAMjE;;;GAGG;AACH,qBAAa,eAAgB,YAAW,YAAY;IAClD,OAAO,CAAC,MAAM,CAA6B;IAC3C,OAAO,CAAC,YAAY,CAAc;IAE5B,MAAM,CAAC,aAAa,EAAE,YAAY,EAAE,EAAE,IAAI,EAAE,YAAY,EAAE,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC;IA4B9E,KAAK,CACT,UAAU,EAAE,cAAc,EAAE,EAC5B,YAAY,CAAC,EAAE,MAAM,EACrB,KAAK,CAAC,EAAE,MAAM,GACb,OAAO,CAAC,WAAW,EAAE,CAAC;IA6CnB,cAAc,CAClB,UAAU,EAAE,cAAc,EAAE,EAC5B,aAAa,EAAE,MAAM,GACpB,OAAO,CAAC,WAAW,EAAE,CAAC;IAInB,iBAAiB,IAAI,OAAO,CAAC,MAAM,CAAC;IAOpC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAI5B;;OAEG;IACH,YAAY,IAAI,WAAW,EAAE;IAI7B;;OAEG;IACH,KAAK,IAAI,IAAI;CAId"}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* In-Memory Storage implementation (for testing)
|
|
3
|
+
*/
|
|
4
|
+
import { isConstrainedCondition } from '../types.js';
|
|
5
|
+
/**
|
|
6
|
+
* In-memory event storage for testing purposes
|
|
7
|
+
* NOT suitable for production use
|
|
8
|
+
*/
|
|
9
|
+
export class InMemoryStorage {
|
|
10
|
+
events = [];
|
|
11
|
+
nextPosition = 1n;
|
|
12
|
+
async append(eventsToStore, keys) {
|
|
13
|
+
if (eventsToStore.length !== keys.length) {
|
|
14
|
+
throw new Error('Events and keys arrays must have the same length');
|
|
15
|
+
}
|
|
16
|
+
let lastPosition = 0n;
|
|
17
|
+
for (let i = 0; i < eventsToStore.length; i++) {
|
|
18
|
+
const event = eventsToStore[i];
|
|
19
|
+
const eventKeys = keys[i];
|
|
20
|
+
const position = this.nextPosition++;
|
|
21
|
+
this.events.push({
|
|
22
|
+
id: event.id,
|
|
23
|
+
type: event.type,
|
|
24
|
+
data: event.data,
|
|
25
|
+
metadata: event.metadata,
|
|
26
|
+
timestamp: event.timestamp,
|
|
27
|
+
position,
|
|
28
|
+
keys: eventKeys,
|
|
29
|
+
});
|
|
30
|
+
lastPosition = position;
|
|
31
|
+
}
|
|
32
|
+
return lastPosition;
|
|
33
|
+
}
|
|
34
|
+
async query(conditions, fromPosition, limit) {
|
|
35
|
+
const startPos = fromPosition ?? 0n;
|
|
36
|
+
// Filter by position first
|
|
37
|
+
let matching = this.events.filter(event => event.position > startPos);
|
|
38
|
+
// If no conditions, return all events
|
|
39
|
+
if (conditions.length === 0) {
|
|
40
|
+
// Sort by position
|
|
41
|
+
matching.sort((a, b) => (a.position < b.position ? -1 : 1));
|
|
42
|
+
const limited = limit !== undefined ? matching.slice(0, limit) : matching;
|
|
43
|
+
return limited.map(({ keys: _keys, ...event }) => event);
|
|
44
|
+
}
|
|
45
|
+
// Filter by conditions
|
|
46
|
+
matching = matching.filter(event => {
|
|
47
|
+
// Must match at least one condition
|
|
48
|
+
return conditions.some(cond => {
|
|
49
|
+
// Type must match
|
|
50
|
+
if (event.type !== cond.type) {
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
53
|
+
// If unconstrained, type match is enough
|
|
54
|
+
if (!isConstrainedCondition(cond)) {
|
|
55
|
+
return true;
|
|
56
|
+
}
|
|
57
|
+
// Key must match
|
|
58
|
+
return event.keys.some(key => key.name === cond.key && key.value === cond.value);
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
// Sort by position
|
|
62
|
+
matching.sort((a, b) => (a.position < b.position ? -1 : 1));
|
|
63
|
+
// Apply limit
|
|
64
|
+
const limited = limit !== undefined ? matching.slice(0, limit) : matching;
|
|
65
|
+
// Strip internal keys
|
|
66
|
+
return limited.map(({ keys: _keys, ...event }) => event);
|
|
67
|
+
}
|
|
68
|
+
async getEventsSince(conditions, sincePosition) {
|
|
69
|
+
return this.query(conditions, sincePosition);
|
|
70
|
+
}
|
|
71
|
+
async getLatestPosition() {
|
|
72
|
+
if (this.events.length === 0) {
|
|
73
|
+
return 0n;
|
|
74
|
+
}
|
|
75
|
+
return this.events[this.events.length - 1].position;
|
|
76
|
+
}
|
|
77
|
+
async close() {
|
|
78
|
+
// Nothing to do
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Get all events (for testing)
|
|
82
|
+
*/
|
|
83
|
+
getAllEvents() {
|
|
84
|
+
return this.events.map(({ keys: _keys, ...event }) => event);
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Clear all events (for testing)
|
|
88
|
+
*/
|
|
89
|
+
clear() {
|
|
90
|
+
this.events = [];
|
|
91
|
+
this.nextPosition = 1n;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
//# sourceMappingURL=memory.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"memory.js","sourceRoot":"","sources":["../../src/storage/memory.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,sBAAsB,EAA4D,MAAM,aAAa,CAAC;AAO/G;;;GAGG;AACH,MAAM,OAAO,eAAe;IAClB,MAAM,GAA0B,EAAE,CAAC;IACnC,YAAY,GAAW,EAAE,CAAC;IAElC,KAAK,CAAC,MAAM,CAAC,aAA6B,EAAE,IAAsB;QAChE,IAAI,aAAa,CAAC,MAAM,KAAK,IAAI,CAAC,MAAM,EAAE,CAAC;YACzC,MAAM,IAAI,KAAK,CAAC,kDAAkD,CAAC,CAAC;QACtE,CAAC;QAED,IAAI,YAAY,GAAW,EAAE,CAAC;QAE9B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,aAAa,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YAC9C,MAAM,KAAK,GAAG,aAAa,CAAC,CAAC,CAAC,CAAC;YAC/B,MAAM,SAAS,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;YAC1B,MAAM,QAAQ,GAAG,IAAI,CAAC,YAAY,EAAE,CAAC;YAErC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC;gBACf,EAAE,EAAE,KAAK,CAAC,EAAE;gBACZ,IAAI,EAAE,KAAK,CAAC,IAAI;gBAChB,IAAI,EAAE,KAAK,CAAC,IAA+B;gBAC3C,QAAQ,EAAE,KAAK,CAAC,QAAQ;gBACxB,SAAS,EAAE,KAAK,CAAC,SAAS;gBAC1B,QAAQ;gBACR,IAAI,EAAE,SAAS;aAChB,CAAC,CAAC;YAEH,YAAY,GAAG,QAAQ,CAAC;QAC1B,CAAC;QAED,OAAO,YAAY,CAAC;IACtB,CAAC;IAED,KAAK,CAAC,KAAK,CACT,UAA4B,EAC5B,YAAqB,EACrB,KAAc;QAEd,MAAM,QAAQ,GAAG,YAAY,IAAI,EAAE,CAAC;QAEpC,2BAA2B;QAC3B,IAAI,QAAQ,GAAG,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,QAAQ,GAAG,QAAQ,CAAC,CAAC;QAEtE,sCAAsC;QACtC,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC5B,mBAAmB;YACnB,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,QAAQ,GAAG,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;YAC5D,MAAM,OAAO,GAAG,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC;YAC1E,OAAO,OAAO,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,KAAK,EAAE,EAAE,EAAE,CAAC,KAAK,CAAC,CAAC;QAC3D,CAAC;QAED,uBAAuB;QACvB,QAAQ,GAAG,QAAQ,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE;YACjC,oCAAoC;YACpC,OAAO,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE;gBAC5B,kBAAkB;gBAClB,IAAI,KAAK,CAAC,IAAI,KAAK,IAAI,CAAC,IAAI,EAAE,CAAC;oBAC7B,OAAO,KAAK,CAAC;gBACf,CAAC;gBAED,yCAAyC;gBACzC,IAAI,CAAC,sBAAsB,CAAC,IAAI,CAAC,EAAE,CAAC;oBAClC,OAAO,IAAI,CAAC;gBACd,CAAC;gBAED,iBAAiB;gBACjB,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CACpB,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,IAAI,KAAK,IAAI,CAAC,GAAG,IAAI,GAAG,CAAC,KAAK,KAAK,IAAI,CAAC,KAAK,CACzD,CAAC;YACJ,CAAC,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;QAEH,mBAAmB;QACnB,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,QAAQ,GAAG,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAE5D,cAAc;QACd,MAAM,OAAO,GAAG,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC;QAE1E,sBAAsB;QACtB,OAAO,OAAO,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,KAAK,EAAE,EAAE,EAAE,CAAC,KAAK,CAAC,CAAC;IAC3D,CAAC;IAED,KAAK,CAAC,cAAc,CAClB,UAA4B,EAC5B,aAAqB;QAErB,OAAO,IAAI,CAAC,KAAK,CAAC,UAAU,EAAE,aAAa,CAAC,CAAC;IAC/C,CAAC;IAED,KAAK,CAAC,iBAAiB;QACrB,IAAI,IAAI,CAAC,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC7B,OAAO,EAAE,CAAC;QACZ,CAAC;QACD,OAAO,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC;IACtD,CAAC;IAED,KAAK,CAAC,KAAK;QACT,gBAAgB;IAClB,CAAC;IAED;;OAEG;IACH,YAAY;QACV,OAAO,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,KAAK,EAAE,EAAE,EAAE,CAAC,KAAK,CAAC,CAAC;IAC/D,CAAC;IAED;;OAEG;IACH,KAAK;QACH,IAAI,CAAC,MAAM,GAAG,EAAE,CAAC;QACjB,IAAI,CAAC,YAAY,GAAG,EAAE,CAAC;IACzB,CAAC;CACF"}
|