Kern-Typen: Event-System, Settings, Query-Interfaces
Alle Event-Typen (file, content, nav, editor, vault, plugin, system), ContentSnapshot/Delta, QueryConfig, ProjectionTemplate, LogfireSettings mit Defaults, deepMerge-Utility. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
4d31129090
commit
d5dfdd6b0d
1 changed files with 348 additions and 0 deletions
348
src/types.ts
Normal file
348
src/types.ts
Normal file
|
|
@ -0,0 +1,348 @@
|
|||
// ---------------------------------------------------------------------------
|
||||
// Event System
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export type EventCategory =
|
||||
| 'file'
|
||||
| 'content'
|
||||
| 'navigation'
|
||||
| 'editor'
|
||||
| 'vault'
|
||||
| 'plugin'
|
||||
| 'system';
|
||||
|
||||
export type EventType =
|
||||
// File events
|
||||
| 'file:create'
|
||||
| 'file:delete'
|
||||
| 'file:rename'
|
||||
| 'file:move'
|
||||
| 'file:modify'
|
||||
// Content events (semantic diffs)
|
||||
| 'content:words-changed'
|
||||
| 'content:link-added'
|
||||
| 'content:link-removed'
|
||||
| 'content:tag-added'
|
||||
| 'content:tag-removed'
|
||||
| 'content:frontmatter-changed'
|
||||
| 'content:heading-changed'
|
||||
| 'content:embed-added'
|
||||
| 'content:embed-removed'
|
||||
// Navigation events
|
||||
| 'nav:file-open'
|
||||
| 'nav:file-close'
|
||||
| 'nav:active-leaf-change'
|
||||
// Editor events
|
||||
| 'editor:change'
|
||||
| 'editor:selection'
|
||||
| 'editor:paste'
|
||||
// Vault structure events
|
||||
| 'vault:folder-create'
|
||||
| 'vault:folder-delete'
|
||||
| 'vault:folder-rename'
|
||||
// System events
|
||||
| 'system:session-start'
|
||||
| 'system:session-end'
|
||||
| 'system:baseline-scan'
|
||||
| 'system:rescan'
|
||||
| 'system:maintenance'
|
||||
// Plugin events
|
||||
| 'plugin:command-executed'
|
||||
// Custom events (from other plugins via API)
|
||||
| `custom:${string}`;
|
||||
|
||||
export interface LogfireEvent {
|
||||
id: string;
|
||||
timestamp: number;
|
||||
type: EventType;
|
||||
category: EventCategory;
|
||||
source: string;
|
||||
target?: string;
|
||||
payload: Record<string, unknown>;
|
||||
session: string;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Category mapping
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
const CATEGORY_MAP: Record<string, EventCategory> = {
|
||||
file: 'file',
|
||||
content: 'content',
|
||||
nav: 'navigation',
|
||||
editor: 'editor',
|
||||
vault: 'vault',
|
||||
plugin: 'plugin',
|
||||
system: 'system',
|
||||
custom: 'plugin',
|
||||
};
|
||||
|
||||
export function categoryForType(type: EventType): EventCategory {
|
||||
const prefix = type.split(':')[0];
|
||||
return CATEGORY_MAP[prefix] ?? 'system';
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Content Analysis
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export interface ContentSnapshot {
|
||||
path: string;
|
||||
wordCount: number;
|
||||
charCount: number;
|
||||
links: string[];
|
||||
tags: string[];
|
||||
headings: { level: number; text: string }[];
|
||||
frontmatter: Record<string, unknown>;
|
||||
embeds: string[];
|
||||
}
|
||||
|
||||
export interface ContentDelta {
|
||||
wordsAdded: number;
|
||||
wordsRemoved: number;
|
||||
linksAdded: string[];
|
||||
linksRemoved: string[];
|
||||
tagsAdded: string[];
|
||||
tagsRemoved: string[];
|
||||
headingsAdded: { level: number; text: string }[];
|
||||
headingsRemoved: { level: number; text: string }[];
|
||||
embedsAdded: string[];
|
||||
embedsRemoved: string[];
|
||||
frontmatterChanged: boolean;
|
||||
frontmatterChanges: Record<string, { old: unknown; new: unknown }>;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Query System
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export type TimeRange =
|
||||
| { type: 'relative'; value: 'today' | 'yesterday' | 'this-week' | 'this-month' | 'last-7-days' | 'last-30-days' }
|
||||
| { type: 'absolute'; from: number; to: number }
|
||||
| { type: 'session'; sessionId?: string };
|
||||
|
||||
export interface QueryConfig {
|
||||
timeRange: TimeRange;
|
||||
eventTypes?: EventType[];
|
||||
categories?: EventCategory[];
|
||||
filePaths?: string[];
|
||||
groupBy?: 'file' | 'type' | 'hour' | 'day' | 'session';
|
||||
orderBy?: 'timestamp' | 'count' | 'words';
|
||||
orderDirection?: 'asc' | 'desc';
|
||||
limit?: number;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Projections
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export interface ProjectionTemplate {
|
||||
id: string;
|
||||
name: string;
|
||||
description: string;
|
||||
enabled: boolean;
|
||||
trigger: ProjectionTrigger;
|
||||
output: OutputConfig;
|
||||
sections: SectionConfig[];
|
||||
}
|
||||
|
||||
export interface ProjectionTrigger {
|
||||
type: 'interval' | 'on-session-end' | 'manual' | 'cron';
|
||||
interval?: number;
|
||||
cron?: string;
|
||||
}
|
||||
|
||||
export interface OutputConfig {
|
||||
folder: string;
|
||||
filePattern: string;
|
||||
mode: 'overwrite' | 'append';
|
||||
frontmatter: Record<string, string>;
|
||||
}
|
||||
|
||||
export interface SectionConfig {
|
||||
id: string;
|
||||
heading: string;
|
||||
type: SectionType;
|
||||
query: QueryConfig;
|
||||
format: FormatConfig;
|
||||
enabled: boolean;
|
||||
}
|
||||
|
||||
export type SectionType = 'timeline' | 'table' | 'summary' | 'chart-data' | 'raw-events' | 'custom';
|
||||
|
||||
export interface FormatConfig {
|
||||
type: 'builtin' | 'custom';
|
||||
builtin?: BuiltinFormat;
|
||||
customTemplate?: string;
|
||||
}
|
||||
|
||||
export type BuiltinFormat =
|
||||
| { name: 'timeline'; showTimestamp: boolean; showPayload: boolean }
|
||||
| { name: 'table'; columns: ColumnDef[] }
|
||||
| { name: 'summary'; metrics: MetricDef[] }
|
||||
| { name: 'metric'; aggregate: 'count' | 'sum' | 'avg' | 'min' | 'max'; field?: string };
|
||||
|
||||
export interface ColumnDef {
|
||||
header: string;
|
||||
value: string;
|
||||
align?: 'left' | 'right' | 'center';
|
||||
}
|
||||
|
||||
export interface MetricDef {
|
||||
label: string;
|
||||
aggregate: 'count' | 'sum' | 'avg' | 'min' | 'max';
|
||||
field?: string;
|
||||
filter?: Partial<QueryConfig>;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Settings
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export interface LogfireSettings {
|
||||
settingsVersion: number;
|
||||
|
||||
general: {
|
||||
logFolder: string;
|
||||
enabled: boolean;
|
||||
pauseOnStartup: boolean;
|
||||
};
|
||||
|
||||
tracking: {
|
||||
fileEvents: boolean;
|
||||
contentAnalysis: boolean;
|
||||
navigation: boolean;
|
||||
editorChanges: boolean;
|
||||
commandTracking: boolean;
|
||||
excludedFolders: string[];
|
||||
excludedPatterns: string[];
|
||||
};
|
||||
|
||||
projections: {
|
||||
templates: ProjectionTemplate[];
|
||||
};
|
||||
|
||||
advanced: {
|
||||
debounceMs: number;
|
||||
flushIntervalMs: number;
|
||||
flushThreshold: number;
|
||||
retention: RetentionSettings;
|
||||
database: DatabaseSettings;
|
||||
};
|
||||
}
|
||||
|
||||
export interface RetentionSettings {
|
||||
rawEventsDays: number;
|
||||
dailyStatsDays: number;
|
||||
monthlyStatsForever: boolean;
|
||||
neverDeleteTypes: EventType[];
|
||||
maintenanceOnStartup: boolean;
|
||||
}
|
||||
|
||||
export interface DatabaseSettings {
|
||||
cacheSizeMb: number;
|
||||
walMode: boolean;
|
||||
mmapSizeMb: number;
|
||||
}
|
||||
|
||||
export const DEFAULT_SETTINGS: LogfireSettings = {
|
||||
settingsVersion: 1,
|
||||
|
||||
general: {
|
||||
logFolder: 'Logfire',
|
||||
enabled: true,
|
||||
pauseOnStartup: false,
|
||||
},
|
||||
|
||||
tracking: {
|
||||
fileEvents: true,
|
||||
contentAnalysis: true,
|
||||
navigation: true,
|
||||
editorChanges: true,
|
||||
commandTracking: true,
|
||||
excludedFolders: ['.obsidian'],
|
||||
excludedPatterns: [],
|
||||
},
|
||||
|
||||
projections: {
|
||||
templates: [],
|
||||
},
|
||||
|
||||
advanced: {
|
||||
debounceMs: 3000,
|
||||
flushIntervalMs: 5000,
|
||||
flushThreshold: 100,
|
||||
retention: {
|
||||
rawEventsDays: 30,
|
||||
dailyStatsDays: 365,
|
||||
monthlyStatsForever: true,
|
||||
neverDeleteTypes: ['file:create', 'file:delete', 'file:rename'],
|
||||
maintenanceOnStartup: true,
|
||||
},
|
||||
database: {
|
||||
cacheSizeMb: 64,
|
||||
walMode: true,
|
||||
mmapSizeMb: 256,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// API types
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export interface VaultStats {
|
||||
eventsTotal: number;
|
||||
wordsAdded: number;
|
||||
wordsRemoved: number;
|
||||
filesCreated: number;
|
||||
filesModified: number;
|
||||
filesDeleted: number;
|
||||
sessionCount: number;
|
||||
activeTimeMs: number;
|
||||
}
|
||||
|
||||
export interface FileStats {
|
||||
path: string;
|
||||
eventsTotal: number;
|
||||
wordsAdded: number;
|
||||
wordsRemoved: number;
|
||||
activeTimeMs: number;
|
||||
lastModified: number;
|
||||
editSessions: number;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Utilities
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export function deepMerge<T>(defaults: T, overrides: Partial<T>): T {
|
||||
if (
|
||||
typeof defaults !== 'object' || defaults === null ||
|
||||
typeof overrides !== 'object' || overrides === null
|
||||
) {
|
||||
return (overrides ?? defaults) as T;
|
||||
}
|
||||
|
||||
const result = { ...defaults } as Record<string, unknown>;
|
||||
const src = overrides as Record<string, unknown>;
|
||||
const def = defaults as Record<string, unknown>;
|
||||
|
||||
for (const key of Object.keys(src)) {
|
||||
const defaultVal = def[key];
|
||||
const overrideVal = src[key];
|
||||
|
||||
if (
|
||||
defaultVal !== null && defaultVal !== undefined &&
|
||||
overrideVal !== null && overrideVal !== undefined &&
|
||||
typeof defaultVal === 'object' && !Array.isArray(defaultVal) &&
|
||||
typeof overrideVal === 'object' && !Array.isArray(overrideVal)
|
||||
) {
|
||||
result[key] = deepMerge(defaultVal, overrideVal);
|
||||
} else if (overrideVal !== undefined) {
|
||||
result[key] = overrideVal;
|
||||
}
|
||||
}
|
||||
|
||||
return result as T;
|
||||
}
|
||||
Loading…
Reference in a new issue