@a-company/sentinel 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,644 @@
1
+ /**
2
+ * Paradigm Sentinel - Type Definitions
3
+ *
4
+ * Comprehensive types for semantic incident recording, pattern matching,
5
+ * and failure triage.
6
+ */
7
+ type IncidentStatus = 'open' | 'investigating' | 'resolved' | 'wont-fix';
8
+ type Environment = 'production' | 'staging' | 'development' | 'test' | string;
9
+ interface ErrorDetails {
10
+ message: string;
11
+ stack?: string;
12
+ code?: string;
13
+ type?: string;
14
+ }
15
+ /**
16
+ * Symbolic context for incidents
17
+ *
18
+ * v2 Note: feature, state, and integration are now #component with tags.
19
+ * These fields are kept for backward compatibility with existing incidents.
20
+ * New incidents should use 'component' with appropriate tags.
21
+ */
22
+ interface SymbolicContext {
23
+ /** @deprecated v2: Use component with tags: [feature] */
24
+ feature?: string;
25
+ component?: string;
26
+ flow?: string;
27
+ gate?: string;
28
+ signal?: string;
29
+ /** @deprecated v2: Use component with tags: [state] */
30
+ state?: string;
31
+ /** @deprecated v2: Use component with tags: [integration] */
32
+ integration?: string;
33
+ }
34
+ interface FlowPosition {
35
+ flowId: string;
36
+ expected: string[];
37
+ actual: string[];
38
+ missing: string[];
39
+ failedAt?: string;
40
+ }
41
+ interface IncidentNote {
42
+ id: string;
43
+ timestamp: string;
44
+ author?: string;
45
+ content: string;
46
+ }
47
+ interface Resolution {
48
+ patternId?: string;
49
+ commitHash?: string;
50
+ prUrl?: string;
51
+ notes?: string;
52
+ }
53
+ interface SymbolicIncidentRecord {
54
+ id: string;
55
+ timestamp: string;
56
+ status: IncidentStatus;
57
+ error: ErrorDetails;
58
+ symbols: SymbolicContext;
59
+ flowPosition?: FlowPosition;
60
+ environment: Environment;
61
+ service?: string;
62
+ version?: string;
63
+ userId?: string;
64
+ requestId?: string;
65
+ groupId?: string;
66
+ notes: IncidentNote[];
67
+ relatedIncidents: string[];
68
+ resolvedAt?: string;
69
+ resolvedBy?: string;
70
+ resolution?: Resolution;
71
+ }
72
+ type CreateIncidentInput = Omit<SymbolicIncidentRecord, 'id' | 'notes' | 'relatedIncidents' | 'timestamp' | 'status'> & {
73
+ timestamp?: string;
74
+ status?: IncidentStatus;
75
+ };
76
+ type PatternSource = 'manual' | 'suggested' | 'imported' | 'community';
77
+ type ResolutionStrategy = 'retry' | 'fallback' | 'fix-data' | 'fix-code' | 'ignore' | 'escalate';
78
+ type PatternPriority = 'low' | 'medium' | 'high' | 'critical';
79
+ /**
80
+ * Symbol criteria for pattern matching
81
+ *
82
+ * v2 Note: feature, state, and integration are now #component with tags.
83
+ * These fields are kept for backward compatibility with existing patterns.
84
+ */
85
+ interface PatternSymbolCriteria {
86
+ /** @deprecated v2: Use component with tags filter */
87
+ feature?: string | string[];
88
+ component?: string | string[];
89
+ flow?: string | string[];
90
+ gate?: string | string[];
91
+ signal?: string | string[];
92
+ /** @deprecated v2: Use component with tags filter */
93
+ state?: string | string[];
94
+ /** @deprecated v2: Use component with tags filter */
95
+ integration?: string | string[];
96
+ /** v2: Filter by tags instead of legacy symbol types */
97
+ tags?: string | string[];
98
+ }
99
+ interface PatternCriteria {
100
+ symbols: PatternSymbolCriteria;
101
+ errorContains?: string[];
102
+ errorMatches?: string;
103
+ errorType?: string[];
104
+ missingSignals?: string[];
105
+ environment?: string[];
106
+ }
107
+ interface PatternResolution {
108
+ description: string;
109
+ strategy: ResolutionStrategy;
110
+ priority: PatternPriority;
111
+ codeHint?: string;
112
+ codeSnippet?: string;
113
+ symbolsToModify?: string[];
114
+ filesLikelyInvolved?: string[];
115
+ commitRef?: string;
116
+ prRef?: string;
117
+ docsRef?: string;
118
+ }
119
+ interface PatternConfidence {
120
+ score: number;
121
+ timesMatched: number;
122
+ timesResolved: number;
123
+ timesRecurred: number;
124
+ avgTimeToResolve?: number;
125
+ lastMatched?: string;
126
+ lastResolved?: string;
127
+ }
128
+ interface FailurePattern {
129
+ id: string;
130
+ name: string;
131
+ description: string;
132
+ pattern: PatternCriteria;
133
+ resolution: PatternResolution;
134
+ confidence: PatternConfidence;
135
+ source: PatternSource;
136
+ private: boolean;
137
+ tags: string[];
138
+ createdAt: string;
139
+ updatedAt: string;
140
+ }
141
+ type CreatePatternInput = Omit<FailurePattern, 'confidence' | 'createdAt' | 'updatedAt'> & {
142
+ confidence?: Partial<PatternConfidence>;
143
+ };
144
+ interface MatchedCriteria {
145
+ symbols: string[];
146
+ errorKeywords: string[];
147
+ missingSignals: string[];
148
+ }
149
+ interface PatternMatch {
150
+ pattern: FailurePattern;
151
+ score: number;
152
+ matchedCriteria: MatchedCriteria;
153
+ confidence: number;
154
+ }
155
+ interface MatcherConfig {
156
+ minScore: number;
157
+ maxResults: number;
158
+ boostConfidence: boolean;
159
+ }
160
+ interface IncidentGroup {
161
+ id: string;
162
+ name?: string;
163
+ incidents: string[];
164
+ commonSymbols: Partial<SymbolicContext>;
165
+ commonErrorPatterns: string[];
166
+ count: number;
167
+ firstSeen: string;
168
+ lastSeen: string;
169
+ environments: string[];
170
+ suggestedPattern?: FailurePattern;
171
+ }
172
+ type CreateGroupInput = Omit<IncidentGroup, 'id' | 'count'>;
173
+ type FlowEventType = 'gate-passed' | 'gate-failed' | 'signal-emitted' | 'state-changed' | 'flow-started' | 'flow-ended' | 'error';
174
+ interface FlowEvent {
175
+ timestamp: string;
176
+ symbol: string;
177
+ type: FlowEventType;
178
+ data?: Record<string, unknown>;
179
+ }
180
+ interface FlowTimeline {
181
+ incidentId: string;
182
+ flowId: string;
183
+ events: FlowEvent[];
184
+ failure: {
185
+ at: string;
186
+ symbol: string;
187
+ reason: string;
188
+ };
189
+ }
190
+ interface DayCount {
191
+ date: string;
192
+ count: number;
193
+ }
194
+ interface PatternEffectiveness {
195
+ patternId: string;
196
+ resolvedCount: number;
197
+ }
198
+ interface PatternRecurrence {
199
+ patternId: string;
200
+ recurrenceRate: number;
201
+ }
202
+ interface SymbolIncidentCount {
203
+ symbol: string;
204
+ count: number;
205
+ }
206
+ interface SymbolResolutionTime {
207
+ symbol: string;
208
+ avgTimeToResolve: number;
209
+ }
210
+ interface SymbolHotspot {
211
+ symbol: string;
212
+ incidentRate: number;
213
+ }
214
+ interface SentinelStats {
215
+ period: {
216
+ start: string;
217
+ end: string;
218
+ };
219
+ incidents: {
220
+ total: number;
221
+ open: number;
222
+ resolved: number;
223
+ byEnvironment: Record<string, number>;
224
+ byDay: DayCount[];
225
+ };
226
+ patterns: {
227
+ total: number;
228
+ avgConfidence: number;
229
+ mostEffective: PatternEffectiveness[];
230
+ leastEffective: PatternRecurrence[];
231
+ };
232
+ symbols: {
233
+ mostIncidents: SymbolIncidentCount[];
234
+ mostResolved: SymbolResolutionTime[];
235
+ hotspots: SymbolHotspot[];
236
+ };
237
+ resolution: {
238
+ avgTimeToResolve: number;
239
+ resolvedWithPattern: number;
240
+ resolvedManually: number;
241
+ resolutionRate: number;
242
+ };
243
+ }
244
+ interface SymbolHealth {
245
+ incidentCount: number;
246
+ avgTimeToResolve: number;
247
+ topPatterns: {
248
+ patternId: string;
249
+ count: number;
250
+ }[];
251
+ }
252
+ interface PatternExport {
253
+ version: string;
254
+ exportedAt: string;
255
+ patterns: FailurePattern[];
256
+ }
257
+ interface BackupExport {
258
+ version: string;
259
+ exportedAt: string;
260
+ incidents: SymbolicIncidentRecord[];
261
+ patterns: FailurePattern[];
262
+ groups: IncidentGroup[];
263
+ }
264
+ interface SymbolEnrichment {
265
+ description?: string;
266
+ definedIn?: string;
267
+ references?: string[];
268
+ referencedBy?: string[];
269
+ }
270
+ interface EnrichedIncident extends SymbolicIncidentRecord {
271
+ enriched: {
272
+ symbols: Record<string, SymbolEnrichment>;
273
+ flowDescription?: string;
274
+ };
275
+ }
276
+ interface IncidentQueryOptions {
277
+ limit?: number;
278
+ offset?: number;
279
+ status?: IncidentStatus | 'all';
280
+ environment?: string;
281
+ symbol?: string;
282
+ search?: string;
283
+ dateFrom?: string;
284
+ dateTo?: string;
285
+ groupId?: string;
286
+ }
287
+ interface PatternQueryOptions {
288
+ source?: PatternSource;
289
+ minConfidence?: number;
290
+ tags?: string[];
291
+ includePrivate?: boolean;
292
+ }
293
+ interface ResolutionRecord {
294
+ id: string;
295
+ incidentId: string;
296
+ patternId?: string;
297
+ commitHash?: string;
298
+ prUrl?: string;
299
+ notes?: string;
300
+ resolvedAt: string;
301
+ recurred: boolean;
302
+ }
303
+ interface ResolutionQueryOptions {
304
+ symbol?: string;
305
+ patternId?: string;
306
+ limit?: number;
307
+ }
308
+ interface PatternCandidate {
309
+ incidents: SymbolicIncidentRecord[];
310
+ suggestedPattern: Partial<FailurePattern>;
311
+ occurrenceCount: number;
312
+ }
313
+ interface PatternTestResult {
314
+ wouldMatch: SymbolicIncidentRecord[];
315
+ matchCount: number;
316
+ avgScore: number;
317
+ }
318
+ interface SentinelConfig {
319
+ /** Project name */
320
+ project: string;
321
+ /** Default environment for captured incidents */
322
+ environment?: Environment;
323
+ /** Service/app name */
324
+ service?: string;
325
+ /** App version */
326
+ version?: string;
327
+ /** Custom SQLite database path */
328
+ dbPath?: string;
329
+ /** Hook called after each incident capture */
330
+ onCapture?: (incident: SymbolicIncidentRecord) => void;
331
+ }
332
+ interface ComponentContext {
333
+ /** Component symbol ID (e.g. '#checkout') */
334
+ id: string;
335
+ /** Capture an error in this component's context */
336
+ capture(error: Error, extra?: Record<string, unknown>): string;
337
+ /** Wrap a function to auto-capture errors in this component's context */
338
+ wrap<T extends (...args: any[]) => any>(fn: T): T;
339
+ }
340
+ type PracticeResult = 'followed' | 'skipped' | 'partial';
341
+ type PracticeCategory = 'discovery' | 'verification' | 'testing' | 'documentation' | 'collaboration' | 'security';
342
+ interface PracticeEvent {
343
+ id: string;
344
+ timestamp: string;
345
+ habitId: string;
346
+ habitCategory: PracticeCategory;
347
+ result: PracticeResult;
348
+ engineer: string;
349
+ sessionId: string;
350
+ loreEntryId?: string;
351
+ taskDescription?: string;
352
+ symbolsTouched: string[];
353
+ filesModified: string[];
354
+ relatedIncidentId?: string;
355
+ notes?: string;
356
+ }
357
+ interface PracticeEventInput {
358
+ habitId: string;
359
+ habitCategory: PracticeCategory;
360
+ result: PracticeResult;
361
+ engineer: string;
362
+ sessionId: string;
363
+ loreEntryId?: string;
364
+ taskDescription?: string;
365
+ symbolsTouched?: string[];
366
+ filesModified?: string[];
367
+ relatedIncidentId?: string;
368
+ notes?: string;
369
+ }
370
+ interface PracticeEventQuery {
371
+ habitId?: string;
372
+ habitCategory?: PracticeCategory;
373
+ result?: PracticeResult;
374
+ engineer?: string;
375
+ sessionId?: string;
376
+ dateFrom?: string;
377
+ dateTo?: string;
378
+ limit?: number;
379
+ offset?: number;
380
+ }
381
+
382
+ /**
383
+ * Paradigm Sentinel - SQLite Storage Layer
384
+ *
385
+ * Persistent storage for incidents, patterns, groups, and resolutions.
386
+ * Uses sql.js for pure JavaScript SQLite (no native compilation needed).
387
+ */
388
+
389
+ declare class SentinelStorage {
390
+ private db;
391
+ private dbPath;
392
+ private incidentCounter;
393
+ private initialized;
394
+ constructor(dbPath?: string);
395
+ private getDefaultDbPath;
396
+ private createSchema;
397
+ private save;
398
+ recordIncident(input: CreateIncidentInput): string;
399
+ private initializeSync;
400
+ /**
401
+ * Run schema migrations from older versions
402
+ */
403
+ private migrateSchema;
404
+ /**
405
+ * Ensure the storage is ready for use. Must be called once before using storage methods.
406
+ */
407
+ ensureReady(): Promise<void>;
408
+ getIncident(id: string): SymbolicIncidentRecord | null;
409
+ getRecentIncidents(options?: IncidentQueryOptions): SymbolicIncidentRecord[];
410
+ updateIncident(id: string, updates: Partial<SymbolicIncidentRecord>): void;
411
+ addIncidentNote(incidentId: string, note: Omit<IncidentNote, 'id'>): void;
412
+ linkIncidents(incidentId: string, relatedId: string): void;
413
+ getIncidentCount(options?: IncidentQueryOptions): number;
414
+ addPattern(input: CreatePatternInput): string;
415
+ getPattern(id: string): FailurePattern | null;
416
+ getAllPatterns(options?: PatternQueryOptions): FailurePattern[];
417
+ updatePattern(id: string, updates: Partial<FailurePattern>): void;
418
+ deletePattern(id: string): void;
419
+ updatePatternConfidence(patternId: string, event: 'matched' | 'resolved' | 'recurred'): void;
420
+ createGroup(input: CreateGroupInput): string;
421
+ getGroup(id: string): IncidentGroup | null;
422
+ getGroups(options?: {
423
+ limit?: number;
424
+ }): IncidentGroup[];
425
+ addToGroup(groupId: string, incidentId: string): void;
426
+ recordResolution(resolution: {
427
+ incidentId: string;
428
+ patternId?: string;
429
+ commitHash?: string;
430
+ prUrl?: string;
431
+ notes?: string;
432
+ }): void;
433
+ markRecurred(incidentId: string): void;
434
+ getResolutionHistory(options?: ResolutionQueryOptions): ResolutionRecord[];
435
+ getStats(period: {
436
+ start: string;
437
+ end: string;
438
+ }): SentinelStats;
439
+ getSymbolHealth(symbol: string): SymbolHealth;
440
+ exportPatterns(options?: {
441
+ includePrivate?: boolean;
442
+ }): PatternExport;
443
+ importPatterns(data: PatternExport, options?: {
444
+ overwrite?: boolean;
445
+ }): {
446
+ imported: number;
447
+ skipped: number;
448
+ };
449
+ exportBackup(): BackupExport;
450
+ importBackup(data: BackupExport): void;
451
+ private rowToIncident;
452
+ private rowToPattern;
453
+ private rowToGroup;
454
+ recordPracticeEvent(input: PracticeEventInput): string;
455
+ getPracticeEvents(options?: PracticeEventQuery): PracticeEvent[];
456
+ getPracticeEventCount(options?: PracticeEventQuery): number;
457
+ getComplianceRate(options?: PracticeEventQuery): {
458
+ total: number;
459
+ followed: number;
460
+ skipped: number;
461
+ partial: number;
462
+ rate: number;
463
+ };
464
+ private rowToPracticeEvent;
465
+ close(): void;
466
+ }
467
+
468
+ /**
469
+ * Paradigm Sentinel - Pattern Matching Engine
470
+ *
471
+ * Matches incidents against failure patterns using symbolic context,
472
+ * error text, and missing signals.
473
+ */
474
+
475
+ declare class PatternMatcher {
476
+ private storage;
477
+ constructor(storage: SentinelStorage);
478
+ /**
479
+ * Match an incident against all patterns and return ranked results
480
+ */
481
+ match(incident: SymbolicIncidentRecord, config?: Partial<MatcherConfig>): PatternMatch[];
482
+ /**
483
+ * Test a pattern against historical incidents
484
+ */
485
+ testPattern(pattern: FailurePattern, limit?: number): PatternTestResult;
486
+ /**
487
+ * Score how well a pattern matches an incident
488
+ */
489
+ private scoreMatch;
490
+ /**
491
+ * Match symbols between pattern and incident
492
+ */
493
+ private matchSymbols;
494
+ /**
495
+ * Match a single symbol value (supports wildcards)
496
+ */
497
+ private matchSingleSymbol;
498
+ /**
499
+ * Match error text keywords and regex
500
+ */
501
+ private matchErrorText;
502
+ /**
503
+ * Match missing signals from flow position
504
+ */
505
+ private matchMissingSignals;
506
+ /**
507
+ * Check if pattern's environment filter matches incident
508
+ */
509
+ private matchEnvironment;
510
+ }
511
+
512
+ /**
513
+ * Sentinel SDK
514
+ *
515
+ * The developer-facing API for capturing errors with symbolic context.
516
+ * Wraps the core storage and pattern matching engine.
517
+ *
518
+ * Usage:
519
+ * import { Sentinel } from '@a-company/sentinel';
520
+ * const sentinel = new Sentinel({ project: 'my-app' });
521
+ * sentinel.capture(new Error('Payment failed'), { component: '#checkout' });
522
+ */
523
+
524
+ /**
525
+ * Flow tracker for monitoring multi-step flows.
526
+ *
527
+ * Usage:
528
+ * const flow = sentinel.flow('$checkout-flow');
529
+ * flow.expect('!payment-authorized', '!order-created');
530
+ * flow.gate('^authenticated', true);
531
+ * flow.signal('!payment-authorized');
532
+ * flow.complete();
533
+ */
534
+ declare class FlowTracker {
535
+ private flowId;
536
+ private sentinel;
537
+ private actual;
538
+ private expected;
539
+ private completed;
540
+ constructor(flowId: string, sentinel: Sentinel);
541
+ /** Declare which signals/gates are expected in this flow */
542
+ expect(...symbols: string[]): this;
543
+ /** Record a generic step in the flow */
544
+ step(symbol: string): this;
545
+ /** Record a gate check result */
546
+ gate(id: string, passed: boolean): this;
547
+ /** Record a signal emission */
548
+ signal(id: string, _data?: object): this;
549
+ /** Mark the flow as successfully completed */
550
+ complete(): void;
551
+ /** Capture an error with full flow position context */
552
+ fail(error: Error): void;
553
+ }
554
+ /**
555
+ * The main Sentinel SDK class.
556
+ *
557
+ * Usage:
558
+ * const sentinel = new Sentinel({ project: 'my-app', environment: 'production' });
559
+ *
560
+ * // Capture errors with symbolic context
561
+ * sentinel.capture(error, { component: '#checkout', gate: '^payment-validated' });
562
+ *
563
+ * // Component context for scoped captures
564
+ * const ctx = sentinel.component('#checkout');
565
+ * ctx.capture(error);
566
+ *
567
+ * // Flow tracking
568
+ * const flow = sentinel.flow('$checkout-flow');
569
+ * flow.gate('^authenticated', true);
570
+ * flow.signal('!payment-authorized');
571
+ * flow.complete();
572
+ */
573
+ declare class Sentinel {
574
+ private storage;
575
+ private matcher;
576
+ private config;
577
+ private ready;
578
+ private readyPromise;
579
+ private seeded;
580
+ constructor(config: SentinelConfig);
581
+ /** Explicitly initialize storage. Optional — auto-called on first capture. */
582
+ init(): Promise<void>;
583
+ private doInit;
584
+ private ensureReady;
585
+ /**
586
+ * Create a component context for scoped error capture.
587
+ *
588
+ * @param id - Component symbol (e.g. '#checkout' or 'checkout')
589
+ * @returns ComponentContext with capture() and wrap() methods
590
+ */
591
+ component(id: string): ComponentContext;
592
+ /**
593
+ * Record a gate check result.
594
+ * If the gate fails, auto-captures an incident.
595
+ *
596
+ * @param id - Gate symbol (e.g. '^authenticated' or 'authenticated')
597
+ * @param passed - Whether the gate passed
598
+ */
599
+ gate(id: string, passed: boolean): void;
600
+ /**
601
+ * Record a signal emission. Primarily for flow tracking context.
602
+ *
603
+ * @param id - Signal symbol (e.g. '!payment-authorized' or 'payment-authorized')
604
+ */
605
+ signal(id: string, _data?: object): void;
606
+ /**
607
+ * Create a flow tracker for monitoring multi-step operations.
608
+ *
609
+ * @param id - Flow symbol (e.g. '$checkout-flow' or 'checkout-flow')
610
+ * @returns FlowTracker instance
611
+ */
612
+ flow(id: string): FlowTracker;
613
+ /**
614
+ * Capture an error with symbolic context.
615
+ *
616
+ * @param error - The error to capture
617
+ * @param context - Symbolic context (component, gate, flow, signal)
618
+ * @param flowPosition - Optional flow position data
619
+ * @returns Incident ID (e.g. 'INC-001')
620
+ */
621
+ capture(error: Error, context?: Partial<SymbolicContext>, flowPosition?: FlowPosition): string;
622
+ /**
623
+ * Get pattern matches for a captured incident.
624
+ *
625
+ * @param incidentId - The incident ID to match
626
+ * @returns Array of pattern matches sorted by confidence
627
+ */
628
+ match(incidentId: string): PatternMatch[];
629
+ /**
630
+ * Create Express error-handling middleware.
631
+ *
632
+ * Usage:
633
+ * app.use(sentinel.express());
634
+ */
635
+ express(): any;
636
+ /** Close the database connection. Call when shutting down. */
637
+ close(): void;
638
+ /** Get the underlying storage instance (for advanced usage). */
639
+ getStorage(): SentinelStorage;
640
+ /** Get the underlying pattern matcher (for advanced usage). */
641
+ getMatcher(): PatternMatcher;
642
+ }
643
+
644
+ export { type SymbolResolutionTime as $, type PatternRecurrence as A, type BackupExport as B, type ComponentContext as C, type DayCount as D, type EnrichedIncident as E, type FlowTimeline as F, type PatternResolution as G, type PatternSource as H, type IncidentGroup as I, type PatternSymbolCriteria as J, type PatternTestResult as K, type PracticeCategory as L, type MatchedCriteria as M, type PracticeEvent as N, type PracticeEventInput as O, type PatternCandidate as P, type PracticeEventQuery as Q, type PracticeResult as R, SentinelStorage as S, type Resolution as T, type ResolutionQueryOptions as U, type ResolutionRecord as V, type ResolutionStrategy as W, Sentinel as X, type SentinelConfig as Y, type SymbolHotspot as Z, type SymbolIncidentCount as _, type SymbolicIncidentRecord as a, type SymbolicContext as a0, type SentinelStats as b, type SymbolHealth as c, type SymbolEnrichment as d, type FailurePattern as e, type PatternExport as f, type CreateGroupInput as g, type CreateIncidentInput as h, type CreatePatternInput as i, type Environment as j, type ErrorDetails as k, type FlowEvent as l, type FlowEventType as m, type FlowPosition as n, FlowTracker as o, type IncidentNote as p, type IncidentQueryOptions as q, type IncidentStatus as r, type MatcherConfig as s, type PatternConfidence as t, type PatternCriteria as u, type PatternEffectiveness as v, type PatternMatch as w, PatternMatcher as x, type PatternPriority as y, type PatternQueryOptions as z };