@edgebasejs/core 0.1.7 → 0.1.9
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 +15 -0
- package/dist/core/src/access-rules/column-security.d.ts +80 -0
- package/dist/core/src/access-rules/column-security.d.ts.map +1 -0
- package/dist/core/src/access-rules/column-security.js +191 -0
- package/dist/core/src/access-rules/column-security.js.map +1 -0
- package/dist/core/src/access-rules/engine.d.ts.map +1 -1
- package/dist/core/src/access-rules/engine.js +2 -1
- package/dist/core/src/access-rules/engine.js.map +1 -1
- package/dist/core/src/audit/audit-manager.d.ts +108 -0
- package/dist/core/src/audit/audit-manager.d.ts.map +1 -0
- package/dist/core/src/audit/audit-manager.js +265 -0
- package/dist/core/src/audit/audit-manager.js.map +1 -0
- package/dist/core/src/encryption/encryption-manager.d.ts +97 -0
- package/dist/core/src/encryption/encryption-manager.d.ts.map +1 -0
- package/dist/core/src/encryption/encryption-manager.js +224 -0
- package/dist/core/src/encryption/encryption-manager.js.map +1 -0
- package/dist/core/src/index.d.ts +12 -0
- package/dist/core/src/index.d.ts.map +1 -1
- package/dist/core/src/index.js +12 -0
- package/dist/core/src/index.js.map +1 -1
- package/dist/core/src/realtime/change-notifier.d.ts +50 -0
- package/dist/core/src/realtime/change-notifier.d.ts.map +1 -0
- package/dist/core/src/realtime/change-notifier.js +145 -0
- package/dist/core/src/realtime/change-notifier.js.map +1 -0
- package/dist/core/src/realtime/message-types.d.ts +39 -0
- package/dist/core/src/realtime/message-types.d.ts.map +1 -0
- package/dist/core/src/realtime/message-types.js +5 -0
- package/dist/core/src/realtime/message-types.js.map +1 -0
- package/dist/core/src/realtime/subscription-manager.d.ts +67 -0
- package/dist/core/src/realtime/subscription-manager.d.ts.map +1 -0
- package/dist/core/src/realtime/subscription-manager.js +229 -0
- package/dist/core/src/realtime/subscription-manager.js.map +1 -0
- package/dist/core/src/search/search-manager.d.ts +93 -0
- package/dist/core/src/search/search-manager.d.ts.map +1 -0
- package/dist/core/src/search/search-manager.js +258 -0
- package/dist/core/src/search/search-manager.js.map +1 -0
- package/dist/core/src/storage/file-manager.d.ts +138 -0
- package/dist/core/src/storage/file-manager.d.ts.map +1 -0
- package/dist/core/src/storage/file-manager.js +224 -0
- package/dist/core/src/storage/file-manager.js.map +1 -0
- package/dist/core/src/sync/batch-processor.d.ts +97 -0
- package/dist/core/src/sync/batch-processor.d.ts.map +1 -0
- package/dist/core/src/sync/batch-processor.js +313 -0
- package/dist/core/src/sync/batch-processor.js.map +1 -0
- package/dist/core/src/sync/csv-processor.d.ts +66 -0
- package/dist/core/src/sync/csv-processor.d.ts.map +1 -0
- package/dist/core/src/sync/csv-processor.js +223 -0
- package/dist/core/src/sync/csv-processor.js.map +1 -0
- package/dist/core/src/sync/sync-engine.d.ts +22 -0
- package/dist/core/src/sync/sync-engine.d.ts.map +1 -1
- package/dist/core/src/sync/sync-engine.js +123 -10
- package/dist/core/src/sync/sync-engine.js.map +1 -1
- package/dist/core/src/sync/transaction-manager.d.ts +83 -0
- package/dist/core/src/sync/transaction-manager.d.ts.map +1 -0
- package/dist/core/src/sync/transaction-manager.js +227 -0
- package/dist/core/src/sync/transaction-manager.js.map +1 -0
- package/dist/core/src/webhooks/webhook-manager.d.ts +137 -0
- package/dist/core/src/webhooks/webhook-manager.d.ts.map +1 -0
- package/dist/core/src/webhooks/webhook-manager.js +334 -0
- package/dist/core/src/webhooks/webhook-manager.js.map +1 -0
- package/dist/index.d.ts +0 -1
- package/dist/index.js +0 -1
- package/package.json +2 -2
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import type { SubscriptionManager } from './subscription-manager';
|
|
2
|
+
import type { ChangeMessage } from './message-types';
|
|
3
|
+
import type { User } from '@edgebasejs/types';
|
|
4
|
+
export interface WebSocketClient {
|
|
5
|
+
send(message: string): void;
|
|
6
|
+
close(): void;
|
|
7
|
+
isOpen(): boolean;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Notifies subscribed clients about data changes
|
|
11
|
+
* Manages broadcasting changes to WebSocket connections
|
|
12
|
+
*/
|
|
13
|
+
export declare class ChangeNotifier {
|
|
14
|
+
private subscriptionManager;
|
|
15
|
+
private clients;
|
|
16
|
+
constructor(subscriptionManager: SubscriptionManager);
|
|
17
|
+
/**
|
|
18
|
+
* Register a WebSocket client connection
|
|
19
|
+
*/
|
|
20
|
+
registerClient(connectionId: string, client: WebSocketClient): void;
|
|
21
|
+
/**
|
|
22
|
+
* Unregister a WebSocket client connection
|
|
23
|
+
*/
|
|
24
|
+
unregisterClient(connectionId: string): void;
|
|
25
|
+
/**
|
|
26
|
+
* Broadcast a change to all subscribed clients
|
|
27
|
+
*/
|
|
28
|
+
notifyChange(entity: string, operation: 'create' | 'update' | 'delete', record: Record<string, unknown>, recordId: string, user: User, version?: number): Promise<void>;
|
|
29
|
+
/**
|
|
30
|
+
* Send a message to a specific connection
|
|
31
|
+
*/
|
|
32
|
+
private sendToConnection;
|
|
33
|
+
/**
|
|
34
|
+
* Broadcast a message to all clients
|
|
35
|
+
*/
|
|
36
|
+
broadcastToAll(message: ChangeMessage, filterSubscriptions?: string[]): void;
|
|
37
|
+
/**
|
|
38
|
+
* Send heartbeat to all connected clients
|
|
39
|
+
*/
|
|
40
|
+
sendHeartbeat(): void;
|
|
41
|
+
/**
|
|
42
|
+
* Send error message to a specific client
|
|
43
|
+
*/
|
|
44
|
+
sendError(connectionId: string, code: string, message: string): void;
|
|
45
|
+
/**
|
|
46
|
+
* Get connected clients count
|
|
47
|
+
*/
|
|
48
|
+
getConnectedClientsCount(): number;
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=change-notifier.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"change-notifier.d.ts","sourceRoot":"","sources":["../../../../src/realtime/change-notifier.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAC;AAClE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AACrD,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,mBAAmB,CAAC;AAE9C,MAAM,WAAW,eAAe;IAC9B,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,KAAK,IAAI,IAAI,CAAC;IACd,MAAM,IAAI,OAAO,CAAC;CACnB;AAED;;;GAGG;AACH,qBAAa,cAAc;IAGb,OAAO,CAAC,mBAAmB;IAFvC,OAAO,CAAC,OAAO,CAAsC;gBAEjC,mBAAmB,EAAE,mBAAmB;IAE5D;;OAEG;IACH,cAAc,CAAC,YAAY,EAAE,MAAM,EAAE,MAAM,EAAE,eAAe,GAAG,IAAI;IAInE;;OAEG;IACH,gBAAgB,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI;IAI5C;;OAEG;IACG,YAAY,CAChB,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,QAAQ,GAAG,QAAQ,GAAG,QAAQ,EACzC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC/B,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,IAAI,EACV,OAAO,CAAC,EAAE,MAAM,GACf,OAAO,CAAC,IAAI,CAAC;IA+BhB;;OAEG;IACH,OAAO,CAAC,gBAAgB;IAgBxB;;OAEG;IACH,cAAc,CAAC,OAAO,EAAE,aAAa,EAAE,mBAAmB,CAAC,EAAE,MAAM,EAAE,GAAG,IAAI;IA8B5E;;OAEG;IACH,aAAa,IAAI,IAAI;IAqBrB;;OAEG;IACH,SAAS,CAAC,YAAY,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI;IAoBpE;;OAEG;IACH,wBAAwB,IAAI,MAAM;CAGnC"}
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Notifies subscribed clients about data changes
|
|
3
|
+
* Manages broadcasting changes to WebSocket connections
|
|
4
|
+
*/
|
|
5
|
+
export class ChangeNotifier {
|
|
6
|
+
constructor(subscriptionManager) {
|
|
7
|
+
this.subscriptionManager = subscriptionManager;
|
|
8
|
+
this.clients = new Map();
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Register a WebSocket client connection
|
|
12
|
+
*/
|
|
13
|
+
registerClient(connectionId, client) {
|
|
14
|
+
this.clients.set(connectionId, client);
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Unregister a WebSocket client connection
|
|
18
|
+
*/
|
|
19
|
+
unregisterClient(connectionId) {
|
|
20
|
+
this.clients.delete(connectionId);
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Broadcast a change to all subscribed clients
|
|
24
|
+
*/
|
|
25
|
+
async notifyChange(entity, operation, record, recordId, user, version) {
|
|
26
|
+
const subscriptions = this.subscriptionManager.getSubscriptionsForEntity(entity);
|
|
27
|
+
for (const subscription of subscriptions) {
|
|
28
|
+
// Check if user has access (basic check - should be extended with full access rules)
|
|
29
|
+
// For now, assume user can see their own data
|
|
30
|
+
if (subscription.userId !== user.id) {
|
|
31
|
+
// In production, would check RLS rules here
|
|
32
|
+
continue;
|
|
33
|
+
}
|
|
34
|
+
// Check if subscription filters match the record
|
|
35
|
+
if (!this.subscriptionManager.matchesFilter(subscription, record)) {
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
// Send change message to client
|
|
39
|
+
const message = {
|
|
40
|
+
type: 'change',
|
|
41
|
+
entity,
|
|
42
|
+
operation,
|
|
43
|
+
record: operation !== 'delete' ? record : undefined,
|
|
44
|
+
recordId,
|
|
45
|
+
timestamp: Date.now(),
|
|
46
|
+
version,
|
|
47
|
+
};
|
|
48
|
+
this.sendToConnection(subscription.subscriptionId, message);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Send a message to a specific connection
|
|
53
|
+
*/
|
|
54
|
+
sendToConnection(subscriptionId, message) {
|
|
55
|
+
const subscription = this.subscriptionManager.getSubscription(subscriptionId);
|
|
56
|
+
if (!subscription)
|
|
57
|
+
return;
|
|
58
|
+
// Find the connection for this subscription
|
|
59
|
+
const subscriptions = this.subscriptionManager.getSubscriptionsForEntity(subscription.entity);
|
|
60
|
+
const matchingSub = subscriptions.find((s) => s.subscriptionId === subscriptionId);
|
|
61
|
+
if (!matchingSub)
|
|
62
|
+
return;
|
|
63
|
+
// This is a placeholder - in actual implementation, we'd need to track
|
|
64
|
+
// which connection ID corresponds to which subscription
|
|
65
|
+
// For now, we'll send to all connections and let them filter
|
|
66
|
+
this.broadcastToAll(message, [subscriptionId]);
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Broadcast a message to all clients
|
|
70
|
+
*/
|
|
71
|
+
broadcastToAll(message, filterSubscriptions) {
|
|
72
|
+
const messageStr = JSON.stringify(message);
|
|
73
|
+
for (const [connectionId, client] of this.clients.entries()) {
|
|
74
|
+
if (!client.isOpen()) {
|
|
75
|
+
this.unregisterClient(connectionId);
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
// If filter subscriptions provided, only send to relevant clients
|
|
79
|
+
if (filterSubscriptions) {
|
|
80
|
+
const subscriptions = this.subscriptionManager.getSubscriptionsForConnection(connectionId);
|
|
81
|
+
const hasMatchingSubscription = subscriptions.some((sub) => filterSubscriptions.includes(sub.subscriptionId));
|
|
82
|
+
if (!hasMatchingSubscription) {
|
|
83
|
+
continue;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
try {
|
|
87
|
+
client.send(messageStr);
|
|
88
|
+
}
|
|
89
|
+
catch (error) {
|
|
90
|
+
console.error(`Failed to send message to client ${connectionId}:`, error);
|
|
91
|
+
this.unregisterClient(connectionId);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Send heartbeat to all connected clients
|
|
97
|
+
*/
|
|
98
|
+
sendHeartbeat() {
|
|
99
|
+
const heartbeatMsg = JSON.stringify({
|
|
100
|
+
type: 'heartbeat',
|
|
101
|
+
timestamp: Date.now(),
|
|
102
|
+
});
|
|
103
|
+
for (const [connectionId, client] of this.clients.entries()) {
|
|
104
|
+
if (!client.isOpen()) {
|
|
105
|
+
this.unregisterClient(connectionId);
|
|
106
|
+
continue;
|
|
107
|
+
}
|
|
108
|
+
try {
|
|
109
|
+
client.send(heartbeatMsg);
|
|
110
|
+
}
|
|
111
|
+
catch (error) {
|
|
112
|
+
console.error(`Failed to send heartbeat to client ${connectionId}:`, error);
|
|
113
|
+
this.unregisterClient(connectionId);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Send error message to a specific client
|
|
119
|
+
*/
|
|
120
|
+
sendError(connectionId, code, message) {
|
|
121
|
+
const client = this.clients.get(connectionId);
|
|
122
|
+
if (!client || !client.isOpen()) {
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
const errorMsg = JSON.stringify({
|
|
126
|
+
type: 'error',
|
|
127
|
+
code,
|
|
128
|
+
message,
|
|
129
|
+
});
|
|
130
|
+
try {
|
|
131
|
+
client.send(errorMsg);
|
|
132
|
+
}
|
|
133
|
+
catch (error) {
|
|
134
|
+
console.error(`Failed to send error to client ${connectionId}:`, error);
|
|
135
|
+
this.unregisterClient(connectionId);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Get connected clients count
|
|
140
|
+
*/
|
|
141
|
+
getConnectedClientsCount() {
|
|
142
|
+
return this.clients.size;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
//# sourceMappingURL=change-notifier.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"change-notifier.js","sourceRoot":"","sources":["../../../../src/realtime/change-notifier.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WebSocket message types for real-time subscriptions
|
|
3
|
+
*/
|
|
4
|
+
export type SubscriptionMessage = SubscribeMessage | UnsubscribeMessage | ChangeMessage | ErrorMessage | HeartbeatMessage;
|
|
5
|
+
export interface SubscribeMessage {
|
|
6
|
+
type: 'subscribe';
|
|
7
|
+
entity: string;
|
|
8
|
+
filters?: Record<string, unknown>;
|
|
9
|
+
}
|
|
10
|
+
export interface UnsubscribeMessage {
|
|
11
|
+
type: 'unsubscribe';
|
|
12
|
+
entity: string;
|
|
13
|
+
}
|
|
14
|
+
export interface ChangeMessage {
|
|
15
|
+
type: 'change';
|
|
16
|
+
entity: string;
|
|
17
|
+
operation: 'create' | 'update' | 'delete';
|
|
18
|
+
record?: Record<string, unknown>;
|
|
19
|
+
recordId?: string;
|
|
20
|
+
timestamp: number;
|
|
21
|
+
version?: number;
|
|
22
|
+
}
|
|
23
|
+
export interface ErrorMessage {
|
|
24
|
+
type: 'error';
|
|
25
|
+
code: string;
|
|
26
|
+
message: string;
|
|
27
|
+
}
|
|
28
|
+
export interface HeartbeatMessage {
|
|
29
|
+
type: 'heartbeat';
|
|
30
|
+
timestamp: number;
|
|
31
|
+
}
|
|
32
|
+
export interface SubscriptionState {
|
|
33
|
+
subscriptionId: string;
|
|
34
|
+
userId: string;
|
|
35
|
+
entity: string;
|
|
36
|
+
filters?: Record<string, unknown>;
|
|
37
|
+
createdAt: number;
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=message-types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"message-types.d.ts","sourceRoot":"","sources":["../../../../src/realtime/message-types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,MAAM,MAAM,mBAAmB,GAC3B,gBAAgB,GAChB,kBAAkB,GAClB,aAAa,GACb,YAAY,GACZ,gBAAgB,CAAC;AAErB,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,WAAW,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACnC;AAED,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,aAAa,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,QAAQ,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,QAAQ,GAAG,QAAQ,GAAG,QAAQ,CAAC;IAC1C,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,OAAO,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,WAAW,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,iBAAiB;IAChC,cAAc,EAAE,MAAM,CAAC;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAClC,SAAS,EAAE,MAAM,CAAC;CACnB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"message-types.js","sourceRoot":"","sources":["../../../../src/realtime/message-types.ts"],"names":[],"mappings":"AAAA;;GAEG"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import type { SyncDatabase } from '../sync/sync-engine';
|
|
2
|
+
import type { SubscriptionState } from './message-types';
|
|
3
|
+
/**
|
|
4
|
+
* Manages active WebSocket subscriptions
|
|
5
|
+
* Tracks which clients are subscribed to which entities and filters
|
|
6
|
+
*/
|
|
7
|
+
export declare class SubscriptionManager {
|
|
8
|
+
private db;
|
|
9
|
+
private subscriptions;
|
|
10
|
+
private userSubscriptions;
|
|
11
|
+
private entitySubscriptions;
|
|
12
|
+
private connectionSubscriptions;
|
|
13
|
+
constructor(db: SyncDatabase);
|
|
14
|
+
/**
|
|
15
|
+
* Create a new subscription
|
|
16
|
+
*/
|
|
17
|
+
subscribe(userId: string, connectionId: string, entity: string, filters?: Record<string, unknown>): Promise<string>;
|
|
18
|
+
/**
|
|
19
|
+
* Remove a subscription
|
|
20
|
+
*/
|
|
21
|
+
unsubscribe(subscriptionId: string): Promise<void>;
|
|
22
|
+
/**
|
|
23
|
+
* Remove all subscriptions for a connection
|
|
24
|
+
*/
|
|
25
|
+
disconnectClient(connectionId: string): Promise<void>;
|
|
26
|
+
/**
|
|
27
|
+
* Get all subscriptions for an entity
|
|
28
|
+
*/
|
|
29
|
+
getSubscriptionsForEntity(entity: string): SubscriptionState[];
|
|
30
|
+
/**
|
|
31
|
+
* Get all subscriptions for a user
|
|
32
|
+
*/
|
|
33
|
+
getSubscriptionsForUser(userId: string): SubscriptionState[];
|
|
34
|
+
/**
|
|
35
|
+
* Get all subscriptions for a connection
|
|
36
|
+
*/
|
|
37
|
+
getSubscriptionsForConnection(connectionId: string): SubscriptionState[];
|
|
38
|
+
/**
|
|
39
|
+
* Update last heartbeat for a subscription
|
|
40
|
+
*/
|
|
41
|
+
updateHeartbeat(subscriptionId: string): Promise<void>;
|
|
42
|
+
/**
|
|
43
|
+
* Get subscription by ID
|
|
44
|
+
*/
|
|
45
|
+
getSubscription(subscriptionId: string): SubscriptionState | undefined;
|
|
46
|
+
/**
|
|
47
|
+
* Check if a subscription matches a filter
|
|
48
|
+
*/
|
|
49
|
+
matchesFilter(subscription: SubscriptionState, data: Record<string, unknown>): boolean;
|
|
50
|
+
/**
|
|
51
|
+
* Load subscriptions from database on startup
|
|
52
|
+
*/
|
|
53
|
+
loadFromDatabase(): Promise<void>;
|
|
54
|
+
/**
|
|
55
|
+
* Cleanup expired subscriptions
|
|
56
|
+
*/
|
|
57
|
+
cleanup(): Promise<void>;
|
|
58
|
+
/**
|
|
59
|
+
* Get subscription statistics
|
|
60
|
+
*/
|
|
61
|
+
getStats(): {
|
|
62
|
+
totalSubscriptions: number;
|
|
63
|
+
activeConnections: number;
|
|
64
|
+
entitiesWithSubscriptions: number;
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
//# sourceMappingURL=subscription-manager.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"subscription-manager.d.ts","sourceRoot":"","sources":["../../../../src/realtime/subscription-manager.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AAEzD;;;GAGG;AACH,qBAAa,mBAAmB;IAMlB,OAAO,CAAC,EAAE;IALtB,OAAO,CAAC,aAAa,CAAwC;IAC7D,OAAO,CAAC,iBAAiB,CAAkC;IAC3D,OAAO,CAAC,mBAAmB,CAAkC;IAC7D,OAAO,CAAC,uBAAuB,CAAkC;gBAE7C,EAAE,EAAE,YAAY;IAEpC;;OAEG;IACG,SAAS,CACb,MAAM,EAAE,MAAM,EACd,YAAY,EAAE,MAAM,EACpB,MAAM,EAAE,MAAM,EACd,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAChC,OAAO,CAAC,MAAM,CAAC;IAqDlB;;OAEG;IACG,WAAW,CAAC,cAAc,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAyBxD;;OAEG;IACG,gBAAgB,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAS3D;;OAEG;IACH,yBAAyB,CAAC,MAAM,EAAE,MAAM,GAAG,iBAAiB,EAAE;IAS9D;;OAEG;IACH,uBAAuB,CAAC,MAAM,EAAE,MAAM,GAAG,iBAAiB,EAAE;IAS5D;;OAEG;IACH,6BAA6B,CAAC,YAAY,EAAE,MAAM,GAAG,iBAAiB,EAAE;IASxE;;OAEG;IACG,eAAe,CAAC,cAAc,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAU5D;;OAEG;IACH,eAAe,CAAC,cAAc,EAAE,MAAM,GAAG,iBAAiB,GAAG,SAAS;IAItE;;OAEG;IACH,aAAa,CAAC,YAAY,EAAE,iBAAiB,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO;IAatF;;OAEG;IACG,gBAAgB,IAAI,OAAO,CAAC,IAAI,CAAC;IA4DvC;;OAEG;IACG,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAmB9B;;OAEG;IACH,QAAQ,IAAI;QACV,kBAAkB,EAAE,MAAM,CAAC;QAC3B,iBAAiB,EAAE,MAAM,CAAC;QAC1B,yBAAyB,EAAE,MAAM,CAAC;KACnC;CAOF"}
|
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Manages active WebSocket subscriptions
|
|
3
|
+
* Tracks which clients are subscribed to which entities and filters
|
|
4
|
+
*/
|
|
5
|
+
export class SubscriptionManager {
|
|
6
|
+
constructor(db) {
|
|
7
|
+
this.db = db;
|
|
8
|
+
this.subscriptions = new Map();
|
|
9
|
+
this.userSubscriptions = new Map();
|
|
10
|
+
this.entitySubscriptions = new Map();
|
|
11
|
+
this.connectionSubscriptions = new Map();
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Create a new subscription
|
|
15
|
+
*/
|
|
16
|
+
async subscribe(userId, connectionId, entity, filters) {
|
|
17
|
+
const subscriptionId = `sub_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
|
18
|
+
const now = Date.now();
|
|
19
|
+
const subscription = {
|
|
20
|
+
subscriptionId,
|
|
21
|
+
userId,
|
|
22
|
+
entity,
|
|
23
|
+
filters,
|
|
24
|
+
createdAt: now,
|
|
25
|
+
};
|
|
26
|
+
// Store in memory
|
|
27
|
+
this.subscriptions.set(subscriptionId, subscription);
|
|
28
|
+
// Update indexes
|
|
29
|
+
if (!this.userSubscriptions.has(userId)) {
|
|
30
|
+
this.userSubscriptions.set(userId, new Set());
|
|
31
|
+
}
|
|
32
|
+
this.userSubscriptions.get(userId).add(subscriptionId);
|
|
33
|
+
if (!this.entitySubscriptions.has(entity)) {
|
|
34
|
+
this.entitySubscriptions.set(entity, new Set());
|
|
35
|
+
}
|
|
36
|
+
this.entitySubscriptions.get(entity).add(subscriptionId);
|
|
37
|
+
if (!this.connectionSubscriptions.has(connectionId)) {
|
|
38
|
+
this.connectionSubscriptions.set(connectionId, new Set());
|
|
39
|
+
}
|
|
40
|
+
this.connectionSubscriptions.get(connectionId).add(subscriptionId);
|
|
41
|
+
// Persist to database
|
|
42
|
+
await this.db.run(`INSERT INTO subscriptions (id, user_id, entity, connection_id, created_at, last_heartbeat)
|
|
43
|
+
VALUES (?, ?, ?, ?, ?, ?)`, [subscriptionId, userId, entity, connectionId, now, now]);
|
|
44
|
+
// Store filters if provided
|
|
45
|
+
if (filters) {
|
|
46
|
+
for (const [key, value] of Object.entries(filters)) {
|
|
47
|
+
const filterId = `filter_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
|
48
|
+
await this.db.run(`INSERT INTO subscription_filters (id, subscription_id, filter_key, filter_value, created_at)
|
|
49
|
+
VALUES (?, ?, ?, ?, ?)`, [filterId, subscriptionId, key, JSON.stringify(value), now]);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return subscriptionId;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Remove a subscription
|
|
56
|
+
*/
|
|
57
|
+
async unsubscribe(subscriptionId) {
|
|
58
|
+
const subscription = this.subscriptions.get(subscriptionId);
|
|
59
|
+
if (!subscription)
|
|
60
|
+
return;
|
|
61
|
+
// Remove from memory
|
|
62
|
+
this.subscriptions.delete(subscriptionId);
|
|
63
|
+
// Update indexes
|
|
64
|
+
this.userSubscriptions.get(subscription.userId)?.delete(subscriptionId);
|
|
65
|
+
this.entitySubscriptions.get(subscription.entity)?.delete(subscriptionId);
|
|
66
|
+
// Find and update connection subscriptions
|
|
67
|
+
for (const [connId, subs] of this.connectionSubscriptions.entries()) {
|
|
68
|
+
if (subs.has(subscriptionId)) {
|
|
69
|
+
subs.delete(subscriptionId);
|
|
70
|
+
if (subs.size === 0) {
|
|
71
|
+
this.connectionSubscriptions.delete(connId);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
// Remove from database
|
|
76
|
+
await this.db.run('DELETE FROM subscriptions WHERE id = ?', [subscriptionId]);
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Remove all subscriptions for a connection
|
|
80
|
+
*/
|
|
81
|
+
async disconnectClient(connectionId) {
|
|
82
|
+
const subscriptionIds = this.connectionSubscriptions.get(connectionId);
|
|
83
|
+
if (!subscriptionIds)
|
|
84
|
+
return;
|
|
85
|
+
for (const subId of subscriptionIds) {
|
|
86
|
+
await this.unsubscribe(subId);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Get all subscriptions for an entity
|
|
91
|
+
*/
|
|
92
|
+
getSubscriptionsForEntity(entity) {
|
|
93
|
+
const subscriptionIds = this.entitySubscriptions.get(entity);
|
|
94
|
+
if (!subscriptionIds)
|
|
95
|
+
return [];
|
|
96
|
+
return Array.from(subscriptionIds)
|
|
97
|
+
.map((id) => this.subscriptions.get(id))
|
|
98
|
+
.filter((sub) => sub !== undefined);
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Get all subscriptions for a user
|
|
102
|
+
*/
|
|
103
|
+
getSubscriptionsForUser(userId) {
|
|
104
|
+
const subscriptionIds = this.userSubscriptions.get(userId);
|
|
105
|
+
if (!subscriptionIds)
|
|
106
|
+
return [];
|
|
107
|
+
return Array.from(subscriptionIds)
|
|
108
|
+
.map((id) => this.subscriptions.get(id))
|
|
109
|
+
.filter((sub) => sub !== undefined);
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Get all subscriptions for a connection
|
|
113
|
+
*/
|
|
114
|
+
getSubscriptionsForConnection(connectionId) {
|
|
115
|
+
const subscriptionIds = this.connectionSubscriptions.get(connectionId);
|
|
116
|
+
if (!subscriptionIds)
|
|
117
|
+
return [];
|
|
118
|
+
return Array.from(subscriptionIds)
|
|
119
|
+
.map((id) => this.subscriptions.get(id))
|
|
120
|
+
.filter((sub) => sub !== undefined);
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Update last heartbeat for a subscription
|
|
124
|
+
*/
|
|
125
|
+
async updateHeartbeat(subscriptionId) {
|
|
126
|
+
const subscription = this.subscriptions.get(subscriptionId);
|
|
127
|
+
if (!subscription)
|
|
128
|
+
return;
|
|
129
|
+
const now = Date.now();
|
|
130
|
+
subscription.createdAt = now; // Update in-memory
|
|
131
|
+
await this.db.run('UPDATE subscriptions SET last_heartbeat = ? WHERE id = ?', [now, subscriptionId]);
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Get subscription by ID
|
|
135
|
+
*/
|
|
136
|
+
getSubscription(subscriptionId) {
|
|
137
|
+
return this.subscriptions.get(subscriptionId);
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Check if a subscription matches a filter
|
|
141
|
+
*/
|
|
142
|
+
matchesFilter(subscription, data) {
|
|
143
|
+
if (!subscription.filters)
|
|
144
|
+
return true;
|
|
145
|
+
for (const [key, filterValue] of Object.entries(subscription.filters)) {
|
|
146
|
+
const dataValue = data[key];
|
|
147
|
+
if (dataValue !== filterValue) {
|
|
148
|
+
return false;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
return true;
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Load subscriptions from database on startup
|
|
155
|
+
*/
|
|
156
|
+
async loadFromDatabase() {
|
|
157
|
+
try {
|
|
158
|
+
const results = await this.db.getAll(`SELECT id, user_id, entity, connection_id, created_at
|
|
159
|
+
FROM subscriptions
|
|
160
|
+
WHERE last_heartbeat > ?`, [Date.now() - 3600000] // Only load subscriptions from last hour
|
|
161
|
+
);
|
|
162
|
+
for (const row of results) {
|
|
163
|
+
const subscriptionId = row.id;
|
|
164
|
+
const userId = row.user_id;
|
|
165
|
+
const entity = row.entity;
|
|
166
|
+
const connectionId = row.connection_id;
|
|
167
|
+
const createdAt = row.created_at;
|
|
168
|
+
// Load filters for this subscription
|
|
169
|
+
const filterResults = await this.db.getAll('SELECT filter_key, filter_value FROM subscription_filters WHERE subscription_id = ?', [subscriptionId]);
|
|
170
|
+
const filters = {};
|
|
171
|
+
for (const filterRow of filterResults) {
|
|
172
|
+
const key = filterRow.filter_key;
|
|
173
|
+
const value = JSON.parse(filterRow.filter_value);
|
|
174
|
+
filters[key] = value;
|
|
175
|
+
}
|
|
176
|
+
const subscription = {
|
|
177
|
+
subscriptionId,
|
|
178
|
+
userId,
|
|
179
|
+
entity,
|
|
180
|
+
filters: Object.keys(filters).length > 0 ? filters : undefined,
|
|
181
|
+
createdAt,
|
|
182
|
+
};
|
|
183
|
+
this.subscriptions.set(subscriptionId, subscription);
|
|
184
|
+
// Update indexes
|
|
185
|
+
if (!this.userSubscriptions.has(userId)) {
|
|
186
|
+
this.userSubscriptions.set(userId, new Set());
|
|
187
|
+
}
|
|
188
|
+
this.userSubscriptions.get(userId).add(subscriptionId);
|
|
189
|
+
if (!this.entitySubscriptions.has(entity)) {
|
|
190
|
+
this.entitySubscriptions.set(entity, new Set());
|
|
191
|
+
}
|
|
192
|
+
this.entitySubscriptions.get(entity).add(subscriptionId);
|
|
193
|
+
if (!this.connectionSubscriptions.has(connectionId)) {
|
|
194
|
+
this.connectionSubscriptions.set(connectionId, new Set());
|
|
195
|
+
}
|
|
196
|
+
this.connectionSubscriptions.get(connectionId).add(subscriptionId);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
catch (error) {
|
|
200
|
+
console.error('Failed to load subscriptions from database:', error);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* Cleanup expired subscriptions
|
|
205
|
+
*/
|
|
206
|
+
async cleanup() {
|
|
207
|
+
const oneDayAgo = Date.now() - 86400000;
|
|
208
|
+
// Delete old subscriptions from database
|
|
209
|
+
await this.db.run('DELETE FROM subscriptions WHERE last_heartbeat < ?', [oneDayAgo]);
|
|
210
|
+
// Remove disconnected subscriptions from memory
|
|
211
|
+
const staleSubscriptions = Array.from(this.subscriptions.entries())
|
|
212
|
+
.filter(([_, sub]) => sub.createdAt < oneDayAgo)
|
|
213
|
+
.map(([id]) => id);
|
|
214
|
+
for (const subId of staleSubscriptions) {
|
|
215
|
+
this.subscriptions.delete(subId);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* Get subscription statistics
|
|
220
|
+
*/
|
|
221
|
+
getStats() {
|
|
222
|
+
return {
|
|
223
|
+
totalSubscriptions: this.subscriptions.size,
|
|
224
|
+
activeConnections: this.connectionSubscriptions.size,
|
|
225
|
+
entitiesWithSubscriptions: this.entitySubscriptions.size,
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
//# sourceMappingURL=subscription-manager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"subscription-manager.js","sourceRoot":"","sources":["../../../../src/realtime/subscription-manager.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Full-text search manager using SQLite FTS5
|
|
3
|
+
* Handles search index creation, updating, and querying
|
|
4
|
+
*/
|
|
5
|
+
export interface SearchIndex {
|
|
6
|
+
entity: string;
|
|
7
|
+
columns: string[];
|
|
8
|
+
tokenize?: string;
|
|
9
|
+
prefix?: number[];
|
|
10
|
+
contentless?: boolean;
|
|
11
|
+
}
|
|
12
|
+
export interface SearchQuery {
|
|
13
|
+
entity: string;
|
|
14
|
+
query: string;
|
|
15
|
+
columns?: string[];
|
|
16
|
+
limit?: number;
|
|
17
|
+
offset?: number;
|
|
18
|
+
highlight?: boolean;
|
|
19
|
+
rank?: boolean;
|
|
20
|
+
}
|
|
21
|
+
export interface SearchResult {
|
|
22
|
+
id: string;
|
|
23
|
+
rank?: number;
|
|
24
|
+
snippet?: string;
|
|
25
|
+
[key: string]: any;
|
|
26
|
+
}
|
|
27
|
+
export interface SearchResponse {
|
|
28
|
+
results: SearchResult[];
|
|
29
|
+
total: number;
|
|
30
|
+
hasMore: boolean;
|
|
31
|
+
}
|
|
32
|
+
export interface SearchDatabase {
|
|
33
|
+
run(sql: string, params: any[]): Promise<any>;
|
|
34
|
+
getOne(sql: string, params: any[]): Promise<any>;
|
|
35
|
+
getAll(sql: string, params: any[]): Promise<any[]>;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Search manager for full-text search
|
|
39
|
+
*/
|
|
40
|
+
export declare class SearchManager {
|
|
41
|
+
private db;
|
|
42
|
+
private indexes;
|
|
43
|
+
constructor(db: SearchDatabase);
|
|
44
|
+
/**
|
|
45
|
+
* Register a search index for an entity
|
|
46
|
+
*/
|
|
47
|
+
registerIndex(index: SearchIndex): void;
|
|
48
|
+
/**
|
|
49
|
+
* Create FTS5 virtual table for an entity
|
|
50
|
+
*/
|
|
51
|
+
createSearchIndex(entity: string): Promise<void>;
|
|
52
|
+
/**
|
|
53
|
+
* Create triggers to automatically update FTS index
|
|
54
|
+
*/
|
|
55
|
+
private createSyncTriggers;
|
|
56
|
+
/**
|
|
57
|
+
* Rebuild search index from scratch
|
|
58
|
+
*/
|
|
59
|
+
rebuildIndex(entity: string): Promise<number>;
|
|
60
|
+
/**
|
|
61
|
+
* Search for records
|
|
62
|
+
*/
|
|
63
|
+
search(query: SearchQuery): Promise<SearchResponse>;
|
|
64
|
+
/**
|
|
65
|
+
* Build FTS5 query string from user query
|
|
66
|
+
*/
|
|
67
|
+
private buildFTSQuery;
|
|
68
|
+
/**
|
|
69
|
+
* Delete search index
|
|
70
|
+
*/
|
|
71
|
+
deleteSearchIndex(entity: string): Promise<void>;
|
|
72
|
+
/**
|
|
73
|
+
* Optimize search index (FTS5 optimize)
|
|
74
|
+
*/
|
|
75
|
+
optimizeIndex(entity: string): Promise<void>;
|
|
76
|
+
/**
|
|
77
|
+
* Get search index statistics
|
|
78
|
+
*/
|
|
79
|
+
getIndexStats(entity: string): Promise<{
|
|
80
|
+
entity: string;
|
|
81
|
+
documentCount: number;
|
|
82
|
+
indexSize: number;
|
|
83
|
+
}>;
|
|
84
|
+
/**
|
|
85
|
+
* Get all registered indexes
|
|
86
|
+
*/
|
|
87
|
+
getRegisteredIndexes(): SearchIndex[];
|
|
88
|
+
/**
|
|
89
|
+
* Check if an entity has a search index
|
|
90
|
+
*/
|
|
91
|
+
hasIndex(entity: string): boolean;
|
|
92
|
+
}
|
|
93
|
+
//# sourceMappingURL=search-manager.d.ts.map
|