vpndetection 1.6.0 → 1.8.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.
package/dist/client.d.ts CHANGED
@@ -32,6 +32,13 @@ export interface Options {
32
32
  concurrency?: number;
33
33
  /** Retry attempts for a transient failure. Default 2. */
34
34
  retries?: number;
35
+ /**
36
+ * How long one request may take before it is abandoned, in milliseconds.
37
+ * Default 30000. Applies per attempt, so a retried call may take longer in
38
+ * total. A dataset transfer is deliberately exempt: it is expected to run
39
+ * for minutes.
40
+ */
41
+ timeoutMs?: number;
35
42
  /** Override the HTTP implementation, mostly for tests. */
36
43
  fetch?: typeof globalThis.fetch;
37
44
  }
@@ -39,6 +46,8 @@ export interface Options {
39
46
  export interface LookupOptions {
40
47
  /** Retry attempts for a transient failure. */
41
48
  retries?: number;
49
+ /** How long one attempt may take before it is abandoned, in milliseconds. */
50
+ timeoutMs?: number;
42
51
  }
43
52
  /** Per-call overrides for one batch. Anything omitted falls back to the client's setting. */
44
53
  export interface BatchOptions extends LookupOptions {
@@ -56,6 +65,7 @@ export declare class VPNDetection {
56
65
  private readonly cache;
57
66
  private readonly limit;
58
67
  private readonly retries;
68
+ private readonly timeoutMs;
59
69
  /** The licensed dataset downloads, for keys that carry the `db.download` scope. */
60
70
  readonly database: DatabaseApi;
61
71
  constructor(options?: Options);
@@ -90,8 +100,9 @@ export declare class VPNDetection {
90
100
  export declare class DatabaseApi {
91
101
  private readonly client;
92
102
  private readonly retries;
103
+ private readonly timeoutMs;
93
104
  private readonly fetchImpl;
94
- constructor(client: Client, retries: number, fetchImpl: typeof globalThis.fetch);
105
+ constructor(client: Client, retries: number, timeoutMs: number, fetchImpl: typeof globalThis.fetch);
95
106
  list(): Promise<LicensedDataset[]>;
96
107
  metadata(id: string): Promise<DatasetMetadata>;
97
108
  /**
package/dist/client.js CHANGED
@@ -7,6 +7,10 @@ import { bogonResult, isBogon } from './bogon.js';
7
7
  import { errorFromResponse, VPNDetectionError } from './errors.js';
8
8
  import { toResult } from './types.js';
9
9
  export const DEFAULT_BASE_URL = 'https://api.vpndetection.io';
10
+ // Matches the other SDKs, whose HTTP clients default to 30s. Node's global
11
+ // fetch has no whole-request limit of its own, so without this a hung API holds
12
+ // a caller until undici's 300s headers timeout - which is why this exists.
13
+ const DEFAULT_TIMEOUT_MS = 30_000;
10
14
  /**
11
15
  * A client for the VPNDetection API.
12
16
  *
@@ -18,6 +22,7 @@ export class VPNDetection {
18
22
  cache;
19
23
  limit;
20
24
  retries;
25
+ timeoutMs;
21
26
  /** The licensed dataset downloads, for keys that carry the `db.download` scope. */
22
27
  database;
23
28
  constructor(options = {}) {
@@ -36,7 +41,8 @@ export class VPNDetection {
36
41
  });
37
42
  this.limit = pLimit(options.concurrency ?? 8);
38
43
  this.retries = options.retries ?? 2;
39
- this.database = new DatabaseApi(this.client, this.retries, fetchImpl);
44
+ this.timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
45
+ this.database = new DatabaseApi(this.client, this.retries, this.timeoutMs, fetchImpl);
40
46
  }
41
47
  /**
42
48
  * Whether an address is private, loopback, link-local, documentation,
@@ -64,8 +70,11 @@ export class VPNDetection {
64
70
  if (hit !== undefined) {
65
71
  return hit;
66
72
  }
73
+ const timeoutMs = options.timeoutMs ?? this.timeoutMs;
67
74
  const result = await withRetry(options.retries ?? this.retries, async () => {
68
- const res = await lookupIp({ client: this.client, path: { ip: ip } });
75
+ const res = await deadline(timeoutMs, (signal) => lookupIp({
76
+ client: this.client, path: { ip: ip }, signal: signal,
77
+ }));
69
78
  const body = unwrap(res);
70
79
  return toResult(body);
71
80
  });
@@ -105,21 +114,27 @@ export class VPNDetection {
105
114
  export class DatabaseApi {
106
115
  client;
107
116
  retries;
117
+ timeoutMs;
108
118
  fetchImpl;
109
- constructor(client, retries, fetchImpl) {
119
+ constructor(client, retries, timeoutMs, fetchImpl) {
110
120
  this.client = client;
111
121
  this.retries = retries;
122
+ this.timeoutMs = timeoutMs;
112
123
  this.fetchImpl = fetchImpl;
113
124
  }
114
125
  async list() {
115
126
  return withRetry(this.retries, async () => {
116
- const res = await listDatabases({ client: this.client });
127
+ const res = await deadline(this.timeoutMs, (signal) => listDatabases({
128
+ client: this.client, signal: signal,
129
+ }));
117
130
  return unwrap(res).datasets;
118
131
  });
119
132
  }
120
133
  async metadata(id) {
121
134
  return withRetry(this.retries, async () => {
122
- const res = await databaseMetadata({ client: this.client, query: { id: id } });
135
+ const res = await deadline(this.timeoutMs, (signal) => databaseMetadata({
136
+ client: this.client, query: { id: id }, signal: signal,
137
+ }));
123
138
  return unwrap(res);
124
139
  });
125
140
  }
@@ -132,15 +147,17 @@ export class DatabaseApi {
132
147
  */
133
148
  async checksums(id, format) {
134
149
  return withRetry(this.retries, async () => {
135
- const res = await databaseChecksum({
136
- client: this.client, query: { id: id, format: format },
137
- });
150
+ const res = await deadline(this.timeoutMs, (signal) => databaseChecksum({
151
+ client: this.client, query: { id: id, format: format }, signal: signal,
152
+ }));
138
153
  return unwrap(res).checksums;
139
154
  });
140
155
  }
141
156
  async downloads() {
142
157
  return withRetry(this.retries, async () => {
143
- const res = await listDownloads({ client: this.client });
158
+ const res = await deadline(this.timeoutMs, (signal) => listDownloads({
159
+ client: this.client, signal: signal,
160
+ }));
144
161
  return unwrap(res).downloads;
145
162
  });
146
163
  }
@@ -154,11 +171,12 @@ export class DatabaseApi {
154
171
  */
155
172
  async downloadUrl(id, format) {
156
173
  return withRetry(this.retries, async () => {
157
- const res = await downloadRedirect({
174
+ const res = await deadline(this.timeoutMs, (signal) => downloadRedirect({
158
175
  client: this.client,
159
176
  query: { id: id, format: format },
160
177
  redirect: 'manual',
161
- });
178
+ signal: signal,
179
+ }));
162
180
  if (res.response === undefined) {
163
181
  throw new VPNDetectionError('network', 'no response from the API');
164
182
  }
@@ -267,6 +285,32 @@ function unwrap(res) {
267
285
  // server-supplied Retry-After, which p-retry has no way to know about. A 429
268
286
  // carrying that header is the only 429 worth retrying, which is why the wait
269
287
  // and the retry decision both key off the same field.
288
+ /**
289
+ * Bound one attempt, and report an expiry as our own error rather than the
290
+ * runtime's `TimeoutError`, whose message says nothing about which call gave up.
291
+ *
292
+ * The signal is built per call, so a retried request gets a fresh budget - the
293
+ * same per-attempt semantics the Go and Python clients have.
294
+ */
295
+ async function deadline(timeoutMs, fn) {
296
+ const controller = new AbortController();
297
+ let timer;
298
+ // Raced, not left to the signal alone: aborting releases the socket, but only
299
+ // a transport that HONORS the signal then settles, and a substituted `fetch`
300
+ // need not. Clearing the timer stops the losing side rejecting into nothing.
301
+ const expiry = new Promise((_, reject) => {
302
+ timer = setTimeout(() => {
303
+ controller.abort();
304
+ reject(new VPNDetectionError('network', `request timed out after ${timeoutMs}ms`));
305
+ }, timeoutMs);
306
+ });
307
+ try {
308
+ return await Promise.race([fn(controller.signal), expiry]);
309
+ }
310
+ finally {
311
+ clearTimeout(timer);
312
+ }
313
+ }
270
314
  async function withRetry(retries, fn) {
271
315
  try {
272
316
  return await pRetry(fn, {
@@ -1 +1 @@
1
- {"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
@@ -37,15 +37,15 @@ export type LookupResponse = {
37
37
  */
38
38
  is_cdn?: boolean;
39
39
  /**
40
- * Whether the address was seen in a residential proxy pool. Max only.
40
+ * Whether the address was seen in a residential proxy pool. Scale and above.
41
41
  */
42
42
  is_resproxy?: boolean;
43
43
  /**
44
- * Whether the address was seen in a datacenter proxy pool. Max only.
44
+ * Whether the address was seen in a datacenter proxy pool. Scale and above.
45
45
  */
46
46
  is_dcproxy?: boolean;
47
47
  /**
48
- * Whether the address was seen in a mobile proxy pool. Max only.
48
+ * Whether the address was seen in a mobile proxy pool. Scale and above.
49
49
  */
50
50
  is_mobproxy?: boolean;
51
51
  /**
@@ -105,7 +105,16 @@ export type VpnDetail = {
105
105
  */
106
106
  confidence?: string;
107
107
  /**
108
- * How the address was attributed to the provider. Max only.
108
+ * The class of evidence the attribution rests on, one of four values.
109
+ * Max only.
110
+ *
111
+ * `scan` - we spoke the VPN protocol to the address ourselves and got a
112
+ * valid server response. `scrape` - the operator published the address
113
+ * through its own API, client or configuration. `registry` - public
114
+ * registration or naming records attribute it to the operator.
115
+ * `infer` - the address was extrapolated from confirmed neighbours in
116
+ * the same block.
117
+ *
109
118
  */
110
119
  method?: string;
111
120
  };
@@ -0,0 +1,59 @@
1
+ import type { Result } from '../types.js';
2
+ /**
3
+ * A bound on a numeric member. Every key present must hold, so
4
+ * `{ gte: 5, lt: 100 }` is a range.
5
+ */
6
+ export interface NumericBound {
7
+ gte?: number;
8
+ gt?: number;
9
+ lte?: number;
10
+ lt?: number;
11
+ }
12
+ type LeafCondition<T> = T extends number ? number | number[] | NumericBound : T extends string ? string | string[] : T extends boolean ? boolean : never;
13
+ type ConditionOn<T> = {
14
+ [K in keyof T]?: NonNullable<T[K]> extends object ? ConditionOn<NonNullable<T[K]>> : LeafCondition<NonNullable<T[K]>> | false | null;
15
+ };
16
+ /**
17
+ * What makes a request worth blocking, written in the shape of a `Result`.
18
+ *
19
+ * Only the members you name are considered, and they must all hold. A member
20
+ * set to `false` or `null` is ignored entirely - a condition states the
21
+ * positive signals you act on, so there is no way to write "block when this is
22
+ * false", which would otherwise read as blocking everybody.
23
+ *
24
+ * ```ts
25
+ * { isVpn: true, vpn: { provider: 'nordvpn' } }
26
+ * { isResproxy: true, resproxy: { hits: { gte: 5 } } }
27
+ * { isTor: true, vpn: { confidence: ['high', 'medium'] } }
28
+ * ```
29
+ *
30
+ * A list means OR, so any one of its entries blocking is enough.
31
+ */
32
+ export type BlockCondition = ConditionOn<Omit<Result, 'raw'>>;
33
+ /** Whether an answer satisfies the condition, and should therefore be blocked. */
34
+ export declare function matchesCondition(condition: BlockCondition | BlockCondition[], result: Result): boolean;
35
+ /**
36
+ * The top-level members a condition names that this answer did not carry.
37
+ *
38
+ * A field your plan does not include is absent rather than false, so a
39
+ * condition naming one can never match and the block would silently never
40
+ * fire. Gating is per top-level member, which is why only the first segment of
41
+ * each path is checked: `vpn` present but empty is a real answer meaning the
42
+ * flag is false, not a plan gap.
43
+ *
44
+ * A locally answered bogon needs no special case: it is synthesized in the
45
+ * widest shape, so every member is present and nothing reads as missing. The
46
+ * corpus pins that, because narrowing the synthesized shape would start
47
+ * reporting a plan gap on every private address.
48
+ */
49
+ export declare function missingMembers(condition: BlockCondition | BlockCondition[], result: Result): string[];
50
+ /**
51
+ * How many leaf constraints a condition actually carries.
52
+ *
53
+ * Callers use this to reject a condition that constrains nothing: with every
54
+ * entry ignored there is nothing left to satisfy, so it would match every
55
+ * answer and block all traffic. Nobody writes that on purpose, and failing at
56
+ * construction beats discovering it in production.
57
+ */
58
+ export declare function constraintCount(condition: unknown): number;
59
+ export {};
@@ -0,0 +1,112 @@
1
+ /** Whether an answer satisfies the condition, and should therefore be blocked. */
2
+ export function matchesCondition(condition, result) {
3
+ const any = Array.isArray(condition) ? condition : [condition];
4
+ return any.some((one) => matchesObject(one, result));
5
+ }
6
+ /**
7
+ * The top-level members a condition names that this answer did not carry.
8
+ *
9
+ * A field your plan does not include is absent rather than false, so a
10
+ * condition naming one can never match and the block would silently never
11
+ * fire. Gating is per top-level member, which is why only the first segment of
12
+ * each path is checked: `vpn` present but empty is a real answer meaning the
13
+ * flag is false, not a plan gap.
14
+ *
15
+ * A locally answered bogon needs no special case: it is synthesized in the
16
+ * widest shape, so every member is present and nothing reads as missing. The
17
+ * corpus pins that, because narrowing the synthesized shape would start
18
+ * reporting a plan gap on every private address.
19
+ */
20
+ export function missingMembers(condition, result) {
21
+ const any = Array.isArray(condition) ? condition : [condition];
22
+ const missing = new Set();
23
+ for (const one of any) {
24
+ for (const [member, want] of Object.entries(one)) {
25
+ if (constraintCount(want) > 0 && !(member in result)) {
26
+ missing.add(member);
27
+ }
28
+ }
29
+ }
30
+ return [...missing];
31
+ }
32
+ /**
33
+ * How many leaf constraints a condition actually carries.
34
+ *
35
+ * Callers use this to reject a condition that constrains nothing: with every
36
+ * entry ignored there is nothing left to satisfy, so it would match every
37
+ * answer and block all traffic. Nobody writes that on purpose, and failing at
38
+ * construction beats discovering it in production.
39
+ */
40
+ export function constraintCount(condition) {
41
+ if (condition === false || condition === null || condition === undefined) {
42
+ return 0;
43
+ }
44
+ if (Array.isArray(condition)) {
45
+ return condition.reduce((n, entry) => n + constraintCount(entry), 0);
46
+ }
47
+ if (isNumericBound(condition)) {
48
+ return 1;
49
+ }
50
+ if (typeof condition === 'object') {
51
+ return Object.values(condition).reduce((n, v) => n + constraintCount(v), 0);
52
+ }
53
+ return 1;
54
+ }
55
+ function matchesObject(condition, value) {
56
+ for (const [key, want] of Object.entries(condition)) {
57
+ if (constraintCount(want) === 0) {
58
+ continue;
59
+ }
60
+ if (!(key in value) || !matchesValue(want, value[key])) {
61
+ return false;
62
+ }
63
+ }
64
+ return true;
65
+ }
66
+ function matchesValue(want, got) {
67
+ if (Array.isArray(want)) {
68
+ return want.some((entry) => matchesValue(entry, got));
69
+ }
70
+ if (isNumericBound(want)) {
71
+ return matchesBound(want, got);
72
+ }
73
+ if (typeof want === 'object' && want !== null) {
74
+ return isRecord(got) && matchesObject(want, got);
75
+ }
76
+ // Providers are lowercase slugs on the wire and a caller should not have to
77
+ // know that, so a string compares without case.
78
+ if (typeof want === 'string' && typeof got === 'string') {
79
+ return want.toLowerCase() === got.toLowerCase();
80
+ }
81
+ return want === got;
82
+ }
83
+ function matchesBound(bound, got) {
84
+ if (typeof got !== 'number') {
85
+ return false;
86
+ }
87
+ if (bound.gte !== undefined && got < bound.gte) {
88
+ return false;
89
+ }
90
+ if (bound.gt !== undefined && got <= bound.gt) {
91
+ return false;
92
+ }
93
+ if (bound.lte !== undefined && got > bound.lte) {
94
+ return false;
95
+ }
96
+ if (bound.lt !== undefined && got >= bound.lt) {
97
+ return false;
98
+ }
99
+ return true;
100
+ }
101
+ const BOUND_KEYS = ['gte', 'gt', 'lte', 'lt'];
102
+ function isNumericBound(value) {
103
+ if (!isRecord(value)) {
104
+ return false;
105
+ }
106
+ const keys = Object.keys(value);
107
+ return keys.length > 0 && keys.every((k) => BOUND_KEYS.includes(k));
108
+ }
109
+ function isRecord(value) {
110
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
111
+ }
112
+ //# sourceMappingURL=condition.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"condition.js","sourceRoot":"","sources":["../../src/middleware/condition.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,128 @@
1
+ import { VPNDetection, type CacheOptions } from '../client.js';
2
+ import { VPNDetectionError } from '../errors.js';
3
+ import type { Result } from '../types.js';
4
+ import { type BlockCondition } from './condition.js';
5
+ /**
6
+ * Enough of an incoming request for a selector to work with, whatever
7
+ * framework it came from. An adapter supplies one of these per request.
8
+ */
9
+ export interface RequestView {
10
+ /** A request header by name, case-insensitively. */
11
+ header(name: string): string | undefined;
12
+ /** The framework's own client-address accessor, whatever that resolves to here. */
13
+ frameworkIp(): string | undefined;
14
+ }
15
+ /**
16
+ * How the client address is decided.
17
+ *
18
+ * There is no portable answer: a framework's own accessor may return the
19
+ * socket peer, or may already have walked a proxy chain, depending on the
20
+ * framework and on how the application configured it. You know your framework
21
+ * and your edge, so this is yours to choose.
22
+ */
23
+ export type IpSelector<Req> = (request: Req) => string | undefined;
24
+ /** What a lookup attached to the request, whether or not it succeeded. */
25
+ export interface Lookup {
26
+ /** The address that was classified, as the selector resolved it. */
27
+ ip?: string;
28
+ /** The answer. Absent when the lookup failed. */
29
+ result?: Result;
30
+ /** Why the lookup failed. Absent when it succeeded. */
31
+ error?: VPNDetectionError;
32
+ /** Whether the condition matched. Always false when no condition was configured. */
33
+ blocked: boolean;
34
+ }
35
+ /** What to do when a condition names a member the plan does not serve. */
36
+ export type MissingFieldAction = 'warn' | 'throw' | 'ignore';
37
+ export interface MiddlewareOptions<Req> {
38
+ /**
39
+ * An existing client to use. Prefer this if you already hold one: two
40
+ * clients mean two caches, and a cache is per instance because two keys can
41
+ * be on different plans and entitled to different fields.
42
+ */
43
+ client?: VPNDetection;
44
+ /** Your API key. Ignored when `client` is given. */
45
+ apiKey?: string;
46
+ /** Ignored when `client` is given. */
47
+ baseUrl?: string;
48
+ /** Ignored when `client` is given. Pass `false` to disable caching. */
49
+ cache?: CacheOptions | false;
50
+ /**
51
+ * How long a lookup may hold the request, in milliseconds. Default 2500.
52
+ *
53
+ * This sits on the request path, so it is a much tighter bound than the
54
+ * client's own 30s default.
55
+ */
56
+ timeoutMs?: number;
57
+ /**
58
+ * Retry attempts for a transient failure. Default 0, unlike the client's 2:
59
+ * on a request path, failing open quickly beats holding a visitor while we
60
+ * try again.
61
+ */
62
+ retries?: number;
63
+ /** How the client address is decided. Defaults to the framework's own accessor. */
64
+ ipSelector?: IpSelector<Req>;
65
+ /**
66
+ * What to block on. Omit it to only enrich the request and leave the
67
+ * decision to your own code.
68
+ */
69
+ blockCondition?: BlockCondition | BlockCondition[];
70
+ /**
71
+ * Block when the lookup itself fails. Default false, so our outage does not
72
+ * become yours.
73
+ */
74
+ failClosed?: boolean;
75
+ /** What to do when the condition names a member your plan does not serve. Default `warn`. */
76
+ onMissingField?: MissingFieldAction;
77
+ /** Skip classification for this request entirely. */
78
+ skip?: (request: Req) => boolean;
79
+ /** Where warnings go. Defaults to `console.warn`. */
80
+ onWarn?: (message: string) => void;
81
+ }
82
+ export interface Core<Req> {
83
+ /** Classify one request. Answers `undefined` when `skip` claimed it. */
84
+ evaluate(request: Req): Promise<Lookup | undefined>;
85
+ }
86
+ /**
87
+ * The framework-agnostic half of a middleware: resolve an address, classify it,
88
+ * and decide whether the condition matched.
89
+ *
90
+ * Adapters own the framework-shaped parts - reading a request, attaching the
91
+ * answer, and refusing a request - and share everything here.
92
+ */
93
+ export declare function createCore<Req>(options: MiddlewareOptions<Req>, defaultIpSelector: IpSelector<Req>): Core<Req>;
94
+ /**
95
+ * Selectors bound to one framework's request type.
96
+ *
97
+ * An adapter calls this once with a function that exposes its own request, and
98
+ * gets the shared implementations back under its own signature - so a caller
99
+ * writing a custom selector still works with the request object they know.
100
+ */
101
+ export declare function bindSelectors<Req>(view: (request: Req) => RequestView): {
102
+ /**
103
+ * The framework's own client-address accessor. What that resolves to
104
+ * depends on the framework and on how you configured it.
105
+ */
106
+ defaultIpSelector: (request: Req) => string | undefined;
107
+ /**
108
+ * An address from `X-Forwarded-For`.
109
+ *
110
+ * **The left-most entry is whatever the caller sent.** Proxies append,
111
+ * so a visitor who sets the header themselves appears first in the list
112
+ * and this returns their forgery. It is only trustworthy when an edge
113
+ * you control overwrites the header rather than appending to it. When
114
+ * you know how many proxies sit in front, count from the right with
115
+ * `depth` instead: `depth: 1` is the address your nearest proxy saw.
116
+ */
117
+ xffIpSelector: (options?: {
118
+ depth?: number;
119
+ }) => (request: Req) => string | undefined;
120
+ /**
121
+ * An address from a single-value header, by name - for an edge that
122
+ * writes one. `headerIpSelector('CF-Connecting-IP')` behind Cloudflare,
123
+ * `headerIpSelector('True-Client-IP')` behind Akamai, and so on.
124
+ *
125
+ * Falls back to the framework's accessor when the header is absent.
126
+ */
127
+ headerIpSelector: (name: string) => (request: Req) => string | undefined;
128
+ };
@@ -0,0 +1,149 @@
1
+ import { isBogon } from '../bogon.js';
2
+ import { VPNDetection } from '../client.js';
3
+ import { VPNDetectionError } from '../errors.js';
4
+ import { constraintCount, matchesCondition, missingMembers } from './condition.js';
5
+ /**
6
+ * The framework-agnostic half of a middleware: resolve an address, classify it,
7
+ * and decide whether the condition matched.
8
+ *
9
+ * Adapters own the framework-shaped parts - reading a request, attaching the
10
+ * answer, and refusing a request - and share everything here.
11
+ */
12
+ export function createCore(options, defaultIpSelector) {
13
+ const condition = options.blockCondition;
14
+ if (condition !== undefined && constraintCount(condition) === 0) {
15
+ throw new Error('vpndetection: blockCondition constrains nothing, which would block every request. '
16
+ + 'A member set to false or null is ignored; state the positive signals you act on.');
17
+ }
18
+ const client = options.client ?? new VPNDetection({
19
+ apiKey: options.apiKey,
20
+ baseUrl: options.baseUrl,
21
+ cache: options.cache,
22
+ });
23
+ const selector = options.ipSelector ?? defaultIpSelector;
24
+ const lookupOptions = {
25
+ timeoutMs: options.timeoutMs ?? 2500,
26
+ retries: options.retries ?? 0,
27
+ };
28
+ const warn = onceEach(options.onWarn ?? ((message) => console.warn(message)));
29
+ return {
30
+ evaluate: async (request) => {
31
+ if (options.skip?.(request) === true) {
32
+ return undefined;
33
+ }
34
+ const ip = selector(request)?.trim();
35
+ if (ip === undefined || ip === '') {
36
+ warn('could not resolve a client address from this request; '
37
+ + 'pass an ipSelector that knows where yours comes from');
38
+ return {
39
+ error: new VPNDetectionError('bad_request', 'no client address on the request'),
40
+ blocked: options.failClosed === true,
41
+ };
42
+ }
43
+ if (isBogon(ip)) {
44
+ // Expected in local development. Anywhere else it means a proxy
45
+ // sits in front and its own address is what reached us.
46
+ warn(`resolved the client address as ${ip}, which is not a public address. `
47
+ + 'If this application runs behind a proxy or load balancer, configure its '
48
+ + 'trusted-proxy setting or pass an ipSelector that reads your edge\'s header.');
49
+ }
50
+ let result;
51
+ try {
52
+ result = await client.lookup(ip, lookupOptions);
53
+ }
54
+ catch (err) {
55
+ return {
56
+ ip: ip,
57
+ error: err instanceof VPNDetectionError
58
+ ? err
59
+ : new VPNDetectionError('network', String(err)),
60
+ blocked: options.failClosed === true,
61
+ };
62
+ }
63
+ if (condition !== undefined) {
64
+ reportMissing(condition, result, options.onMissingField ?? 'warn', warn);
65
+ }
66
+ return {
67
+ ip: ip,
68
+ result: result,
69
+ blocked: condition !== undefined && matchesCondition(condition, result),
70
+ };
71
+ },
72
+ };
73
+ }
74
+ /**
75
+ * Selectors bound to one framework's request type.
76
+ *
77
+ * An adapter calls this once with a function that exposes its own request, and
78
+ * gets the shared implementations back under its own signature - so a caller
79
+ * writing a custom selector still works with the request object they know.
80
+ */
81
+ export function bindSelectors(view) {
82
+ return {
83
+ /**
84
+ * The framework's own client-address accessor. What that resolves to
85
+ * depends on the framework and on how you configured it.
86
+ */
87
+ defaultIpSelector: (request) => view(request).frameworkIp(),
88
+ /**
89
+ * An address from `X-Forwarded-For`.
90
+ *
91
+ * **The left-most entry is whatever the caller sent.** Proxies append,
92
+ * so a visitor who sets the header themselves appears first in the list
93
+ * and this returns their forgery. It is only trustworthy when an edge
94
+ * you control overwrites the header rather than appending to it. When
95
+ * you know how many proxies sit in front, count from the right with
96
+ * `depth` instead: `depth: 1` is the address your nearest proxy saw.
97
+ */
98
+ xffIpSelector: (options = {}) => (request) => {
99
+ const v = view(request);
100
+ const chain = (v.header('x-forwarded-for') ?? '')
101
+ .split(',').map((e) => e.trim()).filter((e) => e !== '');
102
+ if (chain.length === 0) {
103
+ return v.frameworkIp();
104
+ }
105
+ const depth = options.depth ?? 0;
106
+ return depth <= 0 ? chain[0] : chain[chain.length - depth];
107
+ },
108
+ /**
109
+ * An address from a single-value header, by name - for an edge that
110
+ * writes one. `headerIpSelector('CF-Connecting-IP')` behind Cloudflare,
111
+ * `headerIpSelector('True-Client-IP')` behind Akamai, and so on.
112
+ *
113
+ * Falls back to the framework's accessor when the header is absent.
114
+ */
115
+ headerIpSelector: (name) => (request) => {
116
+ const v = view(request);
117
+ return v.header(name)?.trim() || v.frameworkIp();
118
+ },
119
+ };
120
+ }
121
+ function reportMissing(condition, result, action, warn) {
122
+ if (action === 'ignore') {
123
+ return;
124
+ }
125
+ const missing = missingMembers(condition, result);
126
+ if (missing.length === 0) {
127
+ return;
128
+ }
129
+ const message = `blockCondition names ${missing.join(', ')}, which your plan does not `
130
+ + 'include, so those terms can never match. An absent member means "not in your plan", '
131
+ + 'not "checked, and no".';
132
+ if (action === 'throw') {
133
+ throw new Error(`vpndetection: ${message}`);
134
+ }
135
+ warn(message);
136
+ }
137
+ // A misconfiguration is the same on every request, so saying so once is a
138
+ // warning and saying so a million times is an outage of its own.
139
+ function onceEach(sink) {
140
+ const seen = new Set();
141
+ return (message) => {
142
+ if (seen.has(message)) {
143
+ return;
144
+ }
145
+ seen.add(message);
146
+ sink(`[vpndetection] ${message}`);
147
+ };
148
+ }
149
+ //# sourceMappingURL=core.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"core.js","sourceRoot":"","sources":["../../src/middleware/core.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ export { bindSelectors, createCore } from './core.js';
2
+ export type { Core, IpSelector, Lookup, MiddlewareOptions, MissingFieldAction, RequestView, } from './core.js';
3
+ export { constraintCount, matchesCondition, missingMembers } from './condition.js';
4
+ export type { BlockCondition, NumericBound } from './condition.js';
@@ -0,0 +1,3 @@
1
+ export { bindSelectors, createCore } from './core.js';
2
+ export { constraintCount, matchesCondition, missingMembers } from './condition.js';
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/middleware/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAItD,OAAO,EAAE,eAAe,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vpndetection",
3
- "version": "1.6.0",
3
+ "version": "1.8.0",
4
4
  "description": "Official Node.js client library for the VPNDetection API. Detect VPNs, proxies, Tor, hosting and CDN IPs.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -34,6 +34,10 @@
34
34
  "types": "./dist/index.d.ts",
35
35
  "default": "./dist/index.js"
36
36
  },
37
+ "./middleware": {
38
+ "types": "./dist/middleware/index.d.ts",
39
+ "default": "./dist/middleware/index.js"
40
+ },
37
41
  "./package.json": "./package.json"
38
42
  },
39
43
  "files": [