@spfn/monitor 0.1.0-beta.1

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,518 @@
1
+ import { E as ErrorGroupStatus, c as ErrorGroup, N as NewErrorGroup, L as LogLevel, d as NewLog, e as Log } from './index-C9IUDNIv.js';
2
+ export { a as ERROR_GROUP_STATUSES, b as LOG_LEVELS, M as MonitorStats, f as errorGroups, g as getMonitorStats, l as logs, m as monitorRouter } from './index-C9IUDNIv.js';
3
+ import { BaseRepository } from '@spfn/core/db';
4
+ import * as drizzle_orm_pg_core from 'drizzle-orm/pg-core';
5
+ import * as _spfn_core_logger from '@spfn/core/logger';
6
+ import '@spfn/core/route';
7
+ import '@sinclair/typebox';
8
+
9
+ /**
10
+ * @spfn/monitor - Database Schema Definition
11
+ *
12
+ * Defines the 'spfn_monitor' PostgreSQL schema for all monitor-related tables
13
+ */
14
+ /**
15
+ * Monitor schema for all monitoring tables
16
+ * Tables: error_groups, error_events, logs
17
+ */
18
+ declare const monitorSchema: drizzle_orm_pg_core.PgSchema<string>;
19
+
20
+ /**
21
+ * @spfn/monitor - Error Events Entity
22
+ *
23
+ * Individual error occurrences linked to an error group.
24
+ * Stores request-specific context (headers, query, stack trace).
25
+ */
26
+ /**
27
+ * Error events table — individual error occurrences
28
+ */
29
+ declare const errorEvents: drizzle_orm_pg_core.PgTableWithColumns<{
30
+ name: "error_events";
31
+ schema: string;
32
+ columns: {
33
+ createdAt: drizzle_orm_pg_core.PgColumn<{
34
+ name: "created_at";
35
+ tableName: "error_events";
36
+ dataType: "date";
37
+ columnType: "PgTimestamp";
38
+ data: Date;
39
+ driverParam: string;
40
+ notNull: true;
41
+ hasDefault: true;
42
+ isPrimaryKey: false;
43
+ isAutoincrement: false;
44
+ hasRuntimeDefault: false;
45
+ enumValues: undefined;
46
+ baseColumn: never;
47
+ identity: undefined;
48
+ generated: undefined;
49
+ }, {}, {}>;
50
+ updatedAt: drizzle_orm_pg_core.PgColumn<{
51
+ name: "updated_at";
52
+ tableName: "error_events";
53
+ dataType: "date";
54
+ columnType: "PgTimestamp";
55
+ data: Date;
56
+ driverParam: string;
57
+ notNull: true;
58
+ hasDefault: true;
59
+ isPrimaryKey: false;
60
+ isAutoincrement: false;
61
+ hasRuntimeDefault: false;
62
+ enumValues: undefined;
63
+ baseColumn: never;
64
+ identity: undefined;
65
+ generated: undefined;
66
+ }, {}, {}>;
67
+ id: drizzle_orm_pg_core.PgColumn<{
68
+ name: "id";
69
+ tableName: "error_events";
70
+ dataType: "number";
71
+ columnType: "PgBigSerial53";
72
+ data: number;
73
+ driverParam: number;
74
+ notNull: true;
75
+ hasDefault: true;
76
+ isPrimaryKey: true;
77
+ isAutoincrement: false;
78
+ hasRuntimeDefault: false;
79
+ enumValues: undefined;
80
+ baseColumn: never;
81
+ identity: undefined;
82
+ generated: undefined;
83
+ }, {}, {}>;
84
+ groupId: drizzle_orm_pg_core.PgColumn<{
85
+ name: `${string}_id`;
86
+ tableName: "error_events";
87
+ dataType: "number";
88
+ columnType: "PgBigSerial53";
89
+ data: number;
90
+ driverParam: number;
91
+ notNull: true;
92
+ hasDefault: true;
93
+ isPrimaryKey: false;
94
+ isAutoincrement: false;
95
+ hasRuntimeDefault: false;
96
+ enumValues: undefined;
97
+ baseColumn: never;
98
+ identity: undefined;
99
+ generated: undefined;
100
+ }, {}, {}>;
101
+ requestId: drizzle_orm_pg_core.PgColumn<{
102
+ name: "request_id";
103
+ tableName: "error_events";
104
+ dataType: "string";
105
+ columnType: "PgText";
106
+ data: string;
107
+ driverParam: string;
108
+ notNull: false;
109
+ hasDefault: false;
110
+ isPrimaryKey: false;
111
+ isAutoincrement: false;
112
+ hasRuntimeDefault: false;
113
+ enumValues: [string, ...string[]];
114
+ baseColumn: never;
115
+ identity: undefined;
116
+ generated: undefined;
117
+ }, {}, {}>;
118
+ userId: drizzle_orm_pg_core.PgColumn<{
119
+ name: "user_id";
120
+ tableName: "error_events";
121
+ dataType: "string";
122
+ columnType: "PgText";
123
+ data: string;
124
+ driverParam: string;
125
+ notNull: false;
126
+ hasDefault: false;
127
+ isPrimaryKey: false;
128
+ isAutoincrement: false;
129
+ hasRuntimeDefault: false;
130
+ enumValues: [string, ...string[]];
131
+ baseColumn: never;
132
+ identity: undefined;
133
+ generated: undefined;
134
+ }, {}, {}>;
135
+ statusCode: drizzle_orm_pg_core.PgColumn<{
136
+ name: "status_code";
137
+ tableName: "error_events";
138
+ dataType: "number";
139
+ columnType: "PgInteger";
140
+ data: number;
141
+ driverParam: string | number;
142
+ notNull: true;
143
+ hasDefault: false;
144
+ isPrimaryKey: false;
145
+ isAutoincrement: false;
146
+ hasRuntimeDefault: false;
147
+ enumValues: undefined;
148
+ baseColumn: never;
149
+ identity: undefined;
150
+ generated: undefined;
151
+ }, {}, {}>;
152
+ headers: drizzle_orm_pg_core.PgColumn<{
153
+ name: "headers";
154
+ tableName: "error_events";
155
+ dataType: "json";
156
+ columnType: "PgJsonb";
157
+ data: Record<string, string>;
158
+ driverParam: unknown;
159
+ notNull: false;
160
+ hasDefault: false;
161
+ isPrimaryKey: false;
162
+ isAutoincrement: false;
163
+ hasRuntimeDefault: false;
164
+ enumValues: undefined;
165
+ baseColumn: never;
166
+ identity: undefined;
167
+ generated: undefined;
168
+ }, {}, {
169
+ $type: Record<string, string>;
170
+ }>;
171
+ query: drizzle_orm_pg_core.PgColumn<{
172
+ name: "query";
173
+ tableName: "error_events";
174
+ dataType: "json";
175
+ columnType: "PgJsonb";
176
+ data: Record<string, string>;
177
+ driverParam: unknown;
178
+ notNull: false;
179
+ hasDefault: false;
180
+ isPrimaryKey: false;
181
+ isAutoincrement: false;
182
+ hasRuntimeDefault: false;
183
+ enumValues: undefined;
184
+ baseColumn: never;
185
+ identity: undefined;
186
+ generated: undefined;
187
+ }, {}, {
188
+ $type: Record<string, string>;
189
+ }>;
190
+ stackTrace: drizzle_orm_pg_core.PgColumn<{
191
+ name: "stack_trace";
192
+ tableName: "error_events";
193
+ dataType: "string";
194
+ columnType: "PgText";
195
+ data: string;
196
+ driverParam: string;
197
+ notNull: false;
198
+ hasDefault: false;
199
+ isPrimaryKey: false;
200
+ isAutoincrement: false;
201
+ hasRuntimeDefault: false;
202
+ enumValues: [string, ...string[]];
203
+ baseColumn: never;
204
+ identity: undefined;
205
+ generated: undefined;
206
+ }, {}, {}>;
207
+ metadata: drizzle_orm_pg_core.PgColumn<{
208
+ name: "metadata";
209
+ tableName: "error_events";
210
+ dataType: "json";
211
+ columnType: "PgJsonb";
212
+ data: Record<string, unknown>;
213
+ driverParam: unknown;
214
+ notNull: false;
215
+ hasDefault: false;
216
+ isPrimaryKey: false;
217
+ isAutoincrement: false;
218
+ hasRuntimeDefault: false;
219
+ enumValues: undefined;
220
+ baseColumn: never;
221
+ identity: undefined;
222
+ generated: undefined;
223
+ }, {}, {
224
+ $type: Record<string, unknown>;
225
+ }>;
226
+ };
227
+ dialect: "pg";
228
+ }>;
229
+ type ErrorEvent = typeof errorEvents.$inferSelect;
230
+ type NewErrorEvent = typeof errorEvents.$inferInsert;
231
+
232
+ /**
233
+ * @spfn/monitor - Error Tracking Service
234
+ *
235
+ * Core service for tracking errors with fingerprint-based deduplication.
236
+ * Uses DB state transitions (new/reopened) instead of in-memory throttling
237
+ * to determine when Slack notifications should be sent.
238
+ */
239
+
240
+ /**
241
+ * Context from the error handler middleware
242
+ */
243
+ interface ErrorTrackingContext {
244
+ statusCode: number;
245
+ path: string;
246
+ method: string;
247
+ requestId?: string;
248
+ userId?: string;
249
+ headers?: Record<string, string>;
250
+ query?: Record<string, string>;
251
+ }
252
+ /**
253
+ * Generate a deterministic fingerprint for error deduplication
254
+ *
255
+ * SHA-256 of (name:message:path), first 16 hex characters
256
+ */
257
+ declare function generateFingerprint(name: string, message: string, path: string): string;
258
+ /**
259
+ * Track an error occurrence
260
+ *
261
+ * 1. Generate fingerprint
262
+ * 2. Find or create error group
263
+ * 3. Create error event
264
+ * 4. Send Slack notification on new or reopened errors
265
+ */
266
+ declare function trackError(err: Error, ctx: ErrorTrackingContext, metadata?: Record<string, unknown>): Promise<void>;
267
+ /**
268
+ * Update error group status with validation
269
+ */
270
+ declare function updateErrorGroupStatus(groupId: number, newStatus: ErrorGroupStatus): Promise<ErrorGroup>;
271
+
272
+ /**
273
+ * Error Groups Repository
274
+ *
275
+ * Data access for error group management with read/write splitting
276
+ */
277
+
278
+ interface ErrorGroupFilters {
279
+ status?: ErrorGroupStatus;
280
+ path?: string;
281
+ search?: string;
282
+ dateFrom?: Date;
283
+ dateTo?: Date;
284
+ limit?: number;
285
+ offset?: number;
286
+ }
287
+ declare class ErrorGroupsRepository extends BaseRepository {
288
+ findById(id: number): Promise<ErrorGroup | null>;
289
+ findByFingerprint(fingerprint: string): Promise<ErrorGroup | null>;
290
+ findMany(filters?: ErrorGroupFilters): Promise<ErrorGroup[]>;
291
+ create(data: NewErrorGroup): Promise<ErrorGroup>;
292
+ incrementCount(id: number): Promise<ErrorGroup | null>;
293
+ updateStatus(id: number, status: ErrorGroupStatus): Promise<ErrorGroup | null>;
294
+ countByStatus(): Promise<Record<ErrorGroupStatus, number>>;
295
+ }
296
+ declare const errorGroupsRepository: ErrorGroupsRepository;
297
+
298
+ /**
299
+ * Error Events Repository
300
+ *
301
+ * Data access for individual error event records
302
+ */
303
+
304
+ declare class ErrorEventsRepository extends BaseRepository {
305
+ create(data: NewErrorEvent): Promise<ErrorEvent>;
306
+ findByGroupId(groupId: number, options?: {
307
+ limit?: number;
308
+ offset?: number;
309
+ }): Promise<ErrorEvent[]>;
310
+ deleteOlderThan(date: Date): Promise<number>;
311
+ }
312
+ declare const errorEventsRepository: ErrorEventsRepository;
313
+
314
+ /**
315
+ * Logs Repository
316
+ *
317
+ * Data access for developer log entries
318
+ */
319
+
320
+ interface LogFilters {
321
+ level?: LogLevel;
322
+ source?: string;
323
+ search?: string;
324
+ requestId?: string;
325
+ userId?: string;
326
+ dateFrom?: Date;
327
+ dateTo?: Date;
328
+ limit?: number;
329
+ offset?: number;
330
+ }
331
+ declare class LogsRepository extends BaseRepository {
332
+ create(data: NewLog): Promise<Log>;
333
+ findMany(filters?: LogFilters): Promise<Log[]>;
334
+ countByLevel(): Promise<Record<LogLevel, number>>;
335
+ deleteOlderThan(date: Date): Promise<number>;
336
+ }
337
+ declare const logsRepository: LogsRepository;
338
+
339
+ /**
340
+ * @spfn/monitor - Log Service
341
+ *
342
+ * Pluggable log storage with DB as default implementation.
343
+ * Use setLogStore() to swap to S3, ClickHouse, etc.
344
+ */
345
+
346
+ /**
347
+ * Log store interface — implement for custom storage backends
348
+ */
349
+ interface LogStore {
350
+ write(entry: NewLog): Promise<Log>;
351
+ query(filters: LogFilters): Promise<Log[]>;
352
+ purge(olderThan: Date): Promise<number>;
353
+ }
354
+ /**
355
+ * Replace the default log store with a custom implementation
356
+ */
357
+ declare function setLogStore(store: LogStore): void;
358
+ /**
359
+ * Get the current log store
360
+ */
361
+ declare function getLogStore(): LogStore;
362
+ /**
363
+ * Write params for public API
364
+ */
365
+ interface WriteLogParams {
366
+ level: LogLevel;
367
+ message: string;
368
+ source?: string;
369
+ requestId?: string;
370
+ userId?: string;
371
+ metadata?: Record<string, unknown>;
372
+ }
373
+ /**
374
+ * Write a log entry
375
+ */
376
+ declare function writeLog(params: WriteLogParams): Promise<Log>;
377
+ /**
378
+ * Query logs with filters
379
+ */
380
+ declare function queryLogs(filters: LogFilters): Promise<Log[]>;
381
+
382
+ /**
383
+ * @spfn/monitor - Error Handler Integration
384
+ *
385
+ * Drop-in replacement for createErrorSlackNotifier from @spfn/notification.
386
+ * Uses DB-backed error tracking with state-based notifications instead of
387
+ * in-memory time-based throttling.
388
+ *
389
+ * @example
390
+ * ```typescript
391
+ * import { createMonitorErrorHandler } from '@spfn/monitor/server';
392
+ *
393
+ * export default defineServerConfig()
394
+ * .middleware({ onError: createMonitorErrorHandler() })
395
+ * .build();
396
+ * ```
397
+ */
398
+
399
+ interface MonitorErrorHandlerOptions {
400
+ /**
401
+ * Minimum status code to track
402
+ * @default env.SPFN_MONITOR_MIN_STATUS_CODE or 500
403
+ */
404
+ minStatusCode?: number;
405
+ /**
406
+ * Extract custom metadata from error and context
407
+ */
408
+ extractMetadata?: (err: Error, ctx: ErrorTrackingContext) => Record<string, unknown>;
409
+ }
410
+ interface OnErrorContext {
411
+ statusCode: number;
412
+ path: string;
413
+ method: string;
414
+ requestId?: string;
415
+ timestamp: string;
416
+ userId?: string;
417
+ request: {
418
+ headers: Record<string, string>;
419
+ query: Record<string, string>;
420
+ };
421
+ }
422
+ /**
423
+ * Create an onError callback that tracks errors in DB and sends Slack notifications
424
+ *
425
+ * Returns a function matching ErrorHandler's onError signature.
426
+ * Replaces createErrorSlackNotifier from @spfn/notification.
427
+ */
428
+ declare function createMonitorErrorHandler(options?: MonitorErrorHandlerOptions): (err: Error, ctx: OnErrorContext) => Promise<void>;
429
+
430
+ /**
431
+ * @spfn/monitor - Server Lifecycle Hooks
432
+ *
433
+ * Provides lifecycle hooks for SPFN server initialization
434
+ */
435
+ /**
436
+ * Monitor lifecycle configuration
437
+ */
438
+ interface MonitorLifecycleConfig {
439
+ afterInfrastructure?: () => Promise<void>;
440
+ }
441
+ /**
442
+ * Create monitor lifecycle hooks for server configuration
443
+ *
444
+ * @example
445
+ * ```typescript
446
+ * import { defineServerConfig } from '@spfn/core/server';
447
+ * import { createMonitorLifecycle, monitorRouter } from '@spfn/monitor/server';
448
+ *
449
+ * export default defineServerConfig()
450
+ * .routes(appRouter)
451
+ * .lifecycle(createMonitorLifecycle())
452
+ * .build();
453
+ * ```
454
+ */
455
+ declare function createMonitorLifecycle(): MonitorLifecycleConfig;
456
+
457
+ /**
458
+ * @spfn/monitor - Centralized Logger
459
+ *
460
+ * All monitor package loggers with consistent naming
461
+ */
462
+ declare const monitorLogger: {
463
+ errorTracking: _spfn_core_logger.Logger;
464
+ logging: _spfn_core_logger.Logger;
465
+ notification: _spfn_core_logger.Logger;
466
+ lifecycle: _spfn_core_logger.Logger;
467
+ route: _spfn_core_logger.Logger;
468
+ };
469
+
470
+ /**
471
+ * @spfn/monitor/server
472
+ *
473
+ * Server-side Only Module
474
+ *
475
+ * Includes:
476
+ * - Router (admin endpoints)
477
+ * - Repositories (error groups, events, logs)
478
+ * - Services (error tracking, logging, stats)
479
+ * - Integrations (error handler, lifecycle)
480
+ * - Entities
481
+ *
482
+ * @example
483
+ * ```typescript
484
+ * import {
485
+ * createMonitorErrorHandler,
486
+ * createMonitorLifecycle,
487
+ * monitorRouter,
488
+ * writeLog,
489
+ * } from '@spfn/monitor/server';
490
+ *
491
+ * export default defineServerConfig()
492
+ * .middleware({ onError: createMonitorErrorHandler() })
493
+ * .routes(mergeRouters(appRouter, monitorRouter))
494
+ * .lifecycle(createMonitorLifecycle())
495
+ * .build();
496
+ * ```
497
+ */
498
+
499
+ /**
500
+ * Convenience namespace for quick logging
501
+ *
502
+ * @example
503
+ * ```typescript
504
+ * import { monitor } from '@spfn/monitor/server';
505
+ *
506
+ * await monitor.log({
507
+ * level: 'info',
508
+ * message: 'User signed up',
509
+ * source: 'auth',
510
+ * metadata: { userId: 123 },
511
+ * });
512
+ * ```
513
+ */
514
+ declare const monitor: {
515
+ log: typeof writeLog;
516
+ };
517
+
518
+ export { type ErrorEvent, ErrorGroup, type ErrorGroupFilters, ErrorGroupStatus, type ErrorTrackingContext, Log, type LogFilters, LogLevel, type LogStore, type MonitorErrorHandlerOptions, type NewErrorEvent, NewErrorGroup, NewLog, type WriteLogParams, createMonitorErrorHandler, createMonitorLifecycle, errorEvents, errorEventsRepository, errorGroupsRepository, generateFingerprint, getLogStore, logsRepository, monitor, monitorLogger, monitorSchema, queryLogs, setLogStore, trackError, updateErrorGroupStatus, writeLog };