n8n
Version:
n8n Workflow Automation Tool
597 lines • 30.6 kB
JavaScript
;
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.SourceControlStatusService = void 0;
const backend_common_1 = require("@n8n/backend-common");
const db_1 = require("@n8n/db");
const di_1 = require("@n8n/di");
const permissions_1 = require("@n8n/permissions");
const n8n_workflow_1 = require("n8n-workflow");
const forbidden_error_1 = require("../../errors/response-errors/forbidden.error");
const event_service_1 = require("../../events/event.service");
const source_control_git_service_ee_1 = require("./source-control-git.service.ee");
const source_control_helper_ee_1 = require("./source-control-helper.ee");
const source_control_import_service_ee_1 = require("./source-control-import.service.ee");
const source_control_preferences_service_ee_1 = require("./source-control-preferences.service.ee");
const source_control_context_1 = require("./types/source-control-context");
let SourceControlStatusService = class SourceControlStatusService {
constructor(logger, gitService, sourceControlImportService, sourceControlPreferencesService, tagRepository, folderRepository, eventService) {
this.logger = logger;
this.gitService = gitService;
this.sourceControlImportService = sourceControlImportService;
this.sourceControlPreferencesService = sourceControlPreferencesService;
this.tagRepository = tagRepository;
this.folderRepository = folderRepository;
this.eventService = eventService;
}
get gitFolder() {
return this.sourceControlPreferencesService.gitFolder;
}
async getStatus(user, options) {
const context = new source_control_context_1.SourceControlContext(user);
if (options.direction === 'pull' && !(0, permissions_1.hasGlobalScope)(user, 'sourceControl:pull')) {
throw new forbidden_error_1.ForbiddenError('You do not have permission to pull from source control');
}
const sourceControlledFiles = [];
await this.resetWorkfolder();
const { wfRemoteVersionIds, wfLocalVersionIds, wfMissingInLocal, wfMissingInRemote, wfModifiedInEither, } = await this.getStatusWorkflows(options, context, sourceControlledFiles);
const { credMissingInLocal, credMissingInRemote, credModifiedInEither } = await this.getStatusCredentials(options, context, sourceControlledFiles);
const { varMissingInLocal, varMissingInRemote, varModifiedInEither } = await this.getStatusVariables(options, sourceControlledFiles);
const { tagsMissingInLocal, tagsMissingInRemote, tagsModifiedInEither, mappingsMissingInLocal, mappingsMissingInRemote, } = await this.getStatusTagsMappings(options, context, sourceControlledFiles);
const { foldersMissingInLocal, foldersMissingInRemote, foldersModifiedInEither } = await this.getStatusFoldersMapping(options, context, sourceControlledFiles);
const { projectsRemote, projectsLocal, projectsMissingInLocal, projectsMissingInRemote, projectsModifiedInEither, } = await this.getStatusProjects(options, context, sourceControlledFiles);
if (options.direction === 'push') {
this.eventService.emit('source-control-user-started-push-ui', (0, source_control_helper_ee_1.getTrackingInformationFromPrePushResult)(user.id, sourceControlledFiles));
}
else if (options.direction === 'pull') {
this.eventService.emit('source-control-user-started-pull-ui', (0, source_control_helper_ee_1.getTrackingInformationFromPullResult)(user.id, sourceControlledFiles));
}
if (options?.verbose) {
return {
wfRemoteVersionIds,
wfLocalVersionIds,
wfMissingInLocal,
wfMissingInRemote,
wfModifiedInEither,
credMissingInLocal,
credMissingInRemote,
credModifiedInEither,
varMissingInLocal,
varMissingInRemote,
varModifiedInEither,
tagsMissingInLocal,
tagsMissingInRemote,
tagsModifiedInEither,
mappingsMissingInLocal,
mappingsMissingInRemote,
foldersMissingInLocal,
foldersMissingInRemote,
foldersModifiedInEither,
projectsRemote,
projectsLocal,
projectsMissingInLocal,
projectsMissingInRemote,
projectsModifiedInEither,
sourceControlledFiles,
};
}
else {
return sourceControlledFiles;
}
}
async resetWorkfolder() {
if (!this.gitService.git) {
throw new Error('Git service not initialized');
}
try {
await this.gitService.resetBranch();
await this.gitService.pull();
}
catch (error) {
this.logger.error(`Failed to reset workfolder: ${error instanceof Error ? error.message : String(error)}`);
throw new n8n_workflow_1.UserError(`Unable to fetch updates from git - your folder might be out of sync. Try reconnecting from the Source Control settings page. Git error message: ${error instanceof Error ? error.message : String(error)}`);
}
}
async getStatusWorkflows(options, context, sourceControlledFiles) {
const wfRemoteVersionIds = await this.sourceControlImportService.getRemoteVersionIdsFromFiles(context);
const wfLocalVersionIds = await this.sourceControlImportService.getLocalVersionIdsFromDb(context);
let outOfScopeWF = [];
if (!context.hasAccessToAllProjects()) {
outOfScopeWF = await this.sourceControlImportService.getAllLocalVersionIdsFromDb();
outOfScopeWF = outOfScopeWF.filter((wf) => !wfLocalVersionIds.some((local) => local.id === wf.id));
}
const wfMissingInLocal = wfRemoteVersionIds
.filter((remote) => wfLocalVersionIds.findIndex((local) => local.id === remote.id) === -1)
.filter((remote) => !outOfScopeWF.some((outOfScope) => outOfScope.id === remote.id));
const wfMissingInRemote = wfLocalVersionIds.filter((local) => wfRemoteVersionIds.findIndex((remote) => remote.id === local.id) === -1);
const wfModifiedInEither = [];
wfLocalVersionIds.forEach((localWorkflow) => {
const remoteWorkflowWithSameId = wfRemoteVersionIds.find((removeWorkflow) => removeWorkflow.id === localWorkflow.id);
if (!remoteWorkflowWithSameId) {
return;
}
if ((0, source_control_helper_ee_1.isWorkflowModified)(localWorkflow, remoteWorkflowWithSameId)) {
let name = (options?.preferLocalVersion ? localWorkflow?.name : remoteWorkflowWithSameId?.name) ??
'Workflow';
if (localWorkflow.name &&
remoteWorkflowWithSameId?.name &&
localWorkflow.name !== remoteWorkflowWithSameId.name) {
name = options?.preferLocalVersion
? `${localWorkflow.name} (Remote: ${remoteWorkflowWithSameId.name})`
: (name = `${remoteWorkflowWithSameId.name} (Local: ${localWorkflow.name})`);
}
wfModifiedInEither.push({
...localWorkflow,
name,
versionId: options.preferLocalVersion
? localWorkflow.versionId
: remoteWorkflowWithSameId.versionId,
localId: localWorkflow.versionId,
remoteId: remoteWorkflowWithSameId.versionId,
});
}
});
wfMissingInLocal.forEach((item) => {
sourceControlledFiles.push({
id: item.id,
name: item.name ?? 'Workflow',
type: 'workflow',
status: options.direction === 'push' ? 'deleted' : 'created',
location: options.direction === 'push' ? 'local' : 'remote',
conflict: false,
file: item.filename,
updatedAt: item.updatedAt ?? new Date().toISOString(),
owner: item.owner,
});
});
wfMissingInRemote.forEach((item) => {
sourceControlledFiles.push({
id: item.id,
name: item.name ?? 'Workflow',
type: 'workflow',
status: options.direction === 'push' ? 'created' : 'deleted',
location: options.direction === 'push' ? 'local' : 'remote',
conflict: options.direction === 'push' ? false : true,
file: item.filename,
updatedAt: item.updatedAt ?? new Date().toISOString(),
owner: item.owner,
});
});
wfModifiedInEither.forEach((item) => {
sourceControlledFiles.push({
id: item.id,
name: item.name ?? 'Workflow',
type: 'workflow',
status: 'modified',
location: options.direction === 'push' ? 'local' : 'remote',
conflict: true,
file: item.filename,
updatedAt: item.updatedAt ?? new Date().toISOString(),
owner: item.owner,
});
});
return {
wfRemoteVersionIds,
wfLocalVersionIds,
wfMissingInLocal,
wfMissingInRemote,
wfModifiedInEither,
};
}
async getStatusCredentials(options, context, sourceControlledFiles) {
const credRemoteIds = await this.sourceControlImportService.getRemoteCredentialsFromFiles(context);
const credLocalIds = await this.sourceControlImportService.getLocalCredentialsFromDb(context);
const credMissingInLocal = credRemoteIds.filter((remote) => credLocalIds.findIndex((local) => local.id === remote.id) === -1);
const credMissingInRemote = credLocalIds.filter((local) => credRemoteIds.findIndex((remote) => remote.id === local.id) === -1);
const credModifiedInEither = [];
credLocalIds.forEach((local) => {
const mismatchingCreds = credRemoteIds.find((remote) => {
return (remote.id === local.id &&
(remote.name !== local.name ||
remote.type !== local.type ||
(0, source_control_helper_ee_1.hasOwnerChanged)(remote.ownedBy, local.ownedBy) ||
(remote.isGlobal ?? false) !== (local.isGlobal ?? false)));
});
if (mismatchingCreds) {
credModifiedInEither.push({
...local,
name: options?.preferLocalVersion ? local.name : mismatchingCreds.name,
});
}
});
credMissingInLocal.forEach((item) => {
sourceControlledFiles.push({
id: item.id,
name: item.name ?? 'Credential',
type: 'credential',
status: options.direction === 'push' ? 'deleted' : 'created',
location: options.direction === 'push' ? 'local' : 'remote',
conflict: false,
file: item.filename,
updatedAt: new Date().toISOString(),
owner: item.ownedBy,
});
});
credMissingInRemote.forEach((item) => {
sourceControlledFiles.push({
id: item.id,
name: item.name ?? 'Credential',
type: 'credential',
status: options.direction === 'push' ? 'created' : 'deleted',
location: options.direction === 'push' ? 'local' : 'remote',
conflict: options.direction === 'push' ? false : true,
file: item.filename,
updatedAt: new Date().toISOString(),
owner: item.ownedBy,
});
});
credModifiedInEither.forEach((item) => {
sourceControlledFiles.push({
id: item.id,
name: item.name ?? 'Credential',
type: 'credential',
status: 'modified',
location: options.direction === 'push' ? 'local' : 'remote',
conflict: true,
file: item.filename,
updatedAt: new Date().toISOString(),
owner: item.ownedBy,
});
});
return {
credMissingInLocal,
credMissingInRemote,
credModifiedInEither,
};
}
async getStatusVariables(options, sourceControlledFiles) {
const varRemoteIds = await this.sourceControlImportService.getRemoteVariablesFromFile();
const varLocalIds = await this.sourceControlImportService.getLocalGlobalVariablesFromDb();
const varMissingInLocal = varRemoteIds.filter((remote) => varLocalIds.findIndex((local) => local.id === remote.id) === -1);
const varMissingInRemote = varLocalIds.filter((local) => varRemoteIds.findIndex((remote) => remote.id === local.id) === -1);
const varModifiedInEither = [];
varLocalIds.forEach((local) => {
const mismatchingIds = varRemoteIds.find((remote) => (remote.id === local.id && remote.key !== local.key) ||
(remote.id !== local.id && remote.key === local.key));
if (mismatchingIds) {
varModifiedInEither.push(options.preferLocalVersion ? local : mismatchingIds);
}
});
varMissingInLocal.forEach((item) => {
sourceControlledFiles.push({
id: item.id,
name: item.key,
type: 'variables',
status: options.direction === 'push' ? 'deleted' : 'created',
location: options.direction === 'push' ? 'local' : 'remote',
conflict: false,
file: (0, source_control_helper_ee_1.getVariablesPath)(this.gitFolder),
updatedAt: new Date().toISOString(),
});
});
varMissingInRemote.forEach((item) => {
sourceControlledFiles.push({
id: item.id,
name: item.key,
type: 'variables',
status: options.direction === 'push' ? 'created' : 'deleted',
location: options.direction === 'push' ? 'local' : 'remote',
conflict: options.direction === 'push' ? false : true,
file: (0, source_control_helper_ee_1.getVariablesPath)(this.gitFolder),
updatedAt: new Date().toISOString(),
});
});
varModifiedInEither.forEach((item) => {
sourceControlledFiles.push({
id: item.id,
name: item.key,
type: 'variables',
status: 'modified',
location: options.direction === 'push' ? 'local' : 'remote',
conflict: true,
file: (0, source_control_helper_ee_1.getVariablesPath)(this.gitFolder),
updatedAt: new Date().toISOString(),
});
});
return {
varMissingInLocal,
varMissingInRemote,
varModifiedInEither,
};
}
async getStatusTagsMappings(options, context, sourceControlledFiles) {
const lastUpdatedTag = await this.tagRepository.find({
order: { updatedAt: 'DESC' },
take: 1,
select: ['updatedAt'],
});
const lastUpdatedDate = lastUpdatedTag[0]?.updatedAt ?? new Date();
const tagMappingsRemote = await this.sourceControlImportService.getRemoteTagsAndMappingsFromFile(context);
const tagMappingsLocal = await this.sourceControlImportService.getLocalTagsAndMappingsFromDb(context);
const tagsMissingInLocal = tagMappingsRemote.tags.filter((remote) => tagMappingsLocal.tags.findIndex((local) => local.id === remote.id) === -1);
const tagsMissingInRemote = tagMappingsLocal.tags.filter((local) => tagMappingsRemote.tags.findIndex((remote) => remote.id === local.id) === -1);
const tagsModifiedInEither = [];
tagMappingsLocal.tags.forEach((local) => {
const mismatchingIds = tagMappingsRemote.tags.find((remote) => remote.id === local.id && remote.name !== local.name);
if (!mismatchingIds) {
return;
}
tagsModifiedInEither.push(options.preferLocalVersion ? local : mismatchingIds);
});
const mappingsMissingInLocal = tagMappingsRemote.mappings.filter((remote) => tagMappingsLocal.mappings.findIndex((local) => local.tagId === remote.tagId && local.workflowId === remote.workflowId) === -1);
const mappingsMissingInRemote = tagMappingsLocal.mappings.filter((local) => tagMappingsRemote.mappings.findIndex((remote) => remote.tagId === local.tagId && remote.workflowId === remote.workflowId) === -1);
tagsMissingInLocal.forEach((item) => {
sourceControlledFiles.push({
id: item.id,
name: item.name,
type: 'tags',
status: options.direction === 'push' ? 'deleted' : 'created',
location: options.direction === 'push' ? 'local' : 'remote',
conflict: false,
file: (0, source_control_helper_ee_1.getTagsPath)(this.gitFolder),
updatedAt: lastUpdatedDate.toISOString(),
});
});
tagsMissingInRemote.forEach((item) => {
sourceControlledFiles.push({
id: item.id,
name: item.name,
type: 'tags',
status: options.direction === 'push' ? 'created' : 'deleted',
location: options.direction === 'push' ? 'local' : 'remote',
conflict: options.direction === 'push' ? false : true,
file: (0, source_control_helper_ee_1.getTagsPath)(this.gitFolder),
updatedAt: lastUpdatedDate.toISOString(),
});
});
tagsModifiedInEither.forEach((item) => {
sourceControlledFiles.push({
id: item.id,
name: item.name,
type: 'tags',
status: 'modified',
location: options.direction === 'push' ? 'local' : 'remote',
conflict: true,
file: (0, source_control_helper_ee_1.getTagsPath)(this.gitFolder),
updatedAt: lastUpdatedDate.toISOString(),
});
});
return {
tagsMissingInLocal,
tagsMissingInRemote,
tagsModifiedInEither,
mappingsMissingInLocal,
mappingsMissingInRemote,
};
}
async getStatusFoldersMapping(options, context, sourceControlledFiles) {
const lastUpdatedFolder = await this.folderRepository.find({
order: { updatedAt: 'DESC' },
take: 1,
select: ['updatedAt'],
});
const lastUpdatedDate = lastUpdatedFolder[0]?.updatedAt ?? new Date();
const foldersMappingsRemote = await this.sourceControlImportService.getRemoteFoldersAndMappingsFromFile(context);
const foldersMappingsLocal = await this.sourceControlImportService.getLocalFoldersAndMappingsFromDb(context);
const foldersMissingInLocal = foldersMappingsRemote.folders.filter((remote) => foldersMappingsLocal.folders.findIndex((local) => local.id === remote.id) === -1);
const foldersMissingInRemote = foldersMappingsLocal.folders.filter((local) => foldersMappingsRemote.folders.findIndex((remote) => remote.id === local.id) === -1);
const allTeamProjects = await this.sourceControlImportService.getLocalTeamProjectsFromDb();
const foldersModifiedInEither = [];
foldersMappingsLocal.folders.forEach((local) => {
const localHomeProject = allTeamProjects.find((project) => project.id === local.homeProjectId);
const mismatchingIds = foldersMappingsRemote.folders.find((remote) => {
const remoteHomeProject = allTeamProjects.find((project) => project.id === remote.homeProjectId);
const localOwner = localHomeProject
? {
type: 'team',
projectId: localHomeProject.id,
projectName: localHomeProject.name,
}
: undefined;
const remoteOwner = remoteHomeProject
? {
type: 'team',
projectId: remoteHomeProject?.id,
projectName: remoteHomeProject?.name,
}
: undefined;
const ownerChanged = (0, source_control_helper_ee_1.hasOwnerChanged)(localOwner, remoteOwner);
return (remote.id === local.id &&
(remote.name !== local.name ||
remote.parentFolderId !== local.parentFolderId ||
ownerChanged));
});
if (!mismatchingIds) {
return;
}
foldersModifiedInEither.push(options.preferLocalVersion ? local : mismatchingIds);
});
foldersMissingInLocal.forEach((item) => {
sourceControlledFiles.push({
id: item.id,
name: item.name,
type: 'folders',
status: options.direction === 'push' ? 'deleted' : 'created',
location: options.direction === 'push' ? 'local' : 'remote',
conflict: false,
file: (0, source_control_helper_ee_1.getFoldersPath)(this.gitFolder),
updatedAt: lastUpdatedDate.toISOString(),
});
});
foldersMissingInRemote.forEach((item) => {
sourceControlledFiles.push({
id: item.id,
name: item.name,
type: 'folders',
status: options.direction === 'push' ? 'created' : 'deleted',
location: options.direction === 'push' ? 'local' : 'remote',
conflict: options.direction === 'push' ? false : true,
file: (0, source_control_helper_ee_1.getFoldersPath)(this.gitFolder),
updatedAt: lastUpdatedDate.toISOString(),
});
});
foldersModifiedInEither.forEach((item) => {
sourceControlledFiles.push({
id: item.id,
name: item.name,
type: 'folders',
status: 'modified',
location: options.direction === 'push' ? 'local' : 'remote',
conflict: true,
file: (0, source_control_helper_ee_1.getFoldersPath)(this.gitFolder),
updatedAt: lastUpdatedDate.toISOString(),
});
});
return {
foldersMissingInLocal,
foldersMissingInRemote,
foldersModifiedInEither,
};
}
async getStatusProjects(options, context, sourceControlledFiles) {
const projectsRemote = await this.sourceControlImportService.getRemoteProjectsFromFiles(context);
const projectsLocal = await this.sourceControlImportService.getLocalTeamProjectsFromDb(context);
let outOfScopeProjects = [];
if (!context.hasAccessToAllProjects()) {
outOfScopeProjects = await this.sourceControlImportService.getLocalTeamProjectsFromDb();
outOfScopeProjects = outOfScopeProjects.filter((project) => !projectsLocal.some((local) => local.id === project.id));
}
const projectsMissingInLocal = projectsRemote
.filter((remote) => !projectsLocal.some((local) => local.id === remote.id))
.filter((remote) => !outOfScopeProjects.some((outOfScope) => outOfScope.id === remote.id));
const areRemoteProjectsEmpty = projectsRemote.length === 0;
let projectsMissingInRemote = projectsLocal.filter((local) => !projectsRemote.some((remote) => remote.id === local.id));
if (options.direction === 'pull' && areRemoteProjectsEmpty) {
projectsMissingInRemote = [];
}
const projectsModifiedInEither = [];
projectsLocal.forEach((localProject) => {
const remoteProjectWithSameId = projectsRemote.find((remoteProject) => remoteProject.id === localProject.id);
if (!remoteProjectWithSameId) {
return;
}
if (this.isProjectModified(localProject, remoteProjectWithSameId)) {
let name = (options?.preferLocalVersion ? localProject?.name : remoteProjectWithSameId?.name) ??
'Project';
if (localProject.name &&
remoteProjectWithSameId?.name &&
localProject.name !== remoteProjectWithSameId.name) {
name = options?.preferLocalVersion
? `${localProject.name} (Remote: ${remoteProjectWithSameId.name})`
: `${remoteProjectWithSameId.name} (Local: ${localProject.name})`;
}
projectsModifiedInEither.push({
...localProject,
name,
description: options.preferLocalVersion
? localProject.description
: remoteProjectWithSameId.description,
icon: options.preferLocalVersion ? localProject.icon : remoteProjectWithSameId.icon,
variableStubs: options.preferLocalVersion
? localProject.variableStubs
: remoteProjectWithSameId.variableStubs,
});
}
});
const mapExportableProjectWithFileNameToSourceControlledFile = ({ project, status, conflict, }) => {
return {
id: project.id,
name: project.name ?? 'Project',
type: 'project',
status,
location: options.direction === 'push' ? 'local' : 'remote',
conflict,
file: project.filename,
updatedAt: new Date().toISOString(),
owner: {
type: project.owner.type,
projectId: project.owner.teamId,
projectName: project.owner.teamName,
},
};
};
projectsMissingInLocal.forEach((item) => {
sourceControlledFiles.push(mapExportableProjectWithFileNameToSourceControlledFile({
project: item,
status: options.direction === 'push' ? 'deleted' : 'created',
conflict: false,
}));
});
projectsMissingInRemote.forEach((item) => {
sourceControlledFiles.push(mapExportableProjectWithFileNameToSourceControlledFile({
project: item,
status: options.direction === 'push' ? 'created' : 'deleted',
conflict: options.direction === 'push' ? false : true,
}));
});
projectsModifiedInEither.forEach((item) => {
sourceControlledFiles.push(mapExportableProjectWithFileNameToSourceControlledFile({
project: item,
status: 'modified',
conflict: true,
}));
});
return {
projectsRemote,
projectsLocal,
projectsMissingInLocal,
projectsMissingInRemote,
projectsModifiedInEither,
};
}
areVariablesEqual(localVariables, remoteVariables) {
if (Array.isArray(localVariables) !== Array.isArray(remoteVariables)) {
return false;
}
if (localVariables?.length !== remoteVariables?.length) {
return false;
}
const sortedLocalVars = [...(localVariables ?? [])].sort((a, b) => a.key.localeCompare(b.key));
const sortedRemoteVars = [...(remoteVariables ?? [])].sort((a, b) => a.key.localeCompare(b.key));
return sortedLocalVars.every((localVar, index) => {
const remoteVar = sortedRemoteVars[index];
return localVar.key === remoteVar.key && localVar.type === remoteVar.type;
});
}
isProjectModified(local, remote) {
const isIconModified = this.isProjectIconModified({
localIcon: local.icon,
remoteIcon: remote.icon,
});
return (isIconModified ||
remote.type !== local.type ||
remote.name !== local.name ||
remote.description !== local.description ||
!this.areVariablesEqual(local.variableStubs, remote.variableStubs));
}
isProjectIconModified({ localIcon, remoteIcon, }) {
if (!remoteIcon && !!localIcon)
return true;
if (!!remoteIcon && !localIcon)
return true;
if (!!remoteIcon && !!localIcon) {
return remoteIcon.type !== localIcon.type || remoteIcon.value !== localIcon.value;
}
return false;
}
};
exports.SourceControlStatusService = SourceControlStatusService;
exports.SourceControlStatusService = SourceControlStatusService = __decorate([
(0, di_1.Service)(),
__metadata("design:paramtypes", [backend_common_1.Logger,
source_control_git_service_ee_1.SourceControlGitService,
source_control_import_service_ee_1.SourceControlImportService,
source_control_preferences_service_ee_1.SourceControlPreferencesService,
db_1.TagRepository,
db_1.FolderRepository,
event_service_1.EventService])
], SourceControlStatusService);
//# sourceMappingURL=source-control-status.service.ee.js.map