react-embed-docs 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +422 -0
- package/dist/client/components/Breadcrumbs.d.ts +21 -0
- package/dist/client/components/Breadcrumbs.d.ts.map +1 -0
- package/dist/client/components/Breadcrumbs.js +123 -0
- package/dist/client/components/DocsLayout.d.ts +20 -0
- package/dist/client/components/DocsLayout.d.ts.map +1 -0
- package/dist/client/components/DocsLayout.js +387 -0
- package/dist/client/components/DocumentContent.d.ts +5 -0
- package/dist/client/components/DocumentContent.d.ts.map +1 -0
- package/dist/client/components/DocumentContent.js +15 -0
- package/dist/client/components/DocumentEdit.d.ts +6 -0
- package/dist/client/components/DocumentEdit.d.ts.map +1 -0
- package/dist/client/components/DocumentEdit.js +153 -0
- package/dist/client/components/DocumentList.d.ts +5 -0
- package/dist/client/components/DocumentList.d.ts.map +1 -0
- package/dist/client/components/DocumentList.js +39 -0
- package/dist/client/components/DocumentProvider.d.ts +42 -0
- package/dist/client/components/DocumentProvider.d.ts.map +1 -0
- package/dist/client/components/DocumentProvider.js +47 -0
- package/dist/client/components/DocumentView.d.ts +6 -0
- package/dist/client/components/DocumentView.d.ts.map +1 -0
- package/dist/client/components/DocumentView.js +58 -0
- package/dist/client/components/DragOverlayItem.d.ts +5 -0
- package/dist/client/components/DragOverlayItem.d.ts.map +1 -0
- package/dist/client/components/DragOverlayItem.js +9 -0
- package/dist/client/components/EmojiPicker.d.ts +8 -0
- package/dist/client/components/EmojiPicker.d.ts.map +1 -0
- package/dist/client/components/EmojiPicker.js +48 -0
- package/dist/client/components/ExportButton.d.ts +22 -0
- package/dist/client/components/ExportButton.d.ts.map +1 -0
- package/dist/client/components/ExportButton.js +97 -0
- package/dist/client/components/Layout.d.ts +7 -0
- package/dist/client/components/Layout.d.ts.map +1 -0
- package/dist/client/components/Layout.js +172 -0
- package/dist/client/components/ReactEmbedDocs.d.ts +8 -0
- package/dist/client/components/ReactEmbedDocs.d.ts.map +1 -0
- package/dist/client/components/ReactEmbedDocs.js +8 -0
- package/dist/client/components/SearchInput.d.ts +2 -0
- package/dist/client/components/SearchInput.d.ts.map +1 -0
- package/dist/client/components/SearchInput.js +7 -0
- package/dist/client/components/Sidebar.d.ts +10 -0
- package/dist/client/components/Sidebar.d.ts.map +1 -0
- package/dist/client/components/Sidebar.js +176 -0
- package/dist/client/components/SortableTreeItem.d.ts +13 -0
- package/dist/client/components/SortableTreeItem.d.ts.map +1 -0
- package/dist/client/components/SortableTreeItem.js +24 -0
- package/dist/client/components/VersionHistory.d.ts +14 -0
- package/dist/client/components/VersionHistory.d.ts.map +1 -0
- package/dist/client/components/VersionHistory.js +102 -0
- package/dist/client/hooks/useCollaboration.d.ts +99 -0
- package/dist/client/hooks/useCollaboration.d.ts.map +1 -0
- package/dist/client/hooks/useCollaboration.js +180 -0
- package/dist/client/hooks/useDocsQuery.d.ts +84 -0
- package/dist/client/hooks/useDocsQuery.d.ts.map +1 -0
- package/dist/client/hooks/useDocsQuery.js +241 -0
- package/dist/client/hooks/useExport.d.ts +31 -0
- package/dist/client/hooks/useExport.d.ts.map +1 -0
- package/dist/client/hooks/useExport.js +66 -0
- package/dist/client/hooks/useFileUpload.d.ts +44 -0
- package/dist/client/hooks/useFileUpload.d.ts.map +1 -0
- package/dist/client/hooks/useFileUpload.js +193 -0
- package/dist/client/hooks/useSystemTheme.d.ts +2 -0
- package/dist/client/hooks/useSystemTheme.d.ts.map +1 -0
- package/dist/client/hooks/useSystemTheme.js +19 -0
- package/dist/client/hooks/useVersions.d.ts +105 -0
- package/dist/client/hooks/useVersions.d.ts.map +1 -0
- package/dist/client/hooks/useVersions.js +129 -0
- package/dist/client/index.d.ts +23 -0
- package/dist/client/index.d.ts.map +1 -0
- package/dist/client/index.js +18 -0
- package/dist/client/lib/blocknoteTheme.d.ts +13 -0
- package/dist/client/lib/blocknoteTheme.d.ts.map +1 -0
- package/dist/client/lib/blocknoteTheme.js +76 -0
- package/dist/client/lib/path.d.ts +8 -0
- package/dist/client/lib/path.d.ts.map +1 -0
- package/dist/client/lib/path.js +30 -0
- package/dist/client/providers/DocumentProvider.d.ts +1 -0
- package/dist/client/providers/DocumentProvider.d.ts.map +1 -0
- package/dist/client/providers/DocumentProvider.js +1 -0
- package/dist/server/CollaborationService.d.ts +134 -0
- package/dist/server/CollaborationService.d.ts.map +1 -0
- package/dist/server/CollaborationService.js +307 -0
- package/dist/server/DocsService.d.ts +115 -0
- package/dist/server/DocsService.d.ts.map +1 -0
- package/dist/server/DocsService.js +512 -0
- package/dist/server/ExportService.d.ts +106 -0
- package/dist/server/ExportService.d.ts.map +1 -0
- package/dist/server/ExportService.js +501 -0
- package/dist/server/FilesService.d.ts +44 -0
- package/dist/server/FilesService.d.ts.map +1 -0
- package/dist/server/FilesService.js +78 -0
- package/dist/server/VersioningService.d.ts +112 -0
- package/dist/server/VersioningService.d.ts.map +1 -0
- package/dist/server/VersioningService.js +264 -0
- package/dist/server/db.d.ts +7 -0
- package/dist/server/db.d.ts.map +1 -0
- package/dist/server/db.js +22 -0
- package/dist/server/index.d.ts +55 -0
- package/dist/server/index.d.ts.map +1 -0
- package/dist/server/index.js +36 -0
- package/dist/server/routes.d.ts +9 -0
- package/dist/server/routes.d.ts.map +1 -0
- package/dist/server/routes.js +483 -0
- package/dist/server/schema.d.ts +587 -0
- package/dist/server/schema.d.ts.map +1 -0
- package/dist/server/schema.js +126 -0
- package/dist/shared/types.d.ts +314 -0
- package/dist/shared/types.d.ts.map +1 -0
- package/dist/shared/types.js +48 -0
- package/drizzle/migrations/0000_gray_monster_badoon.sql +88 -0
- package/drizzle/migrations/meta/0000_snapshot.json +574 -0
- package/drizzle/migrations/meta/_journal.json +13 -0
- package/package.json +109 -0
- package/styles/docs.css +981 -0
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
import { eq, and, sql } from 'drizzle-orm';
|
|
2
|
+
import * as Y from 'yjs';
|
|
3
|
+
import { Awareness } from 'y-protocols/awareness';
|
|
4
|
+
import { documentsTable, documentPresenceTable } from './schema.js';
|
|
5
|
+
/**
|
|
6
|
+
* Service for real-time collaborative editing using Yjs
|
|
7
|
+
*/
|
|
8
|
+
export class CollaborationService {
|
|
9
|
+
db;
|
|
10
|
+
docs = new Map();
|
|
11
|
+
awarenessMap = new Map();
|
|
12
|
+
updateTimers = new Map();
|
|
13
|
+
DEBOUNCE_MS = 500;
|
|
14
|
+
constructor(db) {
|
|
15
|
+
this.db = db;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Get or create a Yjs document for collaborative editing
|
|
19
|
+
*/
|
|
20
|
+
async getOrCreateYDoc(documentId) {
|
|
21
|
+
if (this.docs.has(documentId)) {
|
|
22
|
+
return this.docs.get(documentId);
|
|
23
|
+
}
|
|
24
|
+
const ydoc = new Y.Doc();
|
|
25
|
+
// Load persisted state from database
|
|
26
|
+
const persisted = await this.db.query.documentsTable.findFirst({
|
|
27
|
+
where: eq(documentsTable.id, documentId),
|
|
28
|
+
columns: { ydocState: true, content: true },
|
|
29
|
+
});
|
|
30
|
+
if (persisted?.ydocState) {
|
|
31
|
+
// Apply persisted Yjs state
|
|
32
|
+
try {
|
|
33
|
+
const state = Buffer.from(persisted.ydocState, 'base64');
|
|
34
|
+
Y.applyUpdate(ydoc, new Uint8Array(state));
|
|
35
|
+
}
|
|
36
|
+
catch (e) {
|
|
37
|
+
console.warn('Failed to apply persisted Yjs state:', e);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
else if (persisted?.content) {
|
|
41
|
+
// Convert BlockNote JSON to Yjs document
|
|
42
|
+
this.blockNoteToYDoc(ydoc, persisted.content);
|
|
43
|
+
}
|
|
44
|
+
// Set up persistence on updates (debounced)
|
|
45
|
+
ydoc.on('update', (update) => {
|
|
46
|
+
this.schedulePersist(documentId, ydoc);
|
|
47
|
+
});
|
|
48
|
+
this.docs.set(documentId, ydoc);
|
|
49
|
+
return ydoc;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Get or create awareness for a document
|
|
53
|
+
*/
|
|
54
|
+
getOrCreateAwareness(documentId) {
|
|
55
|
+
if (this.awarenessMap.has(documentId)) {
|
|
56
|
+
return this.awarenessMap.get(documentId);
|
|
57
|
+
}
|
|
58
|
+
const awareness = new Awareness(this.getOrCreateYDoc(documentId));
|
|
59
|
+
this.awarenessMap.set(documentId, awareness);
|
|
60
|
+
return awareness;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Handle a new WebSocket connection for collaborative editing
|
|
64
|
+
*/
|
|
65
|
+
async handleConnection(documentId, userId, userInfo, connection) {
|
|
66
|
+
const ydoc = await this.getOrCreateYDoc(documentId);
|
|
67
|
+
const awareness = this.getOrCreateAwareness(documentId);
|
|
68
|
+
// Generate user color if not provided
|
|
69
|
+
const color = userInfo.color || this.generateColor(userId);
|
|
70
|
+
// Set local awareness state
|
|
71
|
+
const localState = {
|
|
72
|
+
user: { id: userId, name: userInfo.name, color },
|
|
73
|
+
cursor: null,
|
|
74
|
+
selection: null,
|
|
75
|
+
};
|
|
76
|
+
awareness.setLocalState(localState);
|
|
77
|
+
// Send initial sync step 1
|
|
78
|
+
const syncMessage = this.encodeSyncMessage(ydoc);
|
|
79
|
+
connection.send(syncMessage);
|
|
80
|
+
// Handle incoming messages
|
|
81
|
+
connection.onMessage((data) => {
|
|
82
|
+
this.handleMessage(documentId, data, connection, userId);
|
|
83
|
+
});
|
|
84
|
+
// Handle disconnect
|
|
85
|
+
connection.onClose(async () => {
|
|
86
|
+
await this.handleDisconnect(documentId, userId);
|
|
87
|
+
});
|
|
88
|
+
// Update presence in database
|
|
89
|
+
await this.updatePresence(documentId, userId, userInfo.name, color);
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Handle incoming WebSocket message
|
|
93
|
+
*/
|
|
94
|
+
async handleMessage(documentId, data, connection, userId) {
|
|
95
|
+
const ydoc = await this.getOrCreateYDoc(documentId);
|
|
96
|
+
const awareness = this.getOrCreateAwareness(documentId);
|
|
97
|
+
try {
|
|
98
|
+
// Decode message
|
|
99
|
+
const messageType = data[0];
|
|
100
|
+
if (messageType === 0) {
|
|
101
|
+
// Sync message
|
|
102
|
+
const update = data.slice(1);
|
|
103
|
+
Y.applyUpdate(ydoc, update);
|
|
104
|
+
// Broadcast to other clients
|
|
105
|
+
this.broadcast(documentId, data, userId);
|
|
106
|
+
}
|
|
107
|
+
else if (messageType === 1) {
|
|
108
|
+
// Awareness message
|
|
109
|
+
const awarenessUpdate = data.slice(1);
|
|
110
|
+
awareness.applyUpdate(new Uint8Array(awarenessUpdate));
|
|
111
|
+
// Broadcast awareness to others
|
|
112
|
+
this.broadcast(documentId, data, userId);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
catch (error) {
|
|
116
|
+
console.error('Failed to handle message:', error);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Handle client disconnect
|
|
121
|
+
*/
|
|
122
|
+
async handleDisconnect(documentId, userId) {
|
|
123
|
+
// Remove from awareness
|
|
124
|
+
const awareness = this.awarenessMap.get(documentId);
|
|
125
|
+
if (awareness) {
|
|
126
|
+
awareness.setLocalState(null);
|
|
127
|
+
}
|
|
128
|
+
// Remove from database presence
|
|
129
|
+
await this.db
|
|
130
|
+
.delete(documentPresenceTable)
|
|
131
|
+
.where(and(eq(documentPresenceTable.documentId, documentId), eq(documentPresenceTable.userId, userId)));
|
|
132
|
+
// Persist final state
|
|
133
|
+
const ydoc = this.docs.get(documentId);
|
|
134
|
+
if (ydoc) {
|
|
135
|
+
await this.persistYDoc(documentId, ydoc);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Broadcast message to all connected clients except sender
|
|
140
|
+
*/
|
|
141
|
+
broadcast(documentId, data, excludeUserId) {
|
|
142
|
+
// This is handled by the WebSocket server implementation
|
|
143
|
+
// The service just prepares the message
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Schedule persistence to database (debounced)
|
|
147
|
+
*/
|
|
148
|
+
schedulePersist(documentId, ydoc) {
|
|
149
|
+
const existingTimer = this.updateTimers.get(documentId);
|
|
150
|
+
if (existingTimer) {
|
|
151
|
+
clearTimeout(existingTimer);
|
|
152
|
+
}
|
|
153
|
+
const timer = setTimeout(async () => {
|
|
154
|
+
await this.persistYDoc(documentId, ydoc);
|
|
155
|
+
this.updateTimers.delete(documentId);
|
|
156
|
+
}, this.DEBOUNCE_MS);
|
|
157
|
+
this.updateTimers.set(documentId, timer);
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Persist Yjs document state to database
|
|
161
|
+
*/
|
|
162
|
+
async persistYDoc(documentId, ydoc) {
|
|
163
|
+
try {
|
|
164
|
+
const stateVector = Y.encodeStateAsUpdate(ydoc);
|
|
165
|
+
const ydocState = Buffer.from(stateVector).toString('base64');
|
|
166
|
+
// Convert Yjs back to BlockNote JSON for search indexing
|
|
167
|
+
const content = this.yjsToBlockNote(ydoc);
|
|
168
|
+
await this.db
|
|
169
|
+
.update(documentsTable)
|
|
170
|
+
.set({
|
|
171
|
+
ydocState,
|
|
172
|
+
content: content,
|
|
173
|
+
updatedAt: new Date(),
|
|
174
|
+
})
|
|
175
|
+
.where(eq(documentsTable.id, documentId));
|
|
176
|
+
}
|
|
177
|
+
catch (error) {
|
|
178
|
+
console.error('Failed to persist Yjs document:', error);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Update user presence in database
|
|
183
|
+
*/
|
|
184
|
+
async updatePresence(documentId, userId, userName, userColor) {
|
|
185
|
+
// Delete old presence record if exists
|
|
186
|
+
await this.db
|
|
187
|
+
.delete(documentPresenceTable)
|
|
188
|
+
.where(and(eq(documentPresenceTable.documentId, documentId), eq(documentPresenceTable.userId, userId)));
|
|
189
|
+
// Insert new presence
|
|
190
|
+
await this.db.insert(documentPresenceTable).values({
|
|
191
|
+
documentId,
|
|
192
|
+
userId,
|
|
193
|
+
userName,
|
|
194
|
+
userColor,
|
|
195
|
+
lastSeenAt: new Date(),
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Get active users for a document
|
|
200
|
+
*/
|
|
201
|
+
async getActiveUsers(documentId) {
|
|
202
|
+
const users = await this.db.query.documentPresenceTable.findMany({
|
|
203
|
+
where: eq(documentPresenceTable.documentId, documentId),
|
|
204
|
+
orderBy: (table, { desc }) => [desc(table.lastSeenAt)],
|
|
205
|
+
});
|
|
206
|
+
return users.map((u) => ({
|
|
207
|
+
userId: u.userId,
|
|
208
|
+
userName: u.userName,
|
|
209
|
+
userColor: u.userColor,
|
|
210
|
+
lastSeenAt: u.lastSeenAt,
|
|
211
|
+
}));
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* Clean up stale presence records (older than 5 minutes)
|
|
215
|
+
*/
|
|
216
|
+
async cleanupStalePresence() {
|
|
217
|
+
const result = await this.db
|
|
218
|
+
.delete(documentPresenceTable)
|
|
219
|
+
.where(sql `${documentPresenceTable.lastSeenAt} < NOW() - INTERVAL '5 minutes'`);
|
|
220
|
+
return result.length;
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* Convert BlockNote content to Yjs document
|
|
224
|
+
*/
|
|
225
|
+
blockNoteToYDoc(ydoc, content) {
|
|
226
|
+
const blocks = ydoc.getArray('blocks');
|
|
227
|
+
blocks.delete(0, blocks.length);
|
|
228
|
+
if (Array.isArray(content)) {
|
|
229
|
+
content.forEach((block) => {
|
|
230
|
+
blocks.push([this.serializeBlock(block)]);
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
/**
|
|
235
|
+
* Convert Yjs document to BlockNote content
|
|
236
|
+
*/
|
|
237
|
+
yjsToBlockNote(ydoc) {
|
|
238
|
+
const blocks = ydoc.getArray('blocks');
|
|
239
|
+
const content = [];
|
|
240
|
+
blocks.forEach((blockData) => {
|
|
241
|
+
content.push(this.deserializeBlock(blockData));
|
|
242
|
+
});
|
|
243
|
+
return content;
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* Serialize a BlockNote block for Yjs
|
|
247
|
+
*/
|
|
248
|
+
serializeBlock(block) {
|
|
249
|
+
return {
|
|
250
|
+
id: block.id || this.generateId(),
|
|
251
|
+
type: block.type,
|
|
252
|
+
props: block.props || {},
|
|
253
|
+
content: block.content || [],
|
|
254
|
+
children: block.children?.map((c) => this.serializeBlock(c)) || [],
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
/**
|
|
258
|
+
* Deserialize a Yjs block to BlockNote format
|
|
259
|
+
*/
|
|
260
|
+
deserializeBlock(blockData) {
|
|
261
|
+
return {
|
|
262
|
+
id: blockData.id,
|
|
263
|
+
type: blockData.type,
|
|
264
|
+
props: blockData.props || {},
|
|
265
|
+
content: blockData.content || [],
|
|
266
|
+
children: blockData.children?.map((c) => this.deserializeBlock(c)) || [],
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
/**
|
|
270
|
+
* Encode sync message for initial document sync
|
|
271
|
+
*/
|
|
272
|
+
encodeSyncMessage(ydoc) {
|
|
273
|
+
const state = Y.encodeStateAsUpdate(ydoc);
|
|
274
|
+
const message = new Uint8Array(1 + state.length);
|
|
275
|
+
message[0] = 0; // Sync message type
|
|
276
|
+
message.set(state, 1);
|
|
277
|
+
return message;
|
|
278
|
+
}
|
|
279
|
+
/**
|
|
280
|
+
* Generate a unique ID
|
|
281
|
+
*/
|
|
282
|
+
generateId() {
|
|
283
|
+
return Math.random().toString(36).substring(2, 15);
|
|
284
|
+
}
|
|
285
|
+
/**
|
|
286
|
+
* Generate a consistent color for a user
|
|
287
|
+
*/
|
|
288
|
+
generateColor(userId) {
|
|
289
|
+
const colors = [
|
|
290
|
+
'#EF4444', // red
|
|
291
|
+
'#F97316', // orange
|
|
292
|
+
'#F59E0B', // amber
|
|
293
|
+
'#84CC16', // lime
|
|
294
|
+
'#10B981', // emerald
|
|
295
|
+
'#06B6D4', // cyan
|
|
296
|
+
'#3B82F6', // blue
|
|
297
|
+
'#8B5CF6', // violet
|
|
298
|
+
'#EC4899', // pink
|
|
299
|
+
'#F43F5E', // rose
|
|
300
|
+
];
|
|
301
|
+
let hash = 0;
|
|
302
|
+
for (let i = 0; i < userId.length; i++) {
|
|
303
|
+
hash = userId.charCodeAt(i) + ((hash << 5) - hash);
|
|
304
|
+
}
|
|
305
|
+
return colors[Math.abs(hash) % colors.length];
|
|
306
|
+
}
|
|
307
|
+
}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import type { CreateDocument, Document, ListDocumentsFilters, ListDocumentsOptions, SearchResult } from '../shared/types.js';
|
|
2
|
+
import { DB } from './db.js';
|
|
3
|
+
import { type UpdateDocument } from './schema.js';
|
|
4
|
+
/**
|
|
5
|
+
* Document without content field - used for list/tree views
|
|
6
|
+
* to reduce payload size
|
|
7
|
+
*/
|
|
8
|
+
export type DocumentSummary = Omit<Document, 'content' | 'searchIndex'>;
|
|
9
|
+
/**
|
|
10
|
+
* DocsService
|
|
11
|
+
* Handles all CRUD operations for documents with BlockNote content
|
|
12
|
+
*/
|
|
13
|
+
export declare class DocsService {
|
|
14
|
+
private readonly db;
|
|
15
|
+
constructor(db: DB);
|
|
16
|
+
/**
|
|
17
|
+
* List documents with filtering and pagination
|
|
18
|
+
* Returns documents without content field to reduce payload size
|
|
19
|
+
*/
|
|
20
|
+
list(filters?: ListDocumentsFilters, options?: ListDocumentsOptions): Promise<{
|
|
21
|
+
documents: DocumentSummary[];
|
|
22
|
+
total: number;
|
|
23
|
+
}>;
|
|
24
|
+
/**
|
|
25
|
+
* Full-text search with ranking using searchIndex column
|
|
26
|
+
* More efficient than JSONB search for large datasets
|
|
27
|
+
*/
|
|
28
|
+
searchText(query: string, options?: {
|
|
29
|
+
limit?: number;
|
|
30
|
+
offset?: number;
|
|
31
|
+
}): Promise<{
|
|
32
|
+
results: Array<{
|
|
33
|
+
document: Document;
|
|
34
|
+
rank: number;
|
|
35
|
+
}>;
|
|
36
|
+
total: number;
|
|
37
|
+
}>;
|
|
38
|
+
/**
|
|
39
|
+
* Advanced search with highlighting
|
|
40
|
+
* Searches JSONB content directly for more precise matching
|
|
41
|
+
*/
|
|
42
|
+
search(query: string, options?: {
|
|
43
|
+
limit?: number;
|
|
44
|
+
offset?: number;
|
|
45
|
+
}): Promise<{
|
|
46
|
+
results: SearchResult[];
|
|
47
|
+
total: number;
|
|
48
|
+
}>;
|
|
49
|
+
private extractFromContentItem;
|
|
50
|
+
/**
|
|
51
|
+
* Extract text highlights from BlockNote content
|
|
52
|
+
* Returns surrounding context around matched text
|
|
53
|
+
*/
|
|
54
|
+
private extractHighlights;
|
|
55
|
+
/**
|
|
56
|
+
* Extract plain text from BlockNote content
|
|
57
|
+
* Used for building search index
|
|
58
|
+
*/
|
|
59
|
+
extractTextFromContent(content: unknown): string;
|
|
60
|
+
/**
|
|
61
|
+
* Build search index from title and content
|
|
62
|
+
* Concatenates title and extracted text for full-text search
|
|
63
|
+
*/
|
|
64
|
+
buildSearchIndex(title: string, content: unknown): string;
|
|
65
|
+
/**
|
|
66
|
+
* Get document by ID
|
|
67
|
+
*/
|
|
68
|
+
getById(id: string): Promise<Document | undefined>;
|
|
69
|
+
/**
|
|
70
|
+
* Get document by slug
|
|
71
|
+
*/
|
|
72
|
+
getBySlug(slug: string): Promise<Document | undefined>;
|
|
73
|
+
/**
|
|
74
|
+
* Create new document
|
|
75
|
+
* Automatically generates search index from title and content
|
|
76
|
+
*/
|
|
77
|
+
create(data: CreateDocument): Promise<Document>;
|
|
78
|
+
/**
|
|
79
|
+
* Update existing document
|
|
80
|
+
* Rebuilds search index if title or content changes
|
|
81
|
+
*/
|
|
82
|
+
update(id: string, data: UpdateDocument): Promise<Document | undefined>;
|
|
83
|
+
/**
|
|
84
|
+
* Soft delete document
|
|
85
|
+
* Sets deletedAt timestamp instead of removing from database
|
|
86
|
+
*/
|
|
87
|
+
delete(id: string): Promise<Document | undefined>;
|
|
88
|
+
/**
|
|
89
|
+
* Get children documents by parent ID
|
|
90
|
+
* Returns documents without content field
|
|
91
|
+
*/
|
|
92
|
+
getChildren(parentId: string): Promise<DocumentSummary[]>;
|
|
93
|
+
/**
|
|
94
|
+
* Get all documents as flat tree structure
|
|
95
|
+
* Useful for building document tree UI
|
|
96
|
+
* Returns documents without content field
|
|
97
|
+
*/
|
|
98
|
+
getTree(): Promise<DocumentSummary[]>;
|
|
99
|
+
/**
|
|
100
|
+
* Reorder document - move to new parent and/or position
|
|
101
|
+
* Automatically reorders siblings to maintain consistent ordering
|
|
102
|
+
*/
|
|
103
|
+
reorder(id: string, newParentId: string | null, newOrder: number): Promise<Document | undefined>;
|
|
104
|
+
/**
|
|
105
|
+
* Update just the order field of a document
|
|
106
|
+
*/
|
|
107
|
+
updateOrder(id: string, order: number): Promise<Document | undefined>;
|
|
108
|
+
/**
|
|
109
|
+
* Get breadcrumb path for a document
|
|
110
|
+
* Returns array of ancestors from root to parent, plus the document itself
|
|
111
|
+
* Returns documents without content field
|
|
112
|
+
*/
|
|
113
|
+
getBreadcrumbs(id: string): Promise<DocumentSummary[]>;
|
|
114
|
+
}
|
|
115
|
+
//# sourceMappingURL=DocsService.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DocsService.d.ts","sourceRoot":"","sources":["../../src/server/DocsService.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAIV,cAAc,EACd,QAAQ,EACR,oBAAoB,EACpB,oBAAoB,EACpB,YAAY,EACb,MAAM,oBAAoB,CAAA;AAC3B,OAAO,EAAE,EAAE,EAAE,MAAM,SAAS,CAAA;AAC5B,OAAO,EAEL,KAAK,cAAc,EACpB,MAAM,aAAa,CAAA;AAEpB;;;GAGG;AACH,MAAM,MAAM,eAAe,GAAG,IAAI,CAAC,QAAQ,EAAE,SAAS,GAAG,aAAa,CAAC,CAAA;AAEvE;;;GAGG;AACH,qBAAa,WAAW;IACV,OAAO,CAAC,QAAQ,CAAC,EAAE;gBAAF,EAAE,EAAE,EAAE;IAEnC;;;OAGG;IACG,IAAI,CACR,OAAO,GAAE,oBAAyB,EAClC,OAAO,GAAE,oBAAyB,GACjC,OAAO,CAAC;QAAE,SAAS,EAAE,eAAe,EAAE,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;IA0E3D;;;OAGG;IACG,UAAU,CACd,KAAK,EAAE,MAAM,EACb,OAAO,GAAE;QAAE,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAO,GAChD,OAAO,CAAC;QAAE,OAAO,EAAE,KAAK,CAAC;YAAE,QAAQ,EAAE,QAAQ,CAAC;YAAC,IAAI,EAAE,MAAM,CAAA;SAAE,CAAC,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;IA0CnF;;;OAGG;IACG,MAAM,CACV,KAAK,EAAE,MAAM,EACb,OAAO,GAAE;QAAE,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAO,GAChD,OAAO,CAAC;QAAE,OAAO,EAAE,YAAY,EAAE,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;IAkDtD,OAAO,CAAC,sBAAsB;IAkB9B;;;OAGG;IACH,OAAO,CAAC,iBAAiB;IA+BzB;;;OAGG;IACH,sBAAsB,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM;IAqChD;;;OAGG;IACH,gBAAgB,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,MAAM;IAKzD;;OAEG;IACG,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,GAAG,SAAS,CAAC;IAWxD;;OAEG;IACG,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,GAAG,SAAS,CAAC;IAW5D;;;OAGG;IACG,MAAM,CAAC,IAAI,EAAE,cAAc,GAAG,OAAO,CAAC,QAAQ,CAAC;IAiCrD;;;OAGG;IACG,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,cAAc,GAAG,OAAO,CAAC,QAAQ,GAAG,SAAS,CAAC;IA8B7E;;;OAGG;IACG,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,GAAG,SAAS,CAAC;IAevD;;;OAGG;IACG,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,EAAE,CAAC;IA2B/D;;;;OAIG;IACG,OAAO,IAAI,OAAO,CAAC,eAAe,EAAE,CAAC;IAwB3C;;;OAGG;IACG,OAAO,CAAC,EAAE,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,IAAI,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,GAAG,SAAS,CAAC;IAwEtG;;OAEG;IACG,WAAW,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,GAAG,SAAS,CAAC;IAgB3E;;;;OAIG;IACG,cAAc,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,EAAE,CAAC;CAyC7D"}
|