feat(diagnostics): add types.ts with session, trace, log, screenshot schemas
This commit is contained in:
parent
4163e1410a
commit
f51c352452
381
services/platform-service/src/modules/diagnostics/types.ts
Normal file
381
services/platform-service/src/modules/diagnostics/types.ts
Normal file
@ -0,0 +1,381 @@
|
||||
/**
|
||||
* Diagnostics types — remote debug session management.
|
||||
*
|
||||
* Cosmos containers:
|
||||
* - debug_sessions (pk: /id, TTL: 7 days)
|
||||
* - debug_traces (pk: /pk composite ${productId}:${sessionId}, TTL: 7 days)
|
||||
* - debug_logs (pk: /pk composite ${productId}:${sessionId}, TTL: 3 days)
|
||||
* - debug_screenshots (pk: /sessionId) — metadata only, images in Azure Blob
|
||||
*
|
||||
* @module diagnostics
|
||||
*/
|
||||
|
||||
import { z } from 'zod';
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
// Enums
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
export const SessionStatusEnum = z.enum(['pending', 'active', 'paused', 'completed', 'cancelled']);
|
||||
export type SessionStatus = z.infer<typeof SessionStatusEnum>;
|
||||
|
||||
export const CollectionLevelEnum = z.enum(['standard', 'debug', 'trace']);
|
||||
export type CollectionLevel = z.infer<typeof CollectionLevelEnum>;
|
||||
|
||||
export const LogLevelEnum = z.enum(['debug', 'info', 'warn', 'error', 'fatal']);
|
||||
export type LogLevel = z.infer<typeof LogLevelEnum>;
|
||||
|
||||
export const ScreenshotTriggerEnum = z.enum(['manual', 'error', 'interval', 'user_request']);
|
||||
export type ScreenshotTrigger = z.infer<typeof ScreenshotTriggerEnum>;
|
||||
|
||||
export const SpanStatusEnum = z.enum(['ok', 'error', 'unset']);
|
||||
export type SpanStatus = z.infer<typeof SpanStatusEnum>;
|
||||
|
||||
export const SpanKindEnum = z.enum(['internal', 'server', 'client', 'producer', 'consumer']);
|
||||
export type SpanKind = z.infer<typeof SpanKindEnum>;
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
// DebugSessionDoc
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
export interface DebugSessionDoc {
|
||||
id: string;
|
||||
productId: string;
|
||||
|
||||
// Target (at least one required)
|
||||
targetUserId?: string;
|
||||
targetAnonymousId?: string;
|
||||
targetDeviceId?: string;
|
||||
targetSessionId?: string;
|
||||
|
||||
// Status
|
||||
status: SessionStatus;
|
||||
|
||||
// Collection config
|
||||
collectionLevel: CollectionLevel;
|
||||
captureLogs: boolean;
|
||||
captureNetwork: boolean;
|
||||
captureScreenshots: boolean;
|
||||
screenshotOnError: boolean;
|
||||
maxDurationMinutes: number;
|
||||
|
||||
// Timestamps
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
startedAt?: string;
|
||||
endedAt?: string;
|
||||
expiresAt: string;
|
||||
|
||||
// Stats (denormalized)
|
||||
logCount: number;
|
||||
traceCount: number;
|
||||
screenshotCount: number;
|
||||
|
||||
// Audit
|
||||
createdBy: string;
|
||||
updatedBy?: string;
|
||||
|
||||
// Consent tracking
|
||||
userConsent?: {
|
||||
consentedAt: string;
|
||||
consentMethod: 'prompt' | 'pre_consent' | 'auto';
|
||||
};
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
// DebugTraceDoc (OpenTelemetry-compatible)
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
export interface DebugTraceDoc {
|
||||
id: string;
|
||||
pk: string; // Composite: ${productId}:${sessionId}
|
||||
sessionId: string;
|
||||
productId: string;
|
||||
|
||||
// OTel context
|
||||
traceId: string;
|
||||
parentId?: string;
|
||||
spanId: string;
|
||||
name: string;
|
||||
kind?: SpanKind;
|
||||
|
||||
// Timing
|
||||
startTime: string;
|
||||
endTime?: string;
|
||||
durationMs?: number;
|
||||
|
||||
// Context
|
||||
attributes: Record<string, unknown>;
|
||||
status: SpanStatus;
|
||||
statusMessage?: string;
|
||||
|
||||
// Events within span
|
||||
events?: Array<{
|
||||
name: string;
|
||||
timestamp: string;
|
||||
attributes?: Record<string, unknown>;
|
||||
}>;
|
||||
|
||||
// Links to other traces
|
||||
links?: Array<{
|
||||
traceId: string;
|
||||
spanId: string;
|
||||
attributes?: Record<string, unknown>;
|
||||
}>;
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
// DebugLogEntryDoc
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
export interface DebugLogEntryDoc {
|
||||
id: string;
|
||||
pk: string; // Composite: ${productId}:${sessionId}
|
||||
sessionId: string;
|
||||
productId: string;
|
||||
|
||||
level: LogLevel;
|
||||
message: string;
|
||||
messageHash?: string;
|
||||
|
||||
// Timestamps
|
||||
timestamp: string;
|
||||
receivedAt?: string;
|
||||
|
||||
// Source context
|
||||
module: string;
|
||||
file?: string;
|
||||
line?: number;
|
||||
function?: string;
|
||||
|
||||
// Thread/task context
|
||||
threadId?: string;
|
||||
correlationId?: string;
|
||||
|
||||
// Context (PII-scanned)
|
||||
context: Record<string, unknown>;
|
||||
|
||||
// PII redaction metadata
|
||||
redaction?: {
|
||||
fieldsRedacted: string[];
|
||||
patternsMatched: string[];
|
||||
};
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
// DebugScreenshotDoc (metadata only)
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
export interface DebugScreenshotDoc {
|
||||
id: string;
|
||||
sessionId: string;
|
||||
productId: string;
|
||||
|
||||
// Blob storage reference
|
||||
blobUrl: string;
|
||||
blobPath: string;
|
||||
containerName: string;
|
||||
|
||||
// Metadata
|
||||
capturedAt: string;
|
||||
trigger: ScreenshotTrigger;
|
||||
|
||||
// Dimensions
|
||||
width: number;
|
||||
height: number;
|
||||
format: 'png' | 'jpeg' | 'webp';
|
||||
sizeBytes: number;
|
||||
|
||||
// Privacy
|
||||
sensitiveViewsBlurred: boolean;
|
||||
blurRegions?: Array<{ x: number; y: number; w: number; h: number }>;
|
||||
|
||||
// Context
|
||||
screenName?: string;
|
||||
breadcrumbAtCapture?: string;
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
// Input Schemas
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
export const CreateDebugSessionSchema = z.object({
|
||||
productId: z.string().min(1),
|
||||
targetUserId: z.string().optional(),
|
||||
targetAnonymousId: z.string().optional(),
|
||||
targetDeviceId: z.string().optional(),
|
||||
targetSessionId: z.string().optional(),
|
||||
collectionLevel: CollectionLevelEnum.default('debug'),
|
||||
captureLogs: z.boolean().default(true),
|
||||
captureNetwork: z.boolean().default(true),
|
||||
captureScreenshots: z.boolean().default(false),
|
||||
screenshotOnError: z.boolean().default(true),
|
||||
maxDurationMinutes: z.number().min(5).max(1440).default(60),
|
||||
});
|
||||
|
||||
export const UpdateDebugSessionSchema = z.object({
|
||||
status: SessionStatusEnum.optional(),
|
||||
collectionLevel: CollectionLevelEnum.optional(),
|
||||
captureLogs: z.boolean().optional(),
|
||||
captureNetwork: z.boolean().optional(),
|
||||
captureScreenshots: z.boolean().optional(),
|
||||
screenshotOnError: z.boolean().optional(),
|
||||
maxDurationMinutes: z.number().min(5).max(1440).optional(),
|
||||
});
|
||||
|
||||
export const ListDebugSessionsQuerySchema = z.object({
|
||||
productId: z.string().optional(),
|
||||
status: SessionStatusEnum.optional(),
|
||||
targetUserId: z.string().optional(),
|
||||
from: z.string().datetime().optional(),
|
||||
to: z.string().datetime().optional(),
|
||||
limit: z.coerce.number().min(1).max(100).default(20),
|
||||
offset: z.coerce.number().min(0).default(0),
|
||||
});
|
||||
|
||||
export const IngestTracesSchema = z.object({
|
||||
sessionId: z.string().min(1),
|
||||
traces: z
|
||||
.array(
|
||||
z.object({
|
||||
traceId: z.string().min(1),
|
||||
spanId: z.string().min(1),
|
||||
parentId: z.string().optional(),
|
||||
name: z.string().min(1),
|
||||
kind: SpanKindEnum.optional(),
|
||||
startTime: z.string().datetime(),
|
||||
endTime: z.string().datetime().optional(),
|
||||
durationMs: z.number().optional(),
|
||||
attributes: z.record(z.unknown()).default({}),
|
||||
status: SpanStatusEnum,
|
||||
statusMessage: z.string().optional(),
|
||||
events: z
|
||||
.array(
|
||||
z.object({
|
||||
name: z.string(),
|
||||
timestamp: z.string().datetime(),
|
||||
attributes: z.record(z.unknown()).optional(),
|
||||
})
|
||||
)
|
||||
.optional(),
|
||||
links: z
|
||||
.array(
|
||||
z.object({
|
||||
traceId: z.string(),
|
||||
spanId: z.string(),
|
||||
attributes: z.record(z.unknown()).optional(),
|
||||
})
|
||||
)
|
||||
.optional(),
|
||||
})
|
||||
)
|
||||
.min(1)
|
||||
.max(50),
|
||||
});
|
||||
|
||||
export const IngestLogsSchema = z.object({
|
||||
sessionId: z.string().min(1),
|
||||
logs: z
|
||||
.array(
|
||||
z.object({
|
||||
level: LogLevelEnum,
|
||||
message: z.string().max(4096),
|
||||
timestamp: z.string().datetime(),
|
||||
module: z.string().min(1),
|
||||
file: z.string().optional(),
|
||||
line: z.number().optional(),
|
||||
function: z.string().optional(),
|
||||
threadId: z.string().optional(),
|
||||
correlationId: z.string().optional(),
|
||||
context: z.record(z.unknown()).default({}),
|
||||
})
|
||||
)
|
||||
.min(1)
|
||||
.max(50),
|
||||
});
|
||||
|
||||
export const CreateScreenshotMetadataSchema = z.object({
|
||||
sessionId: z.string().min(1),
|
||||
capturedAt: z.string().datetime(),
|
||||
trigger: ScreenshotTriggerEnum,
|
||||
width: z.number().positive(),
|
||||
height: z.number().positive(),
|
||||
format: z.enum(['png', 'jpeg', 'webp']),
|
||||
sizeBytes: z.number().positive(),
|
||||
sensitiveViewsBlurred: z.boolean(),
|
||||
blurRegions: z
|
||||
.array(z.object({ x: z.number(), y: z.number(), w: z.number(), h: z.number() }))
|
||||
.optional(),
|
||||
screenName: z.string().optional(),
|
||||
breadcrumbAtCapture: z.string().optional(),
|
||||
});
|
||||
|
||||
export const QueryTracesSchema = z.object({
|
||||
limit: z.coerce.number().min(1).max(200).default(50),
|
||||
continuationToken: z.string().optional(),
|
||||
});
|
||||
|
||||
export const QueryLogsSchema = z.object({
|
||||
level: LogLevelEnum.optional(),
|
||||
from: z.string().datetime().optional(),
|
||||
to: z.string().datetime().optional(),
|
||||
search: z.string().max(256).optional(),
|
||||
limit: z.coerce.number().min(1).max(200).default(50),
|
||||
continuationToken: z.string().optional(),
|
||||
});
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
// Inferred Types
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
export type CreateDebugSessionInput = z.infer<typeof CreateDebugSessionSchema>;
|
||||
export type UpdateDebugSessionInput = z.infer<typeof UpdateDebugSessionSchema>;
|
||||
export type ListDebugSessionsQuery = z.infer<typeof ListDebugSessionsQuerySchema>;
|
||||
export type IngestTracesInput = z.infer<typeof IngestTracesSchema>;
|
||||
export type IngestLogsInput = z.infer<typeof IngestLogsSchema>;
|
||||
export type CreateScreenshotMetadataInput = z.infer<typeof CreateScreenshotMetadataSchema>;
|
||||
export type QueryTracesInput = z.infer<typeof QueryTracesSchema>;
|
||||
export type QueryLogsInput = z.infer<typeof QueryLogsSchema>;
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
// Event Bus Event Types
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
export interface DiagnosticsSessionCreatedEvent {
|
||||
sessionId: string;
|
||||
productId: string;
|
||||
targetUserId?: string;
|
||||
createdBy: string;
|
||||
}
|
||||
|
||||
export interface DiagnosticsSessionUpdatedEvent {
|
||||
sessionId: string;
|
||||
productId: string;
|
||||
changes: Partial<DebugSessionDoc>;
|
||||
updatedBy: string;
|
||||
}
|
||||
|
||||
export interface DiagnosticsSessionCancelledEvent {
|
||||
sessionId: string;
|
||||
productId: string;
|
||||
reason?: string;
|
||||
cancelledBy: string;
|
||||
}
|
||||
|
||||
export interface DiagnosticsSessionCompletedEvent {
|
||||
sessionId: string;
|
||||
productId: string;
|
||||
stats: {
|
||||
logCount: number;
|
||||
traceCount: number;
|
||||
screenshotCount: number;
|
||||
};
|
||||
endedAt: string;
|
||||
}
|
||||
|
||||
export interface DiagnosticsIngestFatalEvent {
|
||||
sessionId: string;
|
||||
productId: string;
|
||||
logEntry: DebugLogEntryDoc;
|
||||
timestamp: string;
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user