feat(FN-4313): complete Step 1 — add central default project setting

Fusion-Task-Id: FN-4313
Fusion-Task-Lineage: 36b8aad3-20f4-475b-9b0f-63e5458d1b91
This commit is contained in:
Fusion
2026-05-13 22:24:27 -07:00
committed by gsxdsm
parent edbf093b52
commit 74f3320312
3 changed files with 102 additions and 1 deletions

View File

@@ -2378,6 +2378,58 @@ describe("CentralCore", () => {
});
});
describe("default project setting", () => {
beforeEach(async () => {
await central.init();
});
it("should be undefined by default", async () => {
await expect(central.getDefaultProjectId()).resolves.toBeUndefined();
});
it("should set/get and clear default project id", async () => {
const projectPath = join(tempDir, "default-project");
mkdirSync(projectPath);
projectPaths.push(projectPath);
const project = await central.registerProject({
name: "Default Project",
path: projectPath,
});
await central.setDefaultProjectId(project.id);
await expect(central.getDefaultProjectId()).resolves.toBe(project.id);
await central.setDefaultProjectId(null);
await expect(central.getDefaultProjectId()).resolves.toBeUndefined();
});
it("should reject unknown project id", async () => {
await expect(central.setDefaultProjectId("missing-project-id")).rejects.toThrow(
"Cannot set default project: project not found: missing-project-id",
);
});
it("should persist setting across reopen", async () => {
const projectPath = join(tempDir, "persisted-default-project");
mkdirSync(projectPath);
projectPaths.push(projectPath);
const project = await central.registerProject({
name: "Persisted Default Project",
path: projectPath,
});
await central.setDefaultProjectId(project.id);
await central.close();
central = new CentralCore(tempDir);
await central.init();
await expect(central.getDefaultProjectId()).resolves.toBe(project.id);
});
});
describe("global concurrency", () => {
beforeEach(async () => {
await central.init();

View File

@@ -2424,6 +2424,31 @@ export class CentralCore extends EventEmitter<CentralCoreEvents> {
return deletedCount;
}
async getDefaultProjectId(): Promise<string | undefined> {
this.ensureInitialized();
const row = this.db!.prepare("SELECT defaultProjectId FROM centralSettings WHERE id = 1").get() as
| { defaultProjectId: string | null }
| undefined;
return row?.defaultProjectId ?? undefined;
}
async setDefaultProjectId(projectId: string | null): Promise<void> {
this.ensureInitialized();
if (projectId !== null) {
const project = await this.getProject(projectId);
if (!project) {
throw new Error(`Cannot set default project: project not found: ${projectId}`);
}
}
this.db!
.prepare("UPDATE centralSettings SET defaultProjectId = ?, updatedAt = ? WHERE id = 1")
.run(projectId, new Date().toISOString());
}
// ── Global Concurrency API ─────────────────────────────────────────────
/**

View File

@@ -29,7 +29,7 @@ export { toJson, toJsonNullable, fromJson };
// ── Schema Definition ───────────────────────────────────────────────────
const CENTRAL_SCHEMA_VERSION = 10;
const CENTRAL_SCHEMA_VERSION = 11;
const CENTRAL_SCHEMA_SQL = `
-- Projects table (project registry)
@@ -107,6 +107,15 @@ CREATE TABLE IF NOT EXISTS globalConcurrency (
INSERT OR IGNORE INTO globalConcurrency (id, globalMaxConcurrent, currentlyActive, queuedCount)
VALUES (1, 4, 0, 0);
-- Central settings (single row)
CREATE TABLE IF NOT EXISTS centralSettings (
id INTEGER PRIMARY KEY CHECK (id = 1),
defaultProjectId TEXT,
updatedAt TEXT NOT NULL
);
INSERT OR IGNORE INTO centralSettings (id, defaultProjectId, updatedAt)
VALUES (1, NULL, CURRENT_TIMESTAMP);
-- Nodes table (runtime hosts for project execution)
CREATE TABLE IF NOT EXISTS nodes (
id TEXT PRIMARY KEY,
@@ -433,6 +442,16 @@ CREATE TABLE IF NOT EXISTS meshWriteQueue (
CREATE INDEX IF NOT EXISTS idxMeshWriteQueueReplay ON meshWriteQueue(targetNodeId, status, createdAt, id);
`;
const CENTRAL_SCHEMA_V11_MIGRATION_SQL = `
CREATE TABLE IF NOT EXISTS centralSettings (
id INTEGER PRIMARY KEY CHECK (id = 1),
defaultProjectId TEXT,
updatedAt TEXT NOT NULL
);
INSERT OR IGNORE INTO centralSettings (id, defaultProjectId, updatedAt)
VALUES (1, NULL, CURRENT_TIMESTAMP);
`;
// ── Central Database Class ────────────────────────────────────────────────
export class CentralDatabase {
@@ -575,6 +594,11 @@ export class CentralDatabase {
migrated = true;
}
if (currentVersion < 11) {
this.db.exec(CENTRAL_SCHEMA_V11_MIGRATION_SQL);
migrated = true;
}
if (migrated) {
this.db
.prepare("INSERT INTO __meta (key, value) VALUES ('schemaVersion', ?) ON CONFLICT(key) DO UPDATE SET value = excluded.value")