@internetdata/internetdata 2.1.0 → 2.2.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,118 @@
1
+ import type { Client } from './generated/client/index.js';
2
+ /** Per-call overrides for one OAuth request. Anything omitted falls back to the client's setting. */
3
+ export interface OauthOptions {
4
+ /** How long one attempt may take before it is abandoned, in milliseconds. */
5
+ timeoutMs?: number;
6
+ }
7
+ /** What a device sign-in asks for. A member left out is left out of the request, never sent empty. */
8
+ export interface DeviceAuthorizationOptions extends OauthOptions {
9
+ /** Space-delimited scopes, sent as given. The server narrows them to what the client may ask for. */
10
+ scope?: string;
11
+ /** The API the tokens are meant for. */
12
+ resource?: string;
13
+ }
14
+ export interface PollDeviceTokenOptions {
15
+ /** How long EACH request of the poll may take, in milliseconds. Never bounds the poll as a whole. */
16
+ timeoutMs?: number;
17
+ /** Aborting it stops the wait and any request in flight, and the poll rejects with its reason. */
18
+ signal?: AbortSignal;
19
+ }
20
+ /** The authorization server's discovery document. No call here needs it: each builds on the base URL. */
21
+ export interface OauthMetadata {
22
+ issuer: string;
23
+ authorization_endpoint: string;
24
+ token_endpoint: string;
25
+ device_authorization_endpoint?: string;
26
+ revocation_endpoint?: string;
27
+ scopes_supported?: string[];
28
+ response_types_supported?: string[];
29
+ grant_types_supported?: string[];
30
+ code_challenge_methods_supported?: string[];
31
+ token_endpoint_auth_methods_supported?: string[];
32
+ authorization_response_iss_parameter_supported?: boolean;
33
+ service_documentation?: string;
34
+ }
35
+ /** A started device sign-in. `expires_in` and `interval` are seconds. */
36
+ export interface DeviceAuthorization {
37
+ device_code: string;
38
+ /** What the person types in at `verification_uri`. */
39
+ user_code: string;
40
+ verification_uri: string;
41
+ /** `verification_uri` with the code already in it, for a program that can open a browser. */
42
+ verification_uri_complete?: string;
43
+ expires_in: number;
44
+ interval: number;
45
+ }
46
+ /**
47
+ * What a completed sign-in or a refresh hands back.
48
+ *
49
+ * `apikey_id` is set when the person picked one of their API keys and may still
50
+ * read it back. `apikey`, the key itself, also needs a sign-in rather than a
51
+ * refresh and a key whose secret can be shown again, so `apikey_id` without
52
+ * `apikey` is normal. An empty `scope` is present.
53
+ */
54
+ export interface TokenResponse {
55
+ access_token: string;
56
+ token_type: string;
57
+ expires_in: number;
58
+ /** Spent by the refresh that presents it, so keep the one each refresh returns. */
59
+ refresh_token?: string;
60
+ scope?: string;
61
+ apikey_id?: string;
62
+ apikey?: string;
63
+ }
64
+ /**
65
+ * Signs a person in on their own machine with the OAuth device flow, so a
66
+ * program can be handed one of their API keys instead of asking them to paste
67
+ * it. Reached through `client.oauth`.
68
+ *
69
+ * Every call takes a client ID, issued on request from support@internetdata.io.
70
+ * None of these requests carries the client's API key, and none needs one.
71
+ */
72
+ export declare class OauthApi {
73
+ private readonly client;
74
+ private readonly retries;
75
+ private readonly timeoutMs;
76
+ private clock;
77
+ constructor(client: Client, retries: number, timeoutMs: number);
78
+ metadata(options?: OauthOptions): Promise<OauthMetadata>;
79
+ /**
80
+ * Start a device sign-in: show the person `user_code` and `verification_uri`,
81
+ * then hand the answer to `pollDeviceToken`. It consumes nothing, so it is
82
+ * retried like a lookup; a refusal such as `slow_down` is an `OauthError`.
83
+ */
84
+ deviceAuthorization(clientId: string, options?: DeviceAuthorizationOptions): Promise<DeviceAuthorization>;
85
+ /**
86
+ * Ask once whether the person has approved a device sign-in. Until they do,
87
+ * it rejects with an `OauthError` coded `authorization_pending`;
88
+ * `pollDeviceToken` is the loop around it.
89
+ *
90
+ * Never retried: an approved code is spent by the answer carrying the tokens,
91
+ * so a retry after a lost response could only lose them.
92
+ */
93
+ exchangeDeviceCode(clientId: string, deviceCode: string, options?: OauthOptions): Promise<TokenResponse>;
94
+ /**
95
+ * Trade a refresh token for a new pair. The old one is spent whatever happens
96
+ * next, so this is never retried. A refresh names the key the person picked
97
+ * (`apikey_id`) but never reveals it again (`apikey`).
98
+ */
99
+ exchangeRefreshToken(clientId: string, refreshToken: string, options?: OauthOptions): Promise<TokenResponse>;
100
+ /**
101
+ * End a token. A refresh token ends the whole sign-in and every token it
102
+ * issued, which is how a program signs the machine out; an access token ends
103
+ * only itself. The server answers the same for any token, known or not.
104
+ */
105
+ revoke(clientId: string, token: string, options?: OauthOptions): Promise<void>;
106
+ /**
107
+ * Wait for the person to approve a device sign-in, and return its tokens.
108
+ *
109
+ * Waits `device.interval` seconds (5 when that is below 1) before EVERY
110
+ * request, the first included, and 5 more for the rest of the call each time
111
+ * the server answers `slow_down`. Ends at the first answer that is neither:
112
+ * a denial rejects with `OauthAccessDeniedError`, a code that ran out with
113
+ * `OauthExpiredTokenError` - as does outliving `device.expires_in`, counted
114
+ * from this call, with no `status` - and any other failure as it came.
115
+ */
116
+ pollDeviceToken(clientId: string, device: DeviceAuthorization, options?: PollDeviceTokenOptions): Promise<TokenResponse>;
117
+ private exchange;
118
+ }
package/dist/oauth.js ADDED
@@ -0,0 +1,240 @@
1
+ import { oauthDeviceAuthorization, oauthMetadata, oauthRevoke, oauthToken, } from './generated/sdk.gen.js';
2
+ import { errorFromResponse, InternetDataError, messageFromBody, OauthError, oauthErrorFrom, OauthExpiredTokenError, } from './errors.js';
3
+ import { asError, deadline, withRetry } from './transport.js';
4
+ /**
5
+ * Signs a person in on their own machine with the OAuth device flow, so a
6
+ * program can be handed one of their API keys instead of asking them to paste
7
+ * it. Reached through `client.oauth`.
8
+ *
9
+ * Every call takes a client ID, issued on request from support@internetdata.io.
10
+ * None of these requests carries the client's API key, and none needs one.
11
+ */
12
+ export class OauthApi {
13
+ client;
14
+ retries;
15
+ timeoutMs;
16
+ // The poll's wait and its deadline, which tests replace together.
17
+ clock = { now: () => performance.now(), sleep: sleep };
18
+ constructor(client, retries, timeoutMs) {
19
+ this.client = client;
20
+ this.retries = retries;
21
+ this.timeoutMs = timeoutMs;
22
+ }
23
+ async metadata(options = {}) {
24
+ return withRetry(this.retries, async () => {
25
+ const res = await deadline(options.timeoutMs ?? this.timeoutMs, (signal) => oauthMetadata({
26
+ client: this.client, signal: signal,
27
+ }));
28
+ return decode(res, METADATA);
29
+ });
30
+ }
31
+ /**
32
+ * Start a device sign-in: show the person `user_code` and `verification_uri`,
33
+ * then hand the answer to `pollDeviceToken`. It consumes nothing, so it is
34
+ * retried like a lookup; a refusal such as `slow_down` is an `OauthError`.
35
+ */
36
+ async deviceAuthorization(clientId, options = {}) {
37
+ const body = {
38
+ client_id: clientId,
39
+ scope: options.scope || undefined,
40
+ resource: options.resource || undefined,
41
+ };
42
+ const timeoutMs = options.timeoutMs ?? this.timeoutMs;
43
+ return withRetry(this.retries, async () => {
44
+ const res = await deadline(timeoutMs, (signal) => oauthDeviceAuthorization({
45
+ client: this.client, body: body, signal: signal,
46
+ }));
47
+ return decode(res, DEVICE_AUTHORIZATION);
48
+ });
49
+ }
50
+ /**
51
+ * Ask once whether the person has approved a device sign-in. Until they do,
52
+ * it rejects with an `OauthError` coded `authorization_pending`;
53
+ * `pollDeviceToken` is the loop around it.
54
+ *
55
+ * Never retried: an approved code is spent by the answer carrying the tokens,
56
+ * so a retry after a lost response could only lose them.
57
+ */
58
+ async exchangeDeviceCode(clientId, deviceCode, options = {}) {
59
+ return this.exchange({
60
+ grant_type: DEVICE_CODE_GRANT, device_code: deviceCode, client_id: clientId,
61
+ }, options.timeoutMs);
62
+ }
63
+ /**
64
+ * Trade a refresh token for a new pair. The old one is spent whatever happens
65
+ * next, so this is never retried. A refresh names the key the person picked
66
+ * (`apikey_id`) but never reveals it again (`apikey`).
67
+ */
68
+ async exchangeRefreshToken(clientId, refreshToken, options = {}) {
69
+ return this.exchange({
70
+ grant_type: 'refresh_token', refresh_token: refreshToken, client_id: clientId,
71
+ }, options.timeoutMs);
72
+ }
73
+ /**
74
+ * End a token. A refresh token ends the whole sign-in and every token it
75
+ * issued, which is how a program signs the machine out; an access token ends
76
+ * only itself. The server answers the same for any token, known or not.
77
+ */
78
+ async revoke(clientId, token, options = {}) {
79
+ await withRetry(this.retries, async () => {
80
+ const res = await deadline(options.timeoutMs ?? this.timeoutMs, (signal) => oauthRevoke({
81
+ client: this.client,
82
+ body: { token: token, client_id: clientId },
83
+ // The answer says nothing, so it is read as text and dropped rather than parsed.
84
+ parseAs: 'text',
85
+ signal: signal,
86
+ }));
87
+ throwIfFailed(res);
88
+ });
89
+ }
90
+ /**
91
+ * Wait for the person to approve a device sign-in, and return its tokens.
92
+ *
93
+ * Waits `device.interval` seconds (5 when that is below 1) before EVERY
94
+ * request, the first included, and 5 more for the rest of the call each time
95
+ * the server answers `slow_down`. Ends at the first answer that is neither:
96
+ * a denial rejects with `OauthAccessDeniedError`, a code that ran out with
97
+ * `OauthExpiredTokenError` - as does outliving `device.expires_in`, counted
98
+ * from this call, with no `status` - and any other failure as it came.
99
+ */
100
+ async pollDeviceToken(clientId, device, options = {}) {
101
+ let interval = device.interval >= 1 ? device.interval : 5;
102
+ const expires = this.clock.now() + device.expires_in * 1000;
103
+ for (;;) {
104
+ await this.clock.sleep(interval * 1000, options.signal);
105
+ if (this.clock.now() >= expires) {
106
+ throw new OauthExpiredTokenError();
107
+ }
108
+ try {
109
+ return await this.exchange({
110
+ grant_type: DEVICE_CODE_GRANT, device_code: device.device_code, client_id: clientId,
111
+ }, options.timeoutMs, options.signal);
112
+ }
113
+ catch (err) {
114
+ if (!(err instanceof OauthError)) {
115
+ throw err;
116
+ }
117
+ // RFC 8628: slow_down widens the interval for every later request, not just the next.
118
+ if (err.errorCode === 'slow_down') {
119
+ interval += 5;
120
+ }
121
+ else if (err.errorCode !== 'authorization_pending') {
122
+ throw err;
123
+ }
124
+ }
125
+ }
126
+ }
127
+ async exchange(form, timeoutMs, cancel) {
128
+ const res = await deadline(timeoutMs ?? this.timeoutMs, (signal) => oauthToken({
129
+ client: this.client, body: form, signal: signal,
130
+ }), cancel);
131
+ return decode(res, TOKEN_RESPONSE);
132
+ }
133
+ }
134
+ const DEVICE_CODE_GRANT = 'urn:ietf:params:oauth:grant-type:device_code';
135
+ const METADATA = {
136
+ issuer: { type: 'string', required: true },
137
+ authorization_endpoint: { type: 'string', required: true },
138
+ token_endpoint: { type: 'string', required: true },
139
+ device_authorization_endpoint: { type: 'string' },
140
+ revocation_endpoint: { type: 'string' },
141
+ scopes_supported: { type: 'string[]' },
142
+ response_types_supported: { type: 'string[]' },
143
+ grant_types_supported: { type: 'string[]' },
144
+ code_challenge_methods_supported: { type: 'string[]' },
145
+ token_endpoint_auth_methods_supported: { type: 'string[]' },
146
+ authorization_response_iss_parameter_supported: { type: 'boolean' },
147
+ service_documentation: { type: 'string' },
148
+ };
149
+ const DEVICE_AUTHORIZATION = {
150
+ device_code: { type: 'string', required: true },
151
+ user_code: { type: 'string', required: true },
152
+ verification_uri: { type: 'string', required: true },
153
+ verification_uri_complete: { type: 'string' },
154
+ expires_in: { type: 'number', required: true },
155
+ interval: { type: 'number', required: true },
156
+ };
157
+ const TOKEN_RESPONSE = {
158
+ access_token: { type: 'string', required: true },
159
+ token_type: { type: 'string', required: true },
160
+ expires_in: { type: 'number', required: true },
161
+ refresh_token: { type: 'string' },
162
+ scope: { type: 'string' },
163
+ apikey_id: { type: 'string', wire: 'mslm:apikey_id' },
164
+ apikey: { type: 'string', wire: 'mslm:apikey' },
165
+ };
166
+ // Only the declared members are copied, on PRESENCE, so an absent one stays
167
+ // absent and an empty `scope` stays present. Anything else the server sends is
168
+ // dropped rather than surfaced untyped.
169
+ function decode(res, members) {
170
+ const status = throwIfFailed(res).status;
171
+ if (res.error !== undefined) {
172
+ // A 2xx the generated client could not parse, which reports it here.
173
+ throw asError(res.error);
174
+ }
175
+ const body = res.data;
176
+ if (!isObject(body)) {
177
+ throw new InternetDataError('server_error', 'the answer was not a JSON object', status);
178
+ }
179
+ const out = {};
180
+ for (const [name, member] of Object.entries(members)) {
181
+ const wire = member.wire ?? name;
182
+ const value = body[wire];
183
+ if (value === undefined || value === null) {
184
+ if (member.required) {
185
+ throw new InternetDataError('server_error', `the answer carried no ${wire}`, status);
186
+ }
187
+ continue;
188
+ }
189
+ if (!hasType(value, member.type)) {
190
+ throw new InternetDataError('server_error', `the answer's ${wire} is not a ${member.type}`, status);
191
+ }
192
+ out[name] = value;
193
+ }
194
+ return out;
195
+ }
196
+ // Only a 4xx whose body is a JSON object with a STRING `error` is an OAuth
197
+ // refusal. Every 5xx, whatever it says, is the server failing, and is retried
198
+ // wherever the operation retries.
199
+ function throwIfFailed(res) {
200
+ if (res.response === undefined) {
201
+ throw new InternetDataError('network', 'no response from the API');
202
+ }
203
+ const { status, ok, headers } = res.response;
204
+ if (ok) {
205
+ return res.response;
206
+ }
207
+ const body = res.error;
208
+ if (status >= 400 && status < 500 && isObject(body) && typeof body.error === 'string') {
209
+ const description = typeof body.error_description === 'string' ? body.error_description : undefined;
210
+ throw oauthErrorFrom(body.error, description, status);
211
+ }
212
+ throw errorFromResponse(status, headers, messageFromBody(body));
213
+ }
214
+ function isObject(value) {
215
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
216
+ }
217
+ function hasType(value, type) {
218
+ if (type === 'string[]') {
219
+ return Array.isArray(value) && value.every((item) => typeof item === 'string');
220
+ }
221
+ return typeof value === type;
222
+ }
223
+ function sleep(ms, signal) {
224
+ return new Promise((resolve, reject) => {
225
+ if (signal?.aborted) {
226
+ reject(signal.reason);
227
+ return;
228
+ }
229
+ const onAbort = () => {
230
+ clearTimeout(timer);
231
+ reject(signal?.reason);
232
+ };
233
+ const timer = setTimeout(() => {
234
+ signal?.removeEventListener('abort', onAbort);
235
+ resolve();
236
+ }, ms);
237
+ signal?.addEventListener('abort', onAbort, { once: true });
238
+ });
239
+ }
240
+ //# sourceMappingURL=oauth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"oauth.js","sourceRoot":"","sources":["../src/oauth.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ import { InternetDataError } from './errors.js';
2
+ export interface Res {
3
+ data?: unknown;
4
+ error?: unknown;
5
+ response?: Response;
6
+ }
7
+ export declare function unwrap<T>(res: Res): T;
8
+ /**
9
+ * Bound one attempt, and report an expiry as our own error rather than the
10
+ * runtime's `TimeoutError`, whose message says nothing about which call gave up.
11
+ *
12
+ * The signal is built per call, so a retried request gets a fresh budget - the
13
+ * same per-attempt semantics the Go and Python clients have. Aborting `cancel`
14
+ * ends the attempt at once, rejecting with its reason.
15
+ */
16
+ export declare function deadline<T>(timeoutMs: number, fn: (signal: AbortSignal) => Promise<T>, cancel?: AbortSignal): Promise<T>;
17
+ export declare function withRetry<T>(retries: number, fn: () => Promise<T>): Promise<T>;
18
+ export declare function asError(err: unknown): InternetDataError;
@@ -0,0 +1,82 @@
1
+ import pRetry from 'p-retry';
2
+ import { errorFromResponse, InternetDataError, messageFromBody } from './errors.js';
3
+ export function unwrap(res) {
4
+ if (res.response === undefined) {
5
+ throw new InternetDataError('network', 'no response from the API');
6
+ }
7
+ if (!res.response.ok) {
8
+ throw errorFromResponse(res.response.status, res.response.headers, messageFromBody(res.error ?? res.data));
9
+ }
10
+ return res.data;
11
+ }
12
+ /**
13
+ * Bound one attempt, and report an expiry as our own error rather than the
14
+ * runtime's `TimeoutError`, whose message says nothing about which call gave up.
15
+ *
16
+ * The signal is built per call, so a retried request gets a fresh budget - the
17
+ * same per-attempt semantics the Go and Python clients have. Aborting `cancel`
18
+ * ends the attempt at once, rejecting with its reason.
19
+ */
20
+ export async function deadline(timeoutMs, fn, cancel) {
21
+ cancel?.throwIfAborted();
22
+ const controller = new AbortController();
23
+ let timer;
24
+ let stop;
25
+ // Raced, not left to the signal alone: aborting releases the socket, but only
26
+ // a transport that HONORS the signal then settles, and a substituted `fetch`
27
+ // need not. Clearing the timer stops the losing side rejecting into nothing.
28
+ const expiry = new Promise((_, reject) => {
29
+ timer = setTimeout(() => {
30
+ controller.abort();
31
+ reject(new InternetDataError('network', `request timed out after ${timeoutMs}ms`));
32
+ }, timeoutMs);
33
+ if (cancel !== undefined) {
34
+ stop = () => {
35
+ controller.abort(cancel.reason);
36
+ reject(cancel.reason);
37
+ };
38
+ cancel.addEventListener('abort', stop, { once: true });
39
+ }
40
+ });
41
+ try {
42
+ return await Promise.race([fn(controller.signal), expiry]);
43
+ }
44
+ finally {
45
+ clearTimeout(timer);
46
+ if (stop !== undefined) {
47
+ cancel?.removeEventListener('abort', stop);
48
+ }
49
+ }
50
+ }
51
+ // p-retry owns the backoff schedule; the extra sleep here is what honors a
52
+ // server-supplied Retry-After, which p-retry has no way to know about. A 429
53
+ // carrying that header is the only 429 worth retrying, which is why the wait
54
+ // and the retry decision both key off the same field.
55
+ export async function withRetry(retries, fn) {
56
+ try {
57
+ return await pRetry(fn, {
58
+ retries: retries,
59
+ shouldRetry: ({ error }) => !(error instanceof InternetDataError) || error.retryable,
60
+ onFailedAttempt: async ({ error }) => {
61
+ const seconds = error instanceof InternetDataError ? error.retryAfterSeconds : undefined;
62
+ if (seconds !== undefined && seconds > 0) {
63
+ await new Promise((r) => setTimeout(r, seconds * 1000));
64
+ }
65
+ },
66
+ });
67
+ }
68
+ catch (err) {
69
+ throw asError(err);
70
+ }
71
+ }
72
+ export function asError(err) {
73
+ if (err instanceof InternetDataError) {
74
+ return err;
75
+ }
76
+ const cause = err?.cause;
77
+ if (cause instanceof InternetDataError) {
78
+ return cause;
79
+ }
80
+ return new InternetDataError('network', err instanceof Error ? err.message : String(err));
81
+ }
82
+ //# sourceMappingURL=transport.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"transport.js","sourceRoot":"","sources":["../src/transport.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@internetdata/internetdata",
3
- "version": "2.1.0",
3
+ "version": "2.2.1",
4
4
  "description": "Official Node.js client library for the InternetData API. Download, verify and inspect licensed IP database files.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -51,10 +51,10 @@
51
51
  },
52
52
  "devDependencies": {
53
53
  "@hey-api/openapi-ts": "^0.99.0",
54
- "@mslmio/eslint-config": "^1.0.1",
54
+ "@mslmio/eslint-config": "^1.1.0",
55
55
  "@types/node": "^26.2.0",
56
- "eslint": "^9.39.0",
57
- "typescript": "^5.9.3"
56
+ "eslint": "^10.10.0",
57
+ "typescript": "^6.0.3"
58
58
  },
59
59
  "scripts": {
60
60
  "build": "rm -rf dist && openapi-ts && tsc",