@n8n-as-code/core 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +22 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +11 -0
- package/dist/index.js.map +1 -0
- package/dist/services/directory-utils.d.ts +35 -0
- package/dist/services/directory-utils.d.ts.map +1 -0
- package/dist/services/directory-utils.js +75 -0
- package/dist/services/directory-utils.js.map +1 -0
- package/dist/services/n8n-api-client.d.ts +23 -0
- package/dist/services/n8n-api-client.d.ts.map +1 -0
- package/dist/services/n8n-api-client.js +199 -0
- package/dist/services/n8n-api-client.js.map +1 -0
- package/dist/services/schema-generator.d.ts +9 -0
- package/dist/services/schema-generator.d.ts.map +1 -0
- package/dist/services/schema-generator.js +79 -0
- package/dist/services/schema-generator.js.map +1 -0
- package/dist/services/state-manager.d.ts +42 -0
- package/dist/services/state-manager.d.ts.map +1 -0
- package/dist/services/state-manager.js +99 -0
- package/dist/services/state-manager.js.map +1 -0
- package/dist/services/sync-manager.d.ts +101 -0
- package/dist/services/sync-manager.d.ts.map +1 -0
- package/dist/services/sync-manager.js +692 -0
- package/dist/services/sync-manager.js.map +1 -0
- package/dist/services/trash-service.d.ts +17 -0
- package/dist/services/trash-service.d.ts.map +1 -0
- package/dist/services/trash-service.js +41 -0
- package/dist/services/trash-service.js.map +1 -0
- package/dist/services/workflow-sanitizer.d.ts +18 -0
- package/dist/services/workflow-sanitizer.d.ts.map +1 -0
- package/dist/services/workflow-sanitizer.js +80 -0
- package/dist/services/workflow-sanitizer.js.map +1 -0
- package/dist/types.d.ts +41 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +9 -0
- package/dist/types.js.map +1 -0
- package/package.json +41 -0
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import crypto from 'crypto';
|
|
4
|
+
export class StateManager {
|
|
5
|
+
stateFilePath;
|
|
6
|
+
constructor(directory) {
|
|
7
|
+
this.stateFilePath = path.join(directory, '.n8n-state.json');
|
|
8
|
+
}
|
|
9
|
+
load() {
|
|
10
|
+
if (fs.existsSync(this.stateFilePath)) {
|
|
11
|
+
try {
|
|
12
|
+
const data = JSON.parse(fs.readFileSync(this.stateFilePath, 'utf-8'));
|
|
13
|
+
// Ensure workflows object exists
|
|
14
|
+
if (!data.workflows) {
|
|
15
|
+
data.workflows = {};
|
|
16
|
+
}
|
|
17
|
+
return data;
|
|
18
|
+
}
|
|
19
|
+
catch (e) {
|
|
20
|
+
console.warn('Could not read state file, using empty state');
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
return { workflows: {} };
|
|
24
|
+
}
|
|
25
|
+
save(state) {
|
|
26
|
+
fs.writeFileSync(this.stateFilePath, JSON.stringify(state, null, 2));
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
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
|
+
*/
|
|
39
|
+
getWorkflowState(id) {
|
|
40
|
+
const state = this.load();
|
|
41
|
+
return state.workflows[id];
|
|
42
|
+
}
|
|
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
|
+
/**
|
|
64
|
+
* Gets all tracked workflow IDs.
|
|
65
|
+
*/
|
|
66
|
+
getTrackedWorkflowIds() {
|
|
67
|
+
const state = this.load();
|
|
68
|
+
return Object.keys(state.workflows);
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Checks if a local content matches the last synced state.
|
|
72
|
+
*/
|
|
73
|
+
isLocalSynced(id, localContent) {
|
|
74
|
+
const state = this.getWorkflowState(id);
|
|
75
|
+
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;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Checks if a remote workflow matches the last synced state.
|
|
86
|
+
*/
|
|
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;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
//# sourceMappingURL=state-manager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"state-manager.js","sourceRoot":"","sources":["../../src/services/state-manager.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,101 @@
|
|
|
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 fileToIdMap;
|
|
8
|
+
private selfWrittenCache;
|
|
9
|
+
private isWriting;
|
|
10
|
+
private pendingDeletions;
|
|
11
|
+
private watcher;
|
|
12
|
+
private pollInterval;
|
|
13
|
+
private stateManager;
|
|
14
|
+
private trashService;
|
|
15
|
+
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
|
+
*/
|
|
43
|
+
getWorkflowsStatus(): Promise<IWorkflowStatus[]>;
|
|
44
|
+
private formatSummary;
|
|
45
|
+
/**
|
|
46
|
+
* Scans n8n instance and updates local files (Downstream Sync)
|
|
47
|
+
*/
|
|
48
|
+
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
|
+
syncUp(): Promise<void>;
|
|
80
|
+
/**
|
|
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
|
|
90
|
+
*/
|
|
91
|
+
restoreLocalFile(id: string, filename: string): Promise<boolean>;
|
|
92
|
+
/**
|
|
93
|
+
* Handle FS watcher events
|
|
94
|
+
*/
|
|
95
|
+
handleLocalFileChange(filePath: string, silent?: boolean): Promise<'updated' | 'created' | 'up-to-date' | 'conflict' | 'skipped'>;
|
|
96
|
+
private readLocalFile;
|
|
97
|
+
private readRawFile;
|
|
98
|
+
startWatch(): Promise<void>;
|
|
99
|
+
stopWatch(): void;
|
|
100
|
+
}
|
|
101
|
+
//# 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;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"}
|