obsyncd 1.0.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/README.md +920 -0
- package/dist/cli/commands/init.d.ts +8 -0
- package/dist/cli/commands/init.d.ts.map +1 -0
- package/dist/cli/commands/init.js +104 -0
- package/dist/cli/commands/init.js.map +1 -0
- package/dist/cli/commands/status.d.ts +8 -0
- package/dist/cli/commands/status.d.ts.map +1 -0
- package/dist/cli/commands/status.js +117 -0
- package/dist/cli/commands/status.js.map +1 -0
- package/dist/cli/commands/sync.d.ts +13 -0
- package/dist/cli/commands/sync.d.ts.map +1 -0
- package/dist/cli/commands/sync.js +225 -0
- package/dist/cli/commands/sync.js.map +1 -0
- package/dist/cli/prompts/conflictPrompt.d.ts +10 -0
- package/dist/cli/prompts/conflictPrompt.d.ts.map +1 -0
- package/dist/cli/prompts/conflictPrompt.js +51 -0
- package/dist/cli/prompts/conflictPrompt.js.map +1 -0
- package/dist/cli/prompts/fileBrowser.d.ts +6 -0
- package/dist/cli/prompts/fileBrowser.d.ts.map +1 -0
- package/dist/cli/prompts/fileBrowser.js +91 -0
- package/dist/cli/prompts/fileBrowser.js.map +1 -0
- package/dist/cli/prompts/vaultSelector.d.ts +13 -0
- package/dist/cli/prompts/vaultSelector.d.ts.map +1 -0
- package/dist/cli/prompts/vaultSelector.js +63 -0
- package/dist/cli/prompts/vaultSelector.js.map +1 -0
- package/dist/cli/ui/colors.d.ts +50 -0
- package/dist/cli/ui/colors.d.ts.map +1 -0
- package/dist/cli/ui/colors.js +62 -0
- package/dist/cli/ui/colors.js.map +1 -0
- package/dist/cli/ui/output.d.ts +45 -0
- package/dist/cli/ui/output.d.ts.map +1 -0
- package/dist/cli/ui/output.js +116 -0
- package/dist/cli/ui/output.js.map +1 -0
- package/dist/cli/ui/spinner.d.ts +29 -0
- package/dist/cli/ui/spinner.d.ts.map +1 -0
- package/dist/cli/ui/spinner.js +80 -0
- package/dist/cli/ui/spinner.js.map +1 -0
- package/dist/cli/ui/table.d.ts +28 -0
- package/dist/cli/ui/table.d.ts.map +1 -0
- package/dist/cli/ui/table.js +123 -0
- package/dist/cli/ui/table.js.map +1 -0
- package/dist/cli/utils/terminal.d.ts +21 -0
- package/dist/cli/utils/terminal.d.ts.map +1 -0
- package/dist/cli/utils/terminal.js +59 -0
- package/dist/cli/utils/terminal.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +32 -0
- package/dist/cli.js.map +1 -0
- package/dist/config/index.d.ts +45 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/index.js +112 -0
- package/dist/config/index.js.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -0
- package/dist/storage/index.d.ts +35 -0
- package/dist/storage/index.d.ts.map +1 -0
- package/dist/storage/index.js +97 -0
- package/dist/storage/index.js.map +1 -0
- package/dist/sync/changeDetector.d.ts +29 -0
- package/dist/sync/changeDetector.d.ts.map +1 -0
- package/dist/sync/changeDetector.js +259 -0
- package/dist/sync/changeDetector.js.map +1 -0
- package/dist/sync/conflictResolver.d.ts +29 -0
- package/dist/sync/conflictResolver.d.ts.map +1 -0
- package/dist/sync/conflictResolver.js +116 -0
- package/dist/sync/conflictResolver.js.map +1 -0
- package/dist/sync/directoryLister.d.ts +18 -0
- package/dist/sync/directoryLister.d.ts.map +1 -0
- package/dist/sync/directoryLister.js +39 -0
- package/dist/sync/directoryLister.js.map +1 -0
- package/dist/sync/index.d.ts +29 -0
- package/dist/sync/index.d.ts.map +1 -0
- package/dist/sync/index.js +212 -0
- package/dist/sync/index.js.map +1 -0
- package/dist/sync/manifest.d.ts +48 -0
- package/dist/sync/manifest.d.ts.map +1 -0
- package/dist/sync/manifest.js +137 -0
- package/dist/sync/manifest.js.map +1 -0
- package/dist/sync/types.d.ts +109 -0
- package/dist/sync/types.d.ts.map +1 -0
- package/dist/sync/types.js +5 -0
- package/dist/sync/types.js.map +1 -0
- package/dist/sync/watchMode.d.ts +84 -0
- package/dist/sync/watchMode.d.ts.map +1 -0
- package/dist/sync/watchMode.js +364 -0
- package/dist/sync/watchMode.js.map +1 -0
- package/dist/sync/watcher.d.ts +114 -0
- package/dist/sync/watcher.d.ts.map +1 -0
- package/dist/sync/watcher.js +293 -0
- package/dist/sync/watcher.js.map +1 -0
- package/dist/utils/index.d.ts +8 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +25 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/vault/index.d.ts +38 -0
- package/dist/vault/index.d.ts.map +1 -0
- package/dist/vault/index.js +106 -0
- package/dist/vault/index.js.map +1 -0
- package/package.json +68 -0
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* FileWatcher - Watches directories for file changes with debouncing.
|
|
3
|
+
* Uses chokidar for cross-platform file watching.
|
|
4
|
+
*/
|
|
5
|
+
import * as chokidar from 'chokidar';
|
|
6
|
+
import path from 'path';
|
|
7
|
+
import { EventEmitter } from 'events';
|
|
8
|
+
import picomatch from 'picomatch';
|
|
9
|
+
export class FileWatcher extends EventEmitter {
|
|
10
|
+
watchers = new Map();
|
|
11
|
+
pendingChanges = new Map();
|
|
12
|
+
debounceTimers = new Map();
|
|
13
|
+
debounceMs;
|
|
14
|
+
ignorePatterns;
|
|
15
|
+
ignoreMatchers;
|
|
16
|
+
ignoreInitial;
|
|
17
|
+
usePolling;
|
|
18
|
+
pollInterval;
|
|
19
|
+
depth;
|
|
20
|
+
isReady = new Map();
|
|
21
|
+
lastEventTime = null;
|
|
22
|
+
isStopped = false;
|
|
23
|
+
constructor(options = {}) {
|
|
24
|
+
super();
|
|
25
|
+
this.debounceMs = options.debounceMs ?? 300;
|
|
26
|
+
this.ignorePatterns = options.ignorePatterns ?? [
|
|
27
|
+
'.obsidian/**',
|
|
28
|
+
'.trash/**',
|
|
29
|
+
'.git/**',
|
|
30
|
+
'.obsync/**',
|
|
31
|
+
'.DS_Store',
|
|
32
|
+
'Thumbs.db',
|
|
33
|
+
'*.tmp',
|
|
34
|
+
'*.temp',
|
|
35
|
+
];
|
|
36
|
+
this.ignoreMatchers = this.ignorePatterns.map((pattern) => picomatch(pattern, { dot: true }));
|
|
37
|
+
this.ignoreInitial = options.ignoreInitial ?? true;
|
|
38
|
+
// Use polling by default for better cross-platform compatibility
|
|
39
|
+
this.usePolling = options.usePolling ?? true;
|
|
40
|
+
this.pollInterval = options.pollInterval ?? 100;
|
|
41
|
+
this.depth = options.depth;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Start watching a directory for file changes
|
|
45
|
+
*/
|
|
46
|
+
watch(watchPath) {
|
|
47
|
+
if (this.isStopped) {
|
|
48
|
+
throw new Error('Watcher has been stopped. Create a new instance to watch again.');
|
|
49
|
+
}
|
|
50
|
+
const normalizedPath = path.resolve(watchPath);
|
|
51
|
+
if (this.watchers.has(normalizedPath)) {
|
|
52
|
+
return; // Already watching this path
|
|
53
|
+
}
|
|
54
|
+
this.isReady.set(normalizedPath, false);
|
|
55
|
+
const watcher = chokidar.watch(normalizedPath, {
|
|
56
|
+
ignored: (filePath) => this.shouldIgnore(filePath, normalizedPath),
|
|
57
|
+
persistent: true,
|
|
58
|
+
ignoreInitial: this.ignoreInitial,
|
|
59
|
+
usePolling: this.usePolling,
|
|
60
|
+
interval: this.pollInterval,
|
|
61
|
+
binaryInterval: this.pollInterval,
|
|
62
|
+
depth: this.depth,
|
|
63
|
+
atomic: true,
|
|
64
|
+
awaitWriteFinish: {
|
|
65
|
+
stabilityThreshold: 50,
|
|
66
|
+
pollInterval: 25,
|
|
67
|
+
},
|
|
68
|
+
});
|
|
69
|
+
watcher.on('add', (filePath) => this.handleEvent('add', filePath, normalizedPath));
|
|
70
|
+
watcher.on('change', (filePath) => this.handleEvent('change', filePath, normalizedPath));
|
|
71
|
+
watcher.on('unlink', (filePath) => this.handleEvent('unlink', filePath, normalizedPath));
|
|
72
|
+
watcher.on('error', (error) => this.emit('error', error));
|
|
73
|
+
watcher.on('ready', () => {
|
|
74
|
+
this.isReady.set(normalizedPath, true);
|
|
75
|
+
if (this.areAllWatchersReady()) {
|
|
76
|
+
this.emit('ready');
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
this.watchers.set(normalizedPath, watcher);
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Watch multiple directories
|
|
83
|
+
*/
|
|
84
|
+
watchMultiple(paths) {
|
|
85
|
+
for (const watchPath of paths) {
|
|
86
|
+
this.watch(watchPath);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Stop watching a specific directory
|
|
91
|
+
*/
|
|
92
|
+
async unwatch(watchPath) {
|
|
93
|
+
const normalizedPath = path.resolve(watchPath);
|
|
94
|
+
const watcher = this.watchers.get(normalizedPath);
|
|
95
|
+
if (watcher) {
|
|
96
|
+
await watcher.close();
|
|
97
|
+
this.watchers.delete(normalizedPath);
|
|
98
|
+
this.isReady.delete(normalizedPath);
|
|
99
|
+
// Clear any pending debounce timers for this path
|
|
100
|
+
for (const [key, timer] of this.debounceTimers.entries()) {
|
|
101
|
+
if (key.startsWith(normalizedPath)) {
|
|
102
|
+
clearTimeout(timer);
|
|
103
|
+
this.debounceTimers.delete(key);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
// Clear pending changes for this path
|
|
107
|
+
for (const [key] of this.pendingChanges.entries()) {
|
|
108
|
+
if (key.startsWith(normalizedPath)) {
|
|
109
|
+
this.pendingChanges.delete(key);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Stop watching all directories and clean up
|
|
116
|
+
*/
|
|
117
|
+
async stop() {
|
|
118
|
+
this.isStopped = true;
|
|
119
|
+
// Clear all debounce timers
|
|
120
|
+
for (const timer of this.debounceTimers.values()) {
|
|
121
|
+
clearTimeout(timer);
|
|
122
|
+
}
|
|
123
|
+
this.debounceTimers.clear();
|
|
124
|
+
// Close all watchers
|
|
125
|
+
const closePromises = Array.from(this.watchers.values()).map((w) => w.close());
|
|
126
|
+
await Promise.all(closePromises);
|
|
127
|
+
this.watchers.clear();
|
|
128
|
+
this.pendingChanges.clear();
|
|
129
|
+
this.isReady.clear();
|
|
130
|
+
this.removeAllListeners();
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Get current watcher status
|
|
134
|
+
*/
|
|
135
|
+
getStatus() {
|
|
136
|
+
return {
|
|
137
|
+
isWatching: this.watchers.size > 0,
|
|
138
|
+
watchedPaths: Array.from(this.watchers.keys()),
|
|
139
|
+
pendingChanges: this.pendingChanges.size,
|
|
140
|
+
lastEventTime: this.lastEventTime,
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Check if all watchers are ready
|
|
145
|
+
*/
|
|
146
|
+
areAllWatchersReady() {
|
|
147
|
+
if (this.watchers.size === 0)
|
|
148
|
+
return false;
|
|
149
|
+
return Array.from(this.isReady.values()).every((ready) => ready);
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Force flush all pending changes immediately
|
|
153
|
+
*/
|
|
154
|
+
flushPendingChanges() {
|
|
155
|
+
// Clear all debounce timers
|
|
156
|
+
for (const timer of this.debounceTimers.values()) {
|
|
157
|
+
clearTimeout(timer);
|
|
158
|
+
}
|
|
159
|
+
this.debounceTimers.clear();
|
|
160
|
+
const changes = Array.from(this.pendingChanges.values());
|
|
161
|
+
this.pendingChanges.clear();
|
|
162
|
+
return changes;
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Get count of pending changes
|
|
166
|
+
*/
|
|
167
|
+
getPendingCount() {
|
|
168
|
+
return this.pendingChanges.size;
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Handle file system events with debouncing
|
|
172
|
+
*/
|
|
173
|
+
handleEvent(type, filePath, watchedPath) {
|
|
174
|
+
if (this.isStopped)
|
|
175
|
+
return;
|
|
176
|
+
const normalizedFilePath = path.resolve(filePath);
|
|
177
|
+
const relativePath = path.relative(watchedPath, normalizedFilePath);
|
|
178
|
+
// Skip events for the base directory itself
|
|
179
|
+
if (this.shouldSkipEvent(normalizedFilePath, watchedPath)) {
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
this.lastEventTime = new Date();
|
|
183
|
+
const event = {
|
|
184
|
+
type,
|
|
185
|
+
path: normalizedFilePath,
|
|
186
|
+
relativePath: relativePath.replace(/\\/g, '/'), // Normalize to forward slashes
|
|
187
|
+
watchedPath,
|
|
188
|
+
};
|
|
189
|
+
// Use the relative path as key to properly coalesce events
|
|
190
|
+
const eventKey = `${watchedPath}:${relativePath}`;
|
|
191
|
+
// Store/update the pending change
|
|
192
|
+
this.pendingChanges.set(eventKey, event);
|
|
193
|
+
// Emit immediate change event
|
|
194
|
+
this.emit('change', event);
|
|
195
|
+
// Clear existing debounce timer for this file
|
|
196
|
+
const existingTimer = this.debounceTimers.get(eventKey);
|
|
197
|
+
if (existingTimer) {
|
|
198
|
+
clearTimeout(existingTimer);
|
|
199
|
+
}
|
|
200
|
+
// Set new debounce timer
|
|
201
|
+
const timer = setTimeout(() => {
|
|
202
|
+
this.debounceTimers.delete(eventKey);
|
|
203
|
+
const pendingEvent = this.pendingChanges.get(eventKey);
|
|
204
|
+
if (pendingEvent) {
|
|
205
|
+
this.pendingChanges.delete(eventKey);
|
|
206
|
+
this.emit('debounced', pendingEvent);
|
|
207
|
+
}
|
|
208
|
+
}, this.debounceMs);
|
|
209
|
+
this.debounceTimers.set(eventKey, timer);
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* Check if a path should be ignored (for the ignored option passed to chokidar)
|
|
213
|
+
*/
|
|
214
|
+
shouldIgnore(filePath, watchedPath) {
|
|
215
|
+
const relativePath = path.relative(watchedPath, filePath).replace(/\\/g, '/');
|
|
216
|
+
// Never ignore the base path itself - we need to watch it
|
|
217
|
+
if (relativePath === '' || relativePath === '.') {
|
|
218
|
+
return false;
|
|
219
|
+
}
|
|
220
|
+
// Check against ignore patterns
|
|
221
|
+
return this.ignoreMatchers.some((matcher) => matcher(relativePath));
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* Check if an event should be skipped (for internal filtering)
|
|
225
|
+
*/
|
|
226
|
+
shouldSkipEvent(filePath, watchedPath) {
|
|
227
|
+
const relativePath = path.relative(watchedPath, filePath).replace(/\\/g, '/');
|
|
228
|
+
// Skip events for the base path itself
|
|
229
|
+
if (relativePath === '' || relativePath === '.') {
|
|
230
|
+
return true;
|
|
231
|
+
}
|
|
232
|
+
return false;
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
/**
|
|
236
|
+
* Create a debounced batch watcher that collects multiple changes
|
|
237
|
+
* and emits them as a batch after a delay
|
|
238
|
+
*/
|
|
239
|
+
export class BatchFileWatcher extends FileWatcher {
|
|
240
|
+
batchTimer = null;
|
|
241
|
+
batchedChanges = new Map();
|
|
242
|
+
batchDelayMs;
|
|
243
|
+
constructor(options = {}) {
|
|
244
|
+
super(options);
|
|
245
|
+
this.batchDelayMs = options.batchDelayMs ?? 500;
|
|
246
|
+
// Override debounced event to collect into batch
|
|
247
|
+
this.on('debounced', (event) => {
|
|
248
|
+
const key = `${event.watchedPath}:${event.relativePath}`;
|
|
249
|
+
this.batchedChanges.set(key, event);
|
|
250
|
+
this.scheduleBatchEmit();
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
scheduleBatchEmit() {
|
|
254
|
+
if (this.batchTimer) {
|
|
255
|
+
clearTimeout(this.batchTimer);
|
|
256
|
+
}
|
|
257
|
+
this.batchTimer = setTimeout(() => {
|
|
258
|
+
const batch = Array.from(this.batchedChanges.values());
|
|
259
|
+
this.batchedChanges.clear();
|
|
260
|
+
this.batchTimer = null;
|
|
261
|
+
if (batch.length > 0) {
|
|
262
|
+
this.emit('batch', batch);
|
|
263
|
+
}
|
|
264
|
+
}, this.batchDelayMs);
|
|
265
|
+
}
|
|
266
|
+
/**
|
|
267
|
+
* Get current batch
|
|
268
|
+
*/
|
|
269
|
+
getCurrentBatch() {
|
|
270
|
+
return Array.from(this.batchedChanges.values());
|
|
271
|
+
}
|
|
272
|
+
/**
|
|
273
|
+
* Force emit current batch immediately
|
|
274
|
+
*/
|
|
275
|
+
flushBatch() {
|
|
276
|
+
if (this.batchTimer) {
|
|
277
|
+
clearTimeout(this.batchTimer);
|
|
278
|
+
this.batchTimer = null;
|
|
279
|
+
}
|
|
280
|
+
const batch = Array.from(this.batchedChanges.values());
|
|
281
|
+
this.batchedChanges.clear();
|
|
282
|
+
return batch;
|
|
283
|
+
}
|
|
284
|
+
async stop() {
|
|
285
|
+
if (this.batchTimer) {
|
|
286
|
+
clearTimeout(this.batchTimer);
|
|
287
|
+
this.batchTimer = null;
|
|
288
|
+
}
|
|
289
|
+
this.batchedChanges.clear();
|
|
290
|
+
await super.stop();
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
//# sourceMappingURL=watcher.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"watcher.js","sourceRoot":"","sources":["../../src/sync/watcher.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared utility functions used across the application.
|
|
3
|
+
*/
|
|
4
|
+
export declare function computeFileHash(content: Buffer): string;
|
|
5
|
+
export declare function sanitizePath(path: string): string;
|
|
6
|
+
export declare function isMarkdownFile(filename: string): boolean;
|
|
7
|
+
export declare function formatBytes(bytes: number): string;
|
|
8
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAEvD;AAED,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAGjD;AAED,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAExD;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAWjD"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared utility functions used across the application.
|
|
3
|
+
*/
|
|
4
|
+
import * as crypto from 'crypto';
|
|
5
|
+
export function computeFileHash(content) {
|
|
6
|
+
return crypto.createHash('sha256').update(content).digest('hex');
|
|
7
|
+
}
|
|
8
|
+
export function sanitizePath(path) {
|
|
9
|
+
// TODO: Implement path sanitization
|
|
10
|
+
return path.replace(/\\/g, '/');
|
|
11
|
+
}
|
|
12
|
+
export function isMarkdownFile(filename) {
|
|
13
|
+
return filename.endsWith('.md');
|
|
14
|
+
}
|
|
15
|
+
export function formatBytes(bytes) {
|
|
16
|
+
const units = ['B', 'KB', 'MB', 'GB'];
|
|
17
|
+
let size = bytes;
|
|
18
|
+
let unitIndex = 0;
|
|
19
|
+
while (size >= 1024 && unitIndex < units.length - 1) {
|
|
20
|
+
size /= 1024;
|
|
21
|
+
unitIndex++;
|
|
22
|
+
}
|
|
23
|
+
return `${size.toFixed(2)} ${units[unitIndex]}`;
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,MAAM,MAAM,QAAQ,CAAC;AAEjC,MAAM,UAAU,eAAe,CAAC,OAAe;IAC7C,OAAO,MAAM,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AACnE,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,IAAY;IACvC,oCAAoC;IACpC,OAAO,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;AAClC,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,QAAgB;IAC7C,OAAO,QAAQ,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;AAClC,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,KAAa;IACvC,MAAM,KAAK,GAAG,CAAC,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;IACtC,IAAI,IAAI,GAAG,KAAK,CAAC;IACjB,IAAI,SAAS,GAAG,CAAC,CAAC;IAElB,OAAO,IAAI,IAAI,IAAI,IAAI,SAAS,GAAG,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACpD,IAAI,IAAI,IAAI,CAAC;QACb,SAAS,EAAE,CAAC;IACd,CAAC;IAED,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,SAAS,CAAC,EAAE,CAAC;AAClD,CAAC"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Obsidian vault-specific operations.
|
|
3
|
+
* Handles vault structure parsing, metadata extraction, and validation.
|
|
4
|
+
*/
|
|
5
|
+
export interface VaultMetadata {
|
|
6
|
+
name: string;
|
|
7
|
+
path: string;
|
|
8
|
+
fileCount: number;
|
|
9
|
+
totalSize: number;
|
|
10
|
+
lastModified: Date;
|
|
11
|
+
}
|
|
12
|
+
export interface VaultConfig {
|
|
13
|
+
vaultPath: string;
|
|
14
|
+
excludePatterns?: string[];
|
|
15
|
+
includePatterns?: string[];
|
|
16
|
+
}
|
|
17
|
+
export declare class ObsidianVault {
|
|
18
|
+
private readonly config;
|
|
19
|
+
private storage;
|
|
20
|
+
constructor(config: VaultConfig);
|
|
21
|
+
/**
|
|
22
|
+
* Get metadata about the vault (file count, total size, last modified)
|
|
23
|
+
*/
|
|
24
|
+
getMetadata(): Promise<VaultMetadata>;
|
|
25
|
+
/**
|
|
26
|
+
* List all files in the vault, applying include and exclude patterns
|
|
27
|
+
*/
|
|
28
|
+
listFiles(): Promise<string[]>;
|
|
29
|
+
/**
|
|
30
|
+
* Validate that this is a proper Obsidian vault structure
|
|
31
|
+
*/
|
|
32
|
+
validateStructure(): Promise<boolean>;
|
|
33
|
+
/**
|
|
34
|
+
* Check if a given path is an Obsidian vault (has .obsidian directory)
|
|
35
|
+
*/
|
|
36
|
+
isObsidianVault(vaultPath?: string): Promise<boolean>;
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/vault/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAOH,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,IAAI,CAAC;CACpB;AAED,MAAM,WAAW,WAAW;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;IAC3B,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;CAC5B;AASD,qBAAa,aAAa;IAGZ,OAAO,CAAC,QAAQ,CAAC,MAAM;IAFnC,OAAO,CAAC,OAAO,CAAsB;gBAER,MAAM,EAAE,WAAW;IAIhD;;OAEG;IACG,WAAW,IAAI,OAAO,CAAC,aAAa,CAAC;IA4B3C;;OAEG;IACG,SAAS,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;IA2BpC;;OAEG;IACG,iBAAiB,IAAI,OAAO,CAAC,OAAO,CAAC;IAsB3C;;OAEG;IACG,eAAe,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;CAK5D"}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Obsidian vault-specific operations.
|
|
3
|
+
* Handles vault structure parsing, metadata extraction, and validation.
|
|
4
|
+
*/
|
|
5
|
+
import * as path from 'path';
|
|
6
|
+
import * as fs from 'fs/promises';
|
|
7
|
+
import picomatch from 'picomatch';
|
|
8
|
+
import { LocalStorageAdapter } from '../storage/index.js';
|
|
9
|
+
const DEFAULT_EXCLUDE_PATTERNS = [
|
|
10
|
+
'.obsidian/**',
|
|
11
|
+
'.trash/**',
|
|
12
|
+
'.git/**',
|
|
13
|
+
'.obsync/**',
|
|
14
|
+
];
|
|
15
|
+
export class ObsidianVault {
|
|
16
|
+
config;
|
|
17
|
+
storage;
|
|
18
|
+
constructor(config) {
|
|
19
|
+
this.config = config;
|
|
20
|
+
this.storage = new LocalStorageAdapter();
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Get metadata about the vault (file count, total size, last modified)
|
|
24
|
+
*/
|
|
25
|
+
async getMetadata() {
|
|
26
|
+
const files = await this.listFiles();
|
|
27
|
+
let totalSize = 0;
|
|
28
|
+
let lastModified = new Date(0);
|
|
29
|
+
for (const file of files) {
|
|
30
|
+
const fullPath = path.join(this.config.vaultPath, file);
|
|
31
|
+
try {
|
|
32
|
+
const stats = await fs.stat(fullPath);
|
|
33
|
+
totalSize += stats.size;
|
|
34
|
+
if (stats.mtime > lastModified) {
|
|
35
|
+
lastModified = stats.mtime;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
// Skip files that can't be read
|
|
40
|
+
continue;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return {
|
|
44
|
+
name: path.basename(this.config.vaultPath),
|
|
45
|
+
path: this.config.vaultPath,
|
|
46
|
+
fileCount: files.length,
|
|
47
|
+
totalSize,
|
|
48
|
+
lastModified,
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* List all files in the vault, applying include and exclude patterns
|
|
53
|
+
*/
|
|
54
|
+
async listFiles() {
|
|
55
|
+
const allFiles = await this.storage.list(this.config.vaultPath);
|
|
56
|
+
// Get exclude patterns (defaults + user-specified)
|
|
57
|
+
const excludePatterns = [
|
|
58
|
+
...DEFAULT_EXCLUDE_PATTERNS,
|
|
59
|
+
...(this.config.excludePatterns || []),
|
|
60
|
+
];
|
|
61
|
+
// Create matchers
|
|
62
|
+
const excludeMatcher = picomatch(excludePatterns);
|
|
63
|
+
const includeMatcher = this.config.includePatterns
|
|
64
|
+
? picomatch(this.config.includePatterns)
|
|
65
|
+
: null;
|
|
66
|
+
// Filter files based on patterns
|
|
67
|
+
return allFiles.filter((file) => {
|
|
68
|
+
// If include patterns specified, file must match one
|
|
69
|
+
if (includeMatcher && !includeMatcher(file)) {
|
|
70
|
+
return false;
|
|
71
|
+
}
|
|
72
|
+
// File must not match any exclude pattern
|
|
73
|
+
return !excludeMatcher(file);
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Validate that this is a proper Obsidian vault structure
|
|
78
|
+
*/
|
|
79
|
+
async validateStructure() {
|
|
80
|
+
const obsidianDir = path.join(this.config.vaultPath, '.obsidian');
|
|
81
|
+
// Check if .obsidian directory exists
|
|
82
|
+
if (!(await this.storage.exists(obsidianDir))) {
|
|
83
|
+
return false;
|
|
84
|
+
}
|
|
85
|
+
// Check for at least one of the common Obsidian config files
|
|
86
|
+
const configFiles = ['app.json', 'appearance.json', 'config'];
|
|
87
|
+
for (const configFile of configFiles) {
|
|
88
|
+
const configPath = path.join(obsidianDir, configFile);
|
|
89
|
+
if (await this.storage.exists(configPath)) {
|
|
90
|
+
return true;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
// If .obsidian exists but no config files, still consider it valid
|
|
94
|
+
// (might be a new vault)
|
|
95
|
+
return true;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Check if a given path is an Obsidian vault (has .obsidian directory)
|
|
99
|
+
*/
|
|
100
|
+
async isObsidianVault(vaultPath) {
|
|
101
|
+
const checkPath = vaultPath || this.config.vaultPath;
|
|
102
|
+
const obsidianDir = path.join(checkPath, '.obsidian');
|
|
103
|
+
return await this.storage.exists(obsidianDir);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/vault/index.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "obsyncd",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "A tool for synchronizing Obsidian vaults across devices via Dropbox, Google Drive, or any shared folder",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"bin": {
|
|
9
|
+
"obsyncd": "./dist/cli.js"
|
|
10
|
+
},
|
|
11
|
+
"files": [
|
|
12
|
+
"dist",
|
|
13
|
+
"README.md",
|
|
14
|
+
"LICENSE"
|
|
15
|
+
],
|
|
16
|
+
"scripts": {
|
|
17
|
+
"dev": "bun --watch src/cli.ts",
|
|
18
|
+
"build": "tsc",
|
|
19
|
+
"start": "node dist/cli.js",
|
|
20
|
+
"test": "vitest run",
|
|
21
|
+
"test:watch": "vitest",
|
|
22
|
+
"test:ui": "vitest --ui",
|
|
23
|
+
"lint": "eslint src tests --ext .ts",
|
|
24
|
+
"lint:fix": "eslint src tests --ext .ts --fix",
|
|
25
|
+
"format": "prettier --write \"src/**/*.ts\" \"tests/**/*.ts\"",
|
|
26
|
+
"format:check": "prettier --check \"src/**/*.ts\" \"tests/**/*.ts\"",
|
|
27
|
+
"typecheck": "tsc --noEmit"
|
|
28
|
+
},
|
|
29
|
+
"keywords": [
|
|
30
|
+
"obsidian",
|
|
31
|
+
"sync",
|
|
32
|
+
"vault",
|
|
33
|
+
"backup",
|
|
34
|
+
"synchronization",
|
|
35
|
+
"dropbox",
|
|
36
|
+
"google-drive",
|
|
37
|
+
"icloud",
|
|
38
|
+
"notes",
|
|
39
|
+
"markdown"
|
|
40
|
+
],
|
|
41
|
+
"author": "",
|
|
42
|
+
"license": "MIT",
|
|
43
|
+
"dependencies": {
|
|
44
|
+
"@inquirer/prompts": "^8.1.0",
|
|
45
|
+
"chokidar": "3",
|
|
46
|
+
"commander": "^12.1.0",
|
|
47
|
+
"ora": "^9.0.0",
|
|
48
|
+
"picocolors": "^1.1.1",
|
|
49
|
+
"picomatch": "^4.0.3",
|
|
50
|
+
"uuid": "^13.0.0"
|
|
51
|
+
},
|
|
52
|
+
"devDependencies": {
|
|
53
|
+
"@eslint/js": "^9.39.2",
|
|
54
|
+
"@types/node": "^22.10.5",
|
|
55
|
+
"@types/picomatch": "^4.0.2",
|
|
56
|
+
"@typescript-eslint/eslint-plugin": "^8.19.1",
|
|
57
|
+
"@typescript-eslint/parser": "^8.19.1",
|
|
58
|
+
"eslint": "^9.18.0",
|
|
59
|
+
"prettier": "^3.4.2",
|
|
60
|
+
"tsx": "^4.19.2",
|
|
61
|
+
"typescript": "^5.7.2",
|
|
62
|
+
"typescript-eslint": "^8.52.0",
|
|
63
|
+
"vitest": "^2.1.8"
|
|
64
|
+
},
|
|
65
|
+
"engines": {
|
|
66
|
+
"node": ">=18.0.0"
|
|
67
|
+
}
|
|
68
|
+
}
|