Files
chartdb/src/lib/clone.ts
T
2025-07-29 12:49:28 +03:00

256 lines
7.1 KiB
TypeScript

import type { DBCustomType } from './domain';
import type { Area } from './domain/area';
import type { DBDependency } from './domain/db-dependency';
import type { DBField } from './domain/db-field';
import type { DBIndex } from './domain/db-index';
import type { DBRelationship } from './domain/db-relationship';
import type { DBTable } from './domain/db-table';
import type { Diagram } from './domain/diagram';
import { generateId as defaultGenerateId } from './utils';
const generateIdsMapFromTable = (
table: DBTable,
generateId: () => string = defaultGenerateId
): Map<string, string> => {
const idsMap = new Map<string, string>();
idsMap.set(table.id, generateId());
table.fields.forEach((field) => {
idsMap.set(field.id, generateId());
});
table.indexes.forEach((index) => {
idsMap.set(index.id, generateId());
});
return idsMap;
};
const generateIdsMapFromDiagram = (
diagram: Diagram,
generateId: () => string = defaultGenerateId
): Map<string, string> => {
let idsMap = new Map<string, string>();
diagram.tables?.forEach((table) => {
const tableIdsMap = generateIdsMapFromTable(table, generateId);
idsMap = new Map([...idsMap, ...tableIdsMap]);
});
diagram.relationships?.forEach((relationship) => {
idsMap.set(relationship.id, generateId());
});
diagram.dependencies?.forEach((dependency) => {
idsMap.set(dependency.id, generateId());
});
diagram.areas?.forEach((area) => {
idsMap.set(area.id, generateId());
});
diagram.customTypes?.forEach((customType) => {
idsMap.set(customType.id, generateId());
});
return idsMap;
};
export const cloneTable = (
table: DBTable,
options: {
generateId: () => string;
idsMap: Map<string, string>;
} = {
generateId: defaultGenerateId,
idsMap: new Map<string, string>(),
}
): DBTable => {
const { generateId } = options;
const idsMap = new Map([
...generateIdsMapFromTable(table, generateId),
...options.idsMap,
]);
const getNewId = (id: string): string | null => {
const newId = idsMap.get(id);
if (!newId) {
return null;
}
return newId;
};
const tableId = getNewId(table.id);
if (!tableId) {
throw new Error('Table id not found');
}
const newTable: DBTable = { ...table, id: tableId };
newTable.fields = table.fields
.map((field): DBField | null => {
const id = getNewId(field.id);
if (!id) {
return null;
}
return {
...field,
id,
};
})
.filter((field): field is DBField => field !== null);
newTable.indexes = table.indexes
.map((index): DBIndex | null => {
const id = getNewId(index.id);
if (!id) {
return null;
}
return {
...index,
fieldIds: index.fieldIds
.map((id) => getNewId(id))
.filter((fieldId): fieldId is string => fieldId !== null),
id,
};
})
.filter((index): index is DBIndex => index !== null);
return newTable;
};
export const cloneDiagram = (
diagram: Diagram,
options: {
generateId: () => string;
} = {
generateId: defaultGenerateId,
}
): { diagram: Diagram; idsMap: Map<string, string> } => {
const { generateId } = options;
const diagramId = generateId();
const idsMap = generateIdsMapFromDiagram(diagram, generateId);
const getNewId = (id: string): string | null => {
const newId = idsMap.get(id);
if (!newId) {
return null;
}
return newId;
};
const tables: DBTable[] =
diagram.tables?.map((table) =>
cloneTable(table, { generateId, idsMap })
) ?? [];
const relationships: DBRelationship[] =
diagram.relationships
?.map((relationship): DBRelationship | null => {
const id = getNewId(relationship.id);
const sourceTableId = getNewId(relationship.sourceTableId);
const targetTableId = getNewId(relationship.targetTableId);
const sourceFieldId = getNewId(relationship.sourceFieldId);
const targetFieldId = getNewId(relationship.targetFieldId);
if (
!id ||
!sourceTableId ||
!targetTableId ||
!sourceFieldId ||
!targetFieldId
) {
return null;
}
return {
...relationship,
id,
sourceTableId,
targetTableId,
sourceFieldId,
targetFieldId,
};
})
.filter(
(relationship): relationship is DBRelationship =>
relationship !== null
) ?? [];
const dependencies: DBDependency[] =
diagram.dependencies
?.map((dependency): DBDependency | null => {
const id = getNewId(dependency.id);
const dependentTableId = getNewId(dependency.dependentTableId);
const tableId = getNewId(dependency.tableId);
if (!id || !dependentTableId || !tableId) {
return null;
}
return {
...dependency,
id,
dependentTableId,
tableId,
};
})
.filter(
(dependency): dependency is DBDependency => dependency !== null
) ?? [];
const areas: Area[] =
diagram.areas
?.map((area) => {
const id = getNewId(area.id);
if (!id) {
return null;
}
return {
...area,
id,
} satisfies Area;
})
.filter((area): area is Area => area !== null) ?? [];
const customTypes: DBCustomType[] =
diagram.customTypes
?.map((customType) => {
const id = getNewId(customType.id);
if (!id) {
return null;
}
return {
...customType,
id,
} satisfies DBCustomType;
})
.filter(
(customType): customType is DBCustomType => customType !== null
) ?? [];
return {
diagram: {
...diagram,
id: diagramId,
dependencies,
relationships,
tables,
areas,
customTypes,
createdAt: diagram.createdAt
? new Date(diagram.createdAt)
: new Date(),
updatedAt: diagram.updatedAt
? new Date(diagram.updatedAt)
: new Date(),
},
idsMap,
};
};