@codervisor/devlog-core
Version:
Core devlog management functionality
130 lines (129 loc) • 4.28 kB
JavaScript
/**
* Utilities for querying and analyzing devlog change history
*/
/**
* Extract change records from devlog notes
*/
export function extractChangeRecords(entry) {
if (!entry.notes)
return [];
return entry.notes
.map((note) => note.metadata?.changeRecord)
.filter(Boolean);
}
/**
* Get field change history for a specific field
*/
export function getFieldHistory(entry, fieldName) {
const changeRecords = extractChangeRecords(entry);
return changeRecords
.flatMap((record) => record.changes
.filter((change) => change.fieldName === fieldName)
.map((change) => ({
timestamp: record.timestamp,
previousValue: change.previousValue,
newValue: change.newValue,
source: record.sourceDetails || record.source,
reason: record.reason,
})))
.sort((a, b) => new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime());
}
/**
* Get all field changes for an entry, grouped by timestamp
*/
export function getChangeTimeline(entry) {
const changeRecords = extractChangeRecords(entry);
return changeRecords
.map((record) => ({
timestamp: record.timestamp,
changeType: record.changeType,
source: record.sourceDetails || record.source,
reason: record.reason,
changes: record.changes,
}))
.sort((a, b) => new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime());
}
/**
* Find the most recent value for a field at a specific timestamp
*/
export function getFieldValueAtTime(entry, fieldName, targetTimestamp) {
const history = getFieldHistory(entry, fieldName);
const targetTime = new Date(targetTimestamp).getTime();
// Find the last change before or at the target time
let lastValue = undefined;
for (const change of history) {
const changeTime = new Date(change.timestamp).getTime();
if (changeTime <= targetTime) {
lastValue = change.newValue;
}
else {
break;
}
}
return lastValue !== undefined ? lastValue : entry[fieldName];
}
/**
* Generate a summary of changes made by different sources
*/
export function getChangesSummary(entry) {
const changeRecords = extractChangeRecords(entry);
const changesBySource = {};
const fieldChangeCounts = {};
let totalChanges = 0;
for (const record of changeRecords) {
const source = record.sourceDetails || record.source;
changesBySource[source] = (changesBySource[source] || 0) + 1;
for (const change of record.changes) {
const fieldName = change.fieldName.toString();
fieldChangeCounts[fieldName] = (fieldChangeCounts[fieldName] || 0) + 1;
totalChanges++;
}
}
const mostActiveField = Object.entries(fieldChangeCounts).sort(([, a], [, b]) => b - a)[0]?.[0] || 'none';
return {
totalChanges,
changesBySource,
mostActiveField,
fieldChangeCounts,
};
}
/**
* Generate human-readable change summary for display
*/
export function formatChangeSummary(entry) {
const summary = getChangesSummary(entry);
if (summary.totalChanges === 0) {
return 'No tracked changes';
}
const lines = [
`**Change Summary for "${entry.title}"**`,
`- Total field changes: ${summary.totalChanges}`,
`- Most modified field: ${summary.mostActiveField} (${summary.fieldChangeCounts[summary.mostActiveField]} changes)`,
'',
'**Changes by source:**',
];
for (const [source, count] of Object.entries(summary.changesBySource)) {
lines.push(`- ${source}: ${count} updates`);
}
return lines.join('\n');
}
/**
* Check if a devlog entry has any tracked changes
*/
export function hasTrackedChanges(entry) {
return extractChangeRecords(entry).length > 0;
}
/**
* Get the last person/system that modified a specific field
*/
export function getLastModifier(entry, fieldName) {
const history = getFieldHistory(entry, fieldName);
const lastChange = history[history.length - 1];
if (!lastChange)
return null;
return {
source: lastChange.source,
timestamp: lastChange.timestamp,
reason: lastChange.reason,
};
}