@quereus/sync-coordinator 0.3.5 → 0.3.7
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 +2 -2
- package/dist/src/bin/sync-coordinator.d.ts +6 -0
- package/dist/src/bin/sync-coordinator.d.ts.map +1 -0
- package/dist/src/bin/sync-coordinator.js +85 -0
- package/dist/src/bin/sync-coordinator.js.map +1 -0
- package/dist/src/common/index.d.ts +5 -0
- package/dist/src/common/index.d.ts.map +1 -0
- package/dist/src/common/index.js +5 -0
- package/dist/src/common/index.js.map +1 -0
- package/dist/src/common/logger.d.ts +20 -0
- package/dist/src/common/logger.d.ts.map +1 -0
- package/dist/src/common/logger.js +24 -0
- package/dist/src/common/logger.js.map +1 -0
- package/dist/src/config/index.d.ts +6 -0
- package/dist/src/config/index.d.ts.map +1 -0
- package/dist/src/config/index.js +6 -0
- package/dist/src/config/index.js.map +1 -0
- package/dist/src/config/loader.d.ts +27 -0
- package/dist/src/config/loader.d.ts.map +1 -0
- package/dist/src/config/loader.js +144 -0
- package/dist/src/config/loader.js.map +1 -0
- package/dist/src/config/types.d.ts +74 -0
- package/dist/src/config/types.d.ts.map +1 -0
- package/dist/src/config/types.js +27 -0
- package/dist/src/config/types.js.map +1 -0
- package/dist/src/index.d.ts +21 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +26 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/metrics/coordinator-metrics.d.ts +27 -0
- package/dist/src/metrics/coordinator-metrics.d.ts.map +1 -0
- package/dist/src/metrics/coordinator-metrics.js +57 -0
- package/dist/src/metrics/coordinator-metrics.js.map +1 -0
- package/dist/src/metrics/index.d.ts +7 -0
- package/dist/src/metrics/index.d.ts.map +1 -0
- package/dist/src/metrics/index.js +7 -0
- package/dist/src/metrics/index.js.map +1 -0
- package/dist/src/metrics/registry.d.ts +53 -0
- package/dist/src/metrics/registry.d.ts.map +1 -0
- package/dist/src/metrics/registry.js +158 -0
- package/dist/src/metrics/registry.js.map +1 -0
- package/dist/src/metrics/types.d.ts +62 -0
- package/dist/src/metrics/types.d.ts.map +1 -0
- package/dist/src/metrics/types.js +16 -0
- package/dist/src/metrics/types.js.map +1 -0
- package/dist/src/server/index.d.ts +7 -0
- package/dist/src/server/index.d.ts.map +1 -0
- package/dist/src/server/index.js +7 -0
- package/dist/src/server/index.js.map +1 -0
- package/dist/src/server/routes.d.ts +10 -0
- package/dist/src/server/routes.d.ts.map +1 -0
- package/dist/src/server/routes.js +174 -0
- package/dist/src/server/routes.js.map +1 -0
- package/dist/src/server/server.d.ts +34 -0
- package/dist/src/server/server.d.ts.map +1 -0
- package/dist/src/server/server.js +57 -0
- package/dist/src/server/server.js.map +1 -0
- package/dist/src/server/websocket.d.ts +10 -0
- package/dist/src/server/websocket.d.ts.map +1 -0
- package/dist/src/server/websocket.js +167 -0
- package/dist/src/server/websocket.js.map +1 -0
- package/dist/src/service/coordinator-service.d.ts +122 -0
- package/dist/src/service/coordinator-service.d.ts.map +1 -0
- package/dist/src/service/coordinator-service.js +436 -0
- package/dist/src/service/coordinator-service.js.map +1 -0
- package/dist/src/service/database-ids.d.ts +58 -0
- package/dist/src/service/database-ids.d.ts.map +1 -0
- package/dist/src/service/database-ids.js +128 -0
- package/dist/src/service/database-ids.js.map +1 -0
- package/dist/src/service/index.d.ts +8 -0
- package/dist/src/service/index.d.ts.map +1 -0
- package/dist/src/service/index.js +7 -0
- package/dist/src/service/index.js.map +1 -0
- package/dist/src/service/store-manager.d.ts +85 -0
- package/dist/src/service/store-manager.d.ts.map +1 -0
- package/dist/src/service/store-manager.js +194 -0
- package/dist/src/service/store-manager.js.map +1 -0
- package/dist/src/service/types.d.ts +146 -0
- package/dist/src/service/types.d.ts.map +1 -0
- package/dist/src/service/types.js +5 -0
- package/dist/src/service/types.js.map +1 -0
- package/package.json +4 -3
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Service layer exports.
|
|
3
|
+
*/
|
|
4
|
+
export { CoordinatorService, } from './coordinator-service.js';
|
|
5
|
+
export { StoreManager, } from './store-manager.js';
|
|
6
|
+
export { isValidDatabaseId, parseDatabaseId, formatDatabaseId, } from './database-ids.js';
|
|
7
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/service/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAYH,OAAO,EACL,kBAAkB,GAEnB,MAAM,0BAA0B,CAAC;AAElC,OAAO,EACL,YAAY,GAGb,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EACL,iBAAiB,EACjB,eAAe,EACf,gBAAgB,GACjB,MAAM,mBAAmB,CAAC"}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* StoreManager - Multi-tenant LevelDB store management
|
|
3
|
+
*
|
|
4
|
+
* Manages lazy loading and cleanup of per-database LevelDB stores.
|
|
5
|
+
* Each database gets its own isolated store, opened on-demand and
|
|
6
|
+
* closed after idle timeout.
|
|
7
|
+
*/
|
|
8
|
+
import { StoreEventEmitter } from '@quereus/store';
|
|
9
|
+
import { LevelDBStore } from '@quereus/plugin-leveldb';
|
|
10
|
+
import { type SyncManager } from '@quereus/sync';
|
|
11
|
+
export interface StoreEntry {
|
|
12
|
+
databaseId: string;
|
|
13
|
+
store: LevelDBStore;
|
|
14
|
+
syncManager: SyncManager;
|
|
15
|
+
storeEvents: StoreEventEmitter;
|
|
16
|
+
refCount: number;
|
|
17
|
+
lastAccess: number;
|
|
18
|
+
}
|
|
19
|
+
export interface StoreManagerConfig {
|
|
20
|
+
/** Base directory for all database stores */
|
|
21
|
+
dataDir: string;
|
|
22
|
+
/** Maximum number of stores to keep open (LRU eviction) */
|
|
23
|
+
maxOpenStores: number;
|
|
24
|
+
/** Idle timeout in ms before closing a store with refCount=0 */
|
|
25
|
+
idleTimeoutMs: number;
|
|
26
|
+
/** Interval for cleanup checks */
|
|
27
|
+
cleanupIntervalMs: number;
|
|
28
|
+
/** Sync config passed to createSyncModule */
|
|
29
|
+
syncConfig?: {
|
|
30
|
+
tombstoneTTL?: number;
|
|
31
|
+
batchSize?: number;
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Manages multiple LevelDB stores for multi-tenant sync.
|
|
36
|
+
*/
|
|
37
|
+
export declare class StoreManager {
|
|
38
|
+
private readonly config;
|
|
39
|
+
private readonly stores;
|
|
40
|
+
private cleanupTimer;
|
|
41
|
+
private shutdownPromise;
|
|
42
|
+
constructor(config?: Partial<StoreManagerConfig>);
|
|
43
|
+
/**
|
|
44
|
+
* Start the store manager (begins cleanup interval).
|
|
45
|
+
*/
|
|
46
|
+
start(): void;
|
|
47
|
+
/**
|
|
48
|
+
* Get or open a store for a database. Increments refCount.
|
|
49
|
+
*/
|
|
50
|
+
acquire(databaseId: string): Promise<StoreEntry>;
|
|
51
|
+
/**
|
|
52
|
+
* Release a store reference. Decrements refCount.
|
|
53
|
+
*/
|
|
54
|
+
release(databaseId: string): void;
|
|
55
|
+
/**
|
|
56
|
+
* Check if a store is currently open.
|
|
57
|
+
*/
|
|
58
|
+
isOpen(databaseId: string): boolean;
|
|
59
|
+
/**
|
|
60
|
+
* Get an open store without acquiring (for read-only checks).
|
|
61
|
+
*/
|
|
62
|
+
get(databaseId: string): StoreEntry | undefined;
|
|
63
|
+
/**
|
|
64
|
+
* Get count of open stores.
|
|
65
|
+
*/
|
|
66
|
+
get openCount(): number;
|
|
67
|
+
/**
|
|
68
|
+
* Shutdown all stores.
|
|
69
|
+
*/
|
|
70
|
+
shutdown(): Promise<void>;
|
|
71
|
+
private openStore;
|
|
72
|
+
/**
|
|
73
|
+
* Cleanup idle stores with refCount=0 past timeout.
|
|
74
|
+
*/
|
|
75
|
+
private cleanup;
|
|
76
|
+
/**
|
|
77
|
+
* Evict least recently used store (with refCount=0).
|
|
78
|
+
*/
|
|
79
|
+
private evictLRU;
|
|
80
|
+
/**
|
|
81
|
+
* Close a specific store.
|
|
82
|
+
*/
|
|
83
|
+
private closeStore;
|
|
84
|
+
}
|
|
85
|
+
//# sourceMappingURL=store-manager.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"store-manager.d.ts","sourceRoot":"","sources":["../../../src/service/store-manager.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AACnD,OAAO,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AACvD,OAAO,EAEL,KAAK,WAAW,EACjB,MAAM,eAAe,CAAC;AAIvB,MAAM,WAAW,UAAU;IACzB,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,YAAY,CAAC;IACpB,WAAW,EAAE,WAAW,CAAC;IACzB,WAAW,EAAE,iBAAiB,CAAC;IAC/B,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,kBAAkB;IACjC,6CAA6C;IAC7C,OAAO,EAAE,MAAM,CAAC;IAChB,2DAA2D;IAC3D,aAAa,EAAE,MAAM,CAAC;IACtB,gEAAgE;IAChE,aAAa,EAAE,MAAM,CAAC;IACtB,kCAAkC;IAClC,iBAAiB,EAAE,MAAM,CAAC;IAC1B,6CAA6C;IAC7C,UAAU,CAAC,EAAE;QACX,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,SAAS,CAAC,EAAE,MAAM,CAAC;KACpB,CAAC;CACH;AASD;;GAEG;AACH,qBAAa,YAAY;IACvB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAqB;IAC5C,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAiC;IACxD,OAAO,CAAC,YAAY,CAA+C;IACnE,OAAO,CAAC,eAAe,CAA8B;gBAEzC,MAAM,GAAE,OAAO,CAAC,kBAAkB,CAAM;IAIpD;;OAEG;IACH,KAAK,IAAI,IAAI;IAMb;;OAEG;IACG,OAAO,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC;IAsBtD;;OAEG;IACH,OAAO,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI;IASjC;;OAEG;IACH,MAAM,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO;IAInC;;OAEG;IACH,GAAG,CAAC,UAAU,EAAE,MAAM,GAAG,UAAU,GAAG,SAAS;IAI/C;;OAEG;IACH,IAAI,SAAS,IAAI,MAAM,CAEtB;IAED;;OAEG;IACG,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;YA0BjB,SAAS;IA2BvB;;OAEG;YACW,OAAO;IAmBrB;;OAEG;YACW,QAAQ;IAoBtB;;OAEG;YACW,UAAU;CAYzB"}
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* StoreManager - Multi-tenant LevelDB store management
|
|
3
|
+
*
|
|
4
|
+
* Manages lazy loading and cleanup of per-database LevelDB stores.
|
|
5
|
+
* Each database gets its own isolated store, opened on-demand and
|
|
6
|
+
* closed after idle timeout.
|
|
7
|
+
*/
|
|
8
|
+
import { join } from 'node:path';
|
|
9
|
+
import { StoreEventEmitter } from '@quereus/store';
|
|
10
|
+
import { LevelDBStore } from '@quereus/plugin-leveldb';
|
|
11
|
+
import { createSyncModule, } from '@quereus/sync';
|
|
12
|
+
import { serviceLog } from '../common/logger.js';
|
|
13
|
+
import { getDatabaseStoragePath, parseDatabaseId } from './database-ids.js';
|
|
14
|
+
const DEFAULT_CONFIG = {
|
|
15
|
+
dataDir: './data',
|
|
16
|
+
maxOpenStores: 100,
|
|
17
|
+
idleTimeoutMs: 5 * 60 * 1000, // 5 minutes
|
|
18
|
+
cleanupIntervalMs: 30 * 1000, // 30 seconds
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* Manages multiple LevelDB stores for multi-tenant sync.
|
|
22
|
+
*/
|
|
23
|
+
export class StoreManager {
|
|
24
|
+
config;
|
|
25
|
+
stores = new Map();
|
|
26
|
+
cleanupTimer = null;
|
|
27
|
+
shutdownPromise = null;
|
|
28
|
+
constructor(config = {}) {
|
|
29
|
+
this.config = { ...DEFAULT_CONFIG, ...config };
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Start the store manager (begins cleanup interval).
|
|
33
|
+
*/
|
|
34
|
+
start() {
|
|
35
|
+
if (this.cleanupTimer)
|
|
36
|
+
return;
|
|
37
|
+
this.cleanupTimer = setInterval(() => this.cleanup(), this.config.cleanupIntervalMs);
|
|
38
|
+
serviceLog('StoreManager started with dataDir: %s', this.config.dataDir);
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Get or open a store for a database. Increments refCount.
|
|
42
|
+
*/
|
|
43
|
+
async acquire(databaseId) {
|
|
44
|
+
// Check if already open
|
|
45
|
+
let entry = this.stores.get(databaseId);
|
|
46
|
+
if (entry) {
|
|
47
|
+
entry.refCount++;
|
|
48
|
+
entry.lastAccess = Date.now();
|
|
49
|
+
serviceLog('Store acquired (cached): %s, refCount=%d', databaseId, entry.refCount);
|
|
50
|
+
return entry;
|
|
51
|
+
}
|
|
52
|
+
// Check if we need to evict before opening new
|
|
53
|
+
if (this.stores.size >= this.config.maxOpenStores) {
|
|
54
|
+
await this.evictLRU();
|
|
55
|
+
}
|
|
56
|
+
// Open new store
|
|
57
|
+
entry = await this.openStore(databaseId);
|
|
58
|
+
this.stores.set(databaseId, entry);
|
|
59
|
+
serviceLog('Store acquired (opened): %s', databaseId);
|
|
60
|
+
return entry;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Release a store reference. Decrements refCount.
|
|
64
|
+
*/
|
|
65
|
+
release(databaseId) {
|
|
66
|
+
const entry = this.stores.get(databaseId);
|
|
67
|
+
if (!entry)
|
|
68
|
+
return;
|
|
69
|
+
entry.refCount = Math.max(0, entry.refCount - 1);
|
|
70
|
+
entry.lastAccess = Date.now();
|
|
71
|
+
serviceLog('Store released: %s, refCount=%d', databaseId, entry.refCount);
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Check if a store is currently open.
|
|
75
|
+
*/
|
|
76
|
+
isOpen(databaseId) {
|
|
77
|
+
return this.stores.has(databaseId);
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Get an open store without acquiring (for read-only checks).
|
|
81
|
+
*/
|
|
82
|
+
get(databaseId) {
|
|
83
|
+
return this.stores.get(databaseId);
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Get count of open stores.
|
|
87
|
+
*/
|
|
88
|
+
get openCount() {
|
|
89
|
+
return this.stores.size;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Shutdown all stores.
|
|
93
|
+
*/
|
|
94
|
+
async shutdown() {
|
|
95
|
+
if (this.shutdownPromise)
|
|
96
|
+
return this.shutdownPromise;
|
|
97
|
+
this.shutdownPromise = (async () => {
|
|
98
|
+
if (this.cleanupTimer) {
|
|
99
|
+
clearInterval(this.cleanupTimer);
|
|
100
|
+
this.cleanupTimer = null;
|
|
101
|
+
}
|
|
102
|
+
const closePromises = Array.from(this.stores.entries()).map(async ([id, entry]) => {
|
|
103
|
+
try {
|
|
104
|
+
await entry.store.close();
|
|
105
|
+
serviceLog('Store closed: %s', id);
|
|
106
|
+
}
|
|
107
|
+
catch (err) {
|
|
108
|
+
serviceLog('Error closing store %s: %O', id, err);
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
await Promise.all(closePromises);
|
|
112
|
+
this.stores.clear();
|
|
113
|
+
serviceLog('StoreManager shutdown complete');
|
|
114
|
+
})();
|
|
115
|
+
return this.shutdownPromise;
|
|
116
|
+
}
|
|
117
|
+
async openStore(databaseId) {
|
|
118
|
+
// Validate database ID format
|
|
119
|
+
parseDatabaseId(databaseId);
|
|
120
|
+
const storagePath = getDatabaseStoragePath(databaseId);
|
|
121
|
+
const fullPath = join(this.config.dataDir, storagePath);
|
|
122
|
+
serviceLog('Opening store at: %s', fullPath);
|
|
123
|
+
const store = await LevelDBStore.open({
|
|
124
|
+
path: fullPath,
|
|
125
|
+
createIfMissing: true,
|
|
126
|
+
});
|
|
127
|
+
const storeEvents = new StoreEventEmitter();
|
|
128
|
+
const { syncManager } = await createSyncModule(store, storeEvents, this.config.syncConfig);
|
|
129
|
+
return {
|
|
130
|
+
databaseId,
|
|
131
|
+
store,
|
|
132
|
+
syncManager,
|
|
133
|
+
storeEvents,
|
|
134
|
+
refCount: 1,
|
|
135
|
+
lastAccess: Date.now(),
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Cleanup idle stores with refCount=0 past timeout.
|
|
140
|
+
*/
|
|
141
|
+
async cleanup() {
|
|
142
|
+
const now = Date.now();
|
|
143
|
+
const toClose = [];
|
|
144
|
+
for (const [id, entry] of this.stores) {
|
|
145
|
+
if (entry.refCount === 0 && now - entry.lastAccess > this.config.idleTimeoutMs) {
|
|
146
|
+
toClose.push(id);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
for (const id of toClose) {
|
|
150
|
+
await this.closeStore(id);
|
|
151
|
+
}
|
|
152
|
+
if (toClose.length > 0) {
|
|
153
|
+
serviceLog('Cleanup: closed %d idle stores', toClose.length);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Evict least recently used store (with refCount=0).
|
|
158
|
+
*/
|
|
159
|
+
async evictLRU() {
|
|
160
|
+
let oldest = null;
|
|
161
|
+
for (const [id, entry] of this.stores) {
|
|
162
|
+
// Only evict stores with no active references
|
|
163
|
+
if (entry.refCount === 0) {
|
|
164
|
+
if (!oldest || entry.lastAccess < oldest.lastAccess) {
|
|
165
|
+
oldest = { id, lastAccess: entry.lastAccess };
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
if (oldest) {
|
|
170
|
+
await this.closeStore(oldest.id);
|
|
171
|
+
serviceLog('Evicted LRU store: %s', oldest.id);
|
|
172
|
+
}
|
|
173
|
+
else {
|
|
174
|
+
serviceLog('Warning: Cannot evict, all stores have active references');
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Close a specific store.
|
|
179
|
+
*/
|
|
180
|
+
async closeStore(databaseId) {
|
|
181
|
+
const entry = this.stores.get(databaseId);
|
|
182
|
+
if (!entry)
|
|
183
|
+
return;
|
|
184
|
+
try {
|
|
185
|
+
await entry.store.close();
|
|
186
|
+
this.stores.delete(databaseId);
|
|
187
|
+
serviceLog('Store closed: %s', databaseId);
|
|
188
|
+
}
|
|
189
|
+
catch (err) {
|
|
190
|
+
serviceLog('Error closing store %s: %O', databaseId, err);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
//# sourceMappingURL=store-manager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"store-manager.js","sourceRoot":"","sources":["../../../src/service/store-manager.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AACnD,OAAO,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AACvD,OAAO,EACL,gBAAgB,GAEjB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AACjD,OAAO,EAAE,sBAAsB,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AA2B5E,MAAM,cAAc,GAAuB;IACzC,OAAO,EAAE,QAAQ;IACjB,aAAa,EAAE,GAAG;IAClB,aAAa,EAAE,CAAC,GAAG,EAAE,GAAG,IAAI,EAAE,YAAY;IAC1C,iBAAiB,EAAE,EAAE,GAAG,IAAI,EAAE,aAAa;CAC5C,CAAC;AAEF;;GAEG;AACH,MAAM,OAAO,YAAY;IACN,MAAM,CAAqB;IAC3B,MAAM,GAAG,IAAI,GAAG,EAAsB,CAAC;IAChD,YAAY,GAA0C,IAAI,CAAC;IAC3D,eAAe,GAAyB,IAAI,CAAC;IAErD,YAAY,SAAsC,EAAE;QAClD,IAAI,CAAC,MAAM,GAAG,EAAE,GAAG,cAAc,EAAE,GAAG,MAAM,EAAE,CAAC;IACjD,CAAC;IAED;;OAEG;IACH,KAAK;QACH,IAAI,IAAI,CAAC,YAAY;YAAE,OAAO;QAC9B,IAAI,CAAC,YAAY,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE,IAAI,CAAC,MAAM,CAAC,iBAAiB,CAAC,CAAC;QACrF,UAAU,CAAC,uCAAuC,EAAE,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IAC3E,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,OAAO,CAAC,UAAkB;QAC9B,wBAAwB;QACxB,IAAI,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;QACxC,IAAI,KAAK,EAAE,CAAC;YACV,KAAK,CAAC,QAAQ,EAAE,CAAC;YACjB,KAAK,CAAC,UAAU,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;YAC9B,UAAU,CAAC,0CAA0C,EAAE,UAAU,EAAE,KAAK,CAAC,QAAQ,CAAC,CAAC;YACnF,OAAO,KAAK,CAAC;QACf,CAAC;QAED,+CAA+C;QAC/C,IAAI,IAAI,CAAC,MAAM,CAAC,IAAI,IAAI,IAAI,CAAC,MAAM,CAAC,aAAa,EAAE,CAAC;YAClD,MAAM,IAAI,CAAC,QAAQ,EAAE,CAAC;QACxB,CAAC;QAED,iBAAiB;QACjB,KAAK,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,CAAC;QACzC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC;QACnC,UAAU,CAAC,6BAA6B,EAAE,UAAU,CAAC,CAAC;QACtD,OAAO,KAAK,CAAC;IACf,CAAC;IAED;;OAEG;IACH,OAAO,CAAC,UAAkB;QACxB,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;QAC1C,IAAI,CAAC,KAAK;YAAE,OAAO;QAEnB,KAAK,CAAC,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,CAAC,QAAQ,GAAG,CAAC,CAAC,CAAC;QACjD,KAAK,CAAC,UAAU,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QAC9B,UAAU,CAAC,iCAAiC,EAAE,UAAU,EAAE,KAAK,CAAC,QAAQ,CAAC,CAAC;IAC5E,CAAC;IAED;;OAEG;IACH,MAAM,CAAC,UAAkB;QACvB,OAAO,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;IACrC,CAAC;IAED;;OAEG;IACH,GAAG,CAAC,UAAkB;QACpB,OAAO,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;IACrC,CAAC;IAED;;OAEG;IACH,IAAI,SAAS;QACX,OAAO,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC;IAC1B,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,QAAQ;QACZ,IAAI,IAAI,CAAC,eAAe;YAAE,OAAO,IAAI,CAAC,eAAe,CAAC;QAEtD,IAAI,CAAC,eAAe,GAAG,CAAC,KAAK,IAAI,EAAE;YACjC,IAAI,IAAI,CAAC,YAAY,EAAE,CAAC;gBACtB,aAAa,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;gBACjC,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC;YAC3B,CAAC;YAED,MAAM,aAAa,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC,EAAE,EAAE,KAAK,CAAC,EAAE,EAAE;gBAChF,IAAI,CAAC;oBACH,MAAM,KAAK,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;oBAC1B,UAAU,CAAC,kBAAkB,EAAE,EAAE,CAAC,CAAC;gBACrC,CAAC;gBAAC,OAAO,GAAG,EAAE,CAAC;oBACb,UAAU,CAAC,4BAA4B,EAAE,EAAE,EAAE,GAAG,CAAC,CAAC;gBACpD,CAAC;YACH,CAAC,CAAC,CAAC;YAEH,MAAM,OAAO,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC;YACjC,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;YACpB,UAAU,CAAC,gCAAgC,CAAC,CAAC;QAC/C,CAAC,CAAC,EAAE,CAAC;QAEL,OAAO,IAAI,CAAC,eAAe,CAAC;IAC9B,CAAC;IAEO,KAAK,CAAC,SAAS,CAAC,UAAkB;QACxC,8BAA8B;QAC9B,eAAe,CAAC,UAAU,CAAC,CAAC;QAE5B,MAAM,WAAW,GAAG,sBAAsB,CAAC,UAAU,CAAC,CAAC;QACvD,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,WAAW,CAAC,CAAC;QAExD,UAAU,CAAC,sBAAsB,EAAE,QAAQ,CAAC,CAAC;QAE7C,MAAM,KAAK,GAAG,MAAM,YAAY,CAAC,IAAI,CAAC;YACpC,IAAI,EAAE,QAAQ;YACd,eAAe,EAAE,IAAI;SACtB,CAAC,CAAC;QAEH,MAAM,WAAW,GAAG,IAAI,iBAAiB,EAAE,CAAC;QAC5C,MAAM,EAAE,WAAW,EAAE,GAAG,MAAM,gBAAgB,CAAC,KAAK,EAAE,WAAW,EAAE,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;QAE3F,OAAO;YACL,UAAU;YACV,KAAK;YACL,WAAW;YACX,WAAW;YACX,QAAQ,EAAE,CAAC;YACX,UAAU,EAAE,IAAI,CAAC,GAAG,EAAE;SACvB,CAAC;IACJ,CAAC;IAED;;OAEG;IACK,KAAK,CAAC,OAAO;QACnB,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACvB,MAAM,OAAO,GAAa,EAAE,CAAC;QAE7B,KAAK,MAAM,CAAC,EAAE,EAAE,KAAK,CAAC,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;YACtC,IAAI,KAAK,CAAC,QAAQ,KAAK,CAAC,IAAI,GAAG,GAAG,KAAK,CAAC,UAAU,GAAG,IAAI,CAAC,MAAM,CAAC,aAAa,EAAE,CAAC;gBAC/E,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YACnB,CAAC;QACH,CAAC;QAED,KAAK,MAAM,EAAE,IAAI,OAAO,EAAE,CAAC;YACzB,MAAM,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC;QAC5B,CAAC;QAED,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACvB,UAAU,CAAC,gCAAgC,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC;QAC/D,CAAC;IACH,CAAC;IAED;;OAEG;IACK,KAAK,CAAC,QAAQ;QACpB,IAAI,MAAM,GAA8C,IAAI,CAAC;QAE7D,KAAK,MAAM,CAAC,EAAE,EAAE,KAAK,CAAC,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;YACtC,8CAA8C;YAC9C,IAAI,KAAK,CAAC,QAAQ,KAAK,CAAC,EAAE,CAAC;gBACzB,IAAI,CAAC,MAAM,IAAI,KAAK,CAAC,UAAU,GAAG,MAAM,CAAC,UAAU,EAAE,CAAC;oBACpD,MAAM,GAAG,EAAE,EAAE,EAAE,UAAU,EAAE,KAAK,CAAC,UAAU,EAAE,CAAC;gBAChD,CAAC;YACH,CAAC;QACH,CAAC;QAED,IAAI,MAAM,EAAE,CAAC;YACX,MAAM,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;YACjC,UAAU,CAAC,uBAAuB,EAAE,MAAM,CAAC,EAAE,CAAC,CAAC;QACjD,CAAC;aAAM,CAAC;YACN,UAAU,CAAC,0DAA0D,CAAC,CAAC;QACzE,CAAC;IACH,CAAC;IAED;;OAEG;IACK,KAAK,CAAC,UAAU,CAAC,UAAkB;QACzC,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;QAC1C,IAAI,CAAC,KAAK;YAAE,OAAO;QAEnB,IAAI,CAAC;YACH,MAAM,KAAK,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;YAC1B,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;YAC/B,UAAU,CAAC,kBAAkB,EAAE,UAAU,CAAC,CAAC;QAC7C,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,UAAU,CAAC,4BAA4B,EAAE,UAAU,EAAE,GAAG,CAAC,CAAC;QAC5D,CAAC;IACH,CAAC;CACF"}
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Service layer types - hooks, sessions, and operations.
|
|
3
|
+
*/
|
|
4
|
+
import type { WebSocket } from 'ws';
|
|
5
|
+
import type { FastifyRequest } from 'fastify';
|
|
6
|
+
import type { SiteId, HLC, ChangeSet, ApplyResult } from '@quereus/sync';
|
|
7
|
+
/**
|
|
8
|
+
* Authenticated client identity.
|
|
9
|
+
* Extend this interface for custom auth data.
|
|
10
|
+
*/
|
|
11
|
+
export interface ClientIdentity {
|
|
12
|
+
/** Client's site ID for sync */
|
|
13
|
+
siteId: SiteId;
|
|
14
|
+
/** Optional user ID from authentication */
|
|
15
|
+
userId?: string;
|
|
16
|
+
/** Optional additional metadata */
|
|
17
|
+
metadata?: Record<string, unknown>;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Active WebSocket client session.
|
|
21
|
+
*/
|
|
22
|
+
export interface ClientSession {
|
|
23
|
+
/** Unique connection identifier */
|
|
24
|
+
connectionId: string;
|
|
25
|
+
/** Database ID for multi-tenant routing (e.g., 'a1-s42') */
|
|
26
|
+
databaseId: string;
|
|
27
|
+
/** Client's replica site ID */
|
|
28
|
+
siteId: SiteId;
|
|
29
|
+
/** Authenticated identity */
|
|
30
|
+
identity: ClientIdentity;
|
|
31
|
+
/** Last HLC client synced to */
|
|
32
|
+
lastSyncHLC: HLC | undefined;
|
|
33
|
+
/** Connection timestamp */
|
|
34
|
+
connectedAt: number;
|
|
35
|
+
/** The WebSocket connection */
|
|
36
|
+
socket: WebSocket;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Context provided to authentication hook.
|
|
40
|
+
*/
|
|
41
|
+
export interface AuthContext {
|
|
42
|
+
/** Database ID for multi-tenant routing (e.g., 'a1-s42') */
|
|
43
|
+
databaseId: string;
|
|
44
|
+
/** Authorization header value */
|
|
45
|
+
token?: string;
|
|
46
|
+
/** Client-provided site ID */
|
|
47
|
+
siteId?: SiteId;
|
|
48
|
+
/** Raw site ID string from header */
|
|
49
|
+
siteIdRaw?: string;
|
|
50
|
+
/** Original HTTP request (if available) */
|
|
51
|
+
request?: FastifyRequest;
|
|
52
|
+
/** WebSocket connection (if WebSocket auth) */
|
|
53
|
+
socket?: WebSocket;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Sync operations that can be authorized.
|
|
57
|
+
*/
|
|
58
|
+
export type SyncOperation = {
|
|
59
|
+
type: 'get_changes';
|
|
60
|
+
sinceHLC?: HLC;
|
|
61
|
+
} | {
|
|
62
|
+
type: 'apply_changes';
|
|
63
|
+
changeCount: number;
|
|
64
|
+
} | {
|
|
65
|
+
type: 'get_snapshot';
|
|
66
|
+
} | {
|
|
67
|
+
type: 'resume_snapshot';
|
|
68
|
+
};
|
|
69
|
+
/**
|
|
70
|
+
* A change that was rejected during validation.
|
|
71
|
+
*/
|
|
72
|
+
export interface RejectedChange {
|
|
73
|
+
/** The rejected change */
|
|
74
|
+
change: ChangeSet;
|
|
75
|
+
/** Reason for rejection */
|
|
76
|
+
reason: string;
|
|
77
|
+
/** Error code for programmatic handling */
|
|
78
|
+
code?: string;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Result of change validation.
|
|
82
|
+
*/
|
|
83
|
+
export interface ValidationResult {
|
|
84
|
+
/** Changes approved for application */
|
|
85
|
+
approved: ChangeSet[];
|
|
86
|
+
/** Changes that were rejected */
|
|
87
|
+
rejected: RejectedChange[];
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Coordinator service hooks for customization.
|
|
91
|
+
* All hooks are optional; defaults allow all operations.
|
|
92
|
+
*/
|
|
93
|
+
export interface CoordinatorHooks {
|
|
94
|
+
/**
|
|
95
|
+
* Authenticate an incoming request/connection.
|
|
96
|
+
* Called before any sync operation.
|
|
97
|
+
*
|
|
98
|
+
* @param context - Auth context with token and request info
|
|
99
|
+
* @returns Client identity on success
|
|
100
|
+
* @throws Error to reject authentication
|
|
101
|
+
*/
|
|
102
|
+
onAuthenticate?(context: AuthContext): Promise<ClientIdentity>;
|
|
103
|
+
/**
|
|
104
|
+
* Authorize a specific operation for a client.
|
|
105
|
+
* Called after authentication, before executing the operation.
|
|
106
|
+
*
|
|
107
|
+
* @param client - Authenticated client identity
|
|
108
|
+
* @param operation - The operation being requested
|
|
109
|
+
* @returns true to allow, false to deny
|
|
110
|
+
*/
|
|
111
|
+
onAuthorize?(client: ClientIdentity, operation: SyncOperation): Promise<boolean>;
|
|
112
|
+
/**
|
|
113
|
+
* Validate changes before applying them.
|
|
114
|
+
* Can modify, filter, or reject changes.
|
|
115
|
+
*
|
|
116
|
+
* @param client - Authenticated client identity
|
|
117
|
+
* @param changes - Changes to validate
|
|
118
|
+
* @returns Approved and rejected changes
|
|
119
|
+
*/
|
|
120
|
+
onBeforeApplyChanges?(client: ClientIdentity, changes: ChangeSet[]): Promise<ValidationResult>;
|
|
121
|
+
/**
|
|
122
|
+
* Called after changes are successfully applied.
|
|
123
|
+
* Useful for logging, metrics, or triggering side effects.
|
|
124
|
+
*
|
|
125
|
+
* @param client - Authenticated client identity
|
|
126
|
+
* @param changes - Changes that were applied
|
|
127
|
+
* @param result - Result of the apply operation
|
|
128
|
+
*/
|
|
129
|
+
onAfterApplyChanges?(client: ClientIdentity, changes: ChangeSet[], result: ApplyResult): void;
|
|
130
|
+
/**
|
|
131
|
+
* Called when a WebSocket client connects.
|
|
132
|
+
* Return false to reject the connection.
|
|
133
|
+
*
|
|
134
|
+
* @param client - Authenticated client identity
|
|
135
|
+
* @param socket - The WebSocket connection
|
|
136
|
+
* @returns true to accept, false to reject
|
|
137
|
+
*/
|
|
138
|
+
onClientConnect?(client: ClientIdentity, socket: WebSocket): Promise<boolean>;
|
|
139
|
+
/**
|
|
140
|
+
* Called when a WebSocket client disconnects.
|
|
141
|
+
*
|
|
142
|
+
* @param client - Client identity of disconnected client
|
|
143
|
+
*/
|
|
144
|
+
onClientDisconnect?(client: ClientIdentity): void;
|
|
145
|
+
}
|
|
146
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/service/types.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/service/types.ts"],"names":[],"mappings":"AAAA;;GAEG"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@quereus/sync-coordinator",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.7",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Standalone coordinator backend for Quereus Sync - production-ready sync server",
|
|
6
6
|
"keywords": [
|
|
@@ -45,9 +45,10 @@
|
|
|
45
45
|
"dependencies": {
|
|
46
46
|
"@fastify/cors": "^10.0.2",
|
|
47
47
|
"@fastify/websocket": "^11.0.2",
|
|
48
|
-
"@quereus/plugin-
|
|
49
|
-
"@quereus/plugin-sync": "*",
|
|
48
|
+
"@quereus/plugin-leveldb": "workspace:^",
|
|
50
49
|
"@quereus/quereus": "*",
|
|
50
|
+
"@quereus/store": "*",
|
|
51
|
+
"@quereus/sync": "*",
|
|
51
52
|
"commander": "^14.0.0",
|
|
52
53
|
"debug": "^4.4.0",
|
|
53
54
|
"fastify": "^5.2.1"
|