@n8n-as-code/sync 0.5.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 (50) hide show
  1. package/README.md +22 -0
  2. package/dist/index.d.ts +10 -0
  3. package/dist/index.d.ts.map +1 -0
  4. package/dist/index.js +10 -0
  5. package/dist/index.js.map +1 -0
  6. package/dist/services/directory-utils.d.ts +35 -0
  7. package/dist/services/directory-utils.d.ts.map +1 -0
  8. package/dist/services/directory-utils.js +75 -0
  9. package/dist/services/directory-utils.js.map +1 -0
  10. package/dist/services/hash-utils.d.ts +22 -0
  11. package/dist/services/hash-utils.d.ts.map +1 -0
  12. package/dist/services/hash-utils.js +31 -0
  13. package/dist/services/hash-utils.js.map +1 -0
  14. package/dist/services/n8n-api-client.d.ts +23 -0
  15. package/dist/services/n8n-api-client.d.ts.map +1 -0
  16. package/dist/services/n8n-api-client.js +193 -0
  17. package/dist/services/n8n-api-client.js.map +1 -0
  18. package/dist/services/resolution-manager.d.ts +73 -0
  19. package/dist/services/resolution-manager.d.ts.map +1 -0
  20. package/dist/services/resolution-manager.js +149 -0
  21. package/dist/services/resolution-manager.js.map +1 -0
  22. package/dist/services/state-manager.d.ts +43 -0
  23. package/dist/services/state-manager.d.ts.map +1 -0
  24. package/dist/services/state-manager.js +68 -0
  25. package/dist/services/state-manager.js.map +1 -0
  26. package/dist/services/sync-engine.d.ts +56 -0
  27. package/dist/services/sync-engine.d.ts.map +1 -0
  28. package/dist/services/sync-engine.js +312 -0
  29. package/dist/services/sync-engine.js.map +1 -0
  30. package/dist/services/sync-manager.d.ts +37 -0
  31. package/dist/services/sync-manager.d.ts.map +1 -0
  32. package/dist/services/sync-manager.js +280 -0
  33. package/dist/services/sync-manager.js.map +1 -0
  34. package/dist/services/trash-service.d.ts +17 -0
  35. package/dist/services/trash-service.d.ts.map +1 -0
  36. package/dist/services/trash-service.js +41 -0
  37. package/dist/services/trash-service.js.map +1 -0
  38. package/dist/services/watcher.d.ts +135 -0
  39. package/dist/services/watcher.d.ts.map +1 -0
  40. package/dist/services/watcher.js +655 -0
  41. package/dist/services/watcher.js.map +1 -0
  42. package/dist/services/workflow-sanitizer.d.ts +23 -0
  43. package/dist/services/workflow-sanitizer.d.ts.map +1 -0
  44. package/dist/services/workflow-sanitizer.js +100 -0
  45. package/dist/services/workflow-sanitizer.js.map +1 -0
  46. package/dist/types.d.ts +46 -0
  47. package/dist/types.d.ts.map +1 -0
  48. package/dist/types.js +12 -0
  49. package/dist/types.js.map +1 -0
  50. package/package.json +43 -0
@@ -0,0 +1,37 @@
1
+ import EventEmitter from 'events';
2
+ import { N8nApiClient } from './n8n-api-client.js';
3
+ import { ISyncConfig, IWorkflowStatus } from '../types.js';
4
+ export declare class SyncManager extends EventEmitter {
5
+ private client;
6
+ private config;
7
+ private stateManager;
8
+ private watcher;
9
+ private syncEngine;
10
+ private resolutionManager;
11
+ constructor(client: N8nApiClient, config: ISyncConfig);
12
+ private ensureInitialized;
13
+ getWorkflowsStatus(): Promise<IWorkflowStatus[]>;
14
+ syncDown(): Promise<void>;
15
+ syncUp(): Promise<void>;
16
+ startWatch(): Promise<void>;
17
+ /**
18
+ * Create or update the n8nac-instance.json file
19
+ * This file marks the workspace as initialized and stores the instance identifier
20
+ */
21
+ private ensureInstanceConfigFile;
22
+ /**
23
+ * Handle automatic synchronization based on status changes
24
+ * Only triggered in auto mode
25
+ */
26
+ private handleAutoSync;
27
+ stopWatch(): void;
28
+ refreshState(): Promise<void>;
29
+ getInstanceDirectory(): string;
30
+ resolveConflict(id: string, filename: string, choice: 'local' | 'remote'): Promise<void>;
31
+ handleLocalFileChange(filePath: string): Promise<'updated' | 'created' | 'up-to-date' | 'conflict' | 'skipped'>;
32
+ restoreLocalFile(id: string, filename: string): Promise<boolean>;
33
+ deleteRemoteWorkflow(id: string, filename: string): Promise<boolean>;
34
+ confirmDeletion(id: string, filename: string): Promise<void>;
35
+ restoreRemoteWorkflow(id: string, filename: string): Promise<string>;
36
+ }
37
+ //# sourceMappingURL=sync-manager.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sync-manager.d.ts","sourceRoot":"","sources":["../../src/services/sync-manager.ts"],"names":[],"mappings":"AAEA,OAAO,YAAY,MAAM,QAAQ,CAAC;AAClC,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAKnD,OAAO,EAAE,WAAW,EAAiC,eAAe,EAAE,MAAM,aAAa,CAAC;AAE1F,qBAAa,WAAY,SAAQ,YAAY;IACzC,OAAO,CAAC,MAAM,CAAe;IAC7B,OAAO,CAAC,MAAM,CAAc;IAC5B,OAAO,CAAC,YAAY,CAA6B;IACjD,OAAO,CAAC,OAAO,CAAwB;IACvC,OAAO,CAAC,UAAU,CAA2B;IAC7C,OAAO,CAAC,iBAAiB,CAAkC;gBAE/C,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,WAAW;YAUvC,iBAAiB;IAgEzB,kBAAkB,IAAI,OAAO,CAAC,eAAe,EAAE,CAAC;IAMhD,QAAQ;IAaR,MAAM;IAaN,UAAU;IAUhB;;;OAGG;IACH,OAAO,CAAC,wBAAwB;IAsBhC;;;OAGG;YACW,cAAc;IAkD5B,SAAS;IAKH,YAAY;IAOX,oBAAoB,IAAI,MAAM;IAK/B,eAAe,CAAC,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,GAAG,QAAQ;IASxE,qBAAqB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,GAAG,SAAS,GAAG,YAAY,GAAG,UAAU,GAAG,SAAS,CAAC;IAwB/G,gBAAgB,CAAC,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAmBhE,oBAAoB,CAAC,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAgBpE,eAAe,CAAC,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAa5D,qBAAqB,CAAC,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;CAI7E"}
@@ -0,0 +1,280 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ import EventEmitter from 'events';
4
+ import { StateManager } from './state-manager.js';
5
+ import { Watcher } from './watcher.js';
6
+ import { SyncEngine } from './sync-engine.js';
7
+ import { ResolutionManager } from './resolution-manager.js';
8
+ import { WorkflowSyncStatus } from '../types.js';
9
+ export class SyncManager extends EventEmitter {
10
+ client;
11
+ config;
12
+ stateManager = null;
13
+ watcher = null;
14
+ syncEngine = null;
15
+ resolutionManager = null;
16
+ constructor(client, config) {
17
+ super();
18
+ this.client = client;
19
+ this.config = config;
20
+ if (!fs.existsSync(this.config.directory)) {
21
+ fs.mkdirSync(this.config.directory, { recursive: true });
22
+ }
23
+ }
24
+ async ensureInitialized() {
25
+ if (this.watcher)
26
+ return;
27
+ // Note: instanceIdentifier logic handling omitted for brevity,
28
+ // assuming it's handled or using default directory for now
29
+ // to focus on the 3-way merge integration.
30
+ const instanceDir = path.join(this.config.directory, this.config.instanceIdentifier || 'default');
31
+ if (!fs.existsSync(instanceDir))
32
+ fs.mkdirSync(instanceDir, { recursive: true });
33
+ this.stateManager = new StateManager(instanceDir);
34
+ this.watcher = new Watcher(this.client, {
35
+ directory: instanceDir,
36
+ pollIntervalMs: this.config.pollIntervalMs,
37
+ syncInactive: this.config.syncInactive,
38
+ ignoredTags: this.config.ignoredTags
39
+ });
40
+ this.syncEngine = new SyncEngine(this.client, this.watcher, instanceDir);
41
+ this.resolutionManager = new ResolutionManager(this.syncEngine, this.watcher, this.client);
42
+ this.watcher.on('statusChange', (data) => {
43
+ this.emit('change', data);
44
+ // Emit specific events for deletions and conflicts
45
+ if (data.status === WorkflowSyncStatus.DELETED_LOCALLY && data.workflowId) {
46
+ this.emit('local-deletion', {
47
+ id: data.workflowId,
48
+ filename: data.filename
49
+ });
50
+ }
51
+ else if (data.status === WorkflowSyncStatus.CONFLICT && data.workflowId) {
52
+ // Fetch remote content for conflict notification
53
+ this.client.getWorkflow(data.workflowId).then(remoteContent => {
54
+ this.emit('conflict', {
55
+ id: data.workflowId,
56
+ filename: data.filename,
57
+ remoteContent
58
+ });
59
+ }).catch(err => {
60
+ console.error(`[SyncManager] Failed to fetch remote content for conflict: ${err.message}`);
61
+ });
62
+ }
63
+ // Auto-sync in auto mode
64
+ console.log(`[SyncManager] statusChange event: ${data.filename}, status: ${data.status}, syncMode: ${this.config.syncMode}`);
65
+ if (this.config.syncMode === 'auto') {
66
+ console.log(`[SyncManager] Triggering auto-sync for ${data.filename}`);
67
+ this.handleAutoSync(data).catch(err => {
68
+ console.error('[SyncManager] Auto-sync error:', err);
69
+ this.emit('error', `Auto-sync failed: ${err.message}`);
70
+ });
71
+ }
72
+ else {
73
+ console.log(`[SyncManager] Auto-sync skipped (mode: ${this.config.syncMode})`);
74
+ }
75
+ });
76
+ this.watcher.on('error', (err) => {
77
+ this.emit('error', err);
78
+ });
79
+ this.watcher.on('connection-lost', (err) => {
80
+ this.emit('connection-lost', err);
81
+ });
82
+ }
83
+ async getWorkflowsStatus() {
84
+ await this.ensureInitialized();
85
+ // Return status from watcher
86
+ return this.watcher.getStatusMatrix();
87
+ }
88
+ async syncDown() {
89
+ await this.ensureInitialized();
90
+ const statuses = await this.getWorkflowsStatus();
91
+ for (const s of statuses) {
92
+ if (s.status === WorkflowSyncStatus.EXIST_ONLY_REMOTELY ||
93
+ s.status === WorkflowSyncStatus.MODIFIED_REMOTELY) {
94
+ await this.syncEngine.pull(s.id, s.filename, s.status);
95
+ }
96
+ // DELETED_REMOTELY requires user confirmation via confirmDeletion()
97
+ // Per spec 5.2: "Halt. Trigger Deletion Validation."
98
+ }
99
+ }
100
+ async syncUp() {
101
+ await this.ensureInitialized();
102
+ const statuses = await this.getWorkflowsStatus();
103
+ for (const s of statuses) {
104
+ if (s.status === WorkflowSyncStatus.EXIST_ONLY_LOCALLY || s.status === WorkflowSyncStatus.MODIFIED_LOCALLY) {
105
+ await this.syncEngine.push(s.filename, s.id, s.status);
106
+ }
107
+ else if (s.status === WorkflowSyncStatus.DELETED_LOCALLY) {
108
+ // Per spec: Halt and trigger deletion validation
109
+ throw new Error(`Local deletion detected for workflow "${s.filename}". Use confirmDeletion() to proceed with remote deletion or restoreWorkflow() to restore the file.`);
110
+ }
111
+ }
112
+ }
113
+ async startWatch() {
114
+ await this.ensureInitialized();
115
+ await this.watcher.start();
116
+ // Create instance config file to mark workspace as initialized
117
+ this.ensureInstanceConfigFile();
118
+ this.emit('log', 'Watcher started.');
119
+ }
120
+ /**
121
+ * Create or update the n8nac-instance.json file
122
+ * This file marks the workspace as initialized and stores the instance identifier
123
+ */
124
+ ensureInstanceConfigFile() {
125
+ if (!this.config.instanceConfigPath || !this.config.instanceIdentifier) {
126
+ return;
127
+ }
128
+ const configData = {
129
+ instanceIdentifier: this.config.instanceIdentifier,
130
+ directory: this.config.directory,
131
+ lastSync: new Date().toISOString()
132
+ };
133
+ try {
134
+ fs.writeFileSync(this.config.instanceConfigPath, JSON.stringify(configData, null, 2), 'utf-8');
135
+ }
136
+ catch (error) {
137
+ console.warn(`[SyncManager] Failed to write instance config file: ${error}`);
138
+ }
139
+ }
140
+ /**
141
+ * Handle automatic synchronization based on status changes
142
+ * Only triggered in auto mode
143
+ */
144
+ async handleAutoSync(data) {
145
+ const { filename, workflowId, status } = data;
146
+ try {
147
+ switch (status) {
148
+ case WorkflowSyncStatus.MODIFIED_LOCALLY:
149
+ case WorkflowSyncStatus.EXIST_ONLY_LOCALLY:
150
+ // Auto-push local changes
151
+ this.emit('log', `🔄 Auto-sync: Pushing "${filename}"...`);
152
+ await this.syncEngine.push(filename, workflowId, status);
153
+ this.emit('log', `✅ Auto-sync: Pushed "${filename}"`);
154
+ // Emit event to notify that remote was updated (for webview reload)
155
+ if (workflowId) {
156
+ this.emit('remote-updated', { workflowId, filename });
157
+ }
158
+ break;
159
+ case WorkflowSyncStatus.MODIFIED_REMOTELY:
160
+ case WorkflowSyncStatus.EXIST_ONLY_REMOTELY:
161
+ // Auto-pull remote changes
162
+ if (workflowId) {
163
+ this.emit('log', `🔄 Auto-sync: Pulling "${filename}"...`);
164
+ await this.syncEngine.pull(workflowId, filename, status);
165
+ this.emit('log', `✅ Auto-sync: Pulled "${filename}"`);
166
+ }
167
+ break;
168
+ case WorkflowSyncStatus.CONFLICT:
169
+ // Conflicts require manual resolution
170
+ this.emit('log', `⚠️ Conflict detected for "${filename}". Manual resolution required.`);
171
+ // conflict event is handled in ensureInitialized above
172
+ break;
173
+ case WorkflowSyncStatus.DELETED_LOCALLY:
174
+ case WorkflowSyncStatus.DELETED_REMOTELY:
175
+ // Deletions require manual confirmation
176
+ // Note: local-deletion event is already emitted by the Watcher
177
+ // We don't re-emit it here to avoid duplicates
178
+ this.emit('log', `🗑️ Deletion detected for "${filename}". Manual confirmation required.`);
179
+ break;
180
+ case WorkflowSyncStatus.IN_SYNC:
181
+ // Already in sync, nothing to do
182
+ break;
183
+ }
184
+ }
185
+ catch (error) {
186
+ this.emit('error', `Auto-sync failed for "${filename}": ${error.message}`);
187
+ }
188
+ }
189
+ stopWatch() {
190
+ this.watcher?.stop();
191
+ this.emit('log', 'Watcher stopped.');
192
+ }
193
+ async refreshState() {
194
+ await this.ensureInitialized();
195
+ // Run sequentially to avoid potential race conditions during state loading
196
+ await this.watcher.refreshRemoteState();
197
+ await this.watcher.refreshLocalState();
198
+ }
199
+ getInstanceDirectory() {
200
+ return path.join(this.config.directory, this.config.instanceIdentifier || 'default');
201
+ }
202
+ // Bridge for conflict resolution
203
+ async resolveConflict(id, filename, choice) {
204
+ await this.ensureInitialized();
205
+ if (choice === 'local') {
206
+ await this.resolutionManager.keepLocal(id, filename);
207
+ }
208
+ else {
209
+ await this.resolutionManager.keepRemote(id, filename);
210
+ }
211
+ }
212
+ async handleLocalFileChange(filePath) {
213
+ await this.ensureInitialized();
214
+ const filename = path.basename(filePath);
215
+ console.log(`[DEBUG] handleLocalFileChange: ${filename}`);
216
+ // Ensure we have the latest from both worlds
217
+ await this.refreshState();
218
+ const status = this.watcher.calculateStatus(filename);
219
+ switch (status) {
220
+ case WorkflowSyncStatus.IN_SYNC: return 'updated'; // If it's in-sync, we return updated for legacy compatibility in tests
221
+ case WorkflowSyncStatus.CONFLICT: return 'conflict';
222
+ case WorkflowSyncStatus.EXIST_ONLY_LOCALLY:
223
+ await this.syncEngine.push(filename);
224
+ return 'created';
225
+ case WorkflowSyncStatus.MODIFIED_LOCALLY:
226
+ const wfId = this.watcher.getFileToIdMap().get(filename);
227
+ await this.syncEngine.push(filename, wfId, status);
228
+ return 'updated';
229
+ default: return 'skipped';
230
+ }
231
+ }
232
+ async restoreLocalFile(id, filename) {
233
+ await this.ensureInitialized();
234
+ try {
235
+ // Determine the deletion type based on current status
236
+ const statuses = await this.getWorkflowsStatus();
237
+ const workflow = statuses.find(s => s.id === id);
238
+ if (!workflow) {
239
+ throw new Error(`Workflow ${id} not found in state`);
240
+ }
241
+ const deletionType = workflow.status === WorkflowSyncStatus.DELETED_LOCALLY ? 'local' : 'remote';
242
+ await this.resolutionManager.restoreWorkflow(id, filename, deletionType);
243
+ return true;
244
+ }
245
+ catch {
246
+ return false;
247
+ }
248
+ }
249
+ async deleteRemoteWorkflow(id, filename) {
250
+ await this.ensureInitialized();
251
+ try {
252
+ // Step 1: Archive local file (if exists)
253
+ await this.syncEngine.archive(filename);
254
+ // Step 2: Delete from API
255
+ await this.client.deleteWorkflow(id);
256
+ // Step 3: Remove from state (workflow is completely deleted)
257
+ await this.watcher.removeWorkflowState(id);
258
+ return true;
259
+ }
260
+ catch {
261
+ return false;
262
+ }
263
+ }
264
+ // Deletion Validation Methods (6.2 from spec)
265
+ async confirmDeletion(id, filename) {
266
+ await this.ensureInitialized();
267
+ const statuses = await this.getWorkflowsStatus();
268
+ const workflow = statuses.find(s => s.id === id);
269
+ if (!workflow) {
270
+ throw new Error(`Workflow ${id} not found in state`);
271
+ }
272
+ const deletionType = workflow.status === WorkflowSyncStatus.DELETED_LOCALLY ? 'local' : 'remote';
273
+ await this.resolutionManager.confirmDeletion(id, filename, deletionType);
274
+ }
275
+ async restoreRemoteWorkflow(id, filename) {
276
+ await this.ensureInitialized();
277
+ return await this.resolutionManager.restoreWorkflow(id, filename, 'remote');
278
+ }
279
+ }
280
+ //# sourceMappingURL=sync-manager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sync-manager.js","sourceRoot":"","sources":["../../src/services/sync-manager.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ export declare class TrashService {
2
+ private archiveDir;
3
+ constructor(baseDir: string);
4
+ /**
5
+ * Moves a file to the archive directory.
6
+ * @param filePath Absolute path to the file to move
7
+ * @param filename Name to use in the archive
8
+ */
9
+ archiveFile(filePath: string, filename: string): Promise<string>;
10
+ /**
11
+ * Saves a workflow object to the archive directory.
12
+ * @param workflow The workflow object to save
13
+ * @param filename Base filename
14
+ */
15
+ archiveWorkflow(workflow: any, filename: string): Promise<string>;
16
+ }
17
+ //# sourceMappingURL=trash-service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"trash-service.d.ts","sourceRoot":"","sources":["../../src/services/trash-service.ts"],"names":[],"mappings":"AAGA,qBAAa,YAAY;IACrB,OAAO,CAAC,UAAU,CAAS;gBAEf,OAAO,EAAE,MAAM;IAI3B;;;;OAIG;IACG,WAAW,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAgBtE;;;;OAIG;IACG,eAAe,CAAC,QAAQ,EAAE,GAAG,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;CAa1E"}
@@ -0,0 +1,41 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ export class TrashService {
4
+ archiveDir;
5
+ constructor(baseDir) {
6
+ this.archiveDir = path.join(baseDir, '.archive');
7
+ }
8
+ /**
9
+ * Moves a file to the archive directory.
10
+ * @param filePath Absolute path to the file to move
11
+ * @param filename Name to use in the archive
12
+ */
13
+ async archiveFile(filePath, filename) {
14
+ if (!fs.existsSync(this.archiveDir)) {
15
+ fs.mkdirSync(this.archiveDir, { recursive: true });
16
+ }
17
+ const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
18
+ const archivedName = `${timestamp}_${filename}`;
19
+ const targetPath = path.join(this.archiveDir, archivedName);
20
+ if (fs.existsSync(filePath)) {
21
+ fs.renameSync(filePath, targetPath);
22
+ }
23
+ return targetPath;
24
+ }
25
+ /**
26
+ * Saves a workflow object to the archive directory.
27
+ * @param workflow The workflow object to save
28
+ * @param filename Base filename
29
+ */
30
+ async archiveWorkflow(workflow, filename) {
31
+ if (!fs.existsSync(this.archiveDir)) {
32
+ fs.mkdirSync(this.archiveDir, { recursive: true });
33
+ }
34
+ const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
35
+ const archivedName = `${timestamp}_${filename}`;
36
+ const targetPath = path.join(this.archiveDir, archivedName);
37
+ fs.writeFileSync(targetPath, JSON.stringify(workflow, null, 2));
38
+ return targetPath;
39
+ }
40
+ }
41
+ //# sourceMappingURL=trash-service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"trash-service.js","sourceRoot":"","sources":["../../src/services/trash-service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,IAAI,MAAM,MAAM,CAAC;AAExB,MAAM,OAAO,YAAY;IACb,UAAU,CAAS;IAE3B,YAAY,OAAe;QACvB,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,UAAU,CAAC,CAAC;IACrD,CAAC;IAED;;;;OAIG;IACH,KAAK,CAAC,WAAW,CAAC,QAAgB,EAAE,QAAgB;QAChD,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC;YAClC,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,UAAU,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACvD,CAAC;QAED,MAAM,SAAS,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;QACjE,MAAM,YAAY,GAAG,GAAG,SAAS,IAAI,QAAQ,EAAE,CAAC;QAChD,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,YAAY,CAAC,CAAC;QAE5D,IAAI,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC1B,EAAE,CAAC,UAAU,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC;QACxC,CAAC;QAED,OAAO,UAAU,CAAC;IACtB,CAAC;IAED;;;;OAIG;IACH,KAAK,CAAC,eAAe,CAAC,QAAa,EAAE,QAAgB;QACjD,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC;YAClC,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,UAAU,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACvD,CAAC;QAED,MAAM,SAAS,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;QACjE,MAAM,YAAY,GAAG,GAAG,SAAS,IAAI,QAAQ,EAAE,CAAC;QAChD,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,YAAY,CAAC,CAAC;QAE5D,EAAE,CAAC,aAAa,CAAC,UAAU,EAAE,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;QAEhE,OAAO,UAAU,CAAC;IACtB,CAAC;CACJ"}
@@ -0,0 +1,135 @@
1
+ import EventEmitter from 'events';
2
+ import { N8nApiClient } from './n8n-api-client.js';
3
+ import { WorkflowSyncStatus, IWorkflowStatus } from '../types.js';
4
+ /**
5
+ * Watcher - State Observation Component
6
+ *
7
+ * Responsibilities:
8
+ * 1. File System Watch with debounce
9
+ * 2. Remote Polling with lightweight strategy
10
+ * 3. Canonical Hashing (SHA-256 of sorted JSON)
11
+ * 4. Status Matrix Calculation (3-way comparison)
12
+ * 5. State Persistence (only component that writes to .n8n-state.json)
13
+ *
14
+ * Never performs synchronization actions - only observes reality.
15
+ */
16
+ export declare class Watcher extends EventEmitter {
17
+ private watcher;
18
+ private pollInterval;
19
+ private client;
20
+ private directory;
21
+ private pollIntervalMs;
22
+ private syncInactive;
23
+ private ignoredTags;
24
+ private stateFilePath;
25
+ private isConnected;
26
+ private isInitializing;
27
+ private localHashes;
28
+ private remoteHashes;
29
+ private fileToIdMap;
30
+ private idToFileMap;
31
+ private lastKnownStatuses;
32
+ private isPaused;
33
+ private syncInProgress;
34
+ private pausedFilenames;
35
+ private remoteTimestamps;
36
+ constructor(client: N8nApiClient, options: {
37
+ directory: string;
38
+ pollIntervalMs: number;
39
+ syncInactive: boolean;
40
+ ignoredTags: string[];
41
+ });
42
+ start(): Promise<void>;
43
+ stop(): void;
44
+ /**
45
+ * Pause observation for a workflow during sync operations
46
+ */
47
+ pauseObservation(workflowId: string): void;
48
+ /**
49
+ * Resume observation after sync operations
50
+ */
51
+ resumeObservation(workflowId: string): void;
52
+ /**
53
+ * Pause observation for a filename (for workflows without ID yet)
54
+ */
55
+ pauseObservationByFilename(filename: string): void;
56
+ /**
57
+ * Resume observation for a filename
58
+ */
59
+ resumeObservationByFilename(filename: string): void;
60
+ /**
61
+ * Mark a workflow as being synced (prevents race conditions)
62
+ */
63
+ markSyncInProgress(workflowId: string): void;
64
+ /**
65
+ * Mark a workflow as no longer being synced
66
+ */
67
+ markSyncComplete(workflowId: string): void;
68
+ private onLocalChange;
69
+ private onLocalDelete;
70
+ refreshLocalState(): Promise<void>;
71
+ /**
72
+ * Lightweight polling strategy:
73
+ * 1. Fetch only IDs and updatedAt timestamps
74
+ * 2. Compare with cached timestamps
75
+ * 3. Fetch full content only if timestamp changed
76
+ */
77
+ refreshRemoteState(): Promise<void>;
78
+ /**
79
+ * Finalize sync - update base state after successful sync operation
80
+ * Called by SyncEngine after PULL/PUSH completes
81
+ */
82
+ finalizeSync(workflowId: string): Promise<void>;
83
+ /**
84
+ * Update workflow state in .n8n-state.json
85
+ * Only this component writes to the state file
86
+ */
87
+ private updateWorkflowState;
88
+ /**
89
+ * Remove workflow from state file
90
+ * Called after deletion confirmation
91
+ */
92
+ removeWorkflowState(id: string): Promise<void>;
93
+ /**
94
+ * Load state from .n8n-state.json
95
+ */
96
+ private loadState;
97
+ /**
98
+ * Save state to .n8n-state.json
99
+ */
100
+ private saveState;
101
+ /**
102
+ * Compute canonical hash for content
103
+ */
104
+ private computeHash;
105
+ private broadcastStatus;
106
+ calculateStatus(filename: string, workflowId?: string): WorkflowSyncStatus;
107
+ private shouldIgnore;
108
+ private safeName;
109
+ /**
110
+ * Find local file that contains a specific workflow ID
111
+ * Used when we have an ID but no filename mapping yet (e.g., after file rename)
112
+ */
113
+ private findFilenameByWorkflowId;
114
+ private readJsonFile;
115
+ getFileToIdMap(): Map<string, string>;
116
+ getStatusMatrix(): IWorkflowStatus[];
117
+ /**
118
+ * Get last synced hash for a workflow
119
+ */
120
+ getLastSyncedHash(workflowId: string): string | undefined;
121
+ /**
122
+ * Update remote hash cache (for SyncEngine use)
123
+ * @internal
124
+ */
125
+ setRemoteHash(workflowId: string, hash: string): void;
126
+ /**
127
+ * Get all tracked workflow IDs
128
+ */
129
+ getTrackedWorkflowIds(): string[];
130
+ /**
131
+ * Update workflow ID in state (when a workflow is re-created with a new ID)
132
+ */
133
+ updateWorkflowId(oldId: string, newId: string): Promise<void>;
134
+ }
135
+ //# sourceMappingURL=watcher.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"watcher.d.ts","sourceRoot":"","sources":["../../src/services/watcher.ts"],"names":[],"mappings":"AAEA,OAAO,YAAY,MAAM,QAAQ,CAAC;AAElC,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAGnD,OAAO,EAAE,kBAAkB,EAAE,eAAe,EAAa,MAAM,aAAa,CAAC;AAG7E;;;;;;;;;;;GAWG;AACH,qBAAa,OAAQ,SAAQ,YAAY;IACrC,OAAO,CAAC,OAAO,CAAmC;IAClD,OAAO,CAAC,YAAY,CAA+B;IACnD,OAAO,CAAC,MAAM,CAAe;IAC7B,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,cAAc,CAAS;IAC/B,OAAO,CAAC,YAAY,CAAU;IAC9B,OAAO,CAAC,WAAW,CAAW;IAC9B,OAAO,CAAC,aAAa,CAAS;IAC9B,OAAO,CAAC,WAAW,CAAiB;IACpC,OAAO,CAAC,cAAc,CAAkB;IAGxC,OAAO,CAAC,WAAW,CAAkC;IACrD,OAAO,CAAC,YAAY,CAAkC;IACtD,OAAO,CAAC,WAAW,CAAkC;IACrD,OAAO,CAAC,WAAW,CAAkC;IACrD,OAAO,CAAC,iBAAiB,CAA8C;IAGvE,OAAO,CAAC,QAAQ,CAAqB;IACrC,OAAO,CAAC,cAAc,CAAqB;IAC3C,OAAO,CAAC,eAAe,CAAqB;IAG5C,OAAO,CAAC,gBAAgB,CAAkC;gBAGtD,MAAM,EAAE,YAAY,EACpB,OAAO,EAAE;QACL,SAAS,EAAE,MAAM,CAAC;QAClB,cAAc,EAAE,MAAM,CAAC;QACvB,YAAY,EAAE,OAAO,CAAC;QACtB,WAAW,EAAE,MAAM,EAAE,CAAC;KACzB;IAWQ,KAAK;IAwDX,IAAI;IAWX;;OAEG;IACI,gBAAgB,CAAC,UAAU,EAAE,MAAM;IAI1C;;OAEG;IACI,iBAAiB,CAAC,UAAU,EAAE,MAAM;IAO3C;;OAEG;IACI,0BAA0B,CAAC,QAAQ,EAAE,MAAM;IAIlD;;OAEG;IACI,2BAA2B,CAAC,QAAQ,EAAE,MAAM;IAInD;;OAEG;IACI,kBAAkB,CAAC,UAAU,EAAE,MAAM;IAI5C;;OAEG;IACI,gBAAgB,CAAC,UAAU,EAAE,MAAM;YAI5B,aAAa;YAgCb,aAAa;IAyDd,iBAAiB;IAuC9B;;;;;OAKG;IACU,kBAAkB;IAgG/B;;;OAGG;IACU,YAAY,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAmD5D;;;OAGG;YACW,mBAAmB;IASjC;;;OAGG;IACU,mBAAmB,CAAC,EAAE,EAAE,MAAM;IAe3C;;OAEG;IACH,OAAO,CAAC,SAAS;IAejB;;OAEG;IACH,OAAO,CAAC,SAAS;IAIjB;;OAEG;IACH,OAAO,CAAC,WAAW;IAInB,OAAO,CAAC,eAAe;IAiBhB,eAAe,CAAC,QAAQ,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,kBAAkB;IAuCjF,OAAO,CAAC,YAAY;IASpB,OAAO,CAAC,QAAQ;IAIhB;;;OAGG;IACH,OAAO,CAAC,wBAAwB;IAiBhC,OAAO,CAAC,YAAY;IAQb,cAAc;IAId,eAAe,IAAI,eAAe,EAAE;IAmD3C;;OAEG;IACI,iBAAiB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAKhE;;;OAGG;IACI,aAAa,CAAC,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI;IAI5D;;OAEG;IACI,qBAAqB,IAAI,MAAM,EAAE;IAKxC;;OAEG;IACU,gBAAgB,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CA+B7E"}