@n8n-as-code/core 0.2.0 → 0.3.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/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/dist/services/hash-utils.d.ts +22 -0
- package/dist/services/hash-utils.d.ts.map +1 -0
- package/dist/services/hash-utils.js +31 -0
- package/dist/services/hash-utils.js.map +1 -0
- package/dist/services/n8n-api-client.d.ts.map +1 -1
- package/dist/services/n8n-api-client.js +44 -50
- package/dist/services/n8n-api-client.js.map +1 -1
- package/dist/services/resolution-manager.d.ts +73 -0
- package/dist/services/resolution-manager.d.ts.map +1 -0
- package/dist/services/resolution-manager.js +149 -0
- package/dist/services/resolution-manager.js.map +1 -0
- package/dist/services/state-manager.d.ts +18 -17
- package/dist/services/state-manager.d.ts.map +1 -1
- package/dist/services/state-manager.js +22 -53
- package/dist/services/state-manager.js.map +1 -1
- package/dist/services/sync-engine.d.ts +57 -0
- package/dist/services/sync-engine.d.ts.map +1 -0
- package/dist/services/sync-engine.js +301 -0
- package/dist/services/sync-engine.js.map +1 -0
- package/dist/services/sync-manager.d.ts +19 -83
- package/dist/services/sync-manager.d.ts.map +1 -1
- package/dist/services/sync-manager.js +208 -620
- package/dist/services/sync-manager.js.map +1 -1
- package/dist/services/watcher.d.ts +121 -0
- package/dist/services/watcher.d.ts.map +1 -0
- package/dist/services/watcher.js +609 -0
- package/dist/services/watcher.js.map +1 -0
- package/dist/services/workflow-sanitizer.d.ts +9 -4
- package/dist/services/workflow-sanitizer.d.ts.map +1 -1
- package/dist/services/workflow-sanitizer.js +55 -35
- package/dist/services/workflow-sanitizer.js.map +1 -1
- package/dist/types.d.ts +10 -5
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +8 -5
- package/dist/types.js.map +1 -1
- package/package.json +4 -2
|
@@ -0,0 +1,609 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import EventEmitter from 'events';
|
|
4
|
+
import * as chokidar from 'chokidar';
|
|
5
|
+
import { WorkflowSanitizer } from './workflow-sanitizer.js';
|
|
6
|
+
import { HashUtils } from './hash-utils.js';
|
|
7
|
+
import { WorkflowSyncStatus } from '../types.js';
|
|
8
|
+
/**
|
|
9
|
+
* Watcher - State Observation Component
|
|
10
|
+
*
|
|
11
|
+
* Responsibilities:
|
|
12
|
+
* 1. File System Watch with debounce
|
|
13
|
+
* 2. Remote Polling with lightweight strategy
|
|
14
|
+
* 3. Canonical Hashing (SHA-256 of sorted JSON)
|
|
15
|
+
* 4. Status Matrix Calculation (3-way comparison)
|
|
16
|
+
* 5. State Persistence (only component that writes to .n8n-state.json)
|
|
17
|
+
*
|
|
18
|
+
* Never performs synchronization actions - only observes reality.
|
|
19
|
+
*/
|
|
20
|
+
export class Watcher extends EventEmitter {
|
|
21
|
+
watcher = null;
|
|
22
|
+
pollInterval = null;
|
|
23
|
+
client;
|
|
24
|
+
directory;
|
|
25
|
+
pollIntervalMs;
|
|
26
|
+
syncInactive;
|
|
27
|
+
ignoredTags;
|
|
28
|
+
stateFilePath;
|
|
29
|
+
isConnected = true;
|
|
30
|
+
isInitializing = false;
|
|
31
|
+
// Internal state tracking
|
|
32
|
+
localHashes = new Map(); // filename -> hash
|
|
33
|
+
remoteHashes = new Map(); // workflowId -> hash
|
|
34
|
+
fileToIdMap = new Map(); // filename -> workflowId
|
|
35
|
+
idToFileMap = new Map(); // workflowId -> filename
|
|
36
|
+
lastKnownStatuses = new Map(); // workflowId or filename -> status
|
|
37
|
+
// Concurrency control
|
|
38
|
+
isPaused = new Set(); // IDs for which observation is paused
|
|
39
|
+
syncInProgress = new Set(); // IDs currently being synced
|
|
40
|
+
// Lightweight polling cache
|
|
41
|
+
remoteTimestamps = new Map(); // workflowId -> updatedAt
|
|
42
|
+
constructor(client, options) {
|
|
43
|
+
super();
|
|
44
|
+
this.client = client;
|
|
45
|
+
this.directory = options.directory;
|
|
46
|
+
this.pollIntervalMs = options.pollIntervalMs;
|
|
47
|
+
this.syncInactive = options.syncInactive;
|
|
48
|
+
this.ignoredTags = options.ignoredTags;
|
|
49
|
+
this.stateFilePath = path.join(this.directory, '.n8n-state.json');
|
|
50
|
+
}
|
|
51
|
+
async start() {
|
|
52
|
+
if (this.watcher || this.pollInterval)
|
|
53
|
+
return;
|
|
54
|
+
this.isInitializing = true;
|
|
55
|
+
// Initial scan - throw error if connection fails on startup
|
|
56
|
+
try {
|
|
57
|
+
await this.refreshRemoteState();
|
|
58
|
+
}
|
|
59
|
+
catch (error) {
|
|
60
|
+
// Check if it's a connection error
|
|
61
|
+
const isConnectionError = error.code === 'ECONNREFUSED' ||
|
|
62
|
+
error.code === 'ENOTFOUND' ||
|
|
63
|
+
error.code === 'ETIMEDOUT' ||
|
|
64
|
+
error.message?.includes('fetch failed') ||
|
|
65
|
+
error.message?.includes('ECONNREFUSED') ||
|
|
66
|
+
error.message?.includes('ENOTFOUND') ||
|
|
67
|
+
error.cause?.code === 'ECONNREFUSED';
|
|
68
|
+
if (isConnectionError) {
|
|
69
|
+
this.isInitializing = false;
|
|
70
|
+
// On startup, throw the error to prevent initialization
|
|
71
|
+
throw new Error('Cannot connect to n8n instance. Please check if n8n is running and the host URL is correct.');
|
|
72
|
+
}
|
|
73
|
+
// For other errors, re-throw
|
|
74
|
+
this.isInitializing = false;
|
|
75
|
+
throw error;
|
|
76
|
+
}
|
|
77
|
+
await this.refreshLocalState();
|
|
78
|
+
this.isInitializing = false;
|
|
79
|
+
// Local Watch with debounce
|
|
80
|
+
this.watcher = chokidar.watch(this.directory, {
|
|
81
|
+
ignored: [
|
|
82
|
+
/(^|[\/\\])\../, // Hidden files
|
|
83
|
+
'**/_archive/**', // Archive folder (strictly ignored)
|
|
84
|
+
'**/.n8n-state.json' // State file
|
|
85
|
+
],
|
|
86
|
+
persistent: true,
|
|
87
|
+
ignoreInitial: true,
|
|
88
|
+
awaitWriteFinish: { stabilityThreshold: 500, pollInterval: 100 } // 500ms debounce
|
|
89
|
+
});
|
|
90
|
+
this.watcher
|
|
91
|
+
.on('add', (p) => this.onLocalChange(p))
|
|
92
|
+
.on('change', (p) => this.onLocalChange(p))
|
|
93
|
+
.on('unlink', (p) => this.onLocalDelete(p));
|
|
94
|
+
// Remote Poll
|
|
95
|
+
if (this.pollIntervalMs > 0) {
|
|
96
|
+
this.pollInterval = setInterval(() => this.refreshRemoteState(), this.pollIntervalMs);
|
|
97
|
+
}
|
|
98
|
+
this.emit('ready');
|
|
99
|
+
}
|
|
100
|
+
stop() {
|
|
101
|
+
if (this.watcher) {
|
|
102
|
+
this.watcher.close();
|
|
103
|
+
this.watcher = null;
|
|
104
|
+
}
|
|
105
|
+
if (this.pollInterval) {
|
|
106
|
+
clearInterval(this.pollInterval);
|
|
107
|
+
this.pollInterval = null;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Pause observation for a workflow during sync operations
|
|
112
|
+
*/
|
|
113
|
+
pauseObservation(workflowId) {
|
|
114
|
+
this.isPaused.add(workflowId);
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Resume observation after sync operations
|
|
118
|
+
*/
|
|
119
|
+
resumeObservation(workflowId) {
|
|
120
|
+
this.isPaused.delete(workflowId);
|
|
121
|
+
// Force refresh to get latest state
|
|
122
|
+
this.refreshRemoteState();
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Mark a workflow as being synced (prevents race conditions)
|
|
126
|
+
*/
|
|
127
|
+
markSyncInProgress(workflowId) {
|
|
128
|
+
this.syncInProgress.add(workflowId);
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Mark a workflow as no longer being synced
|
|
132
|
+
*/
|
|
133
|
+
markSyncComplete(workflowId) {
|
|
134
|
+
this.syncInProgress.delete(workflowId);
|
|
135
|
+
}
|
|
136
|
+
async onLocalChange(filePath) {
|
|
137
|
+
const filename = path.basename(filePath);
|
|
138
|
+
if (!filename.endsWith('.json'))
|
|
139
|
+
return;
|
|
140
|
+
const content = this.readJsonFile(filePath);
|
|
141
|
+
if (!content)
|
|
142
|
+
return;
|
|
143
|
+
const workflowId = content.id || this.fileToIdMap.get(filename);
|
|
144
|
+
if (workflowId && (this.isPaused.has(workflowId) || this.syncInProgress.has(workflowId))) {
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
// IMPORTANT: Hash is calculated on the SANITIZED version
|
|
148
|
+
// This means versionId, versionCounter, pinData, etc. are ignored
|
|
149
|
+
// The file on disk can contain these fields, but they won't affect the hash
|
|
150
|
+
const clean = WorkflowSanitizer.cleanForStorage(content);
|
|
151
|
+
const hash = this.computeHash(clean);
|
|
152
|
+
this.localHashes.set(filename, hash);
|
|
153
|
+
if (workflowId) {
|
|
154
|
+
this.fileToIdMap.set(filename, workflowId);
|
|
155
|
+
this.idToFileMap.set(workflowId, filename);
|
|
156
|
+
}
|
|
157
|
+
this.broadcastStatus(filename, workflowId);
|
|
158
|
+
}
|
|
159
|
+
async onLocalDelete(filePath) {
|
|
160
|
+
const filename = path.basename(filePath);
|
|
161
|
+
let workflowId = this.fileToIdMap.get(filename);
|
|
162
|
+
// If workflowId not found via filename mapping, try to find it via state
|
|
163
|
+
if (!workflowId) {
|
|
164
|
+
const state = this.loadState();
|
|
165
|
+
for (const [id, stateData] of Object.entries(state.workflows)) {
|
|
166
|
+
const mappedFilename = this.idToFileMap.get(id);
|
|
167
|
+
if (mappedFilename === filename) {
|
|
168
|
+
workflowId = id;
|
|
169
|
+
break;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
if (workflowId && (this.isPaused.has(workflowId) || this.syncInProgress.has(workflowId))) {
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
// CRITICAL: Per spec 5.3 DELETED_LOCALLY - Archive Remote to _archive/ IMMEDIATELY
|
|
177
|
+
// This happens BEFORE user confirmation, to ensure we have a backup
|
|
178
|
+
if (workflowId) {
|
|
179
|
+
const remoteHash = this.remoteHashes.get(workflowId);
|
|
180
|
+
const lastSyncedHash = this.getLastSyncedHash(workflowId);
|
|
181
|
+
// Only archive if remote exists and matches last synced (true local deletion)
|
|
182
|
+
if (remoteHash && remoteHash === lastSyncedHash) {
|
|
183
|
+
try {
|
|
184
|
+
// Fetch remote workflow content
|
|
185
|
+
const remoteWorkflow = await this.client.getWorkflow(workflowId);
|
|
186
|
+
if (remoteWorkflow) {
|
|
187
|
+
// Create archive directory if it doesn't exist
|
|
188
|
+
const archiveDir = path.join(this.directory, '.archive');
|
|
189
|
+
if (!fs.existsSync(archiveDir)) {
|
|
190
|
+
fs.mkdirSync(archiveDir, { recursive: true });
|
|
191
|
+
}
|
|
192
|
+
// Save to archive with timestamp
|
|
193
|
+
const clean = WorkflowSanitizer.cleanForStorage(remoteWorkflow);
|
|
194
|
+
const archivePath = path.join(archiveDir, `${Date.now()}_${filename}`);
|
|
195
|
+
fs.writeFileSync(archivePath, JSON.stringify(clean, null, 2));
|
|
196
|
+
console.log(`[Watcher] Archived remote workflow to: ${archivePath}`);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
catch (error) {
|
|
200
|
+
console.warn(`[Watcher] Failed to archive remote workflow ${workflowId}:`, error);
|
|
201
|
+
// Continue anyway - deletion detection should still work
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
this.localHashes.delete(filename);
|
|
206
|
+
this.broadcastStatus(filename, workflowId);
|
|
207
|
+
}
|
|
208
|
+
async refreshLocalState() {
|
|
209
|
+
if (!fs.existsSync(this.directory)) {
|
|
210
|
+
console.log(`[DEBUG] refreshLocalState: Directory missing: ${this.directory}`);
|
|
211
|
+
// Clear all local hashes since directory doesn't exist
|
|
212
|
+
this.localHashes.clear();
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
const files = fs.readdirSync(this.directory).filter(f => f.endsWith('.json') && !f.startsWith('.'));
|
|
216
|
+
const currentFiles = new Set(files);
|
|
217
|
+
// Remove entries for files that no longer exist
|
|
218
|
+
for (const filename of this.localHashes.keys()) {
|
|
219
|
+
if (!currentFiles.has(filename)) {
|
|
220
|
+
this.localHashes.delete(filename);
|
|
221
|
+
const workflowId = this.fileToIdMap.get(filename);
|
|
222
|
+
if (workflowId) {
|
|
223
|
+
// Broadcast status change for deleted file
|
|
224
|
+
this.broadcastStatus(filename, workflowId);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
// Add/update entries for existing files
|
|
229
|
+
for (const filename of files) {
|
|
230
|
+
const filePath = path.join(this.directory, filename);
|
|
231
|
+
const content = this.readJsonFile(filePath);
|
|
232
|
+
if (content) {
|
|
233
|
+
const clean = WorkflowSanitizer.cleanForStorage(content);
|
|
234
|
+
const hash = this.computeHash(clean);
|
|
235
|
+
this.localHashes.set(filename, hash);
|
|
236
|
+
if (content.id) {
|
|
237
|
+
this.fileToIdMap.set(filename, content.id);
|
|
238
|
+
this.idToFileMap.set(content.id, filename);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* Lightweight polling strategy:
|
|
245
|
+
* 1. Fetch only IDs and updatedAt timestamps
|
|
246
|
+
* 2. Compare with cached timestamps
|
|
247
|
+
* 3. Fetch full content only if timestamp changed
|
|
248
|
+
*/
|
|
249
|
+
async refreshRemoteState() {
|
|
250
|
+
try {
|
|
251
|
+
const remoteWorkflows = await this.client.getAllWorkflows();
|
|
252
|
+
this.isConnected = true;
|
|
253
|
+
const currentRemoteIds = new Set();
|
|
254
|
+
for (const wf of remoteWorkflows) {
|
|
255
|
+
if (this.shouldIgnore(wf))
|
|
256
|
+
continue;
|
|
257
|
+
if (this.isPaused.has(wf.id) || this.syncInProgress.has(wf.id))
|
|
258
|
+
continue;
|
|
259
|
+
currentRemoteIds.add(wf.id);
|
|
260
|
+
const filename = `${this.safeName(wf.name)}.json`;
|
|
261
|
+
this.idToFileMap.set(wf.id, filename);
|
|
262
|
+
this.fileToIdMap.set(filename, wf.id);
|
|
263
|
+
// Check if we need to fetch full content
|
|
264
|
+
const cachedTimestamp = this.remoteTimestamps.get(wf.id);
|
|
265
|
+
const needsFullFetch = !cachedTimestamp ||
|
|
266
|
+
(wf.updatedAt && wf.updatedAt !== cachedTimestamp);
|
|
267
|
+
if (needsFullFetch) {
|
|
268
|
+
try {
|
|
269
|
+
const fullWf = await this.client.getWorkflow(wf.id);
|
|
270
|
+
if (fullWf) {
|
|
271
|
+
const clean = WorkflowSanitizer.cleanForStorage(fullWf);
|
|
272
|
+
const hash = this.computeHash(clean);
|
|
273
|
+
this.remoteHashes.set(wf.id, hash);
|
|
274
|
+
if (wf.updatedAt) {
|
|
275
|
+
this.remoteTimestamps.set(wf.id, wf.updatedAt);
|
|
276
|
+
}
|
|
277
|
+
this.broadcastStatus(filename, wf.id);
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
catch (e) {
|
|
281
|
+
console.warn(`[Watcher] Could not fetch workflow ${wf.id}:`, e);
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
else {
|
|
285
|
+
// Timestamp unchanged, use cached hash
|
|
286
|
+
const cachedHash = this.remoteHashes.get(wf.id);
|
|
287
|
+
if (cachedHash) {
|
|
288
|
+
this.broadcastStatus(filename, wf.id);
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
// Prune remoteHashes for deleted workflows
|
|
293
|
+
for (const id of this.remoteHashes.keys()) {
|
|
294
|
+
if (!currentRemoteIds.has(id)) {
|
|
295
|
+
this.remoteHashes.delete(id);
|
|
296
|
+
this.remoteTimestamps.delete(id);
|
|
297
|
+
const filename = this.idToFileMap.get(id);
|
|
298
|
+
if (filename)
|
|
299
|
+
this.broadcastStatus(filename, id);
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
catch (error) {
|
|
304
|
+
// Check if it's a connection error
|
|
305
|
+
const isConnectionError = error.code === 'ECONNREFUSED' ||
|
|
306
|
+
error.code === 'ENOTFOUND' ||
|
|
307
|
+
error.code === 'ETIMEDOUT' ||
|
|
308
|
+
error.message?.includes('fetch failed') ||
|
|
309
|
+
error.message?.includes('ECONNREFUSED') ||
|
|
310
|
+
error.message?.includes('ENOTFOUND') ||
|
|
311
|
+
error.cause?.code === 'ECONNREFUSED';
|
|
312
|
+
if (isConnectionError) {
|
|
313
|
+
this.isConnected = false;
|
|
314
|
+
// Stop polling to avoid spamming errors
|
|
315
|
+
if (this.pollInterval) {
|
|
316
|
+
clearInterval(this.pollInterval);
|
|
317
|
+
this.pollInterval = null;
|
|
318
|
+
}
|
|
319
|
+
// Emit a specific connection error
|
|
320
|
+
this.emit('connection-lost', new Error('Lost connection to n8n instance. Please check if n8n is running.'));
|
|
321
|
+
}
|
|
322
|
+
else {
|
|
323
|
+
// For other errors, just emit the error
|
|
324
|
+
this.emit('error', error);
|
|
325
|
+
}
|
|
326
|
+
// Re-throw so that start() can catch it on initial call
|
|
327
|
+
throw error;
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
/**
|
|
331
|
+
* Finalize sync - update base state after successful sync operation
|
|
332
|
+
* Called by SyncEngine after PULL/PUSH completes
|
|
333
|
+
*/
|
|
334
|
+
async finalizeSync(workflowId) {
|
|
335
|
+
let filename = this.idToFileMap.get(workflowId);
|
|
336
|
+
// If workflow not tracked yet (first sync of local-only workflow),
|
|
337
|
+
// scan directory to find the file with this ID
|
|
338
|
+
if (!filename) {
|
|
339
|
+
const files = fs.readdirSync(this.directory).filter(f => f.endsWith('.json') && !f.startsWith('.'));
|
|
340
|
+
for (const file of files) {
|
|
341
|
+
const filePath = path.join(this.directory, file);
|
|
342
|
+
const content = this.readJsonFile(filePath);
|
|
343
|
+
if (content?.id === workflowId) {
|
|
344
|
+
filename = file;
|
|
345
|
+
// Initialize tracking for this workflow
|
|
346
|
+
this.fileToIdMap.set(filename, workflowId);
|
|
347
|
+
this.idToFileMap.set(workflowId, filename);
|
|
348
|
+
break;
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
if (!filename) {
|
|
352
|
+
throw new Error(`Cannot finalize sync: workflow ${workflowId} not found in directory`);
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
// Get current reality
|
|
356
|
+
const filePath = path.join(this.directory, filename);
|
|
357
|
+
const content = this.readJsonFile(filePath);
|
|
358
|
+
if (!content) {
|
|
359
|
+
throw new Error(`Cannot finalize sync: local file not found for ${workflowId}`);
|
|
360
|
+
}
|
|
361
|
+
const clean = WorkflowSanitizer.cleanForStorage(content);
|
|
362
|
+
const computedHash = this.computeHash(clean);
|
|
363
|
+
// After a successful sync, local and remote should be identical
|
|
364
|
+
// Use the computed hash for both
|
|
365
|
+
const localHash = computedHash;
|
|
366
|
+
const remoteHash = computedHash;
|
|
367
|
+
// Update caches
|
|
368
|
+
this.localHashes.set(filename, localHash);
|
|
369
|
+
this.remoteHashes.set(workflowId, remoteHash);
|
|
370
|
+
// Update base state
|
|
371
|
+
await this.updateWorkflowState(workflowId, localHash);
|
|
372
|
+
// Broadcast new IN_SYNC status
|
|
373
|
+
this.broadcastStatus(filename, workflowId);
|
|
374
|
+
}
|
|
375
|
+
/**
|
|
376
|
+
* Update workflow state in .n8n-state.json
|
|
377
|
+
* Only this component writes to the state file
|
|
378
|
+
*/
|
|
379
|
+
async updateWorkflowState(id, hash) {
|
|
380
|
+
const state = this.loadState();
|
|
381
|
+
state.workflows[id] = {
|
|
382
|
+
lastSyncedHash: hash,
|
|
383
|
+
lastSyncedAt: new Date().toISOString()
|
|
384
|
+
};
|
|
385
|
+
this.saveState(state);
|
|
386
|
+
}
|
|
387
|
+
/**
|
|
388
|
+
* Remove workflow from state file
|
|
389
|
+
* Called after deletion confirmation
|
|
390
|
+
*/
|
|
391
|
+
async removeWorkflowState(id) {
|
|
392
|
+
const state = this.loadState();
|
|
393
|
+
delete state.workflows[id];
|
|
394
|
+
this.saveState(state);
|
|
395
|
+
// Clean up internal tracking
|
|
396
|
+
const filename = this.idToFileMap.get(id);
|
|
397
|
+
if (filename) {
|
|
398
|
+
this.fileToIdMap.delete(filename);
|
|
399
|
+
}
|
|
400
|
+
this.idToFileMap.delete(id);
|
|
401
|
+
this.remoteHashes.delete(id);
|
|
402
|
+
this.remoteTimestamps.delete(id);
|
|
403
|
+
}
|
|
404
|
+
/**
|
|
405
|
+
* Load state from .n8n-state.json
|
|
406
|
+
*/
|
|
407
|
+
loadState() {
|
|
408
|
+
if (fs.existsSync(this.stateFilePath)) {
|
|
409
|
+
try {
|
|
410
|
+
const data = JSON.parse(fs.readFileSync(this.stateFilePath, 'utf-8'));
|
|
411
|
+
if (!data.workflows) {
|
|
412
|
+
data.workflows = {};
|
|
413
|
+
}
|
|
414
|
+
return data;
|
|
415
|
+
}
|
|
416
|
+
catch (e) {
|
|
417
|
+
console.warn('Could not read state file, using empty state');
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
return { workflows: {} };
|
|
421
|
+
}
|
|
422
|
+
/**
|
|
423
|
+
* Save state to .n8n-state.json
|
|
424
|
+
*/
|
|
425
|
+
saveState(state) {
|
|
426
|
+
fs.writeFileSync(this.stateFilePath, JSON.stringify(state, null, 2));
|
|
427
|
+
}
|
|
428
|
+
/**
|
|
429
|
+
* Compute canonical hash for content
|
|
430
|
+
*/
|
|
431
|
+
computeHash(content) {
|
|
432
|
+
return HashUtils.computeHash(content);
|
|
433
|
+
}
|
|
434
|
+
broadcastStatus(filename, workflowId) {
|
|
435
|
+
if (this.isInitializing)
|
|
436
|
+
return;
|
|
437
|
+
const status = this.calculateStatus(filename, workflowId);
|
|
438
|
+
const key = workflowId || filename;
|
|
439
|
+
const lastStatus = this.lastKnownStatuses.get(key);
|
|
440
|
+
if (status !== lastStatus) {
|
|
441
|
+
this.lastKnownStatuses.set(key, status);
|
|
442
|
+
this.emit('statusChange', {
|
|
443
|
+
filename,
|
|
444
|
+
workflowId,
|
|
445
|
+
status
|
|
446
|
+
});
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
calculateStatus(filename, workflowId) {
|
|
450
|
+
if (!workflowId)
|
|
451
|
+
workflowId = this.fileToIdMap.get(filename);
|
|
452
|
+
const localHash = this.localHashes.get(filename);
|
|
453
|
+
const remoteHash = workflowId ? this.remoteHashes.get(workflowId) : undefined;
|
|
454
|
+
// If we are disconnected and don't have a remote hash, don't claim it's deleted
|
|
455
|
+
if (!this.isConnected && !remoteHash && workflowId) {
|
|
456
|
+
return WorkflowSyncStatus.IN_SYNC; // Treat as in-sync or unknown to avoid "deleted" panic
|
|
457
|
+
}
|
|
458
|
+
// Get base state
|
|
459
|
+
const state = this.loadState();
|
|
460
|
+
const baseState = workflowId ? state.workflows[workflowId] : undefined;
|
|
461
|
+
const lastSyncedHash = baseState?.lastSyncedHash;
|
|
462
|
+
// Implementation of 4.2 Status Logic Matrix from SPECS/REFACTO_CORE.md
|
|
463
|
+
if (localHash && !lastSyncedHash && !remoteHash)
|
|
464
|
+
return WorkflowSyncStatus.EXIST_ONLY_LOCALLY;
|
|
465
|
+
if (remoteHash && !lastSyncedHash && !localHash)
|
|
466
|
+
return WorkflowSyncStatus.EXIST_ONLY_REMOTELY;
|
|
467
|
+
if (localHash && remoteHash && localHash === remoteHash)
|
|
468
|
+
return WorkflowSyncStatus.IN_SYNC;
|
|
469
|
+
if (lastSyncedHash) {
|
|
470
|
+
// Check deletions first (they take precedence over modifications)
|
|
471
|
+
if (!localHash && remoteHash === lastSyncedHash)
|
|
472
|
+
return WorkflowSyncStatus.DELETED_LOCALLY;
|
|
473
|
+
if (!remoteHash && localHash === lastSyncedHash)
|
|
474
|
+
return WorkflowSyncStatus.DELETED_REMOTELY;
|
|
475
|
+
// Then check modifications
|
|
476
|
+
const localModified = localHash !== lastSyncedHash;
|
|
477
|
+
const remoteModified = remoteHash && remoteHash !== lastSyncedHash;
|
|
478
|
+
if (localModified && remoteModified)
|
|
479
|
+
return WorkflowSyncStatus.CONFLICT;
|
|
480
|
+
if (localModified && remoteHash === lastSyncedHash)
|
|
481
|
+
return WorkflowSyncStatus.MODIFIED_LOCALLY;
|
|
482
|
+
if (remoteModified && localHash === lastSyncedHash)
|
|
483
|
+
return WorkflowSyncStatus.MODIFIED_REMOTELY;
|
|
484
|
+
}
|
|
485
|
+
// Fallback for edge cases
|
|
486
|
+
return WorkflowSyncStatus.CONFLICT;
|
|
487
|
+
}
|
|
488
|
+
shouldIgnore(wf) {
|
|
489
|
+
if (!this.syncInactive && !wf.active)
|
|
490
|
+
return true;
|
|
491
|
+
if (wf.tags) {
|
|
492
|
+
const hasIgnoredTag = wf.tags.some(t => this.ignoredTags.includes(t.name.toLowerCase()));
|
|
493
|
+
if (hasIgnoredTag)
|
|
494
|
+
return true;
|
|
495
|
+
}
|
|
496
|
+
return false;
|
|
497
|
+
}
|
|
498
|
+
safeName(name) {
|
|
499
|
+
return name.replace(/[\/\\:]/g, '_').replace(/\s+/g, ' ').trim();
|
|
500
|
+
}
|
|
501
|
+
readJsonFile(filePath) {
|
|
502
|
+
try {
|
|
503
|
+
return JSON.parse(fs.readFileSync(filePath, 'utf8'));
|
|
504
|
+
}
|
|
505
|
+
catch {
|
|
506
|
+
return null;
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
getFileToIdMap() {
|
|
510
|
+
return this.fileToIdMap;
|
|
511
|
+
}
|
|
512
|
+
getStatusMatrix() {
|
|
513
|
+
const results = new Map();
|
|
514
|
+
const state = this.loadState();
|
|
515
|
+
// 1. Process all local files
|
|
516
|
+
for (const [filename, hash] of this.localHashes.entries()) {
|
|
517
|
+
const workflowId = this.fileToIdMap.get(filename);
|
|
518
|
+
const status = this.calculateStatus(filename, workflowId);
|
|
519
|
+
results.set(filename, {
|
|
520
|
+
id: workflowId || '',
|
|
521
|
+
name: filename.replace('.json', ''),
|
|
522
|
+
filename: filename,
|
|
523
|
+
status: status,
|
|
524
|
+
active: true
|
|
525
|
+
});
|
|
526
|
+
}
|
|
527
|
+
// 2. Process all remote workflows not yet in results
|
|
528
|
+
for (const [workflowId, remoteHash] of this.remoteHashes.entries()) {
|
|
529
|
+
const filename = this.idToFileMap.get(workflowId) || `${workflowId}.json`;
|
|
530
|
+
if (!results.has(filename)) {
|
|
531
|
+
const status = this.calculateStatus(filename, workflowId);
|
|
532
|
+
results.set(filename, {
|
|
533
|
+
id: workflowId,
|
|
534
|
+
name: filename.replace('.json', ''),
|
|
535
|
+
filename: filename,
|
|
536
|
+
status: status,
|
|
537
|
+
active: true
|
|
538
|
+
});
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
// 3. Process tracked but deleted workflows
|
|
542
|
+
for (const id of Object.keys(state.workflows)) {
|
|
543
|
+
const filename = this.idToFileMap.get(id) || `${id}.json`;
|
|
544
|
+
if (!results.has(filename)) {
|
|
545
|
+
const status = this.calculateStatus(filename, id);
|
|
546
|
+
results.set(filename, {
|
|
547
|
+
id,
|
|
548
|
+
name: filename.replace('.json', ''),
|
|
549
|
+
filename,
|
|
550
|
+
status,
|
|
551
|
+
active: true
|
|
552
|
+
});
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
return Array.from(results.values()).sort((a, b) => a.name.localeCompare(b.name));
|
|
556
|
+
}
|
|
557
|
+
/**
|
|
558
|
+
* Get last synced hash for a workflow
|
|
559
|
+
*/
|
|
560
|
+
getLastSyncedHash(workflowId) {
|
|
561
|
+
const state = this.loadState();
|
|
562
|
+
return state.workflows[workflowId]?.lastSyncedHash;
|
|
563
|
+
}
|
|
564
|
+
/**
|
|
565
|
+
* Update remote hash cache (for SyncEngine use)
|
|
566
|
+
* @internal
|
|
567
|
+
*/
|
|
568
|
+
setRemoteHash(workflowId, hash) {
|
|
569
|
+
this.remoteHashes.set(workflowId, hash);
|
|
570
|
+
}
|
|
571
|
+
/**
|
|
572
|
+
* Get all tracked workflow IDs
|
|
573
|
+
*/
|
|
574
|
+
getTrackedWorkflowIds() {
|
|
575
|
+
const state = this.loadState();
|
|
576
|
+
return Object.keys(state.workflows);
|
|
577
|
+
}
|
|
578
|
+
/**
|
|
579
|
+
* Update workflow ID in state (when a workflow is re-created with a new ID)
|
|
580
|
+
*/
|
|
581
|
+
async updateWorkflowId(oldId, newId) {
|
|
582
|
+
const state = this.loadState();
|
|
583
|
+
// Migrate state from old ID to new ID
|
|
584
|
+
if (state.workflows[oldId]) {
|
|
585
|
+
state.workflows[newId] = state.workflows[oldId];
|
|
586
|
+
delete state.workflows[oldId];
|
|
587
|
+
this.saveState(state);
|
|
588
|
+
}
|
|
589
|
+
// Update internal mappings
|
|
590
|
+
const filename = this.idToFileMap.get(oldId);
|
|
591
|
+
if (filename) {
|
|
592
|
+
this.idToFileMap.delete(oldId);
|
|
593
|
+
this.idToFileMap.set(newId, filename);
|
|
594
|
+
this.fileToIdMap.set(filename, newId);
|
|
595
|
+
}
|
|
596
|
+
// Update hash maps
|
|
597
|
+
const remoteHash = this.remoteHashes.get(oldId);
|
|
598
|
+
if (remoteHash) {
|
|
599
|
+
this.remoteHashes.delete(oldId);
|
|
600
|
+
this.remoteHashes.set(newId, remoteHash);
|
|
601
|
+
}
|
|
602
|
+
const timestamp = this.remoteTimestamps.get(oldId);
|
|
603
|
+
if (timestamp) {
|
|
604
|
+
this.remoteTimestamps.delete(oldId);
|
|
605
|
+
this.remoteTimestamps.set(newId, timestamp);
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
//# sourceMappingURL=watcher.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"watcher.js","sourceRoot":"","sources":["../../src/services/watcher.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,9 +1,5 @@
|
|
|
1
1
|
import { IWorkflow } from '../types.js';
|
|
2
2
|
export declare class WorkflowSanitizer {
|
|
3
|
-
/**
|
|
4
|
-
* Recursively sorts object keys to ensure deterministic JSON output.
|
|
5
|
-
*/
|
|
6
|
-
private static sortKeys;
|
|
7
3
|
/**
|
|
8
4
|
* Prepares a workflow JSON for storage on disk (GIT).
|
|
9
5
|
* Removes dynamic IDs, execution URLs, and standardizes key order.
|
|
@@ -12,6 +8,15 @@ export declare class WorkflowSanitizer {
|
|
|
12
8
|
/**
|
|
13
9
|
* Prepares a local workflow JSON for pushing to n8n API.
|
|
14
10
|
* Removes read-only fields or fields that shouldn't be overwritten blindly (like tags if needed).
|
|
11
|
+
*
|
|
12
|
+
* n8n API v1 PUT /workflows/{id} expects a very specific schema.
|
|
13
|
+
* Based on n8n 2.2.6 API documentation, the allowed fields are:
|
|
14
|
+
* - name (string, required)
|
|
15
|
+
* - nodes (array, required)
|
|
16
|
+
* - connections (object, required)
|
|
17
|
+
* - settings (object, optional but with strict schema)
|
|
18
|
+
* - staticData (object, optional)
|
|
19
|
+
* - triggerCount (number, optional)
|
|
15
20
|
*/
|
|
16
21
|
static cleanForPush(workflow: Partial<IWorkflow>): Partial<IWorkflow>;
|
|
17
22
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"workflow-sanitizer.d.ts","sourceRoot":"","sources":["../../src/services/workflow-sanitizer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAExC,qBAAa,iBAAiB;IAC1B
|
|
1
|
+
{"version":3,"file":"workflow-sanitizer.d.ts","sourceRoot":"","sources":["../../src/services/workflow-sanitizer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAExC,qBAAa,iBAAiB;IAC1B;;;OAGG;IACH,MAAM,CAAC,eAAe,CAAC,QAAQ,EAAE,SAAS,GAAG,OAAO,CAAC,SAAS,CAAC;IAwD/D;;;;;;;;;;;;OAYG;IACH,MAAM,CAAC,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,SAAS,CAAC,GAAG,OAAO,CAAC,SAAS,CAAC;CAoCxE"}
|