@edgebasejs/client-core 0.1.8 → 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/client-core/src/index.d.ts +4 -0
- package/dist/client-core/src/index.d.ts.map +1 -1
- package/dist/client-core/src/index.js +4 -0
- package/dist/client-core/src/index.js.map +1 -1
- package/dist/client-core/src/mutations/batch-processor-client.d.ts +67 -0
- package/dist/client-core/src/mutations/batch-processor-client.d.ts.map +1 -0
- package/dist/client-core/src/mutations/batch-processor-client.js +64 -0
- package/dist/client-core/src/mutations/batch-processor-client.js.map +1 -0
- package/dist/client-core/src/mutations/transaction-hook.d.ts +80 -0
- package/dist/client-core/src/mutations/transaction-hook.d.ts.map +1 -0
- package/dist/client-core/src/mutations/transaction-hook.js +204 -0
- package/dist/client-core/src/mutations/transaction-hook.js.map +1 -0
- package/dist/client-core/src/realtime/realtime-sync-manager.d.ts +55 -0
- package/dist/client-core/src/realtime/realtime-sync-manager.d.ts.map +1 -0
- package/dist/client-core/src/realtime/realtime-sync-manager.js +208 -0
- package/dist/client-core/src/realtime/realtime-sync-manager.js.map +1 -0
- package/dist/client-core/src/realtime/subscription-handler.d.ts +74 -0
- package/dist/client-core/src/realtime/subscription-handler.d.ts.map +1 -0
- package/dist/client-core/src/realtime/subscription-handler.js +224 -0
- package/dist/client-core/src/realtime/subscription-handler.js.map +1 -0
- package/dist/client-core/src/sync/sync-engine.d.ts +10 -0
- package/dist/client-core/src/sync/sync-engine.d.ts.map +1 -1
- package/dist/client-core/src/sync/sync-engine.js +37 -5
- package/dist/client-core/src/sync/sync-engine.js.map +1 -1
- 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 +26 -0
- package/dist/core/src/access-rules/engine.d.ts.map +1 -0
- package/dist/core/src/access-rules/engine.js +76 -0
- package/dist/core/src/access-rules/engine.js.map +1 -0
- package/dist/core/src/access-rules/index.d.ts +3 -0
- package/dist/core/src/access-rules/index.d.ts.map +1 -0
- package/dist/core/src/access-rules/index.js +3 -0
- package/dist/core/src/access-rules/index.js.map +1 -0
- 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/auth/auth-service.d.ts +71 -0
- package/dist/core/src/auth/auth-service.d.ts.map +1 -0
- package/dist/core/src/auth/auth-service.js +177 -0
- package/dist/core/src/auth/auth-service.js.map +1 -0
- package/dist/core/src/auth/index.d.ts +4 -0
- package/dist/core/src/auth/index.d.ts.map +1 -0
- package/dist/core/src/auth/index.js +4 -0
- package/dist/core/src/auth/index.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 +16 -0
- package/dist/core/src/index.d.ts.map +1 -0
- package/dist/core/src/index.js +16 -0
- package/dist/core/src/index.js.map +1 -0
- 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/index.d.ts +3 -0
- package/dist/core/src/sync/index.d.ts.map +1 -0
- package/dist/core/src/sync/index.js +3 -0
- package/dist/core/src/sync/index.js.map +1 -0
- package/dist/core/src/sync/sync-engine.d.ts +68 -0
- package/dist/core/src/sync/sync-engine.d.ts.map +1 -0
- package/dist/core/src/sync/sync-engine.js +317 -0
- package/dist/core/src/sync/sync-engine.js.map +1 -0
- 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/package.json +2 -2
|
@@ -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":["../../../../../core/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
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"search-manager.d.ts","sourceRoot":"","sources":["../../../../../core/src/search/search-manager.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAED,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;CACpB;AAED,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,YAAY,EAAE,CAAC;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,cAAc;IAC7B,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;IAC9C,MAAM,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;IACjD,MAAM,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC;CACpD;AAED;;GAEG;AACH,qBAAa,aAAa;IACxB,OAAO,CAAC,EAAE,CAAiB;IAC3B,OAAO,CAAC,OAAO,CAAuC;gBAE1C,EAAE,EAAE,cAAc;IAI9B;;OAEG;IACH,aAAa,CAAC,KAAK,EAAE,WAAW,GAAG,IAAI;IAIvC;;OAEG;IACG,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAyCtD;;OAEG;YACW,kBAAkB;IAuChC;;OAEG;IACG,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAwBnD;;OAEG;IACG,MAAM,CAAC,KAAK,EAAE,WAAW,GAAG,OAAO,CAAC,cAAc,CAAC;IA0FzD;;OAEG;IACH,OAAO,CAAC,aAAa;IA4BrB;;OAEG;IACG,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAetD;;OAEG;IACG,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAKlD;;OAEG;IACG,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC;QAC3C,MAAM,EAAE,MAAM,CAAC;QACf,aAAa,EAAE,MAAM,CAAC;QACtB,SAAS,EAAE,MAAM,CAAC;KACnB,CAAC;IAoBF;;OAEG;IACH,oBAAoB,IAAI,WAAW,EAAE;IAIrC;;OAEG;IACH,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO;CAGlC"}
|
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Full-text search manager using SQLite FTS5
|
|
3
|
+
* Handles search index creation, updating, and querying
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Search manager for full-text search
|
|
7
|
+
*/
|
|
8
|
+
export class SearchManager {
|
|
9
|
+
constructor(db) {
|
|
10
|
+
this.indexes = new Map();
|
|
11
|
+
this.db = db;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Register a search index for an entity
|
|
15
|
+
*/
|
|
16
|
+
registerIndex(index) {
|
|
17
|
+
this.indexes.set(index.entity, index);
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Create FTS5 virtual table for an entity
|
|
21
|
+
*/
|
|
22
|
+
async createSearchIndex(entity) {
|
|
23
|
+
const index = this.indexes.get(entity);
|
|
24
|
+
if (!index) {
|
|
25
|
+
throw new Error(`No search index configured for entity: ${entity}`);
|
|
26
|
+
}
|
|
27
|
+
const ftsTableName = `${entity}_fts`;
|
|
28
|
+
// Build FTS5 options
|
|
29
|
+
const ftsOptions = [];
|
|
30
|
+
// Add columns
|
|
31
|
+
ftsOptions.push(...index.columns);
|
|
32
|
+
// Add tokenizer
|
|
33
|
+
if (index.tokenize) {
|
|
34
|
+
ftsOptions.push(`tokenize='${index.tokenize}'`);
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
ftsOptions.push(`tokenize='porter unicode61'`); // Default: porter stemming + unicode
|
|
38
|
+
}
|
|
39
|
+
// Add prefix indexes for autocomplete
|
|
40
|
+
if (index.prefix && index.prefix.length > 0) {
|
|
41
|
+
ftsOptions.push(`prefix='${index.prefix.join(' ')}'`);
|
|
42
|
+
}
|
|
43
|
+
// Contentless option (smaller index, no snippet support)
|
|
44
|
+
if (index.contentless) {
|
|
45
|
+
ftsOptions.push(`content=''`);
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
ftsOptions.push(`content='${entity}'`);
|
|
49
|
+
}
|
|
50
|
+
// Create FTS5 virtual table
|
|
51
|
+
const createSQL = `CREATE VIRTUAL TABLE IF NOT EXISTS ${ftsTableName} USING fts5(${ftsOptions.join(', ')})`;
|
|
52
|
+
await this.db.run(createSQL, []);
|
|
53
|
+
// Create triggers to keep FTS index in sync with main table
|
|
54
|
+
await this.createSyncTriggers(entity, index);
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Create triggers to automatically update FTS index
|
|
58
|
+
*/
|
|
59
|
+
async createSyncTriggers(entity, index) {
|
|
60
|
+
const ftsTableName = `${entity}_fts`;
|
|
61
|
+
const columns = index.columns.join(', ');
|
|
62
|
+
const columnValues = index.columns.map((col) => `NEW.${col}`).join(', ');
|
|
63
|
+
// Insert trigger
|
|
64
|
+
await this.db.run(`CREATE TRIGGER IF NOT EXISTS ${entity}_fts_insert
|
|
65
|
+
AFTER INSERT ON ${entity}
|
|
66
|
+
BEGIN
|
|
67
|
+
INSERT INTO ${ftsTableName} (rowid, ${columns})
|
|
68
|
+
VALUES (NEW.rowid, ${columnValues});
|
|
69
|
+
END`, []);
|
|
70
|
+
// Update trigger
|
|
71
|
+
await this.db.run(`CREATE TRIGGER IF NOT EXISTS ${entity}_fts_update
|
|
72
|
+
AFTER UPDATE ON ${entity}
|
|
73
|
+
BEGIN
|
|
74
|
+
UPDATE ${ftsTableName}
|
|
75
|
+
SET ${index.columns.map((col) => `${col} = NEW.${col}`).join(', ')}
|
|
76
|
+
WHERE rowid = NEW.rowid;
|
|
77
|
+
END`, []);
|
|
78
|
+
// Delete trigger
|
|
79
|
+
await this.db.run(`CREATE TRIGGER IF NOT EXISTS ${entity}_fts_delete
|
|
80
|
+
AFTER DELETE ON ${entity}
|
|
81
|
+
BEGIN
|
|
82
|
+
DELETE FROM ${ftsTableName} WHERE rowid = OLD.rowid;
|
|
83
|
+
END`, []);
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Rebuild search index from scratch
|
|
87
|
+
*/
|
|
88
|
+
async rebuildIndex(entity) {
|
|
89
|
+
const index = this.indexes.get(entity);
|
|
90
|
+
if (!index) {
|
|
91
|
+
throw new Error(`No search index configured for entity: ${entity}`);
|
|
92
|
+
}
|
|
93
|
+
const ftsTableName = `${entity}_fts`;
|
|
94
|
+
// Clear existing FTS data
|
|
95
|
+
await this.db.run(`DELETE FROM ${ftsTableName}`, []);
|
|
96
|
+
// Rebuild from main table
|
|
97
|
+
const columns = index.columns.join(', ');
|
|
98
|
+
const insertSQL = `
|
|
99
|
+
INSERT INTO ${ftsTableName} (rowid, ${columns})
|
|
100
|
+
SELECT rowid, ${columns} FROM ${entity}
|
|
101
|
+
`;
|
|
102
|
+
await this.db.run(insertSQL, []);
|
|
103
|
+
// Get count
|
|
104
|
+
const result = await this.db.getOne(`SELECT COUNT(*) as count FROM ${ftsTableName}`, []);
|
|
105
|
+
return result?.count || 0;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Search for records
|
|
109
|
+
*/
|
|
110
|
+
async search(query) {
|
|
111
|
+
const index = this.indexes.get(query.entity);
|
|
112
|
+
if (!index) {
|
|
113
|
+
throw new Error(`No search index configured for entity: ${query.entity}`);
|
|
114
|
+
}
|
|
115
|
+
const ftsTableName = `${query.entity}_fts`;
|
|
116
|
+
const limit = query.limit || 20;
|
|
117
|
+
const offset = query.offset || 0;
|
|
118
|
+
// Build FTS5 query
|
|
119
|
+
const ftsQuery = this.buildFTSQuery(query.query, query.columns);
|
|
120
|
+
// Build SQL with optional ranking and highlighting
|
|
121
|
+
let sql = `
|
|
122
|
+
SELECT
|
|
123
|
+
${query.entity}.id,
|
|
124
|
+
${query.entity}.*
|
|
125
|
+
`;
|
|
126
|
+
if (query.rank) {
|
|
127
|
+
sql += `, ${ftsTableName}.rank as _rank`;
|
|
128
|
+
}
|
|
129
|
+
if (query.highlight) {
|
|
130
|
+
// Use snippet function for highlighting
|
|
131
|
+
const snippetColumns = (query.columns || index.columns).map((col) => {
|
|
132
|
+
return `snippet(${ftsTableName}, ${index.columns.indexOf(col)}, '<mark>', '</mark>', '...', 32)`;
|
|
133
|
+
});
|
|
134
|
+
sql += `, ${snippetColumns[0]} as _snippet`;
|
|
135
|
+
}
|
|
136
|
+
sql += `
|
|
137
|
+
FROM ${ftsTableName}
|
|
138
|
+
JOIN ${query.entity} ON ${ftsTableName}.rowid = ${query.entity}.rowid
|
|
139
|
+
WHERE ${ftsTableName} MATCH ?
|
|
140
|
+
`;
|
|
141
|
+
if (query.rank) {
|
|
142
|
+
sql += ` ORDER BY ${ftsTableName}.rank`;
|
|
143
|
+
}
|
|
144
|
+
sql += ` LIMIT ? OFFSET ?`;
|
|
145
|
+
const results = await this.db.getAll(sql, [ftsQuery, limit + 1, offset]);
|
|
146
|
+
// Check if there are more results
|
|
147
|
+
const hasMore = results.length > limit;
|
|
148
|
+
const trimmedResults = hasMore ? results.slice(0, limit) : results;
|
|
149
|
+
// Get total count
|
|
150
|
+
const countSQL = `
|
|
151
|
+
SELECT COUNT(*) as total
|
|
152
|
+
FROM ${ftsTableName}
|
|
153
|
+
WHERE ${ftsTableName} MATCH ?
|
|
154
|
+
`;
|
|
155
|
+
const countResult = await this.db.getOne(countSQL, [ftsQuery]);
|
|
156
|
+
const total = countResult?.total || 0;
|
|
157
|
+
// Format results
|
|
158
|
+
const formattedResults = trimmedResults.map((row) => {
|
|
159
|
+
const result = {
|
|
160
|
+
id: row.id,
|
|
161
|
+
};
|
|
162
|
+
if (query.rank) {
|
|
163
|
+
result.rank = row._rank;
|
|
164
|
+
}
|
|
165
|
+
if (query.highlight) {
|
|
166
|
+
result.snippet = row._snippet;
|
|
167
|
+
}
|
|
168
|
+
// Add all other columns
|
|
169
|
+
for (const key in row) {
|
|
170
|
+
if (key !== '_rank' && key !== '_snippet' && key !== 'rowid') {
|
|
171
|
+
result[key] = row[key];
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
return result;
|
|
175
|
+
});
|
|
176
|
+
return {
|
|
177
|
+
results: formattedResults,
|
|
178
|
+
total,
|
|
179
|
+
hasMore,
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Build FTS5 query string from user query
|
|
184
|
+
*/
|
|
185
|
+
buildFTSQuery(query, columns) {
|
|
186
|
+
// Escape special FTS5 characters
|
|
187
|
+
let escapedQuery = query.replace(/["']/g, '');
|
|
188
|
+
// If specific columns are specified, use column filter
|
|
189
|
+
if (columns && columns.length > 0) {
|
|
190
|
+
const columnQueries = columns.map((col) => `${col}:${escapedQuery}`);
|
|
191
|
+
return columnQueries.join(' OR ');
|
|
192
|
+
}
|
|
193
|
+
// Support phrase queries (quoted strings)
|
|
194
|
+
if (query.includes('"')) {
|
|
195
|
+
return query;
|
|
196
|
+
}
|
|
197
|
+
// Support prefix matching with *
|
|
198
|
+
if (!query.endsWith('*')) {
|
|
199
|
+
const words = escapedQuery.split(/\s+/).filter((w) => w.length > 0);
|
|
200
|
+
// Add prefix matching to last word for autocomplete
|
|
201
|
+
if (words.length > 0) {
|
|
202
|
+
words[words.length - 1] += '*';
|
|
203
|
+
}
|
|
204
|
+
return words.join(' ');
|
|
205
|
+
}
|
|
206
|
+
return escapedQuery;
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Delete search index
|
|
210
|
+
*/
|
|
211
|
+
async deleteSearchIndex(entity) {
|
|
212
|
+
const ftsTableName = `${entity}_fts`;
|
|
213
|
+
// Drop triggers
|
|
214
|
+
await this.db.run(`DROP TRIGGER IF EXISTS ${entity}_fts_insert`, []);
|
|
215
|
+
await this.db.run(`DROP TRIGGER IF EXISTS ${entity}_fts_update`, []);
|
|
216
|
+
await this.db.run(`DROP TRIGGER IF EXISTS ${entity}_fts_delete`, []);
|
|
217
|
+
// Drop FTS table
|
|
218
|
+
await this.db.run(`DROP TABLE IF EXISTS ${ftsTableName}`, []);
|
|
219
|
+
// Remove from registered indexes
|
|
220
|
+
this.indexes.delete(entity);
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* Optimize search index (FTS5 optimize)
|
|
224
|
+
*/
|
|
225
|
+
async optimizeIndex(entity) {
|
|
226
|
+
const ftsTableName = `${entity}_fts`;
|
|
227
|
+
await this.db.run(`INSERT INTO ${ftsTableName}(${ftsTableName}) VALUES('optimize')`, []);
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
230
|
+
* Get search index statistics
|
|
231
|
+
*/
|
|
232
|
+
async getIndexStats(entity) {
|
|
233
|
+
const ftsTableName = `${entity}_fts`;
|
|
234
|
+
const countResult = await this.db.getOne(`SELECT COUNT(*) as count FROM ${ftsTableName}`, []);
|
|
235
|
+
const documentCount = countResult?.count || 0;
|
|
236
|
+
// Get index size (approximate)
|
|
237
|
+
const sizeResult = await this.db.getOne(`SELECT page_count * page_size as size FROM pragma_page_count('${ftsTableName}'), pragma_page_size()`, []);
|
|
238
|
+
const indexSize = sizeResult?.size || 0;
|
|
239
|
+
return {
|
|
240
|
+
entity,
|
|
241
|
+
documentCount,
|
|
242
|
+
indexSize,
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* Get all registered indexes
|
|
247
|
+
*/
|
|
248
|
+
getRegisteredIndexes() {
|
|
249
|
+
return Array.from(this.indexes.values());
|
|
250
|
+
}
|
|
251
|
+
/**
|
|
252
|
+
* Check if an entity has a search index
|
|
253
|
+
*/
|
|
254
|
+
hasIndex(entity) {
|
|
255
|
+
return this.indexes.has(entity);
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
//# sourceMappingURL=search-manager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"search-manager.js","sourceRoot":"","sources":["../../../../../core/src/search/search-manager.ts"],"names":[],"mappings":"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"}
|