Commits merged: - feat(FN-3860): complete Step 8 — documentation and delivery - test(FN-3860): complete Step 7 — add scaffold seam coverage - feat(FN-3860): complete Step 6 — re-export scaffold seams - feat(FN-3860): complete Step 5 — add pipeline orchestrator seam - feat(FN-3860): complete Step 4 — add in-memory runs store seam - feat(FN-3860): complete Step 3 — add aggregation seam - feat(FN-3860): complete Step 2 — add cadence resolution seam - feat(FN-3846): short-circuit phantom merges for already-landed branches Files changed: .changeset/fn-3860-reports-scaffold-files.md | 5 ++ plugins/fusion-plugin-reports/README.md | 9 +++ .../src/__tests__/scaffold.test.ts | 60 +++++++++++++++++++ plugins/fusion-plugin-reports/src/aggregation.ts | 23 ++++++++ plugins/fusion-plugin-reports/src/cadence.ts | 23 ++++++++ plugins/fusion-plugin-reports/src/index.ts | 4 ++ plugins/fusion-plugin-reports/src/pipeline.ts | 58 ++++++++++++++++++ plugins/fusion-plugin-reports/src/runs-store.ts | 69 ++++++++++++++++++++++ 8 files changed, 251 insertions(+) Fusion-Task-Id: FN-3860
70 lines
2.0 KiB
TypeScript
70 lines
2.0 KiB
TypeScript
/**
|
|
* INTERIM IN-MEMORY STORE — replaced by FN-3784's persistent storage.
|
|
* The `ReportRunRecord` shape is intended to be a strict subset of FN-3784's eventual schema.
|
|
*/
|
|
import type { ReportsCadence } from "./cadence.js";
|
|
|
|
export type ReportRunStatus = "queued" | "running" | "review" | "approved" | "published" | "failed";
|
|
|
|
export interface ReportRunRecord {
|
|
id: string;
|
|
cadence: ReportsCadence;
|
|
status: ReportRunStatus;
|
|
createdAt: string;
|
|
updatedAt: string;
|
|
error?: string;
|
|
reportId?: string;
|
|
}
|
|
|
|
export interface ReportsRunsStore {
|
|
create(record: ReportRunRecord): Promise<ReportRunRecord>;
|
|
update(
|
|
id: string,
|
|
patch: Partial<Omit<ReportRunRecord, "id" | "createdAt">>,
|
|
): Promise<ReportRunRecord | undefined>;
|
|
get(id: string): Promise<ReportRunRecord | undefined>;
|
|
list(limit?: number): Promise<ReportRunRecord[]>;
|
|
}
|
|
|
|
function cloneRecord(record: ReportRunRecord): ReportRunRecord {
|
|
return { ...record };
|
|
}
|
|
|
|
export function createInMemoryReportsRunsStore(seed: ReportRunRecord[] = []): ReportsRunsStore {
|
|
const records = new Map<string, ReportRunRecord>(seed.map((record) => [record.id, cloneRecord(record)]));
|
|
|
|
return {
|
|
async create(record) {
|
|
const stored = cloneRecord(record);
|
|
records.set(stored.id, stored);
|
|
return cloneRecord(stored);
|
|
},
|
|
|
|
async update(id, patch) {
|
|
const current = records.get(id);
|
|
if (!current) return undefined;
|
|
|
|
const next: ReportRunRecord = {
|
|
...current,
|
|
...patch,
|
|
updatedAt: patch.updatedAt ?? new Date().toISOString(),
|
|
};
|
|
records.set(id, next);
|
|
return cloneRecord(next);
|
|
},
|
|
|
|
async get(id) {
|
|
const record = records.get(id);
|
|
return record ? cloneRecord(record) : undefined;
|
|
},
|
|
|
|
async list(limit = 50) {
|
|
const clampedLimit = Math.max(0, limit);
|
|
return Array.from(records.values())
|
|
.sort((a, b) => b.createdAt.localeCompare(a.createdAt))
|
|
.slice(0, clampedLimit)
|
|
.map((record) => cloneRecord(record));
|
|
},
|
|
};
|
|
}
|