@n8n-as-code/core 0.2.0 → 0.3.1

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 (40) hide show
  1. package/dist/index.d.ts +3 -0
  2. package/dist/index.d.ts.map +1 -1
  3. package/dist/index.js +3 -0
  4. package/dist/index.js.map +1 -1
  5. package/dist/services/hash-utils.d.ts +22 -0
  6. package/dist/services/hash-utils.d.ts.map +1 -0
  7. package/dist/services/hash-utils.js +31 -0
  8. package/dist/services/hash-utils.js.map +1 -0
  9. package/dist/services/n8n-api-client.d.ts.map +1 -1
  10. package/dist/services/n8n-api-client.js +44 -50
  11. package/dist/services/n8n-api-client.js.map +1 -1
  12. package/dist/services/resolution-manager.d.ts +73 -0
  13. package/dist/services/resolution-manager.d.ts.map +1 -0
  14. package/dist/services/resolution-manager.js +149 -0
  15. package/dist/services/resolution-manager.js.map +1 -0
  16. package/dist/services/state-manager.d.ts +18 -17
  17. package/dist/services/state-manager.d.ts.map +1 -1
  18. package/dist/services/state-manager.js +22 -53
  19. package/dist/services/state-manager.js.map +1 -1
  20. package/dist/services/sync-engine.d.ts +57 -0
  21. package/dist/services/sync-engine.d.ts.map +1 -0
  22. package/dist/services/sync-engine.js +301 -0
  23. package/dist/services/sync-engine.js.map +1 -0
  24. package/dist/services/sync-manager.d.ts +19 -83
  25. package/dist/services/sync-manager.d.ts.map +1 -1
  26. package/dist/services/sync-manager.js +208 -620
  27. package/dist/services/sync-manager.js.map +1 -1
  28. package/dist/services/watcher.d.ts +121 -0
  29. package/dist/services/watcher.d.ts.map +1 -0
  30. package/dist/services/watcher.js +609 -0
  31. package/dist/services/watcher.js.map +1 -0
  32. package/dist/services/workflow-sanitizer.d.ts +9 -4
  33. package/dist/services/workflow-sanitizer.d.ts.map +1 -1
  34. package/dist/services/workflow-sanitizer.js +55 -35
  35. package/dist/services/workflow-sanitizer.js.map +1 -1
  36. package/dist/types.d.ts +10 -5
  37. package/dist/types.d.ts.map +1 -1
  38. package/dist/types.js +8 -5
  39. package/dist/types.js.map +1 -1
  40. package/package.json +4 -2
@@ -1,11 +1,23 @@
1
1
  import fs from 'fs';
2
2
  import path from 'path';
3
- import crypto from 'crypto';
3
+ /**
4
+ * Read-only State Manager
5
+ *
6
+ * Responsibilities:
7
+ * 1. Read state from .n8n-state.json
8
+ * 2. Provide read-only access to workflow states
9
+ *
10
+ * Note: Write operations are handled exclusively by Watcher component
11
+ * to maintain single source of truth for state mutations.
12
+ */
4
13
  export class StateManager {
5
14
  stateFilePath;
6
15
  constructor(directory) {
7
16
  this.stateFilePath = path.join(directory, '.n8n-state.json');
8
17
  }
18
+ /**
19
+ * Load state from disk (private method)
20
+ */
9
21
  load() {
10
22
  if (fs.existsSync(this.stateFilePath)) {
11
23
  try {
@@ -22,44 +34,13 @@ export class StateManager {
22
34
  }
23
35
  return { workflows: {} };
24
36
  }
25
- save(state) {
26
- fs.writeFileSync(this.stateFilePath, JSON.stringify(state, null, 2));
27
- }
28
37
  /**
29
- * Computes a stable hash for a workflow object.
30
- */
31
- static computeHash(workflow) {
32
- // We use the cleaned version to ensure stable hashing (no dynamic IDs or timestamps)
33
- const content = typeof workflow === 'string' ? workflow : JSON.stringify(workflow);
34
- return crypto.createHash('sha256').update(content).digest('hex');
35
- }
36
- /**
37
- * Gets the last known state for a workflow.
38
+ * Gets the last known state (Base) for a workflow.
38
39
  */
39
40
  getWorkflowState(id) {
40
41
  const state = this.load();
41
42
  return state.workflows[id];
42
43
  }
43
- /**
44
- * Updates the last known state for a workflow.
45
- */
46
- updateWorkflowState(id, workflow) {
47
- const state = this.load();
48
- const hash = StateManager.computeHash(workflow);
49
- state.workflows[id] = {
50
- lastSyncedHash: hash,
51
- lastSyncedAt: new Date().toISOString()
52
- };
53
- this.save(state);
54
- }
55
- /**
56
- * Removes a workflow from state.
57
- */
58
- removeWorkflowState(id) {
59
- const state = this.load();
60
- delete state.workflows[id];
61
- this.save(state);
62
- }
63
44
  /**
64
45
  * Gets all tracked workflow IDs.
65
46
  */
@@ -68,32 +49,20 @@ export class StateManager {
68
49
  return Object.keys(state.workflows);
69
50
  }
70
51
  /**
71
- * Checks if a local content matches the last synced state.
52
+ * Checks if a hash matches the last synced state.
72
53
  */
73
- isLocalSynced(id, localContent) {
54
+ isSynced(id, currentHash) {
74
55
  const state = this.getWorkflowState(id);
75
56
  if (!state)
76
- return true; // If no state, we assume it's new
77
- const currentHash = StateManager.computeHash(localContent);
78
- const result = state.lastSyncedHash === currentHash;
79
- if (!result) {
80
- // Changed from console.log to silent for production
81
- }
82
- return result;
57
+ return false;
58
+ return state.lastSyncedHash === currentHash;
83
59
  }
84
60
  /**
85
- * Checks if a remote workflow matches the last synced state.
61
+ * Get the entire state object (for Watcher's internal use)
62
+ * @internal
86
63
  */
87
- isRemoteSynced(id, remoteWorkflow) {
88
- const state = this.getWorkflowState(id);
89
- if (!state)
90
- return true;
91
- const remoteHash = StateManager.computeHash(remoteWorkflow);
92
- const result = state.lastSyncedHash === remoteHash;
93
- if (!result) {
94
- // Changed from console.log to silent for production
95
- }
96
- return result;
64
+ getFullState() {
65
+ return this.load();
97
66
  }
98
67
  }
99
68
  //# sourceMappingURL=state-manager.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"state-manager.js","sourceRoot":"","sources":["../../src/services/state-manager.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"state-manager.js","sourceRoot":"","sources":["../../src/services/state-manager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,IAAI,MAAM,MAAM,CAAC;AAYxB;;;;;;;;;GASG;AACH,MAAM,OAAO,YAAY;IACb,aAAa,CAAS;IAE9B,YAAY,SAAiB;QACzB,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,iBAAiB,CAAC,CAAC;IACjE,CAAC;IAED;;OAEG;IACK,IAAI;QACR,IAAI,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,aAAa,CAAC,EAAE,CAAC;YACpC,IAAI,CAAC;gBACD,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,aAAa,EAAE,OAAO,CAAC,CAAC,CAAC;gBACtE,iCAAiC;gBACjC,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC;oBAClB,IAAI,CAAC,SAAS,GAAG,EAAE,CAAC;gBACxB,CAAC;gBACD,OAAO,IAAI,CAAC;YAChB,CAAC;YAAC,OAAO,CAAC,EAAE,CAAC;gBACT,OAAO,CAAC,IAAI,CAAC,8CAA8C,CAAC,CAAC;YACjE,CAAC;QACL,CAAC;QACD,OAAO,EAAE,SAAS,EAAE,EAAE,EAAE,CAAC;IAC7B,CAAC;IAED;;OAEG;IACH,gBAAgB,CAAC,EAAU;QACvB,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;QAC1B,OAAO,KAAK,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC;IAC/B,CAAC;IAED;;OAEG;IACH,qBAAqB;QACjB,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;QAC1B,OAAO,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;IACxC,CAAC;IAED;;OAEG;IACH,QAAQ,CAAC,EAAU,EAAE,WAAmB;QACpC,MAAM,KAAK,GAAG,IAAI,CAAC,gBAAgB,CAAC,EAAE,CAAC,CAAC;QACxC,IAAI,CAAC,KAAK;YAAE,OAAO,KAAK,CAAC;QACzB,OAAO,KAAK,CAAC,cAAc,KAAK,WAAW,CAAC;IAChD,CAAC;IAED;;;OAGG;IACH,YAAY;QACR,OAAO,IAAI,CAAC,IAAI,EAAE,CAAC;IACvB,CAAC;CACJ"}
@@ -0,0 +1,57 @@
1
+ import { N8nApiClient } from './n8n-api-client.js';
2
+ import { Watcher } from './watcher.js';
3
+ import { WorkflowSyncStatus } from '../types.js';
4
+ /**
5
+ * Sync Engine - State Mutation Component
6
+ *
7
+ * Responsibilities:
8
+ * 1. Execute PULL/PUSH operations based on status
9
+ * 2. Follow strategy tables from SPECS/REFACTO_CORE.md
10
+ * 3. Call Watcher.finalizeSync after successful operations
11
+ * 4. Handle archive operations
12
+ *
13
+ * Stateless regarding history - never writes to state file directly
14
+ */
15
+ export declare class SyncEngine {
16
+ private client;
17
+ private watcher;
18
+ private directory;
19
+ private archiveDirectory;
20
+ constructor(client: N8nApiClient, watcher: Watcher, directory: string);
21
+ /**
22
+ * PULL Strategy: Remote -> Local
23
+ * Based on spec 5.2 PULL Strategy table
24
+ */
25
+ pull(workflowId: string, filename: string, status: WorkflowSyncStatus): Promise<void>;
26
+ /**
27
+ * PUSH Strategy: Local -> Remote
28
+ * Based on spec 5.3 PUSH Strategy table
29
+ */
30
+ push(filename: string, workflowId?: string, status?: WorkflowSyncStatus): Promise<string>;
31
+ /**
32
+ * Force PULL - overwrite local with remote (for conflict resolution)
33
+ */
34
+ forcePull(workflowId: string, filename: string): Promise<void>;
35
+ /**
36
+ * Force PUSH - overwrite remote with local (for conflict resolution and restoration)
37
+ * If workflow doesn't exist on remote, creates it
38
+ */
39
+ forcePush(workflowId: string, filename: string): Promise<string>;
40
+ /**
41
+ * Delete remote workflow (for deletion validation)
42
+ * Note: The Watcher already archived the remote content when it detected the local deletion
43
+ */
44
+ deleteRemote(workflowId: string, filename: string): Promise<void>;
45
+ /**
46
+ * Restore from archive (for deletion validation)
47
+ * Moves the file from archive back to workflows directory
48
+ * Then DELETES the archive file (no need to keep it after restoration)
49
+ */
50
+ restoreFromArchive(filename: string): Promise<boolean>;
51
+ private executePull;
52
+ private executeUpdate;
53
+ private executeCreate;
54
+ archive(filename: string): Promise<void>;
55
+ private readJsonFile;
56
+ }
57
+ //# sourceMappingURL=sync-engine.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sync-engine.d.ts","sourceRoot":"","sources":["../../src/services/sync-engine.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAGnD,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,OAAO,EAAE,kBAAkB,EAAa,MAAM,aAAa,CAAC;AAE5D;;;;;;;;;;GAUG;AACH,qBAAa,UAAU;IACnB,OAAO,CAAC,MAAM,CAAe;IAC7B,OAAO,CAAC,OAAO,CAAU;IACzB,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,gBAAgB,CAAS;gBAG7B,MAAM,EAAE,YAAY,EACpB,OAAO,EAAE,OAAO,EAChB,SAAS,EAAE,MAAM;IAYrB;;;OAGG;IACU,IAAI,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC;IAoDlG;;;OAGG;IACU,IAAI,CAAC,QAAQ,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,kBAAkB,GAAG,OAAO,CAAC,MAAM,CAAC;IAuDtG;;OAEG;IACU,SAAS,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAa3E;;;OAGG;IACU,SAAS,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAkC7E;;;OAGG;IACU,YAAY,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAkB9E;;;;OAIG;IACU,kBAAkB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;YAyBrD,WAAW;YAwBX,aAAa;YAwBb,aAAa;IAwBd,OAAO,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAQrD,OAAO,CAAC,YAAY;CAOvB"}
@@ -0,0 +1,301 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ import { WorkflowSanitizer } from './workflow-sanitizer.js';
4
+ import { HashUtils } from './hash-utils.js';
5
+ import { WorkflowSyncStatus } from '../types.js';
6
+ /**
7
+ * Sync Engine - State Mutation Component
8
+ *
9
+ * Responsibilities:
10
+ * 1. Execute PULL/PUSH operations based on status
11
+ * 2. Follow strategy tables from SPECS/REFACTO_CORE.md
12
+ * 3. Call Watcher.finalizeSync after successful operations
13
+ * 4. Handle archive operations
14
+ *
15
+ * Stateless regarding history - never writes to state file directly
16
+ */
17
+ export class SyncEngine {
18
+ client;
19
+ watcher;
20
+ directory;
21
+ archiveDirectory;
22
+ constructor(client, watcher, directory) {
23
+ this.client = client;
24
+ this.watcher = watcher;
25
+ this.directory = directory;
26
+ this.archiveDirectory = path.join(directory, '.archive');
27
+ if (!fs.existsSync(this.archiveDirectory)) {
28
+ fs.mkdirSync(this.archiveDirectory, { recursive: true });
29
+ }
30
+ }
31
+ /**
32
+ * PULL Strategy: Remote -> Local
33
+ * Based on spec 5.2 PULL Strategy table
34
+ */
35
+ async pull(workflowId, filename, status) {
36
+ // Mark sync in progress to prevent race conditions
37
+ this.watcher.markSyncInProgress(workflowId);
38
+ this.watcher.pauseObservation(workflowId);
39
+ try {
40
+ switch (status) {
41
+ case WorkflowSyncStatus.EXIST_ONLY_REMOTELY:
42
+ // Download Remote JSON -> Write to disk
43
+ await this.executePull(workflowId, filename);
44
+ // Initialize lastSyncedHash via finalizeSync
45
+ await this.watcher.finalizeSync(workflowId);
46
+ break;
47
+ case WorkflowSyncStatus.MODIFIED_REMOTELY:
48
+ // Download Remote JSON -> Overwrite local file
49
+ await this.executePull(workflowId, filename);
50
+ // Update lastSyncedHash via finalizeSync
51
+ await this.watcher.finalizeSync(workflowId);
52
+ break;
53
+ case WorkflowSyncStatus.DELETED_REMOTELY:
54
+ // Move local file to archive
55
+ await this.archive(filename);
56
+ // Remove from state (handled by watcher after observation resumes)
57
+ // Watcher will detect file deletion and update status
58
+ break;
59
+ case WorkflowSyncStatus.CONFLICT:
60
+ // Halt - trigger conflict resolution
61
+ throw new Error(`Conflict detected for workflow ${workflowId}. Use resolveConflict instead.`);
62
+ case WorkflowSyncStatus.DELETED_LOCALLY:
63
+ // No action per spec
64
+ break;
65
+ case WorkflowSyncStatus.EXIST_ONLY_LOCALLY:
66
+ case WorkflowSyncStatus.IN_SYNC:
67
+ case WorkflowSyncStatus.MODIFIED_LOCALLY:
68
+ // No action per spec
69
+ break;
70
+ default:
71
+ console.warn(`[SyncEngine] Unhandled status ${status} for PULL operation`);
72
+ break;
73
+ }
74
+ }
75
+ finally {
76
+ this.watcher.markSyncComplete(workflowId);
77
+ this.watcher.resumeObservation(workflowId);
78
+ }
79
+ }
80
+ /**
81
+ * PUSH Strategy: Local -> Remote
82
+ * Based on spec 5.3 PUSH Strategy table
83
+ */
84
+ async push(filename, workflowId, status) {
85
+ if (workflowId) {
86
+ this.watcher.markSyncInProgress(workflowId);
87
+ this.watcher.pauseObservation(workflowId);
88
+ }
89
+ try {
90
+ // If no workflowId, treat as EXIST_ONLY_LOCALLY
91
+ if (!workflowId || status === WorkflowSyncStatus.EXIST_ONLY_LOCALLY) {
92
+ // POST to API (Create)
93
+ const newWorkflowId = await this.executeCreate(filename);
94
+ // Initialize lastSyncedHash via finalizeSync
95
+ await this.watcher.finalizeSync(newWorkflowId);
96
+ return newWorkflowId;
97
+ }
98
+ // With workflowId and status
99
+ switch (status) {
100
+ case WorkflowSyncStatus.MODIFIED_LOCALLY:
101
+ // PUT to API (Update)
102
+ await this.executeUpdate(workflowId, filename);
103
+ // Update lastSyncedHash via finalizeSync
104
+ await this.watcher.finalizeSync(workflowId);
105
+ return workflowId;
106
+ case WorkflowSyncStatus.DELETED_LOCALLY:
107
+ // Step 1: Archive Remote to _archive/
108
+ await this.archive(filename);
109
+ // Step 2: Trigger Deletion Validation (caller should handle)
110
+ // Note: Actual API deletion happens in ResolutionManager
111
+ throw new Error(`Local deletion detected for workflow ${workflowId}. Use confirmDeletion instead.`);
112
+ case WorkflowSyncStatus.CONFLICT:
113
+ // Halt - trigger conflict resolution
114
+ throw new Error(`Conflict detected for workflow ${workflowId}. Use resolveConflict instead.`);
115
+ case WorkflowSyncStatus.EXIST_ONLY_REMOTELY:
116
+ case WorkflowSyncStatus.IN_SYNC:
117
+ case WorkflowSyncStatus.MODIFIED_REMOTELY:
118
+ case WorkflowSyncStatus.DELETED_REMOTELY:
119
+ // No action per spec
120
+ return workflowId;
121
+ default:
122
+ console.warn(`[SyncEngine] Unhandled status ${status} for PUSH operation`);
123
+ return workflowId;
124
+ }
125
+ }
126
+ finally {
127
+ if (workflowId) {
128
+ this.watcher.markSyncComplete(workflowId);
129
+ this.watcher.resumeObservation(workflowId);
130
+ }
131
+ }
132
+ }
133
+ /**
134
+ * Force PULL - overwrite local with remote (for conflict resolution)
135
+ */
136
+ async forcePull(workflowId, filename) {
137
+ this.watcher.markSyncInProgress(workflowId);
138
+ this.watcher.pauseObservation(workflowId);
139
+ try {
140
+ await this.executePull(workflowId, filename);
141
+ await this.watcher.finalizeSync(workflowId);
142
+ }
143
+ finally {
144
+ this.watcher.markSyncComplete(workflowId);
145
+ this.watcher.resumeObservation(workflowId);
146
+ }
147
+ }
148
+ /**
149
+ * Force PUSH - overwrite remote with local (for conflict resolution and restoration)
150
+ * If workflow doesn't exist on remote, creates it
151
+ */
152
+ async forcePush(workflowId, filename) {
153
+ this.watcher.markSyncInProgress(workflowId);
154
+ this.watcher.pauseObservation(workflowId);
155
+ let finalWorkflowId = workflowId;
156
+ try {
157
+ // Try to update first
158
+ try {
159
+ await this.executeUpdate(workflowId, filename);
160
+ }
161
+ catch (error) {
162
+ // If update fails with 404, create the workflow instead
163
+ if (error.response?.status === 404 || error.message?.includes('404') || error.message?.includes('Not Found')) {
164
+ console.log(`[SyncEngine] Workflow ${workflowId} not found, creating new workflow`);
165
+ const newWorkflowId = await this.executeCreate(filename);
166
+ // Migrate state from old ID to new ID
167
+ if (newWorkflowId !== workflowId) {
168
+ await this.watcher.updateWorkflowId(workflowId, newWorkflowId);
169
+ finalWorkflowId = newWorkflowId;
170
+ }
171
+ }
172
+ else {
173
+ throw error;
174
+ }
175
+ }
176
+ await this.watcher.finalizeSync(finalWorkflowId);
177
+ return finalWorkflowId;
178
+ }
179
+ finally {
180
+ this.watcher.markSyncComplete(finalWorkflowId);
181
+ this.watcher.resumeObservation(finalWorkflowId);
182
+ }
183
+ }
184
+ /**
185
+ * Delete remote workflow (for deletion validation)
186
+ * Note: The Watcher already archived the remote content when it detected the local deletion
187
+ */
188
+ async deleteRemote(workflowId, filename) {
189
+ this.watcher.markSyncInProgress(workflowId);
190
+ this.watcher.pauseObservation(workflowId);
191
+ try {
192
+ // Delete from API
193
+ await this.client.deleteWorkflow(workflowId);
194
+ // Archive local file if it still exists (edge case - shouldn't happen for DELETED_LOCALLY)
195
+ await this.archive(filename);
196
+ // Note: State removal will be handled by caller (ResolutionManager)
197
+ }
198
+ finally {
199
+ this.watcher.markSyncComplete(workflowId);
200
+ this.watcher.resumeObservation(workflowId);
201
+ }
202
+ }
203
+ /**
204
+ * Restore from archive (for deletion validation)
205
+ * Moves the file from archive back to workflows directory
206
+ * Then DELETES the archive file (no need to keep it after restoration)
207
+ */
208
+ async restoreFromArchive(filename) {
209
+ const archiveFiles = fs.readdirSync(this.archiveDirectory);
210
+ const matchingArchives = archiveFiles.filter(f => f.includes(filename));
211
+ if (matchingArchives.length === 0) {
212
+ return false;
213
+ }
214
+ // Get most recent archive
215
+ const mostRecent = matchingArchives.sort().reverse()[0];
216
+ const archivePath = path.join(this.archiveDirectory, mostRecent);
217
+ const targetPath = path.join(this.directory, filename);
218
+ // Read content from archive
219
+ const content = fs.readFileSync(archivePath, 'utf-8');
220
+ // Write to target location
221
+ fs.writeFileSync(targetPath, content);
222
+ // Delete the archive file (no need to keep it after restoration)
223
+ fs.unlinkSync(archivePath);
224
+ return true;
225
+ }
226
+ async executePull(workflowId, filename) {
227
+ const fullWf = await this.client.getWorkflow(workflowId);
228
+ if (!fullWf) {
229
+ // Workflow might have been deleted (DELETED_REMOTELY case)
230
+ // Check if local file exists - if so, archive it
231
+ const filePath = path.join(this.directory, filename);
232
+ if (fs.existsSync(filePath)) {
233
+ await this.archive(filename);
234
+ // Don't throw - archiving is the expected behavior for DELETED_REMOTELY
235
+ return;
236
+ }
237
+ throw new Error(`Remote workflow ${workflowId} not found during pull`);
238
+ }
239
+ const clean = WorkflowSanitizer.cleanForStorage(fullWf);
240
+ const filePath = path.join(this.directory, filename);
241
+ fs.writeFileSync(filePath, JSON.stringify(clean, null, 2));
242
+ // Update Watcher's remote hash cache since we just fetched the workflow
243
+ // This ensures finalizeSync has the remote hash
244
+ const hash = HashUtils.computeHash(clean);
245
+ this.watcher.setRemoteHash(workflowId, hash);
246
+ }
247
+ async executeUpdate(workflowId, filename) {
248
+ const filePath = path.join(this.directory, filename);
249
+ const localWf = this.readJsonFile(filePath);
250
+ if (!localWf) {
251
+ throw new Error('Local file not found during push');
252
+ }
253
+ const payload = WorkflowSanitizer.cleanForPush(localWf);
254
+ const updatedWf = await this.client.updateWorkflow(workflowId, payload);
255
+ if (!updatedWf) {
256
+ throw new Error('Failed to update remote workflow');
257
+ }
258
+ // CRITICAL: Write the API response back to local file to ensure consistency
259
+ // This ensures local and remote have identical content after push
260
+ const clean = WorkflowSanitizer.cleanForStorage(updatedWf);
261
+ fs.writeFileSync(filePath, JSON.stringify(clean, null, 2));
262
+ // Update Watcher's remote hash cache with the updated workflow
263
+ const hash = HashUtils.computeHash(clean);
264
+ this.watcher.setRemoteHash(workflowId, hash);
265
+ }
266
+ async executeCreate(filename) {
267
+ const filePath = path.join(this.directory, filename);
268
+ const localWf = this.readJsonFile(filePath);
269
+ if (!localWf) {
270
+ throw new Error('Local file not found during creation');
271
+ }
272
+ const payload = WorkflowSanitizer.cleanForPush(localWf);
273
+ if (!payload.name) {
274
+ payload.name = path.parse(filename).name;
275
+ }
276
+ const newWf = await this.client.createWorkflow(payload);
277
+ if (!newWf || !newWf.id) {
278
+ throw new Error('Failed to create remote workflow');
279
+ }
280
+ // Update local file with new ID
281
+ localWf.id = newWf.id;
282
+ fs.writeFileSync(filePath, JSON.stringify(localWf, null, 2));
283
+ return newWf.id;
284
+ }
285
+ async archive(filename) {
286
+ const filePath = path.join(this.directory, filename);
287
+ if (fs.existsSync(filePath)) {
288
+ const archivePath = path.join(this.archiveDirectory, `${Date.now()}_${filename}`);
289
+ fs.renameSync(filePath, archivePath);
290
+ }
291
+ }
292
+ readJsonFile(filePath) {
293
+ try {
294
+ return JSON.parse(fs.readFileSync(filePath, 'utf8'));
295
+ }
296
+ catch {
297
+ return null;
298
+ }
299
+ }
300
+ }
301
+ //# sourceMappingURL=sync-engine.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sync-engine.js","sourceRoot":"","sources":["../../src/services/sync-engine.ts"],"names":[],"mappings":"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"}
@@ -4,98 +4,34 @@ import { ISyncConfig, IWorkflowStatus } from '../types.js';
4
4
  export declare class SyncManager extends EventEmitter {
5
5
  private client;
6
6
  private config;
7
- private fileToIdMap;
8
- private selfWrittenCache;
9
- private isWriting;
10
- private pendingDeletions;
11
- private watcher;
12
- private pollInterval;
13
7
  private stateManager;
14
- private trashService;
8
+ private watcher;
9
+ private syncEngine;
10
+ private resolutionManager;
15
11
  constructor(client: N8nApiClient, config: ISyncConfig);
16
- /**
17
- * Get the path to the instance configuration file
18
- */
19
- private getInstanceConfigPath;
20
- /**
21
- * Load instance configuration from disk
22
- */
23
- private loadInstanceConfig;
24
- /**
25
- * Save instance configuration to disk
26
- */
27
- private saveInstanceConfig;
28
- /**
29
- * Ensure instance identifier is set and persistent
30
- */
31
- private ensureInstanceIdentifier;
32
- private initializeInstanceIdentifier;
33
- getInstanceDirectory(): string;
34
- private getFilePath;
35
- private safeName;
36
- private normalizeContent;
37
- private markAsSelfWritten;
38
- private isSelfWritten;
39
- loadRemoteState(): Promise<void>;
40
- /**
41
- * Retrieves the status of all workflows (local and remote)
42
- */
12
+ private ensureInitialized;
43
13
  getWorkflowsStatus(): Promise<IWorkflowStatus[]>;
44
- private formatSummary;
45
- /**
46
- * Scans n8n instance and updates local files (Downstream Sync)
47
- */
48
14
  syncDown(): Promise<void>;
49
- /**
50
- * Identifies and handles workflows deleted on n8n but still present locally and tracked in state
51
- */
52
- private processRemoteDeletions;
53
- /**
54
- * Scans n8n instance and updates local files with conflict resolution
55
- */
56
- syncDownWithConflictResolution(): Promise<void>;
57
- /**
58
- * Pulls a single workflow by ID and writes to filename
59
- * @param force If true, overwrites local changes without checking for conflicts
60
- */
61
- pullWorkflow(filename: string, id: string, force?: boolean): Promise<void>;
62
- /**
63
- * Pulls a single workflow with conflict resolution
64
- * @returns 'updated' if file was updated, 'skipped' if no change or conflict, 'new' if file was created
65
- */
66
- pullWorkflowWithConflictResolution(filename: string, id: string, remoteUpdatedAt?: string): Promise<'updated' | 'skipped' | 'new' | 'up-to-date' | 'conflict'>;
67
- /**
68
- * Writes file to disk only if changed
69
- */
70
- private writeLocalFile;
71
- private shouldIgnore;
72
- /**
73
- * Uploads local files that don't exist remotely (Upstream Sync - Init)
74
- */
75
- syncUpMissing(): Promise<void>;
76
- /**
77
- * Full Upstream Sync: Updates existing and Creates new.
78
- */
79
15
  syncUp(): Promise<void>;
16
+ startWatch(): Promise<void>;
80
17
  /**
81
- * Handles local file deletion (detected by watcher)
82
- */
83
- handleLocalFileDeletion(filePath: string): Promise<void>;
84
- /**
85
- * Actually deletes the remote workflow and cleans up local state
86
- */
87
- deleteRemoteWorkflow(id: string, filename: string): Promise<boolean>;
88
- /**
89
- * Restore a deleted local file from remote
18
+ * Create or update the n8n-as-code-instance.json file
19
+ * This file marks the workspace as initialized and stores the instance identifier
90
20
  */
91
- restoreLocalFile(id: string, filename: string): Promise<boolean>;
21
+ private ensureInstanceConfigFile;
92
22
  /**
93
- * Handle FS watcher events
23
+ * Handle automatic synchronization based on status changes
24
+ * Only triggered in auto mode
94
25
  */
95
- handleLocalFileChange(filePath: string, silent?: boolean): Promise<'updated' | 'created' | 'up-to-date' | 'conflict' | 'skipped'>;
96
- private readLocalFile;
97
- private readRawFile;
98
- startWatch(): Promise<void>;
26
+ private handleAutoSync;
99
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>;
100
36
  }
101
37
  //# sourceMappingURL=sync-manager.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"sync-manager.d.ts","sourceRoot":"","sources":["../../src/services/sync-manager.ts"],"names":[],"mappings":"AAEA,OAAO,YAAY,MAAM,QAAQ,CAAC;AAGlC,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAKnD,OAAO,EAAE,WAAW,EAAiC,eAAe,EAAE,MAAM,aAAa,CAAC;AAO1F,qBAAa,WAAY,SAAQ,YAAY;IACzC,OAAO,CAAC,MAAM,CAAe;IAC7B,OAAO,CAAC,MAAM,CAAc;IAG5B,OAAO,CAAC,WAAW,CAAkC;IAErD,OAAO,CAAC,gBAAgB,CAAkC;IAE1D,OAAO,CAAC,SAAS,CAAqB;IAEtC,OAAO,CAAC,gBAAgB,CAAqB;IAE7C,OAAO,CAAC,OAAO,CAAmC;IAClD,OAAO,CAAC,YAAY,CAA+B;IACnD,OAAO,CAAC,YAAY,CAA6B;IACjD,OAAO,CAAC,YAAY,CAA6B;gBAErC,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,WAAW;IAWrD;;OAEG;IACH,OAAO,CAAC,qBAAqB;IAI7B;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAa1B;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAK1B;;OAEG;YACW,wBAAwB;YA+BxB,4BAA4B;IAkBnC,oBAAoB,IAAI,MAAM;IAarC,OAAO,CAAC,WAAW;IAInB,OAAO,CAAC,QAAQ;IAIhB,OAAO,CAAC,gBAAgB;IAIxB,OAAO,CAAC,iBAAiB;IAIzB,OAAO,CAAC,aAAa;IAOf,eAAe;IAarB;;OAEG;IACG,kBAAkB,IAAI,OAAO,CAAC,eAAe,EAAE,CAAC;IA8DtD,OAAO,CAAC,aAAa;IASrB;;OAEG;IACG,QAAQ;IAmCd;;OAEG;YACW,sBAAsB;IAoCpC;;OAEG;IACG,8BAA8B;IAkCpC;;;OAGG;IACG,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,KAAK,GAAE,OAAe;IA4BvE;;;OAGG;IACG,kCAAkC,CAAC,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,eAAe,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,GAAG,SAAS,GAAG,KAAK,GAAG,YAAY,GAAG,UAAU,CAAC;IA8CpK;;OAEG;YACW,cAAc;IAqC5B,OAAO,CAAC,YAAY;IASpB;;OAEG;IACG,aAAa;IAenB;;OAEG;IACG,MAAM;IAqBZ;;OAEG;IACG,uBAAuB,CAAC,QAAQ,EAAE,MAAM;IAiB9C;;OAEG;IACG,oBAAoB,CAAC,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAuB1E;;OAEG;IACG,gBAAgB,CAAC,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAkBtE;;OAEG;IACG,qBAAqB,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,GAAE,OAAe,GAAG,OAAO,CAAC,SAAS,GAAG,SAAS,GAAG,YAAY,GAAG,UAAU,GAAG,SAAS,CAAC;IA0F9I,OAAO,CAAC,aAAa;IAIrB,OAAO,CAAC,WAAW;IAIb,UAAU;IAwChB,SAAS;CAWZ"}
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"}