Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .Jules/bolt.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
## 2025-06-27 - [Map Initialization Overhead]
**Learning:** Initializing Maps with `new Map(array.map(...))` creates unnecessary intermediate arrays, consuming memory and triggering garbage collection overhead, especially noticeable when dealing with many nodes.
**Action:** Use a `for...of` loop to directly `map.set()` elements rather than creating an intermediate array of tuples, especially in frequently executed or rendering paths.
## 2025-07-15 - [O(N^2) Array Searches in Edge Handling]
**Learning:** Using `Array.find()` multiple times inside an iteration (like finding columns corresponding to an edge's source/target handle) across large node graphs scales poorly (O(N^2)) and slows down export operations.
**Action:** Replace `Array.find()` with direct iteration and early breaking (`break`), or a single pass lookup map for frequent lookups on the same data.
3 changes: 2 additions & 1 deletion backend/app/rate_limit.py
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,8 @@ async def middleware(
if not policy.enabled:
return await call_next(request)

path = request.url.path
# ⚑ Bolt: Fast string prefix matching
path = request.scope.get("path", "")
if not path.startswith(policy.route_prefix):
return await call_next(request)

Expand Down
5 changes: 3 additions & 2 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,5 +36,6 @@
},
"overrides": {
"esbuild": "^0.25.0"
}
}
},
"packageManager": "pnpm@10.30.3"
}
6 changes: 4 additions & 2 deletions frontend/src/App.coverage.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -609,6 +609,7 @@ describe('App orchestration coverage', () => {
it('logs auto-layout failures and preserves nodes added after the undo snapshot', async () => {
await renderReadyApp()
fireEvent.click(screen.getByRole('button', { name: 'λ‹€μ΄μ–΄κ·Έλž¨' }))
await waitFor(() => expect(screen.getAllByRole('button', { name: 'μ—΄κΈ°' }).length).toBeGreaterThan(0))
vi.useFakeTimers()
fireEvent.click(screen.getAllByRole('button', { name: 'μ—΄κΈ°' })[0]!)
await act(async () => {
Expand Down Expand Up @@ -776,13 +777,14 @@ describe('App orchestration coverage', () => {
it('falls back to node ids when auto-layout receives legacy nodes without titles', async () => {
vi.mocked(snapshotToGraph).mockReturnValueOnce({
nodes: [
{ id: 'z-node', type: 'tableNode', position: { x: 0, y: 0 }, data: { columns: [], badges: { pk: false, fk: false } } },
{ id: 'a-node', type: 'tableNode', position: { x: 1, y: 1 }, data: { columns: [], badges: { pk: false, fk: false } } },
{ id: 'z-node', type: 'tableNode', position: { x: 0, y: 0 }, data: { title: '', columns: [], badges: { pk: false, fk: false } } },
{ id: 'a-node', type: 'tableNode', position: { x: 1, y: 1 }, data: { title: '', columns: [], badges: { pk: false, fk: false } } },
] as any,
edges: [],
})
await renderReadyApp()
fireEvent.click(screen.getByRole('button', { name: 'λ‹€μ΄μ–΄κ·Έλž¨' }))
await waitFor(() => expect(screen.getAllByRole('button', { name: 'μ—΄κΈ°' }).length).toBeGreaterThan(0))
vi.useFakeTimers()
fireEvent.click(screen.getAllByRole('button', { name: 'μ—΄κΈ°' })[0]!)
await act(async () => {
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -454,8 +454,8 @@ export default function App() {
currentNodes: Array<Node<TableNodeData>>,
): Array<Node<TableNodeData>> {
const sorted = [...currentNodes].sort((a, b) => {
const aTitle = a.data?.title ?? a.id;
const bTitle = b.data?.title ?? b.id;
const aTitle = a.data?.title || a.id;
const bTitle = b.data?.title || b.id;
return aTitle.localeCompare(bTitle, "en");
});

Expand Down
26 changes: 18 additions & 8 deletions frontend/src/erd/export.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,12 +67,22 @@ function fkColumnsForEdge(
return { sourceColumns, targetColumns };
}

const sourceHandleColumn = (sourceNode.data.columns || [])
.find((column) => sourceColumnHandleId(column.column_name) === edge.sourceHandle)
?.column_name;
const targetHandleColumn = (targetNode.data.columns || [])
.find((column) => targetColumnHandleId(column.column_name) === edge.targetHandle)
?.column_name;
// ⚑ Bolt: Cache handle ids to avoid repetitive string manipulations during O(N) lookup.
let sourceHandleColumn: string | undefined;
for (const column of sourceNode.data.columns || []) {
if (sourceColumnHandleId(column.column_name) === edge.sourceHandle) {
sourceHandleColumn = column.column_name;
break;
}
}

let targetHandleColumn: string | undefined;
for (const column of targetNode.data.columns || []) {
if (targetColumnHandleId(column.column_name) === edge.targetHandle) {
targetHandleColumn = column.column_name;
break;
}
}
if (sourceHandleColumn && targetHandleColumn) {
return { sourceColumns: [sourceHandleColumn], targetColumns: [targetHandleColumn] };
}
Expand All @@ -93,7 +103,7 @@ function fkColumnsForEdge(
export function exportDDL(nodes: Node<TableNodeData>[], edges: Edge[]): string {
let ddl = '-- Generated DDL\n\n';

// Bolt: Use map for O(1) node lookup instead of O(N) array find
// ⚑ Bolt: Use map for O(1) node lookup instead of O(N) array find
// Avoid Map(array.map) to prevent O(N) intermediate tuple array allocation overhead
const nodesById = new Map<string, Node<TableNodeData>>();
for (const n of nodes) {
Expand Down Expand Up @@ -287,7 +297,7 @@ export function exportDiagramSvg(
edges: Edge[],
snapshot?: SnapshotJson | null,
): string {
// Bolt: Use map for O(1) node lookup instead of O(N) array find
// ⚑ Bolt: Use map for O(1) node lookup instead of O(N) array find
// Avoid Map(array.map) to prevent O(N) intermediate tuple array allocation overhead
const nodesById = new Map<string, Node<TableNodeData>>();
for (const n of nodes) {
Expand Down
Loading