22
33from __future__ import annotations
44
5+ import os
56import sqlite3
67from dataclasses import dataclass , replace
78from typing import Any
89
910from models import Bubble
1011from services .workspace_db import (
12+ COMPOSER_ROWS_WITH_HEADERS_SQL ,
1113 build_composer_id_to_workspace_id ,
1214 build_composer_id_to_workspace_id_cached ,
1315 collect_invalid_workspace_ids ,
1416 collect_workspace_entries ,
17+ global_storage_db_path ,
1518 load_bubble_map ,
1619 load_project_layouts_map ,
20+ safe_fetchall ,
1721)
1822from services .workspace_resolver import (
1923 create_project_name_to_workspace_id_map ,
2024 create_workspace_path_to_id_map ,
25+ infer_invalid_workspace_aliases ,
2126)
2227
2328
@@ -32,6 +37,7 @@ class WorkspaceContext:
3237 workspace_path_to_id : dict [str , str ]
3338 project_layouts_map : dict [str , list [str ]]
3439 bubble_map : dict [str , Bubble ]
40+ invalid_workspace_aliases : dict [str , str ] | None = None
3541
3642
3743def _entries (
@@ -135,3 +141,102 @@ def enrich_workspace_context_from_global_db(
135141 if not updates :
136142 return ctx
137143 return replace (ctx , ** updates )
144+
145+
146+ def resolve_invalid_workspace_aliases_cached (
147+ ctx : WorkspaceContext ,
148+ global_db : sqlite3 .Connection ,
149+ workspace_path : str ,
150+ rules : list [Any ],
151+ * ,
152+ nocache : bool = False ,
153+ project_layouts_map : dict [str , list [str ]] | None = None ,
154+ ) -> dict [str , str ]:
155+ """Return invalid-workspace alias map, using the summary-cache fingerprint.
156+
157+ Computes ``infer_invalid_workspace_aliases`` at most once per storage
158+ fingerprint (same mtime key as composer-map / tab-summary caches). When
159+ *ctx* already carries a populated ``invalid_workspace_aliases`` field,
160+ that value is returned without touching disk or the global DB roster.
161+
162+ Args:
163+ ctx: Workspace maps from :func:`resolve_workspace_context_cached`.
164+ global_db: Open global ``state.vscdb`` connection.
165+ workspace_path: Cursor ``workspaceStorage`` root.
166+ rules: Exclusion rule token lists (fingerprint input).
167+ nocache: When ``True``, bypass disk cache reads and writes.
168+ project_layouts_map: Pre-loaded layouts; loaded from *global_db* when
169+ ``None``.
170+
171+ Returns:
172+ ``{invalid_id: replacement_id}``, or ``{}`` when every workspace is valid.
173+ """
174+ if ctx .invalid_workspace_aliases is not None :
175+ return ctx .invalid_workspace_aliases
176+ if not ctx .invalid_workspace_ids :
177+ return {}
178+
179+ from services .summary_cache import (
180+ fingerprint_workspace_storage ,
181+ get_cached_invalid_workspace_aliases ,
182+ nocache_enabled ,
183+ set_cached_invalid_workspace_aliases ,
184+ )
185+ from utils .workspace_path import get_cli_chats_path
186+
187+ gdb = global_storage_db_path (workspace_path )
188+ cli_path = get_cli_chats_path ()
189+ fingerprint = fingerprint_workspace_storage (
190+ workspace_path ,
191+ ctx .workspace_entries ,
192+ global_db_path = gdb if os .path .isfile (gdb ) else None ,
193+ rules = rules ,
194+ cli_chats_path = cli_path if os .path .isdir (cli_path ) else None ,
195+ )
196+ if not nocache_enabled (request_nocache = nocache ):
197+ cached = get_cached_invalid_workspace_aliases (fingerprint )
198+ if cached is not None :
199+ return cached
200+
201+ layouts = (
202+ project_layouts_map
203+ if project_layouts_map is not None
204+ else load_project_layouts_map (global_db )
205+ )
206+ composer_rows = safe_fetchall (global_db , COMPOSER_ROWS_WITH_HEADERS_SQL )
207+ aliases = infer_invalid_workspace_aliases (
208+ composer_rows = composer_rows ,
209+ project_layouts_map = layouts ,
210+ project_name_map = ctx .project_name_to_workspace_id ,
211+ workspace_path_map = ctx .workspace_path_to_id ,
212+ workspace_entries = ctx .workspace_entries ,
213+ bubble_map = {},
214+ composer_id_to_ws = ctx .composer_id_to_workspace_id ,
215+ invalid_workspace_ids = ctx .invalid_workspace_ids ,
216+ )
217+ if not nocache_enabled (request_nocache = nocache ):
218+ set_cached_invalid_workspace_aliases (fingerprint , aliases )
219+ return aliases
220+
221+
222+ def with_invalid_workspace_aliases (
223+ ctx : WorkspaceContext ,
224+ global_db : sqlite3 .Connection ,
225+ workspace_path : str ,
226+ rules : list [Any ],
227+ * ,
228+ nocache : bool = False ,
229+ project_layouts_map : dict [str , list [str ]] | None = None ,
230+ ) -> WorkspaceContext :
231+ """Return *ctx* with ``invalid_workspace_aliases`` populated from cache."""
232+ if ctx .invalid_workspace_aliases is not None :
233+ return ctx
234+ aliases = resolve_invalid_workspace_aliases_cached (
235+ ctx ,
236+ global_db ,
237+ workspace_path ,
238+ rules ,
239+ nocache = nocache ,
240+ project_layouts_map = project_layouts_map ,
241+ )
242+ return replace (ctx , invalid_workspace_aliases = aliases )
0 commit comments