refactor: stop persisting workspace state in the browser
This commit is contained in:
@@ -4,16 +4,6 @@ import type {
|
||||
} from "../api/workspaces";
|
||||
export { workspacePreferences, type WorkspacePreference } from "./preferences";
|
||||
|
||||
export interface WorkspaceBootstrapDraft {
|
||||
workspaceId: string;
|
||||
baseCommit: string;
|
||||
workspace: CanonicalWorkspace;
|
||||
updatedAt: string;
|
||||
}
|
||||
|
||||
/** @deprecated Use WorkspaceBootstrapDraft. */
|
||||
export type WorkspaceDraft = WorkspaceBootstrapDraft;
|
||||
|
||||
export const WORKSPACE_SUMMARY_ERROR = "Could not load workspace registry. Please retry.";
|
||||
export const WORKSPACE_POLICY_ERROR = "Could not load selected workspace policy. Please retry.";
|
||||
|
||||
@@ -117,14 +107,6 @@ export const workspacePolicyGate = {
|
||||
},
|
||||
};
|
||||
|
||||
const LEGACY_PREFIX = "thothii.workspace-registry.v1";
|
||||
const PREFIX = "thothii.workspace-registry.v2";
|
||||
const DRAFT_PREFIX = `${PREFIX}.bootstrap.`;
|
||||
|
||||
function storage(): Storage | undefined {
|
||||
try { return window.localStorage; } catch { return undefined; }
|
||||
}
|
||||
|
||||
function record(value: unknown): Record<string, unknown> | undefined {
|
||||
return value && typeof value === "object" && !Array.isArray(value)
|
||||
? value as Record<string, unknown>
|
||||
@@ -473,108 +455,3 @@ export function sanitizeCanonicalWorkspace(value: unknown): CanonicalWorkspace |
|
||||
...(evidence ? { evidence } : {}),
|
||||
};
|
||||
}
|
||||
|
||||
function repairBootstrapWorkspace(value: unknown, id: string): CanonicalWorkspace | undefined {
|
||||
const direct = sanitizeCanonicalWorkspace(value);
|
||||
if (direct) return direct;
|
||||
const source = exactRecord(value, [
|
||||
"workspace", "dwh", "semantic_index", "llm_policy", "diagnostics", "evidence",
|
||||
]);
|
||||
const evidence = exactRecord(source?.evidence, ["source", "policy"]);
|
||||
const evidenceSource = exactRecord(evidence?.source, ["type", "uri", "patterns", "max_bytes"]);
|
||||
if (
|
||||
source
|
||||
&& evidence
|
||||
&& evidenceSource?.type === "filesystem"
|
||||
&& typeof evidenceSource.uri === "string"
|
||||
&& /^[a-z][a-z0-9-]{2,62}\/evidence$/.test(evidenceSource.uri)
|
||||
) {
|
||||
return sanitizeCanonicalWorkspace({
|
||||
...source,
|
||||
evidence: {
|
||||
...evidence,
|
||||
source: {
|
||||
...evidenceSource,
|
||||
uri: `${id}/evidence`,
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function normalize(value: unknown): WorkspaceBootstrapDraft | undefined {
|
||||
const source = exactRecord(value, ["workspaceId", "baseCommit", "workspace", "updatedAt"]);
|
||||
const id = workspaceId(source?.workspaceId);
|
||||
const workspace = id ? repairBootstrapWorkspace(source?.workspace, id) : undefined;
|
||||
const baseCommit = typeof source?.baseCommit === "string" && /^[0-9a-f]{40}$/.test(source.baseCommit) ? source.baseCommit : undefined;
|
||||
const updatedAt = typeof source?.updatedAt === "string" && Number.isFinite(Date.parse(source.updatedAt)) ? source.updatedAt : undefined;
|
||||
if (!source || !workspace || !id || id !== workspace.workspace.id || !baseCommit || !updatedAt) {
|
||||
return undefined;
|
||||
}
|
||||
return {
|
||||
workspaceId: id,
|
||||
baseCommit,
|
||||
workspace,
|
||||
updatedAt,
|
||||
};
|
||||
}
|
||||
|
||||
function key(id: string): string {
|
||||
return `${DRAFT_PREFIX}${encodeURIComponent(id)}`;
|
||||
}
|
||||
|
||||
function purgeLegacyRegistryDrafts(): void {
|
||||
const store = storage();
|
||||
if (!store) return;
|
||||
const keys = Array.from({ length: store.length }, (_, index) => store.key(index)).filter((value): value is string => value !== null);
|
||||
for (const entry of keys) {
|
||||
if (entry.startsWith(`${LEGACY_PREFIX}.draft.`) || entry.startsWith(`${LEGACY_PREFIX}.delete.`)) {
|
||||
store.removeItem(entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Browser-only bootstrap drafts. Saving or editing one never calls the server. */
|
||||
export const workspaceBootstrapDrafts = {
|
||||
load(id: string): WorkspaceBootstrapDraft | undefined {
|
||||
purgeLegacyRegistryDrafts();
|
||||
try {
|
||||
const raw = storage()?.getItem(key(id));
|
||||
return raw ? normalize(JSON.parse(raw)) : undefined;
|
||||
} catch {
|
||||
return undefined;
|
||||
}
|
||||
},
|
||||
|
||||
save(draft: WorkspaceBootstrapDraft): void {
|
||||
purgeLegacyRegistryDrafts();
|
||||
const safe = normalize(draft);
|
||||
if (!safe) return;
|
||||
try { storage()?.setItem(key(safe.workspaceId), JSON.stringify(safe)); } catch { /* storage is optional */ }
|
||||
},
|
||||
|
||||
discard(id: string): void {
|
||||
purgeLegacyRegistryDrafts();
|
||||
try { storage()?.removeItem(key(id)); } catch { /* storage is optional */ }
|
||||
},
|
||||
};
|
||||
|
||||
/** @deprecated Use workspaceBootstrapDrafts. */
|
||||
export const workspaceDrafts = workspaceBootstrapDrafts;
|
||||
|
||||
|
||||
/** @deprecated Removed in P1.1; existing workspaces are curator-owned and deletions are not drafted in-browser. */
|
||||
export interface WorkspaceDeletionDraft {
|
||||
id: string;
|
||||
baseCommit: string;
|
||||
baseBlob: string;
|
||||
updatedAt: string;
|
||||
}
|
||||
|
||||
/** @deprecated Removed in P1.1; kept temporarily so legacy imports compile during the UI transition. */
|
||||
export const workspaceDeletionDrafts = {
|
||||
load(_id: string): WorkspaceDeletionDraft | undefined { return undefined; },
|
||||
save(_draft: WorkspaceDeletionDraft): void {},
|
||||
discard(_id: string): void {},
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user