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.
Files changed (102) hide show
  1. package/README.md +920 -0
  2. package/dist/cli/commands/init.d.ts +8 -0
  3. package/dist/cli/commands/init.d.ts.map +1 -0
  4. package/dist/cli/commands/init.js +104 -0
  5. package/dist/cli/commands/init.js.map +1 -0
  6. package/dist/cli/commands/status.d.ts +8 -0
  7. package/dist/cli/commands/status.d.ts.map +1 -0
  8. package/dist/cli/commands/status.js +117 -0
  9. package/dist/cli/commands/status.js.map +1 -0
  10. package/dist/cli/commands/sync.d.ts +13 -0
  11. package/dist/cli/commands/sync.d.ts.map +1 -0
  12. package/dist/cli/commands/sync.js +225 -0
  13. package/dist/cli/commands/sync.js.map +1 -0
  14. package/dist/cli/prompts/conflictPrompt.d.ts +10 -0
  15. package/dist/cli/prompts/conflictPrompt.d.ts.map +1 -0
  16. package/dist/cli/prompts/conflictPrompt.js +51 -0
  17. package/dist/cli/prompts/conflictPrompt.js.map +1 -0
  18. package/dist/cli/prompts/fileBrowser.d.ts +6 -0
  19. package/dist/cli/prompts/fileBrowser.d.ts.map +1 -0
  20. package/dist/cli/prompts/fileBrowser.js +91 -0
  21. package/dist/cli/prompts/fileBrowser.js.map +1 -0
  22. package/dist/cli/prompts/vaultSelector.d.ts +13 -0
  23. package/dist/cli/prompts/vaultSelector.d.ts.map +1 -0
  24. package/dist/cli/prompts/vaultSelector.js +63 -0
  25. package/dist/cli/prompts/vaultSelector.js.map +1 -0
  26. package/dist/cli/ui/colors.d.ts +50 -0
  27. package/dist/cli/ui/colors.d.ts.map +1 -0
  28. package/dist/cli/ui/colors.js +62 -0
  29. package/dist/cli/ui/colors.js.map +1 -0
  30. package/dist/cli/ui/output.d.ts +45 -0
  31. package/dist/cli/ui/output.d.ts.map +1 -0
  32. package/dist/cli/ui/output.js +116 -0
  33. package/dist/cli/ui/output.js.map +1 -0
  34. package/dist/cli/ui/spinner.d.ts +29 -0
  35. package/dist/cli/ui/spinner.d.ts.map +1 -0
  36. package/dist/cli/ui/spinner.js +80 -0
  37. package/dist/cli/ui/spinner.js.map +1 -0
  38. package/dist/cli/ui/table.d.ts +28 -0
  39. package/dist/cli/ui/table.d.ts.map +1 -0
  40. package/dist/cli/ui/table.js +123 -0
  41. package/dist/cli/ui/table.js.map +1 -0
  42. package/dist/cli/utils/terminal.d.ts +21 -0
  43. package/dist/cli/utils/terminal.d.ts.map +1 -0
  44. package/dist/cli/utils/terminal.js +59 -0
  45. package/dist/cli/utils/terminal.js.map +1 -0
  46. package/dist/cli.d.ts +3 -0
  47. package/dist/cli.d.ts.map +1 -0
  48. package/dist/cli.js +32 -0
  49. package/dist/cli.js.map +1 -0
  50. package/dist/config/index.d.ts +45 -0
  51. package/dist/config/index.d.ts.map +1 -0
  52. package/dist/config/index.js +112 -0
  53. package/dist/config/index.js.map +1 -0
  54. package/dist/index.d.ts +6 -0
  55. package/dist/index.d.ts.map +1 -0
  56. package/dist/index.js +5 -0
  57. package/dist/index.js.map +1 -0
  58. package/dist/storage/index.d.ts +35 -0
  59. package/dist/storage/index.d.ts.map +1 -0
  60. package/dist/storage/index.js +97 -0
  61. package/dist/storage/index.js.map +1 -0
  62. package/dist/sync/changeDetector.d.ts +29 -0
  63. package/dist/sync/changeDetector.d.ts.map +1 -0
  64. package/dist/sync/changeDetector.js +259 -0
  65. package/dist/sync/changeDetector.js.map +1 -0
  66. package/dist/sync/conflictResolver.d.ts +29 -0
  67. package/dist/sync/conflictResolver.d.ts.map +1 -0
  68. package/dist/sync/conflictResolver.js +116 -0
  69. package/dist/sync/conflictResolver.js.map +1 -0
  70. package/dist/sync/directoryLister.d.ts +18 -0
  71. package/dist/sync/directoryLister.d.ts.map +1 -0
  72. package/dist/sync/directoryLister.js +39 -0
  73. package/dist/sync/directoryLister.js.map +1 -0
  74. package/dist/sync/index.d.ts +29 -0
  75. package/dist/sync/index.d.ts.map +1 -0
  76. package/dist/sync/index.js +212 -0
  77. package/dist/sync/index.js.map +1 -0
  78. package/dist/sync/manifest.d.ts +48 -0
  79. package/dist/sync/manifest.d.ts.map +1 -0
  80. package/dist/sync/manifest.js +137 -0
  81. package/dist/sync/manifest.js.map +1 -0
  82. package/dist/sync/types.d.ts +109 -0
  83. package/dist/sync/types.d.ts.map +1 -0
  84. package/dist/sync/types.js +5 -0
  85. package/dist/sync/types.js.map +1 -0
  86. package/dist/sync/watchMode.d.ts +84 -0
  87. package/dist/sync/watchMode.d.ts.map +1 -0
  88. package/dist/sync/watchMode.js +364 -0
  89. package/dist/sync/watchMode.js.map +1 -0
  90. package/dist/sync/watcher.d.ts +114 -0
  91. package/dist/sync/watcher.d.ts.map +1 -0
  92. package/dist/sync/watcher.js +293 -0
  93. package/dist/sync/watcher.js.map +1 -0
  94. package/dist/utils/index.d.ts +8 -0
  95. package/dist/utils/index.d.ts.map +1 -0
  96. package/dist/utils/index.js +25 -0
  97. package/dist/utils/index.js.map +1 -0
  98. package/dist/vault/index.d.ts +38 -0
  99. package/dist/vault/index.d.ts.map +1 -0
  100. package/dist/vault/index.js +106 -0
  101. package/dist/vault/index.js.map +1 -0
  102. package/package.json +68 -0
@@ -0,0 +1,259 @@
1
+ /**
2
+ * Change detection using three-way merge algorithm.
3
+ * Compares source, destination, and base (last sync state) to detect changes.
4
+ */
5
+ import { promises as fs } from 'fs';
6
+ import path from 'path';
7
+ import { computeFileHash } from '../utils/index.js';
8
+ export class ChangeDetector {
9
+ sourceAdapter;
10
+ destAdapter;
11
+ constructor(sourceAdapter, destAdapter) {
12
+ this.sourceAdapter = sourceAdapter;
13
+ this.destAdapter = destAdapter;
14
+ }
15
+ /**
16
+ * Detect all changes between source and destination using three-way merge.
17
+ * @param sourcePath - Source vault path
18
+ * @param destPath - Destination vault path
19
+ * @param sourceManifest - Source manifest (base state)
20
+ * @param destManifest - Destination manifest (base state)
21
+ * @param fileList - List of files to check (from vault listing)
22
+ */
23
+ async detectChanges(sourcePath, destPath, sourceManifest, destManifest, fileList) {
24
+ const changes = [];
25
+ const processedFiles = new Set();
26
+ // Process all files in current file list
27
+ for (const relativePath of fileList) {
28
+ processedFiles.add(relativePath);
29
+ const sourceFilePath = path.join(sourcePath, relativePath);
30
+ const destFilePath = path.join(destPath, relativePath);
31
+ // Get current states
32
+ const sourceState = await this.getFileState(sourceFilePath, this.sourceAdapter);
33
+ const destState = await this.getFileState(destFilePath, this.destAdapter);
34
+ // Get base states from manifests
35
+ const sourceBaseState = sourceManifest?.files[relativePath] ?? null;
36
+ const destBaseState = destManifest?.files[relativePath] ?? null;
37
+ // Determine change type using three-way merge logic
38
+ const change = this.determineChangeType(relativePath, sourceState, destState, sourceBaseState, destBaseState);
39
+ if (change) {
40
+ changes.push(change);
41
+ }
42
+ }
43
+ // Check for deleted files (in manifest but not in current file list)
44
+ if (sourceManifest) {
45
+ for (const relativePath of Object.keys(sourceManifest.files)) {
46
+ if (!processedFiles.has(relativePath)) {
47
+ // File was in source before but not now - deleted from source
48
+ const destFilePath = path.join(destPath, relativePath);
49
+ const destState = await this.getFileState(destFilePath, this.destAdapter);
50
+ if (destState) {
51
+ // Still exists in destination
52
+ const destBaseState = destManifest?.files[relativePath] ?? null;
53
+ if (destBaseState && destState.hash !== destBaseState.hash) {
54
+ // Conflict: deleted in source, modified in destination
55
+ changes.push({
56
+ path: relativePath,
57
+ type: 'conflict',
58
+ sourceState: null,
59
+ destinationState: destState,
60
+ baseState: sourceManifest.files[relativePath],
61
+ });
62
+ }
63
+ else {
64
+ // Deleted in source, unchanged in destination - propagate deletion
65
+ changes.push({
66
+ path: relativePath,
67
+ type: 'deleted',
68
+ sourceState: null,
69
+ destinationState: destState,
70
+ baseState: sourceManifest.files[relativePath],
71
+ });
72
+ }
73
+ }
74
+ }
75
+ }
76
+ }
77
+ if (destManifest) {
78
+ for (const relativePath of Object.keys(destManifest.files)) {
79
+ if (!processedFiles.has(relativePath)) {
80
+ // File was in destination before but not now - deleted from destination
81
+ const sourceFilePath = path.join(sourcePath, relativePath);
82
+ const sourceState = await this.getFileState(sourceFilePath, this.sourceAdapter);
83
+ if (sourceState) {
84
+ // Still exists in source
85
+ const sourceBaseState = sourceManifest?.files[relativePath] ?? null;
86
+ if (sourceBaseState && sourceState.hash !== sourceBaseState.hash) {
87
+ // Conflict: deleted in destination, modified in source
88
+ changes.push({
89
+ path: relativePath,
90
+ type: 'conflict',
91
+ sourceState: sourceState,
92
+ destinationState: null,
93
+ baseState: destManifest.files[relativePath],
94
+ });
95
+ }
96
+ else {
97
+ // Deleted in destination, unchanged in source - propagate deletion
98
+ changes.push({
99
+ path: relativePath,
100
+ type: 'deleted',
101
+ sourceState: sourceState,
102
+ destinationState: null,
103
+ baseState: destManifest.files[relativePath],
104
+ });
105
+ }
106
+ }
107
+ }
108
+ }
109
+ }
110
+ return changes;
111
+ }
112
+ /**
113
+ * Determine change type using three-way merge algorithm
114
+ */
115
+ determineChangeType(relativePath, sourceState, destState, sourceBaseState, destBaseState) {
116
+ // Both files don't exist - no change
117
+ if (!sourceState && !destState) {
118
+ return null;
119
+ }
120
+ // File exists in source but not destination
121
+ if (sourceState && !destState) {
122
+ if (!sourceBaseState) {
123
+ // New file in source
124
+ return {
125
+ path: relativePath,
126
+ type: 'added',
127
+ sourceState,
128
+ destinationState: null,
129
+ };
130
+ }
131
+ else {
132
+ // File was deleted from destination
133
+ if (sourceState.hash !== sourceBaseState.hash) {
134
+ // Modified in source, deleted in destination - CONFLICT
135
+ return {
136
+ path: relativePath,
137
+ type: 'conflict',
138
+ sourceState,
139
+ destinationState: null,
140
+ baseState: sourceBaseState,
141
+ };
142
+ }
143
+ else {
144
+ // Unchanged in source, deleted in destination
145
+ return {
146
+ path: relativePath,
147
+ type: 'deleted',
148
+ sourceState,
149
+ destinationState: null,
150
+ baseState: sourceBaseState,
151
+ };
152
+ }
153
+ }
154
+ }
155
+ // File exists in destination but not source
156
+ if (!sourceState && destState) {
157
+ if (!destBaseState) {
158
+ // New file in destination
159
+ return {
160
+ path: relativePath,
161
+ type: 'added',
162
+ sourceState: null,
163
+ destinationState: destState,
164
+ };
165
+ }
166
+ else {
167
+ // File was deleted from source
168
+ if (destState.hash !== destBaseState.hash) {
169
+ // Deleted in source, modified in destination - CONFLICT
170
+ return {
171
+ path: relativePath,
172
+ type: 'conflict',
173
+ sourceState: null,
174
+ destinationState: destState,
175
+ baseState: destBaseState,
176
+ };
177
+ }
178
+ else {
179
+ // Deleted in source, unchanged in destination
180
+ return {
181
+ path: relativePath,
182
+ type: 'deleted',
183
+ sourceState: null,
184
+ destinationState: destState,
185
+ baseState: destBaseState,
186
+ };
187
+ }
188
+ }
189
+ }
190
+ // Both files exist - check for modifications
191
+ if (sourceState && destState) {
192
+ const sourceChanged = !sourceBaseState || sourceState.hash !== sourceBaseState.hash;
193
+ const destChanged = !destBaseState || destState.hash !== destBaseState.hash;
194
+ if (sourceChanged && destChanged) {
195
+ // Both modified - CONFLICT (unless they converged to same hash)
196
+ if (sourceState.hash === destState.hash) {
197
+ // Converged to same content - no sync needed
198
+ return null;
199
+ }
200
+ return {
201
+ path: relativePath,
202
+ type: 'conflict',
203
+ sourceState,
204
+ destinationState: destState,
205
+ baseState: sourceBaseState || destBaseState,
206
+ };
207
+ }
208
+ else if (sourceChanged) {
209
+ // Only source changed
210
+ return {
211
+ path: relativePath,
212
+ type: 'modified',
213
+ sourceState,
214
+ destinationState: destState,
215
+ baseState: sourceBaseState,
216
+ };
217
+ }
218
+ else if (destChanged) {
219
+ // Only destination changed
220
+ return {
221
+ path: relativePath,
222
+ type: 'modified',
223
+ sourceState,
224
+ destinationState: destState,
225
+ baseState: destBaseState,
226
+ };
227
+ }
228
+ else {
229
+ // Neither changed - no sync needed
230
+ return null;
231
+ }
232
+ }
233
+ return null;
234
+ }
235
+ /**
236
+ * Get current file state (hash, size, mtime)
237
+ */
238
+ async getFileState(filePath, adapter) {
239
+ try {
240
+ const exists = await adapter.exists(filePath);
241
+ if (!exists) {
242
+ return null;
243
+ }
244
+ const content = await adapter.read(filePath);
245
+ const hash = computeFileHash(content);
246
+ const stats = await fs.stat(filePath);
247
+ return {
248
+ hash,
249
+ size: stats.size,
250
+ mtime: stats.mtime.toISOString(),
251
+ };
252
+ }
253
+ catch {
254
+ // File doesn't exist or can't be read
255
+ return null;
256
+ }
257
+ }
258
+ }
259
+ //# sourceMappingURL=changeDetector.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"changeDetector.js","sourceRoot":"","sources":["../../src/sync/changeDetector.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Conflict resolution strategies for sync operations.
3
+ * Resolves conflicts when same file is modified on both sides.
4
+ */
5
+ import type { FileChange, ConflictStrategy } from './types.js';
6
+ export interface ResolvedConflict {
7
+ path: string;
8
+ action: 'use-source' | 'use-destination' | 'skip';
9
+ reason: string;
10
+ }
11
+ export declare class ConflictResolver {
12
+ /**
13
+ * Resolve conflicts using specified strategy
14
+ */
15
+ resolveConflicts(conflicts: FileChange[], strategy: ConflictStrategy): Promise<ResolvedConflict[]>;
16
+ /**
17
+ * Resolve a single conflict based on strategy
18
+ */
19
+ private resolveConflict;
20
+ /**
21
+ * Resolve conflict by choosing newest file (by mtime)
22
+ */
23
+ private resolveByNewest;
24
+ /**
25
+ * Get human-readable summary of conflict resolutions
26
+ */
27
+ getSummary(resolutions: ResolvedConflict[]): string;
28
+ }
29
+ //# sourceMappingURL=conflictResolver.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"conflictResolver.d.ts","sourceRoot":"","sources":["../../src/sync/conflictResolver.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAE/D,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,YAAY,GAAG,iBAAiB,GAAG,MAAM,CAAC;IAClD,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,qBAAa,gBAAgB;IAC3B;;OAEG;IACG,gBAAgB,CACpB,SAAS,EAAE,UAAU,EAAE,EACvB,QAAQ,EAAE,gBAAgB,GACzB,OAAO,CAAC,gBAAgB,EAAE,CAAC;IAW9B;;OAEG;YACW,eAAe;IAuC7B;;OAEG;IACH,OAAO,CAAC,eAAe;IA8CvB;;OAEG;IACH,UAAU,CAAC,WAAW,EAAE,gBAAgB,EAAE,GAAG,MAAM;CAepD"}
@@ -0,0 +1,116 @@
1
+ /**
2
+ * Conflict resolution strategies for sync operations.
3
+ * Resolves conflicts when same file is modified on both sides.
4
+ */
5
+ export class ConflictResolver {
6
+ /**
7
+ * Resolve conflicts using specified strategy
8
+ */
9
+ async resolveConflicts(conflicts, strategy) {
10
+ const resolved = [];
11
+ for (const conflict of conflicts) {
12
+ const resolution = await this.resolveConflict(conflict, strategy);
13
+ resolved.push(resolution);
14
+ }
15
+ return resolved;
16
+ }
17
+ /**
18
+ * Resolve a single conflict based on strategy
19
+ */
20
+ async resolveConflict(conflict, strategy) {
21
+ switch (strategy) {
22
+ case 'source':
23
+ return {
24
+ path: conflict.path,
25
+ action: 'use-source',
26
+ reason: 'Strategy: always prefer source',
27
+ };
28
+ case 'destination':
29
+ return {
30
+ path: conflict.path,
31
+ action: 'use-destination',
32
+ reason: 'Strategy: always prefer destination',
33
+ };
34
+ case 'newest':
35
+ return this.resolveByNewest(conflict);
36
+ case 'skip':
37
+ return {
38
+ path: conflict.path,
39
+ action: 'skip',
40
+ reason: 'Strategy: skip conflicts',
41
+ };
42
+ default:
43
+ // Default to skip for unknown strategies
44
+ return {
45
+ path: conflict.path,
46
+ action: 'skip',
47
+ reason: 'Unknown strategy, skipping',
48
+ };
49
+ }
50
+ }
51
+ /**
52
+ * Resolve conflict by choosing newest file (by mtime)
53
+ */
54
+ resolveByNewest(conflict) {
55
+ const { sourceState, destinationState } = conflict;
56
+ // Handle deletion conflicts
57
+ if (!sourceState || !destinationState) {
58
+ if (!sourceState) {
59
+ return {
60
+ path: conflict.path,
61
+ action: 'use-destination',
62
+ reason: 'File exists only in destination',
63
+ };
64
+ }
65
+ else {
66
+ return {
67
+ path: conflict.path,
68
+ action: 'use-source',
69
+ reason: 'File exists only in source',
70
+ };
71
+ }
72
+ }
73
+ // Compare modification times
74
+ const sourceMtime = new Date(sourceState.mtime).getTime();
75
+ const destMtime = new Date(destinationState.mtime).getTime();
76
+ if (sourceMtime > destMtime) {
77
+ return {
78
+ path: conflict.path,
79
+ action: 'use-source',
80
+ reason: `Source is newer (${sourceState.mtime} vs ${destinationState.mtime})`,
81
+ };
82
+ }
83
+ else if (destMtime > sourceMtime) {
84
+ return {
85
+ path: conflict.path,
86
+ action: 'use-destination',
87
+ reason: `Destination is newer (${destinationState.mtime} vs ${sourceState.mtime})`,
88
+ };
89
+ }
90
+ else {
91
+ // Same mtime - prefer source as tiebreaker
92
+ return {
93
+ path: conflict.path,
94
+ action: 'use-source',
95
+ reason: 'Both files have same modification time, preferring source',
96
+ };
97
+ }
98
+ }
99
+ /**
100
+ * Get human-readable summary of conflict resolutions
101
+ */
102
+ getSummary(resolutions) {
103
+ const useSource = resolutions.filter((r) => r.action === 'use-source').length;
104
+ const useDest = resolutions.filter((r) => r.action === 'use-destination').length;
105
+ const skipped = resolutions.filter((r) => r.action === 'skip').length;
106
+ const lines = [
107
+ `Conflict Resolution Summary:`,
108
+ ` Using source: ${useSource}`,
109
+ ` Using destination: ${useDest}`,
110
+ ` Skipped: ${skipped}`,
111
+ ` Total conflicts: ${resolutions.length}`,
112
+ ];
113
+ return lines.join('\n');
114
+ }
115
+ }
116
+ //# sourceMappingURL=conflictResolver.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"conflictResolver.js","sourceRoot":"","sources":["../../src/sync/conflictResolver.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Simple directory file lister for non-vault destinations.
3
+ * Used in remote mode when the destination is not an Obsidian vault.
4
+ */
5
+ export interface DirectoryListerConfig {
6
+ path: string;
7
+ excludePatterns?: string[];
8
+ }
9
+ export declare class DirectoryLister {
10
+ private config;
11
+ private storage;
12
+ constructor(config: DirectoryListerConfig);
13
+ /**
14
+ * List all files in the directory, applying exclusion patterns
15
+ */
16
+ listFiles(): Promise<string[]>;
17
+ }
18
+ //# sourceMappingURL=directoryLister.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"directoryLister.d.ts","sourceRoot":"","sources":["../../src/sync/directoryLister.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAKH,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;CAC5B;AAUD,qBAAa,eAAe;IAGd,OAAO,CAAC,MAAM;IAF1B,OAAO,CAAC,OAAO,CAAsB;gBAEjB,MAAM,EAAE,qBAAqB;IAIjD;;OAEG;IACG,SAAS,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;CAgBrC"}
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Simple directory file lister for non-vault destinations.
3
+ * Used in remote mode when the destination is not an Obsidian vault.
4
+ */
5
+ import picomatch from 'picomatch';
6
+ import { LocalStorageAdapter } from '../storage/index.js';
7
+ const DEFAULT_EXCLUDES = [
8
+ '.obsync/**',
9
+ '.DS_Store',
10
+ '**/.DS_Store',
11
+ 'Thumbs.db',
12
+ '**/Thumbs.db',
13
+ ];
14
+ export class DirectoryLister {
15
+ config;
16
+ storage;
17
+ constructor(config) {
18
+ this.config = config;
19
+ this.storage = new LocalStorageAdapter();
20
+ }
21
+ /**
22
+ * List all files in the directory, applying exclusion patterns
23
+ */
24
+ async listFiles() {
25
+ // Check if directory exists first
26
+ const exists = await this.storage.exists(this.config.path);
27
+ if (!exists) {
28
+ return [];
29
+ }
30
+ const allFiles = await this.storage.list(this.config.path);
31
+ const excludePatterns = [
32
+ ...DEFAULT_EXCLUDES,
33
+ ...(this.config.excludePatterns || []),
34
+ ];
35
+ const excludeMatcher = picomatch(excludePatterns);
36
+ return allFiles.filter((file) => !excludeMatcher(file));
37
+ }
38
+ }
39
+ //# sourceMappingURL=directoryLister.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"directoryLister.js","sourceRoot":"","sources":["../../src/sync/directoryLister.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,SAAS,MAAM,WAAW,CAAC;AAClC,OAAO,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAO1D,MAAM,gBAAgB,GAAG;IACvB,YAAY;IACZ,WAAW;IACX,cAAc;IACd,WAAW;IACX,cAAc;CACf,CAAC;AAEF,MAAM,OAAO,eAAe;IAGN;IAFZ,OAAO,CAAsB;IAErC,YAAoB,MAA6B;QAA7B,WAAM,GAAN,MAAM,CAAuB;QAC/C,IAAI,CAAC,OAAO,GAAG,IAAI,mBAAmB,EAAE,CAAC;IAC3C,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,SAAS;QACb,kCAAkC;QAClC,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QAC3D,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,OAAO,EAAE,CAAC;QACZ,CAAC;QAED,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QAC3D,MAAM,eAAe,GAAG;YACtB,GAAG,gBAAgB;YACnB,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,eAAe,IAAI,EAAE,CAAC;SACvC,CAAC;QACF,MAAM,cAAc,GAAG,SAAS,CAAC,eAAe,CAAC,CAAC;QAElD,OAAO,QAAQ,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC,CAAC;IAC1D,CAAC;CACF"}
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Core synchronization logic for Obsidian vaults.
3
+ * Handles file comparison, conflict detection, and sync operations.
4
+ */
5
+ import type { SyncOptions, SyncResult, FileChange } from './types.js';
6
+ export * from './types.js';
7
+ export * from './watcher.js';
8
+ export * from './watchMode.js';
9
+ export * from './directoryLister.js';
10
+ export declare class SyncEngine {
11
+ private sourceAdapter;
12
+ private destAdapter;
13
+ private changeDetector;
14
+ private conflictResolver;
15
+ constructor();
16
+ /**
17
+ * Synchronize source vault to destination
18
+ */
19
+ sync(options: SyncOptions): Promise<SyncResult>;
20
+ /**
21
+ * Apply file changes to destination
22
+ */
23
+ private applyChanges;
24
+ /**
25
+ * Detect changes between source and destination
26
+ */
27
+ detectChanges(source: string, destination: string): Promise<FileChange[]>;
28
+ }
29
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/sync/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAWH,OAAO,KAAK,EAAE,WAAW,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAEtE,cAAc,YAAY,CAAC;AAC3B,cAAc,cAAc,CAAC;AAC7B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,sBAAsB,CAAC;AAErC,qBAAa,UAAU;IACrB,OAAO,CAAC,aAAa,CAAsB;IAC3C,OAAO,CAAC,WAAW,CAAsB;IACzC,OAAO,CAAC,cAAc,CAAiB;IACvC,OAAO,CAAC,gBAAgB,CAAmB;;IAS3C;;OAEG;IACG,IAAI,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,UAAU,CAAC;IA+IrD;;OAEG;YACW,YAAY;IA8E1B;;OAEG;IACG,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC;CAuBhF"}