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