98 lines
2.8 KiB
TypeScript
98 lines
2.8 KiB
TypeScript
/**
|
|
* SubprojectRegistry - Maps composite project IDs to their split data.
|
|
*
|
|
* When multiple sessions in the same encoded directory have different `cwd` values,
|
|
* they are split into separate "subprojects". Each subproject gets a composite ID
|
|
* of the form `{encodedDir}::{sha256(cwd).slice(0,8)}`.
|
|
*
|
|
* This singleton registry tracks:
|
|
* - Which base directory a composite ID maps to
|
|
* - Which cwd each subproject represents
|
|
* - Which session IDs belong to each subproject
|
|
*/
|
|
|
|
import * as crypto from 'crypto';
|
|
|
|
interface SubprojectEntry {
|
|
baseDir: string;
|
|
cwd: string;
|
|
sessionIds: Set<string>;
|
|
}
|
|
|
|
class SubprojectRegistryImpl {
|
|
private readonly entries = new Map<string, SubprojectEntry>();
|
|
|
|
/**
|
|
* Register a subproject and return its composite ID.
|
|
*
|
|
* @param baseDir - The encoded directory name (e.g., "-Users-name-project")
|
|
* @param cwd - The actual working directory for this subproject
|
|
* @param sessionIds - Session IDs belonging to this subproject
|
|
* @returns Composite ID in the form `{baseDir}::{hash}`
|
|
*/
|
|
register(baseDir: string, cwd: string, sessionIds: string[]): string {
|
|
const hash = crypto.createHash('sha256').update(cwd).digest('hex').slice(0, 8);
|
|
const compositeId = `${baseDir}::${hash}`;
|
|
this.entries.set(compositeId, {
|
|
baseDir,
|
|
cwd,
|
|
sessionIds: new Set(sessionIds),
|
|
});
|
|
return compositeId;
|
|
}
|
|
|
|
/**
|
|
* Extract the base directory from any project ID (composite or plain).
|
|
* For composite IDs (`{encoded}::{hash}`), returns the encoded part.
|
|
* For plain IDs, returns the ID as-is.
|
|
*/
|
|
getBaseDir(projectId: string): string {
|
|
const sep = projectId.indexOf('::');
|
|
if (sep !== -1) {
|
|
return projectId.slice(0, sep);
|
|
}
|
|
return projectId;
|
|
}
|
|
|
|
/**
|
|
* Check if a project ID is a composite (split) ID.
|
|
*/
|
|
isComposite(projectId: string): boolean {
|
|
return projectId.includes('::');
|
|
}
|
|
|
|
/**
|
|
* Get the session ID filter set for a composite project ID.
|
|
* Returns null for plain (non-composite) IDs.
|
|
*/
|
|
getSessionFilter(projectId: string): Set<string> | null {
|
|
const entry = this.entries.get(projectId);
|
|
return entry?.sessionIds ?? null;
|
|
}
|
|
|
|
/**
|
|
* Get the cwd for a composite project ID.
|
|
* Returns null for plain (non-composite) IDs.
|
|
*/
|
|
getCwd(projectId: string): string | null {
|
|
const entry = this.entries.get(projectId);
|
|
return entry?.cwd ?? null;
|
|
}
|
|
|
|
/**
|
|
* Get the full entry for a composite project ID.
|
|
*/
|
|
getEntry(projectId: string): SubprojectEntry | undefined {
|
|
return this.entries.get(projectId);
|
|
}
|
|
|
|
/**
|
|
* Clear all registered subprojects. Called at the start of a full re-scan.
|
|
*/
|
|
clear(): void {
|
|
this.entries.clear();
|
|
}
|
|
}
|
|
|
|
/** Module-level singleton */
|
|
export const subprojectRegistry = new SubprojectRegistryImpl();
|