devn-bot-detector-core 0.1.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,312 @@
1
+ /** Default session lifetime (30 minutes). */
2
+ declare const DEFAULT_SESSION_DURATION_MS: number;
3
+ /** Default client telemetry flush interval. */
4
+ declare const DEFAULT_TELEMETRY_INTERVAL_MS = 5000;
5
+ /** Soft max size for a telemetry JSON payload. */
6
+ declare const MAX_TELEMETRY_BYTES: number;
7
+ /** Default risk token lifetime (5 minutes). */
8
+ declare const DEFAULT_RISK_TOKEN_DURATION_MS: number;
9
+ /** Protocol version embedded in telemetry. */
10
+ declare const TELEMETRY_VERSION: 1;
11
+ /** HTTP header for the public site key. */
12
+ declare const SITE_KEY_HEADER = "X-Bot-Site-Key";
13
+ /** API path suffixes (relative to endpoint base). */
14
+ declare const API_PATHS: {
15
+ readonly session: "/v1/session";
16
+ readonly analyze: "/v1/analyze";
17
+ };
18
+ /**
19
+ * Default human-score thresholds (higher = more human-like).
20
+ * allow >= 70, monitor >= 50, challenge >= 30, block < 30
21
+ */
22
+ declare const DEFAULT_RISK_THRESHOLDS: {
23
+ readonly allow: 70;
24
+ readonly monitor: 50;
25
+ readonly challenge: 30;
26
+ };
27
+ declare const ERROR_CODES: readonly ["INVALID_SITE_KEY", "INVALID_HOSTNAME", "INVALID_SESSION", "SESSION_EXPIRED", "INVALID_NONCE", "INVALID_SEQUENCE", "RATE_LIMITED", "INVALID_TELEMETRY", "MALFORMED_REQUEST", "INTERNAL_ERROR", "IP_BLOCKED", "TOKEN_ALREADY_USED", "ACTION_MISMATCH"];
28
+ type ErrorCode = (typeof ERROR_CODES)[number];
29
+
30
+ type RiskDecision = 'allow' | 'monitor' | 'challenge' | 'block';
31
+ type RiskLevel = 'low' | 'medium' | 'high';
32
+ type SiteStatus = 'active' | 'disabled' | 'suspended';
33
+ /** Aggregated browser environment signals (no cookies / storage / tokens). */
34
+ interface BrowserSignals {
35
+ readonly userAgent: string;
36
+ readonly language: string;
37
+ readonly languagesLength: number;
38
+ readonly timezone: string;
39
+ readonly timezoneOffset: number;
40
+ readonly platform: string;
41
+ readonly screenWidth: number;
42
+ readonly screenHeight: number;
43
+ readonly colorDepth: number;
44
+ readonly devicePixelRatio: number;
45
+ readonly hardwareConcurrency: number | null;
46
+ readonly maxTouchPoints: number;
47
+ readonly cookieEnabled: boolean;
48
+ /** One risk signal among many — never decisive alone. */
49
+ readonly webdriver: boolean;
50
+ readonly webglRenderer?: string | null;
51
+ readonly webglVendor?: string | null;
52
+ readonly hasChrome?: boolean;
53
+ readonly pluginsCount?: number;
54
+ readonly screenOrientation?: string | null;
55
+ }
56
+ /** Aggregated pointer/mouse statistics (no raw coordinates retained). */
57
+ interface PointerSignals {
58
+ readonly eventCount: number;
59
+ readonly movementDistance: number;
60
+ readonly averageInterval: number | null;
61
+ readonly minInterval: number | null;
62
+ readonly maxInterval: number | null;
63
+ readonly directionChanges: number;
64
+ readonly idlePeriods: number;
65
+ readonly activeDuration: number;
66
+ readonly clickCount: number;
67
+ }
68
+ /** Aggregated keyboard timing statistics (no keys / characters / codes). */
69
+ interface KeyboardSignals {
70
+ readonly keydownCount: number;
71
+ readonly keyupCount: number;
72
+ readonly averageInterval: number | null;
73
+ readonly minInterval: number | null;
74
+ readonly maxInterval: number | null;
75
+ readonly timingVariance: number | null;
76
+ readonly pasteCount: number;
77
+ }
78
+ /** Aggregated touch statistics. */
79
+ interface TouchSignals {
80
+ readonly touchCount: number;
81
+ readonly averageInterval: number | null;
82
+ readonly activeDuration: number;
83
+ readonly multiTouchCount: number;
84
+ }
85
+ /** Aggregated scroll statistics. */
86
+ interface ScrollSignals {
87
+ readonly scrollCount: number;
88
+ readonly averageInterval: number | null;
89
+ readonly totalDistance: number;
90
+ readonly directionChanges: number;
91
+ readonly activeDuration: number;
92
+ }
93
+ /** Focus / blur statistics. */
94
+ interface FocusSignals {
95
+ readonly focusCount: number;
96
+ readonly blurCount: number;
97
+ }
98
+ /** Page visibility statistics. */
99
+ interface VisibilitySignals {
100
+ readonly visibilityChanges: number;
101
+ readonly visibleDuration: number;
102
+ readonly hiddenDuration: number;
103
+ }
104
+ /** High-level interaction timing (no form values). */
105
+ interface InteractionSignals {
106
+ readonly pageLoadTime: number;
107
+ readonly firstInteractionTime: number | null;
108
+ readonly timeToFirstInteraction: number | null;
109
+ readonly clickCount: number;
110
+ readonly interactionCount: number;
111
+ readonly formInteractionCount: number;
112
+ readonly scrollCount: number;
113
+ readonly focusCount: number;
114
+ }
115
+ /** Privacy-safe aggregated telemetry payload. */
116
+ interface Telemetry {
117
+ readonly version: 1;
118
+ readonly browser?: BrowserSignals;
119
+ readonly pointer?: PointerSignals;
120
+ readonly keyboard?: KeyboardSignals;
121
+ readonly touch?: TouchSignals;
122
+ readonly scroll?: ScrollSignals;
123
+ readonly focus?: FocusSignals;
124
+ readonly visibility?: VisibilitySignals;
125
+ readonly interaction?: InteractionSignals;
126
+ }
127
+ interface ClientSession {
128
+ readonly sessionId: string;
129
+ readonly nonce: string;
130
+ readonly createdAt: number;
131
+ readonly expiresAt: number;
132
+ }
133
+ interface SessionData {
134
+ readonly sessionId: string;
135
+ readonly siteKey: string;
136
+ readonly siteId: string;
137
+ readonly tenantId: string;
138
+ readonly nonce: string;
139
+ readonly createdAt: number;
140
+ readonly expiresAt: number;
141
+ readonly hostname: string;
142
+ /** Last accepted sequence number (replay protection). */
143
+ sequence: number;
144
+ /** Nonces already consumed for this session (short-lived). */
145
+ usedNonces: string[];
146
+ }
147
+ interface RequestContext {
148
+ readonly ip?: string;
149
+ readonly userAgent?: string;
150
+ readonly origin?: string;
151
+ readonly host?: string;
152
+ readonly forwardedFor?: string;
153
+ }
154
+ interface SiteInfo {
155
+ readonly siteId: string;
156
+ readonly tenantId: string;
157
+ readonly allowedDomains: readonly string[];
158
+ readonly status: SiteStatus;
159
+ }
160
+ interface RiskReason {
161
+ readonly code: string;
162
+ readonly weight: number;
163
+ readonly detail?: string;
164
+ }
165
+ interface RiskResult {
166
+ readonly requestId: string;
167
+ readonly score: number;
168
+ readonly confidence: number;
169
+ readonly decision: RiskDecision;
170
+ readonly riskLevel: RiskLevel;
171
+ readonly action?: string;
172
+ readonly riskToken?: string;
173
+ readonly expiresAt?: string;
174
+ readonly timestamp: number;
175
+ }
176
+ interface RiskConfig {
177
+ readonly allow: number;
178
+ readonly monitor: number;
179
+ readonly challenge: number;
180
+ }
181
+ type ClientCredentialsMode = 'omit' | 'same-origin' | 'include';
182
+ interface ClientConfig {
183
+ readonly siteKey: string;
184
+ readonly endpoint: string;
185
+ readonly autoStart?: boolean;
186
+ readonly telemetryInterval?: number;
187
+ readonly sessionDuration?: number;
188
+ readonly collectPointer?: boolean;
189
+ readonly collectKeyboardTiming?: boolean;
190
+ readonly collectTouch?: boolean;
191
+ readonly collectScroll?: boolean;
192
+ readonly collectFocus?: boolean;
193
+ readonly collectVisibility?: boolean;
194
+ readonly collectBrowser?: boolean;
195
+ readonly failOpen?: boolean;
196
+ readonly maxTelemetryBytes?: number;
197
+ readonly debug?: boolean;
198
+ readonly headers?: Record<string, string> | (() => Record<string, string> | Promise<Record<string, string>>);
199
+ readonly fetchFn?: typeof fetch;
200
+ readonly credentials?: ClientCredentialsMode;
201
+ readonly timeoutMs?: number;
202
+ readonly retries?: number;
203
+ readonly retryDelayMs?: number;
204
+ readonly sampleRate?: number;
205
+ readonly onSessionCreated?: (session: ClientSession) => void;
206
+ readonly onCheck?: (result: RiskResult) => void;
207
+ readonly onError?: (error: Error) => void;
208
+ }
209
+ interface ServerConfig {
210
+ readonly secret: string;
211
+ readonly sessionDuration?: number;
212
+ readonly riskThresholds?: Partial<RiskConfig>;
213
+ readonly actionThresholds?: Record<string, Partial<RiskConfig>>;
214
+ readonly ipAllowlist?: readonly string[];
215
+ readonly ipDenylist?: readonly string[];
216
+ readonly detectorWeights?: Record<string, number>;
217
+ readonly enableRiskToken?: boolean;
218
+ readonly riskTokenDuration?: number;
219
+ readonly trustProxy?: boolean;
220
+ readonly debug?: boolean;
221
+ }
222
+ interface ApiErrorBody {
223
+ readonly error: {
224
+ readonly code: ErrorCode;
225
+ readonly message: string;
226
+ readonly requestId: string;
227
+ };
228
+ }
229
+ interface CreateSessionRequest {
230
+ readonly hostname: string;
231
+ readonly userAgent?: string;
232
+ readonly timestamp: number;
233
+ }
234
+ interface CreateSessionResponse {
235
+ readonly sessionId: string;
236
+ readonly nonce: string;
237
+ readonly expiresAt: string;
238
+ }
239
+ interface AnalyzeRequest {
240
+ readonly sessionId: string;
241
+ readonly sequence: number;
242
+ readonly nonce?: string;
243
+ readonly timestamp: number;
244
+ readonly hostname: string;
245
+ readonly telemetry: Telemetry;
246
+ readonly action?: string;
247
+ readonly metadata?: Record<string, unknown>;
248
+ }
249
+ interface AnalyzeResponse extends RiskResult {
250
+ }
251
+ interface RateLimitResult {
252
+ readonly allowed: boolean;
253
+ readonly remaining: number;
254
+ readonly retryAfter?: number;
255
+ }
256
+ interface RiskTokenPayload {
257
+ readonly requestId: string;
258
+ readonly siteId: string;
259
+ readonly sessionId: string;
260
+ readonly decision: RiskDecision;
261
+ readonly issuedAt: number;
262
+ readonly expiresAt: number;
263
+ readonly action?: string;
264
+ readonly score?: number;
265
+ }
266
+ interface CheckOptions {
267
+ readonly action?: string;
268
+ readonly metadata?: Record<string, unknown>;
269
+ }
270
+ interface VerifyRiskTokenOptions {
271
+ readonly expectedAction?: string;
272
+ readonly maxAgeMs?: number;
273
+ }
274
+ interface TokenRevocationStore {
275
+ isRevoked(tokenId: string): Promise<boolean> | boolean;
276
+ revoke(tokenId: string, ttlMs: number): Promise<void> | void;
277
+ }
278
+
279
+ declare class BotDetectorError extends Error {
280
+ readonly code: ErrorCode;
281
+ readonly requestId: string;
282
+ constructor(code: ErrorCode, message: string, requestId: string);
283
+ toJSON(): ApiErrorBody;
284
+ }
285
+ declare function isBotDetectorError(value: unknown): value is BotDetectorError;
286
+
287
+ declare function clamp(value: number, min: number, max: number): number;
288
+ declare function decisionFromScore(score: number, thresholds?: RiskConfig): RiskDecision;
289
+ declare function riskLevelFromScore(score: number): RiskLevel;
290
+ declare function mergeRiskThresholds(partial?: Partial<RiskConfig>): RiskConfig;
291
+ declare function isPlainObject(value: unknown): value is Record<string, unknown>;
292
+ declare function utf8ByteLength(value: string): number;
293
+ /** Normalize hostname for allowlist comparison. */
294
+ declare function normalizeHostname(hostname: string): string;
295
+ /** Check whether hostname matches an allowed domain (exact or subdomain). */
296
+ declare function hostnameAllowed(hostname: string, allowedDomains: readonly string[]): boolean;
297
+ declare function extractHostnameFromOrigin(origin: string | undefined): string | null;
298
+ /** Check whether an IP matches any entry in a list of exact IPs, wildcards, or IPv4 CIDR blocks. */
299
+ declare function ipMatches(ip: string | undefined, patterns: readonly string[]): boolean;
300
+
301
+ type ValidationResult<T> = {
302
+ readonly ok: true;
303
+ readonly value: T;
304
+ } | {
305
+ readonly ok: false;
306
+ readonly message: string;
307
+ };
308
+ declare function validateTelemetry(raw: unknown, maxBytes?: number): ValidationResult<Telemetry>;
309
+ declare function validateCreateSessionRequest(raw: unknown): ValidationResult<CreateSessionRequest>;
310
+ declare function validateAnalyzeRequest(raw: unknown, maxBytes?: number): ValidationResult<AnalyzeRequest>;
311
+
312
+ export { API_PATHS, type AnalyzeRequest, type AnalyzeResponse, type ApiErrorBody, BotDetectorError, type BrowserSignals, type CheckOptions, type ClientConfig, type ClientCredentialsMode, type ClientSession, type CreateSessionRequest, type CreateSessionResponse, DEFAULT_RISK_THRESHOLDS, DEFAULT_RISK_TOKEN_DURATION_MS, DEFAULT_SESSION_DURATION_MS, DEFAULT_TELEMETRY_INTERVAL_MS, ERROR_CODES, type ErrorCode, type FocusSignals, type InteractionSignals, type KeyboardSignals, MAX_TELEMETRY_BYTES, type PointerSignals, type RateLimitResult, type RequestContext, type RiskConfig, type RiskDecision, type RiskLevel, type RiskReason, type RiskResult, type RiskTokenPayload, SITE_KEY_HEADER, type ScrollSignals, type ServerConfig, type SessionData, type SiteInfo, type SiteStatus, TELEMETRY_VERSION, type Telemetry, type TokenRevocationStore, type TouchSignals, type ValidationResult, type VerifyRiskTokenOptions, type VisibilitySignals, clamp, decisionFromScore, extractHostnameFromOrigin, hostnameAllowed, ipMatches, isBotDetectorError, isPlainObject, mergeRiskThresholds, normalizeHostname, riskLevelFromScore, utf8ByteLength, validateAnalyzeRequest, validateCreateSessionRequest, validateTelemetry };
@@ -0,0 +1,312 @@
1
+ /** Default session lifetime (30 minutes). */
2
+ declare const DEFAULT_SESSION_DURATION_MS: number;
3
+ /** Default client telemetry flush interval. */
4
+ declare const DEFAULT_TELEMETRY_INTERVAL_MS = 5000;
5
+ /** Soft max size for a telemetry JSON payload. */
6
+ declare const MAX_TELEMETRY_BYTES: number;
7
+ /** Default risk token lifetime (5 minutes). */
8
+ declare const DEFAULT_RISK_TOKEN_DURATION_MS: number;
9
+ /** Protocol version embedded in telemetry. */
10
+ declare const TELEMETRY_VERSION: 1;
11
+ /** HTTP header for the public site key. */
12
+ declare const SITE_KEY_HEADER = "X-Bot-Site-Key";
13
+ /** API path suffixes (relative to endpoint base). */
14
+ declare const API_PATHS: {
15
+ readonly session: "/v1/session";
16
+ readonly analyze: "/v1/analyze";
17
+ };
18
+ /**
19
+ * Default human-score thresholds (higher = more human-like).
20
+ * allow >= 70, monitor >= 50, challenge >= 30, block < 30
21
+ */
22
+ declare const DEFAULT_RISK_THRESHOLDS: {
23
+ readonly allow: 70;
24
+ readonly monitor: 50;
25
+ readonly challenge: 30;
26
+ };
27
+ declare const ERROR_CODES: readonly ["INVALID_SITE_KEY", "INVALID_HOSTNAME", "INVALID_SESSION", "SESSION_EXPIRED", "INVALID_NONCE", "INVALID_SEQUENCE", "RATE_LIMITED", "INVALID_TELEMETRY", "MALFORMED_REQUEST", "INTERNAL_ERROR", "IP_BLOCKED", "TOKEN_ALREADY_USED", "ACTION_MISMATCH"];
28
+ type ErrorCode = (typeof ERROR_CODES)[number];
29
+
30
+ type RiskDecision = 'allow' | 'monitor' | 'challenge' | 'block';
31
+ type RiskLevel = 'low' | 'medium' | 'high';
32
+ type SiteStatus = 'active' | 'disabled' | 'suspended';
33
+ /** Aggregated browser environment signals (no cookies / storage / tokens). */
34
+ interface BrowserSignals {
35
+ readonly userAgent: string;
36
+ readonly language: string;
37
+ readonly languagesLength: number;
38
+ readonly timezone: string;
39
+ readonly timezoneOffset: number;
40
+ readonly platform: string;
41
+ readonly screenWidth: number;
42
+ readonly screenHeight: number;
43
+ readonly colorDepth: number;
44
+ readonly devicePixelRatio: number;
45
+ readonly hardwareConcurrency: number | null;
46
+ readonly maxTouchPoints: number;
47
+ readonly cookieEnabled: boolean;
48
+ /** One risk signal among many — never decisive alone. */
49
+ readonly webdriver: boolean;
50
+ readonly webglRenderer?: string | null;
51
+ readonly webglVendor?: string | null;
52
+ readonly hasChrome?: boolean;
53
+ readonly pluginsCount?: number;
54
+ readonly screenOrientation?: string | null;
55
+ }
56
+ /** Aggregated pointer/mouse statistics (no raw coordinates retained). */
57
+ interface PointerSignals {
58
+ readonly eventCount: number;
59
+ readonly movementDistance: number;
60
+ readonly averageInterval: number | null;
61
+ readonly minInterval: number | null;
62
+ readonly maxInterval: number | null;
63
+ readonly directionChanges: number;
64
+ readonly idlePeriods: number;
65
+ readonly activeDuration: number;
66
+ readonly clickCount: number;
67
+ }
68
+ /** Aggregated keyboard timing statistics (no keys / characters / codes). */
69
+ interface KeyboardSignals {
70
+ readonly keydownCount: number;
71
+ readonly keyupCount: number;
72
+ readonly averageInterval: number | null;
73
+ readonly minInterval: number | null;
74
+ readonly maxInterval: number | null;
75
+ readonly timingVariance: number | null;
76
+ readonly pasteCount: number;
77
+ }
78
+ /** Aggregated touch statistics. */
79
+ interface TouchSignals {
80
+ readonly touchCount: number;
81
+ readonly averageInterval: number | null;
82
+ readonly activeDuration: number;
83
+ readonly multiTouchCount: number;
84
+ }
85
+ /** Aggregated scroll statistics. */
86
+ interface ScrollSignals {
87
+ readonly scrollCount: number;
88
+ readonly averageInterval: number | null;
89
+ readonly totalDistance: number;
90
+ readonly directionChanges: number;
91
+ readonly activeDuration: number;
92
+ }
93
+ /** Focus / blur statistics. */
94
+ interface FocusSignals {
95
+ readonly focusCount: number;
96
+ readonly blurCount: number;
97
+ }
98
+ /** Page visibility statistics. */
99
+ interface VisibilitySignals {
100
+ readonly visibilityChanges: number;
101
+ readonly visibleDuration: number;
102
+ readonly hiddenDuration: number;
103
+ }
104
+ /** High-level interaction timing (no form values). */
105
+ interface InteractionSignals {
106
+ readonly pageLoadTime: number;
107
+ readonly firstInteractionTime: number | null;
108
+ readonly timeToFirstInteraction: number | null;
109
+ readonly clickCount: number;
110
+ readonly interactionCount: number;
111
+ readonly formInteractionCount: number;
112
+ readonly scrollCount: number;
113
+ readonly focusCount: number;
114
+ }
115
+ /** Privacy-safe aggregated telemetry payload. */
116
+ interface Telemetry {
117
+ readonly version: 1;
118
+ readonly browser?: BrowserSignals;
119
+ readonly pointer?: PointerSignals;
120
+ readonly keyboard?: KeyboardSignals;
121
+ readonly touch?: TouchSignals;
122
+ readonly scroll?: ScrollSignals;
123
+ readonly focus?: FocusSignals;
124
+ readonly visibility?: VisibilitySignals;
125
+ readonly interaction?: InteractionSignals;
126
+ }
127
+ interface ClientSession {
128
+ readonly sessionId: string;
129
+ readonly nonce: string;
130
+ readonly createdAt: number;
131
+ readonly expiresAt: number;
132
+ }
133
+ interface SessionData {
134
+ readonly sessionId: string;
135
+ readonly siteKey: string;
136
+ readonly siteId: string;
137
+ readonly tenantId: string;
138
+ readonly nonce: string;
139
+ readonly createdAt: number;
140
+ readonly expiresAt: number;
141
+ readonly hostname: string;
142
+ /** Last accepted sequence number (replay protection). */
143
+ sequence: number;
144
+ /** Nonces already consumed for this session (short-lived). */
145
+ usedNonces: string[];
146
+ }
147
+ interface RequestContext {
148
+ readonly ip?: string;
149
+ readonly userAgent?: string;
150
+ readonly origin?: string;
151
+ readonly host?: string;
152
+ readonly forwardedFor?: string;
153
+ }
154
+ interface SiteInfo {
155
+ readonly siteId: string;
156
+ readonly tenantId: string;
157
+ readonly allowedDomains: readonly string[];
158
+ readonly status: SiteStatus;
159
+ }
160
+ interface RiskReason {
161
+ readonly code: string;
162
+ readonly weight: number;
163
+ readonly detail?: string;
164
+ }
165
+ interface RiskResult {
166
+ readonly requestId: string;
167
+ readonly score: number;
168
+ readonly confidence: number;
169
+ readonly decision: RiskDecision;
170
+ readonly riskLevel: RiskLevel;
171
+ readonly action?: string;
172
+ readonly riskToken?: string;
173
+ readonly expiresAt?: string;
174
+ readonly timestamp: number;
175
+ }
176
+ interface RiskConfig {
177
+ readonly allow: number;
178
+ readonly monitor: number;
179
+ readonly challenge: number;
180
+ }
181
+ type ClientCredentialsMode = 'omit' | 'same-origin' | 'include';
182
+ interface ClientConfig {
183
+ readonly siteKey: string;
184
+ readonly endpoint: string;
185
+ readonly autoStart?: boolean;
186
+ readonly telemetryInterval?: number;
187
+ readonly sessionDuration?: number;
188
+ readonly collectPointer?: boolean;
189
+ readonly collectKeyboardTiming?: boolean;
190
+ readonly collectTouch?: boolean;
191
+ readonly collectScroll?: boolean;
192
+ readonly collectFocus?: boolean;
193
+ readonly collectVisibility?: boolean;
194
+ readonly collectBrowser?: boolean;
195
+ readonly failOpen?: boolean;
196
+ readonly maxTelemetryBytes?: number;
197
+ readonly debug?: boolean;
198
+ readonly headers?: Record<string, string> | (() => Record<string, string> | Promise<Record<string, string>>);
199
+ readonly fetchFn?: typeof fetch;
200
+ readonly credentials?: ClientCredentialsMode;
201
+ readonly timeoutMs?: number;
202
+ readonly retries?: number;
203
+ readonly retryDelayMs?: number;
204
+ readonly sampleRate?: number;
205
+ readonly onSessionCreated?: (session: ClientSession) => void;
206
+ readonly onCheck?: (result: RiskResult) => void;
207
+ readonly onError?: (error: Error) => void;
208
+ }
209
+ interface ServerConfig {
210
+ readonly secret: string;
211
+ readonly sessionDuration?: number;
212
+ readonly riskThresholds?: Partial<RiskConfig>;
213
+ readonly actionThresholds?: Record<string, Partial<RiskConfig>>;
214
+ readonly ipAllowlist?: readonly string[];
215
+ readonly ipDenylist?: readonly string[];
216
+ readonly detectorWeights?: Record<string, number>;
217
+ readonly enableRiskToken?: boolean;
218
+ readonly riskTokenDuration?: number;
219
+ readonly trustProxy?: boolean;
220
+ readonly debug?: boolean;
221
+ }
222
+ interface ApiErrorBody {
223
+ readonly error: {
224
+ readonly code: ErrorCode;
225
+ readonly message: string;
226
+ readonly requestId: string;
227
+ };
228
+ }
229
+ interface CreateSessionRequest {
230
+ readonly hostname: string;
231
+ readonly userAgent?: string;
232
+ readonly timestamp: number;
233
+ }
234
+ interface CreateSessionResponse {
235
+ readonly sessionId: string;
236
+ readonly nonce: string;
237
+ readonly expiresAt: string;
238
+ }
239
+ interface AnalyzeRequest {
240
+ readonly sessionId: string;
241
+ readonly sequence: number;
242
+ readonly nonce?: string;
243
+ readonly timestamp: number;
244
+ readonly hostname: string;
245
+ readonly telemetry: Telemetry;
246
+ readonly action?: string;
247
+ readonly metadata?: Record<string, unknown>;
248
+ }
249
+ interface AnalyzeResponse extends RiskResult {
250
+ }
251
+ interface RateLimitResult {
252
+ readonly allowed: boolean;
253
+ readonly remaining: number;
254
+ readonly retryAfter?: number;
255
+ }
256
+ interface RiskTokenPayload {
257
+ readonly requestId: string;
258
+ readonly siteId: string;
259
+ readonly sessionId: string;
260
+ readonly decision: RiskDecision;
261
+ readonly issuedAt: number;
262
+ readonly expiresAt: number;
263
+ readonly action?: string;
264
+ readonly score?: number;
265
+ }
266
+ interface CheckOptions {
267
+ readonly action?: string;
268
+ readonly metadata?: Record<string, unknown>;
269
+ }
270
+ interface VerifyRiskTokenOptions {
271
+ readonly expectedAction?: string;
272
+ readonly maxAgeMs?: number;
273
+ }
274
+ interface TokenRevocationStore {
275
+ isRevoked(tokenId: string): Promise<boolean> | boolean;
276
+ revoke(tokenId: string, ttlMs: number): Promise<void> | void;
277
+ }
278
+
279
+ declare class BotDetectorError extends Error {
280
+ readonly code: ErrorCode;
281
+ readonly requestId: string;
282
+ constructor(code: ErrorCode, message: string, requestId: string);
283
+ toJSON(): ApiErrorBody;
284
+ }
285
+ declare function isBotDetectorError(value: unknown): value is BotDetectorError;
286
+
287
+ declare function clamp(value: number, min: number, max: number): number;
288
+ declare function decisionFromScore(score: number, thresholds?: RiskConfig): RiskDecision;
289
+ declare function riskLevelFromScore(score: number): RiskLevel;
290
+ declare function mergeRiskThresholds(partial?: Partial<RiskConfig>): RiskConfig;
291
+ declare function isPlainObject(value: unknown): value is Record<string, unknown>;
292
+ declare function utf8ByteLength(value: string): number;
293
+ /** Normalize hostname for allowlist comparison. */
294
+ declare function normalizeHostname(hostname: string): string;
295
+ /** Check whether hostname matches an allowed domain (exact or subdomain). */
296
+ declare function hostnameAllowed(hostname: string, allowedDomains: readonly string[]): boolean;
297
+ declare function extractHostnameFromOrigin(origin: string | undefined): string | null;
298
+ /** Check whether an IP matches any entry in a list of exact IPs, wildcards, or IPv4 CIDR blocks. */
299
+ declare function ipMatches(ip: string | undefined, patterns: readonly string[]): boolean;
300
+
301
+ type ValidationResult<T> = {
302
+ readonly ok: true;
303
+ readonly value: T;
304
+ } | {
305
+ readonly ok: false;
306
+ readonly message: string;
307
+ };
308
+ declare function validateTelemetry(raw: unknown, maxBytes?: number): ValidationResult<Telemetry>;
309
+ declare function validateCreateSessionRequest(raw: unknown): ValidationResult<CreateSessionRequest>;
310
+ declare function validateAnalyzeRequest(raw: unknown, maxBytes?: number): ValidationResult<AnalyzeRequest>;
311
+
312
+ export { API_PATHS, type AnalyzeRequest, type AnalyzeResponse, type ApiErrorBody, BotDetectorError, type BrowserSignals, type CheckOptions, type ClientConfig, type ClientCredentialsMode, type ClientSession, type CreateSessionRequest, type CreateSessionResponse, DEFAULT_RISK_THRESHOLDS, DEFAULT_RISK_TOKEN_DURATION_MS, DEFAULT_SESSION_DURATION_MS, DEFAULT_TELEMETRY_INTERVAL_MS, ERROR_CODES, type ErrorCode, type FocusSignals, type InteractionSignals, type KeyboardSignals, MAX_TELEMETRY_BYTES, type PointerSignals, type RateLimitResult, type RequestContext, type RiskConfig, type RiskDecision, type RiskLevel, type RiskReason, type RiskResult, type RiskTokenPayload, SITE_KEY_HEADER, type ScrollSignals, type ServerConfig, type SessionData, type SiteInfo, type SiteStatus, TELEMETRY_VERSION, type Telemetry, type TokenRevocationStore, type TouchSignals, type ValidationResult, type VerifyRiskTokenOptions, type VisibilitySignals, clamp, decisionFromScore, extractHostnameFromOrigin, hostnameAllowed, ipMatches, isBotDetectorError, isPlainObject, mergeRiskThresholds, normalizeHostname, riskLevelFromScore, utf8ByteLength, validateAnalyzeRequest, validateCreateSessionRequest, validateTelemetry };