@fingerprint/node-sdk 7.0.0-test.0 → 7.0.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/index.cjs CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Fingerprint Server Node.js SDK v7.0.0-test.0 - Copyright (c) FingerprintJS, Inc, 2026 (https://fingerprint.com)
2
+ * Fingerprint Server Node.js SDK v7.0.0 - Copyright (c) FingerprintJS, Inc, 2026 (https://fingerprint.com)
3
3
  * Licensed under the MIT (http://www.opensource.org/licenses/mit-license.php) license.
4
4
  */
5
5
 
@@ -17,7 +17,7 @@ exports.Region = void 0;
17
17
  Region["Global"] = "Global";
18
18
  })(exports.Region || (exports.Region = {}));
19
19
 
20
- var version = "7.0.0-test.0";
20
+ var version = "7.0.0";
21
21
 
22
22
  const apiVersion = 'v4';
23
23
  const euRegionUrl = 'https://eu.api.fpjs.io/';
@@ -65,13 +65,13 @@ function getServerApiUrl(region) {
65
65
  *
66
66
  * @internal
67
67
  *
68
- * @param {GetRequestPathOptions<Path, Method>} options
69
- * @param {Path} options.path - The path of the API endpoint
68
+ * @param {GetRequestPathOptions} options
69
+ * @param {keyof paths} options.path - The path of the API endpoint
70
70
  * @param {string[]} [options.pathParams] - Path parameters to be replaced in the path
71
- * @param {QueryParams<Path, Method>["queryParams"]} [options.queryParams] - Query string
71
+ * @param {GetRequestPathOptions["queryParams"]} [options.queryParams] - Query string
72
72
  * parameters to be appended to the URL
73
73
  * @param {Region} options.region - The region of the API endpoint
74
- * @param {Method} options.method - The method of the API endpoint
74
+ * @param {HttpMethod} options.method - The method of the API endpoint
75
75
  *
76
76
  * @returns {string} The formatted URL with parameters replaced and query string
77
77
  * parameters appended
@@ -233,6 +233,7 @@ class FingerprintServerApiClient {
233
233
  pathParams: [eventId],
234
234
  method: 'get',
235
235
  queryParams: options,
236
+ expect: 'json',
236
237
  });
237
238
  }
238
239
  /**
@@ -243,8 +244,8 @@ class FingerprintServerApiClient {
243
244
  *
244
245
  * **Warning** It's not possible to update events older than one month.
245
246
  *
246
- * @param body - Data to update the event with.
247
247
  * @param eventId The unique event [identifier](https://docs.fingerprint.com/reference/js-agent-v4-get-function#event_id).
248
+ * @param body - Data to update the event with.
248
249
  *
249
250
  * @return {Promise<void>}
250
251
  *
@@ -256,7 +257,7 @@ class FingerprintServerApiClient {
256
257
  * }
257
258
  *
258
259
  * client
259
- * .updateEvent(body, '<eventId>')
260
+ * .updateEvent('<eventId>', body)
260
261
  * .then(() => {
261
262
  * // Event was successfully updated
262
263
  * })
@@ -273,7 +274,7 @@ class FingerprintServerApiClient {
273
274
  * })
274
275
  * ```
275
276
  */
276
- async updateEvent(body, eventId) {
277
+ async updateEvent(eventId, body) {
277
278
  if (!body) {
278
279
  throw new TypeError('body is not set');
279
280
  }
@@ -285,6 +286,7 @@ class FingerprintServerApiClient {
285
286
  pathParams: [eventId],
286
287
  method: 'patch',
287
288
  body: JSON.stringify(body),
289
+ expect: 'void',
288
290
  });
289
291
  }
290
292
  /**
@@ -321,6 +323,7 @@ class FingerprintServerApiClient {
321
323
  path: '/visitors/{visitor_id}',
322
324
  pathParams: [visitorId],
323
325
  method: 'delete',
326
+ expect: 'void',
324
327
  });
325
328
  }
326
329
  /**
@@ -386,6 +389,7 @@ class FingerprintServerApiClient {
386
389
  path: '/events',
387
390
  method: 'get',
388
391
  queryParams: filter,
392
+ expect: 'json',
389
393
  });
390
394
  }
391
395
  async callApi(options) {
@@ -393,53 +397,53 @@ class FingerprintServerApiClient {
393
397
  ...options,
394
398
  region: this.region,
395
399
  });
400
+ const requestInit = {
401
+ method: options.method.toUpperCase(),
402
+ headers: {
403
+ ...this.defaultHeaders,
404
+ ...options.headers,
405
+ },
406
+ };
407
+ if (options.body !== undefined) {
408
+ requestInit.body = options.body;
409
+ }
396
410
  let response;
397
411
  try {
398
- response = await this.fetch(url, {
399
- method: options.method.toUpperCase(),
400
- headers: {
401
- ...this.defaultHeaders,
402
- ...options.headers,
403
- },
404
- body: options.body,
405
- });
412
+ response = await this.fetch(url, requestInit);
406
413
  }
407
414
  catch (e) {
408
- throw new SdkError('Network or fetch error', undefined, e);
415
+ throw new SdkError('Network or fetch error', undefined, toError(e));
409
416
  }
410
- const contentType = response.headers.get('content-type') ?? '';
411
- const isJson = contentType.includes('application/json');
412
417
  if (response.ok) {
418
+ if (options.expect === 'void') {
419
+ return;
420
+ }
413
421
  const hasNoBody = response.status === 204 || response.headers.get('content-length') === '0';
414
422
  if (hasNoBody) {
415
- return undefined;
423
+ throw new SdkError('Expected JSON response but response body is empty', response);
416
424
  }
417
- if (!isJson) {
425
+ const contentType = response.headers.get('content-type') ?? '';
426
+ if (!contentType.includes('application/json')) {
418
427
  throw new SdkError('Expected JSON response but received non-JSON content type', response);
419
428
  }
420
- let data;
421
- try {
422
- data = await response.clone().json();
423
- }
424
- catch (e) {
425
- throw new SdkError('Failed to parse JSON response', response, toError(e));
426
- }
427
- return data;
429
+ return this.parseJson(response);
428
430
  }
429
- let errPayload;
431
+ const errorPayload = await this.parseJson(response.clone());
432
+ if (response.status === 429 && isErrorResponse(errorPayload)) {
433
+ throw new TooManyRequestsError(errorPayload, response);
434
+ }
435
+ if (isErrorResponse(errorPayload)) {
436
+ throw new RequestError(errorPayload.error.message, errorPayload, response.status, errorPayload.error.code, response);
437
+ }
438
+ throw RequestError.unknown(response);
439
+ }
440
+ async parseJson(response) {
430
441
  try {
431
- errPayload = await response.clone().json();
442
+ return (await response.json());
432
443
  }
433
444
  catch (e) {
434
445
  throw new SdkError('Failed to parse JSON response', response, toError(e));
435
446
  }
436
- if (isErrorResponse(errPayload)) {
437
- if (response.status === 429) {
438
- throw new TooManyRequestsError(errPayload, response);
439
- }
440
- throw new RequestError(errPayload.error.message, errPayload, response.status, errPayload.error.code, response);
441
- }
442
- throw RequestError.unknown(response);
443
447
  }
444
448
  }
445
449
 
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Fingerprint Server Node.js SDK v7.0.0-test.0 - Copyright (c) FingerprintJS, Inc, 2026 (https://fingerprint.com)
2
+ * Fingerprint Server Node.js SDK v7.0.0 - Copyright (c) FingerprintJS, Inc, 2026 (https://fingerprint.com)
3
3
  * Licensed under the MIT (http://www.opensource.org/licenses/mit-license.php) license.
4
4
  */
5
5
 
@@ -1542,41 +1542,12 @@ type Event = components['schemas']['Event'];
1542
1542
  type GetEventOptions = paths['/events/{event_id}']['get']['parameters']['query'];
1543
1543
  type EventUpdate = components['schemas']['EventUpdate'];
1544
1544
  type EventRuleAction = components['schemas']['EventRuleAction'];
1545
- type ExtractPathParamStrings<Path> = Path extends {
1546
- parameters: {
1547
- path: infer P;
1548
- };
1549
- } ? P extends Record<string, any> ? [P[keyof P]] : [] : [];
1550
- type ExtractQueryParams<Path> = Path extends {
1551
- parameters: {
1552
- query?: infer Q;
1553
- };
1554
- } ? undefined extends Q ? Q | undefined : Q : never;
1555
- type ExtractRequestBody<Path> = Path extends {
1556
- requestBody: {
1557
- content: {
1558
- 'application/json': infer B;
1559
- };
1560
- };
1561
- } ? B : never;
1562
- type ExtractResponse<Path> = Path extends {
1563
- responses: {
1564
- 200: {
1565
- content: {
1566
- 'application/json': infer R;
1567
- };
1568
- };
1569
- };
1570
- } ? R : void;
1571
- type ApiMethodArgs<Path extends keyof operations> = [
1572
- ...(ExtractRequestBody<operations[Path]> extends never ? [] : [body: ExtractRequestBody<operations[Path]>]),
1573
- ...ExtractPathParamStrings<operations[Path]>,
1574
- ...(ExtractQueryParams<operations[Path]> extends never ? [] : [params: ExtractQueryParams<operations[Path]>])
1575
- ];
1576
- type ApiMethod<Path extends keyof operations> = (...args: ApiMethodArgs<Path>) => Promise<ExtractResponse<operations[Path]>>;
1577
- type FingerprintApi = {
1578
- [Operation in keyof operations]: ApiMethod<Operation>;
1579
- };
1545
+ interface FingerprintApi {
1546
+ getEvent(eventId: string, options?: GetEventOptions): Promise<Event>;
1547
+ updateEvent(eventId: string, body: EventUpdate): Promise<void>;
1548
+ searchEvents(filter: SearchEventsFilter): Promise<SearchEventsResponse>;
1549
+ deleteVisitorData(visitorId: string): Promise<void>;
1550
+ }
1580
1551
 
1581
1552
  declare class FingerprintServerApiClient implements FingerprintApi {
1582
1553
  readonly region: Region;
@@ -1638,8 +1609,8 @@ declare class FingerprintServerApiClient implements FingerprintApi {
1638
1609
  *
1639
1610
  * **Warning** It's not possible to update events older than one month.
1640
1611
  *
1641
- * @param body - Data to update the event with.
1642
1612
  * @param eventId The unique event [identifier](https://docs.fingerprint.com/reference/js-agent-v4-get-function#event_id).
1613
+ * @param body - Data to update the event with.
1643
1614
  *
1644
1615
  * @return {Promise<void>}
1645
1616
  *
@@ -1651,7 +1622,7 @@ declare class FingerprintServerApiClient implements FingerprintApi {
1651
1622
  * }
1652
1623
  *
1653
1624
  * client
1654
- * .updateEvent(body, '<eventId>')
1625
+ * .updateEvent('<eventId>', body)
1655
1626
  * .then(() => {
1656
1627
  * // Event was successfully updated
1657
1628
  * })
@@ -1668,7 +1639,7 @@ declare class FingerprintServerApiClient implements FingerprintApi {
1668
1639
  * })
1669
1640
  * ```
1670
1641
  */
1671
- updateEvent(body: EventUpdate, eventId: string): Promise<void>;
1642
+ updateEvent(eventId: string, body: EventUpdate): Promise<void>;
1672
1643
  /**
1673
1644
  * Delete data by visitor ID
1674
1645
  * Request deleting all data associated with the specified visitor ID. This API is useful for compliance with privacy regulations. All delete requests are queued:
@@ -1756,6 +1727,7 @@ declare class FingerprintServerApiClient implements FingerprintApi {
1756
1727
  * */
1757
1728
  searchEvents(filter: SearchEventsFilter): Promise<SearchEventsResponse>;
1758
1729
  private callApi;
1730
+ private parseJson;
1759
1731
  }
1760
1732
 
1761
1733
  declare enum DecryptionAlgorithm {
package/dist/index.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Fingerprint Server Node.js SDK v7.0.0-test.0 - Copyright (c) FingerprintJS, Inc, 2026 (https://fingerprint.com)
2
+ * Fingerprint Server Node.js SDK v7.0.0 - Copyright (c) FingerprintJS, Inc, 2026 (https://fingerprint.com)
3
3
  * Licensed under the MIT (http://www.opensource.org/licenses/mit-license.php) license.
4
4
  */
5
5
 
@@ -15,7 +15,7 @@ var Region;
15
15
  Region["Global"] = "Global";
16
16
  })(Region || (Region = {}));
17
17
 
18
- var version = "7.0.0-test.0";
18
+ var version = "7.0.0";
19
19
 
20
20
  const apiVersion = 'v4';
21
21
  const euRegionUrl = 'https://eu.api.fpjs.io/';
@@ -63,13 +63,13 @@ function getServerApiUrl(region) {
63
63
  *
64
64
  * @internal
65
65
  *
66
- * @param {GetRequestPathOptions<Path, Method>} options
67
- * @param {Path} options.path - The path of the API endpoint
66
+ * @param {GetRequestPathOptions} options
67
+ * @param {keyof paths} options.path - The path of the API endpoint
68
68
  * @param {string[]} [options.pathParams] - Path parameters to be replaced in the path
69
- * @param {QueryParams<Path, Method>["queryParams"]} [options.queryParams] - Query string
69
+ * @param {GetRequestPathOptions["queryParams"]} [options.queryParams] - Query string
70
70
  * parameters to be appended to the URL
71
71
  * @param {Region} options.region - The region of the API endpoint
72
- * @param {Method} options.method - The method of the API endpoint
72
+ * @param {HttpMethod} options.method - The method of the API endpoint
73
73
  *
74
74
  * @returns {string} The formatted URL with parameters replaced and query string
75
75
  * parameters appended
@@ -231,6 +231,7 @@ class FingerprintServerApiClient {
231
231
  pathParams: [eventId],
232
232
  method: 'get',
233
233
  queryParams: options,
234
+ expect: 'json',
234
235
  });
235
236
  }
236
237
  /**
@@ -241,8 +242,8 @@ class FingerprintServerApiClient {
241
242
  *
242
243
  * **Warning** It's not possible to update events older than one month.
243
244
  *
244
- * @param body - Data to update the event with.
245
245
  * @param eventId The unique event [identifier](https://docs.fingerprint.com/reference/js-agent-v4-get-function#event_id).
246
+ * @param body - Data to update the event with.
246
247
  *
247
248
  * @return {Promise<void>}
248
249
  *
@@ -254,7 +255,7 @@ class FingerprintServerApiClient {
254
255
  * }
255
256
  *
256
257
  * client
257
- * .updateEvent(body, '<eventId>')
258
+ * .updateEvent('<eventId>', body)
258
259
  * .then(() => {
259
260
  * // Event was successfully updated
260
261
  * })
@@ -271,7 +272,7 @@ class FingerprintServerApiClient {
271
272
  * })
272
273
  * ```
273
274
  */
274
- async updateEvent(body, eventId) {
275
+ async updateEvent(eventId, body) {
275
276
  if (!body) {
276
277
  throw new TypeError('body is not set');
277
278
  }
@@ -283,6 +284,7 @@ class FingerprintServerApiClient {
283
284
  pathParams: [eventId],
284
285
  method: 'patch',
285
286
  body: JSON.stringify(body),
287
+ expect: 'void',
286
288
  });
287
289
  }
288
290
  /**
@@ -319,6 +321,7 @@ class FingerprintServerApiClient {
319
321
  path: '/visitors/{visitor_id}',
320
322
  pathParams: [visitorId],
321
323
  method: 'delete',
324
+ expect: 'void',
322
325
  });
323
326
  }
324
327
  /**
@@ -384,6 +387,7 @@ class FingerprintServerApiClient {
384
387
  path: '/events',
385
388
  method: 'get',
386
389
  queryParams: filter,
390
+ expect: 'json',
387
391
  });
388
392
  }
389
393
  async callApi(options) {
@@ -391,53 +395,53 @@ class FingerprintServerApiClient {
391
395
  ...options,
392
396
  region: this.region,
393
397
  });
398
+ const requestInit = {
399
+ method: options.method.toUpperCase(),
400
+ headers: {
401
+ ...this.defaultHeaders,
402
+ ...options.headers,
403
+ },
404
+ };
405
+ if (options.body !== undefined) {
406
+ requestInit.body = options.body;
407
+ }
394
408
  let response;
395
409
  try {
396
- response = await this.fetch(url, {
397
- method: options.method.toUpperCase(),
398
- headers: {
399
- ...this.defaultHeaders,
400
- ...options.headers,
401
- },
402
- body: options.body,
403
- });
410
+ response = await this.fetch(url, requestInit);
404
411
  }
405
412
  catch (e) {
406
- throw new SdkError('Network or fetch error', undefined, e);
413
+ throw new SdkError('Network or fetch error', undefined, toError(e));
407
414
  }
408
- const contentType = response.headers.get('content-type') ?? '';
409
- const isJson = contentType.includes('application/json');
410
415
  if (response.ok) {
416
+ if (options.expect === 'void') {
417
+ return;
418
+ }
411
419
  const hasNoBody = response.status === 204 || response.headers.get('content-length') === '0';
412
420
  if (hasNoBody) {
413
- return undefined;
421
+ throw new SdkError('Expected JSON response but response body is empty', response);
414
422
  }
415
- if (!isJson) {
423
+ const contentType = response.headers.get('content-type') ?? '';
424
+ if (!contentType.includes('application/json')) {
416
425
  throw new SdkError('Expected JSON response but received non-JSON content type', response);
417
426
  }
418
- let data;
419
- try {
420
- data = await response.clone().json();
421
- }
422
- catch (e) {
423
- throw new SdkError('Failed to parse JSON response', response, toError(e));
424
- }
425
- return data;
427
+ return this.parseJson(response);
426
428
  }
427
- let errPayload;
429
+ const errorPayload = await this.parseJson(response.clone());
430
+ if (response.status === 429 && isErrorResponse(errorPayload)) {
431
+ throw new TooManyRequestsError(errorPayload, response);
432
+ }
433
+ if (isErrorResponse(errorPayload)) {
434
+ throw new RequestError(errorPayload.error.message, errorPayload, response.status, errorPayload.error.code, response);
435
+ }
436
+ throw RequestError.unknown(response);
437
+ }
438
+ async parseJson(response) {
428
439
  try {
429
- errPayload = await response.clone().json();
440
+ return (await response.json());
430
441
  }
431
442
  catch (e) {
432
443
  throw new SdkError('Failed to parse JSON response', response, toError(e));
433
444
  }
434
- if (isErrorResponse(errPayload)) {
435
- if (response.status === 429) {
436
- throw new TooManyRequestsError(errPayload, response);
437
- }
438
- throw new RequestError(errPayload.error.message, errPayload, response.status, errPayload.error.code, response);
439
- }
440
- throw RequestError.unknown(response);
441
445
  }
442
446
  }
443
447
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fingerprint/node-sdk",
3
- "version": "7.0.0-test.0",
3
+ "version": "7.0.0",
4
4
  "description": "Node.js wrapper for Fingerprint Server API",
5
5
  "main": "dist/index.cjs",
6
6
  "module": "dist/index.mjs",
@@ -17,6 +17,10 @@
17
17
  "type": "git",
18
18
  "url": "https://github.com/fingerprintjs/node-sdk"
19
19
  },
20
+ "publishConfig": {
21
+ "access": "public",
22
+ "provenance": true
23
+ },
20
24
  "engines": {
21
25
  "node": ">=18.17.0"
22
26
  },
@@ -1,4 +1,4 @@
1
- import { AllowedMethod, getRequestPath, GetRequestPathOptions, SuccessJsonOrVoid } from './urlUtils'
1
+ import { getRequestPath, GetRequestPathOptions } from './urlUtils'
2
2
  import {
3
3
  Event,
4
4
  EventUpdate,
@@ -9,11 +9,16 @@ import {
9
9
  SearchEventsFilter,
10
10
  SearchEventsResponse,
11
11
  } from './types'
12
- import { paths } from './generatedApiTypes'
13
12
  import { RequestError, SdkError, TooManyRequestsError } from './errors/apiErrors'
14
13
  import { isErrorResponse } from './errors/handleErrorResponse'
15
14
  import { toError } from './errors/toError'
16
15
 
16
+ type CallApiOptions = GetRequestPathOptions & {
17
+ headers?: Record<string, string>
18
+ body?: BodyInit
19
+ expect: 'json' | 'void'
20
+ }
21
+
17
22
  export class FingerprintServerApiClient implements FingerprintApi {
18
23
  public readonly region: Region
19
24
 
@@ -97,6 +102,7 @@ export class FingerprintServerApiClient implements FingerprintApi {
97
102
  pathParams: [eventId],
98
103
  method: 'get',
99
104
  queryParams: options,
105
+ expect: 'json',
100
106
  })
101
107
  }
102
108
 
@@ -108,8 +114,8 @@ export class FingerprintServerApiClient implements FingerprintApi {
108
114
  *
109
115
  * **Warning** It's not possible to update events older than one month.
110
116
  *
111
- * @param body - Data to update the event with.
112
117
  * @param eventId The unique event [identifier](https://docs.fingerprint.com/reference/js-agent-v4-get-function#event_id).
118
+ * @param body - Data to update the event with.
113
119
  *
114
120
  * @return {Promise<void>}
115
121
  *
@@ -121,7 +127,7 @@ export class FingerprintServerApiClient implements FingerprintApi {
121
127
  * }
122
128
  *
123
129
  * client
124
- * .updateEvent(body, '<eventId>')
130
+ * .updateEvent('<eventId>', body)
125
131
  * .then(() => {
126
132
  * // Event was successfully updated
127
133
  * })
@@ -138,7 +144,7 @@ export class FingerprintServerApiClient implements FingerprintApi {
138
144
  * })
139
145
  * ```
140
146
  */
141
- public async updateEvent(body: EventUpdate, eventId: string): Promise<void> {
147
+ public async updateEvent(eventId: string, body: EventUpdate): Promise<void> {
142
148
  if (!body) {
143
149
  throw new TypeError('body is not set')
144
150
  }
@@ -152,6 +158,7 @@ export class FingerprintServerApiClient implements FingerprintApi {
152
158
  pathParams: [eventId],
153
159
  method: 'patch',
154
160
  body: JSON.stringify(body),
161
+ expect: 'void',
155
162
  })
156
163
  }
157
164
 
@@ -190,6 +197,7 @@ export class FingerprintServerApiClient implements FingerprintApi {
190
197
  path: '/visitors/{visitor_id}',
191
198
  pathParams: [visitorId],
192
199
  method: 'delete',
200
+ expect: 'void',
193
201
  })
194
202
  }
195
203
 
@@ -256,66 +264,78 @@ export class FingerprintServerApiClient implements FingerprintApi {
256
264
  path: '/events',
257
265
  method: 'get',
258
266
  queryParams: filter,
267
+ expect: 'json',
259
268
  })
260
269
  }
261
270
 
262
- private async callApi<Path extends keyof paths, Method extends AllowedMethod<Path>>(
263
- options: GetRequestPathOptions<Path, Method> & { headers?: Record<string, string>; body?: BodyInit }
264
- ): Promise<SuccessJsonOrVoid<Path, Method>> {
271
+ private async callApi<T>(options: CallApiOptions & { expect: 'json' }): Promise<T>
272
+ private async callApi(options: CallApiOptions & { expect: 'void' }): Promise<void>
273
+ private async callApi<T>(options: CallApiOptions): Promise<T | void> {
265
274
  const url = getRequestPath({
266
275
  ...options,
267
276
  region: this.region,
268
277
  })
269
278
 
279
+ const requestInit: RequestInit = {
280
+ method: options.method.toUpperCase(),
281
+ headers: {
282
+ ...this.defaultHeaders,
283
+ ...options.headers,
284
+ },
285
+ }
286
+
287
+ if (options.body !== undefined) {
288
+ requestInit.body = options.body
289
+ }
290
+
270
291
  let response: Response
271
292
  try {
272
- response = await this.fetch(url, {
273
- method: options.method.toUpperCase(),
274
- headers: {
275
- ...this.defaultHeaders,
276
- ...options.headers,
277
- },
278
- body: options.body,
279
- })
293
+ response = await this.fetch(url, requestInit)
280
294
  } catch (e) {
281
- throw new SdkError('Network or fetch error', undefined, e as Error)
295
+ throw new SdkError('Network or fetch error', undefined, toError(e))
282
296
  }
283
297
 
284
- const contentType = response.headers.get('content-type') ?? ''
285
- const isJson = contentType.includes('application/json')
286
-
287
298
  if (response.ok) {
288
- const hasNoBody = response.status === 204 || response.headers.get('content-length') === '0'
299
+ if (options.expect === 'void') {
300
+ return
301
+ }
289
302
 
303
+ const hasNoBody = response.status === 204 || response.headers.get('content-length') === '0'
290
304
  if (hasNoBody) {
291
- return undefined as SuccessJsonOrVoid<Path, Method>
305
+ throw new SdkError('Expected JSON response but response body is empty', response)
292
306
  }
293
307
 
294
- if (!isJson) {
308
+ const contentType = response.headers.get('content-type') ?? ''
309
+ if (!contentType.includes('application/json')) {
295
310
  throw new SdkError('Expected JSON response but received non-JSON content type', response)
296
311
  }
297
312
 
298
- let data
299
- try {
300
- data = await response.clone().json()
301
- } catch (e) {
302
- throw new SdkError('Failed to parse JSON response', response, toError(e))
303
- }
304
- return data as SuccessJsonOrVoid<Path, Method>
313
+ return this.parseJson(response)
314
+ }
315
+
316
+ const errorPayload = await this.parseJson<unknown>(response.clone())
317
+
318
+ if (response.status === 429 && isErrorResponse(errorPayload)) {
319
+ throw new TooManyRequestsError(errorPayload, response)
320
+ }
321
+ if (isErrorResponse(errorPayload)) {
322
+ throw new RequestError(
323
+ errorPayload.error.message,
324
+ errorPayload,
325
+ response.status,
326
+ errorPayload.error.code,
327
+ response
328
+ )
305
329
  }
306
330
 
307
- let errPayload
331
+ throw RequestError.unknown(response)
332
+ }
333
+
334
+ private async parseJson<T>(response: Response): Promise<T> {
308
335
  try {
309
- errPayload = await response.clone().json()
336
+ return (await response.json()) as T
310
337
  } catch (e) {
311
338
  throw new SdkError('Failed to parse JSON response', response, toError(e))
312
339
  }
313
- if (isErrorResponse(errPayload)) {
314
- if (response.status === 429) {
315
- throw new TooManyRequestsError(errPayload, response)
316
- }
317
- throw new RequestError(errPayload.error.message, errPayload, response.status, errPayload.error.code, response)
318
- }
319
- throw RequestError.unknown(response)
320
340
  }
321
341
  }
package/src/types.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { components, operations, paths } from './generatedApiTypes'
1
+ import { components, paths } from './generatedApiTypes'
2
2
 
3
3
  export enum Region {
4
4
  EU = 'EU',
@@ -49,42 +49,9 @@ export type EventUpdate = components['schemas']['EventUpdate']
49
49
 
50
50
  export type EventRuleAction = components['schemas']['EventRuleAction']
51
51
 
52
- // Extract just the `path` parameters as a tuple of strings
53
- type ExtractPathParamStrings<Path> = Path extends { parameters: { path: infer P } }
54
- ? P extends Record<string, any>
55
- ? [P[keyof P]] // We extract the path parameter values as a tuple of strings
56
- : []
57
- : []
58
-
59
- // Utility type to extract query parameters from an operation and differentiate required/optional
60
- export type ExtractQueryParams<Path> = Path extends { parameters: { query?: infer Q } }
61
- ? undefined extends Q // Check if Q can be undefined (meaning it's optional)
62
- ? Q | undefined // If so, it's optional
63
- : Q // Otherwise, it's required
64
- : never // If no query parameters, return never
65
-
66
- // Utility type to extract request body from an operation (for POST, PUT, etc.)
67
- type ExtractRequestBody<Path> = Path extends { requestBody: { content: { 'application/json': infer B } } } ? B : never
68
-
69
- // Utility type to extract the response type for 200 status code
70
- type ExtractResponse<Path> = Path extends { responses: { 200: { content: { 'application/json': infer R } } } }
71
- ? R
72
- : void
73
-
74
- // Extracts args to given API method
75
- type ApiMethodArgs<Path extends keyof operations> = [
76
- // If method has body, extract it as first parameter
77
- ...(ExtractRequestBody<operations[Path]> extends never ? [] : [body: ExtractRequestBody<operations[Path]>]),
78
- // Next are path params, e.g. for path "/events/{event_id}" it will be one string parameter,
79
- ...ExtractPathParamStrings<operations[Path]>,
80
- // Last parameter will be the query params, if any
81
- ...(ExtractQueryParams<operations[Path]> extends never ? [] : [params: ExtractQueryParams<operations[Path]>]),
82
- ]
83
-
84
- type ApiMethod<Path extends keyof operations> = (
85
- ...args: ApiMethodArgs<Path>
86
- ) => Promise<ExtractResponse<operations[Path]>>
87
-
88
- export type FingerprintApi = {
89
- [Operation in keyof operations]: ApiMethod<Operation>
52
+ export interface FingerprintApi {
53
+ getEvent(eventId: string, options?: GetEventOptions): Promise<Event>
54
+ updateEvent(eventId: string, body: EventUpdate): Promise<void>
55
+ searchEvents(filter: SearchEventsFilter): Promise<SearchEventsResponse>
56
+ deleteVisitorData(visitorId: string): Promise<void>
90
57
  }
package/src/urlUtils.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { ExtractQueryParams, Region } from './types'
1
+ import { Region } from './types'
2
2
  import { version } from '../package.json'
3
3
  import { paths } from './generatedApiTypes'
4
4
 
@@ -10,8 +10,7 @@ const globalRegionUrl = 'https://api.fpjs.io/'
10
10
 
11
11
  type QueryStringScalar = string | number | boolean | null | undefined
12
12
 
13
- type QueryStringParameters = Record<string, QueryStringScalar | string[]> & {
14
- api_key?: string
13
+ type QueryStringParameters = Record<string, QueryStringScalar | QueryStringScalar[]> & {
15
14
  ii: string
16
15
  }
17
16
 
@@ -57,67 +56,15 @@ function getServerApiUrl(region: Region): string {
57
56
  }
58
57
  }
59
58
 
60
- /**
61
- * Extracts parameter placeholders into a literal union type.
62
- * For example `extractPathParams<'/users/{userId}/posts/{postId}'>` resolves to `"userId" | "postId"
63
- */
64
- type ExtractPathParams<T extends string> = T extends `${string}{${infer Param}}${infer Rest}`
65
- ? Param | ExtractPathParams<Rest>
66
- : never
67
-
68
- type PathParams<Path extends keyof paths> =
69
- ExtractPathParams<Path> extends never
70
- ? { pathParams?: never }
71
- : {
72
- pathParams: ExtractPathParams<Path> extends never ? never : string[]
73
- }
74
-
75
- type QueryParams<Path extends keyof paths, Method extends keyof paths[Path]> =
76
- ExtractQueryParams<paths[Path][Method]> extends never
77
- ? { queryParams?: any } // No query params
78
- : {
79
- queryParams?: ExtractQueryParams<paths[Path][Method]> // Optional query params
80
- }
81
-
82
- type IsNever<Type> = [Exclude<Type, undefined>] extends [never] ? true : false
83
- export type NonNeverKeys<Type> = {
84
- [Key in keyof Type]-?: IsNever<Type[Key]> extends true ? never : Key
85
- }[keyof Type]
86
- export type AllowedMethod<Path extends keyof paths> = Extract<Exclude<NonNeverKeys<paths[Path]>, 'parameters'>, string>
87
-
88
- type JsonContentOf<Response> = Response extends { content: { 'application/json': infer T } } ? T : never
89
-
90
- type UnionJsonFromResponses<Response> = {
91
- [StatusCode in keyof Response]: JsonContentOf<Response[StatusCode]>
92
- }[keyof Response]
59
+ export type HttpMethod = 'get' | 'put' | 'post' | 'delete' | 'options' | 'head' | 'patch' | 'trace'
93
60
 
94
- type StartingWithSuccessCode<Response> = {
95
- [StatusCode in keyof Response]: `${StatusCode & number}` extends `2${number}${number}` ? StatusCode : never
96
- }[keyof Response]
97
-
98
- type SuccessResponses<Response> = Pick<Response, Extract<StartingWithSuccessCode<Response>, keyof Response>>
99
-
100
- type OperationOf<Path extends keyof paths, Method extends AllowedMethod<Path>> = paths[Path][Method]
101
-
102
- type ResponsesOf<Path extends keyof paths, Method extends AllowedMethod<Path>> =
103
- OperationOf<Path, Method> extends { responses: infer Response } ? Response : never
104
-
105
- type SuccessJson<Path extends keyof paths, Method extends AllowedMethod<Path>> = UnionJsonFromResponses<
106
- SuccessResponses<ResponsesOf<Path, Method>>
107
- >
108
-
109
- export type SuccessJsonOrVoid<Path extends keyof paths, Method extends AllowedMethod<Path>> = [
110
- SuccessJson<Path, Method>,
111
- ] extends [never]
112
- ? void
113
- : SuccessJson<Path, Method>
114
-
115
- export type GetRequestPathOptions<Path extends keyof paths, Method extends AllowedMethod<Path>> = {
116
- path: Path
117
- method: Method
61
+ export interface GetRequestPathOptions {
62
+ path: keyof paths
63
+ method: HttpMethod
64
+ pathParams?: string[]
65
+ queryParams?: Record<string, QueryStringScalar | QueryStringScalar[]>
118
66
  region?: Region
119
- } & PathParams<Path> &
120
- QueryParams<Path, Method>
67
+ }
121
68
 
122
69
  /**
123
70
  * Formats a URL for the FingerprintJS server API by replacing placeholders and
@@ -125,18 +72,18 @@ export type GetRequestPathOptions<Path extends keyof paths, Method extends Allow
125
72
  *
126
73
  * @internal
127
74
  *
128
- * @param {GetRequestPathOptions<Path, Method>} options
129
- * @param {Path} options.path - The path of the API endpoint
75
+ * @param {GetRequestPathOptions} options
76
+ * @param {keyof paths} options.path - The path of the API endpoint
130
77
  * @param {string[]} [options.pathParams] - Path parameters to be replaced in the path
131
- * @param {QueryParams<Path, Method>["queryParams"]} [options.queryParams] - Query string
78
+ * @param {GetRequestPathOptions["queryParams"]} [options.queryParams] - Query string
132
79
  * parameters to be appended to the URL
133
80
  * @param {Region} options.region - The region of the API endpoint
134
- * @param {Method} options.method - The method of the API endpoint
81
+ * @param {HttpMethod} options.method - The method of the API endpoint
135
82
  *
136
83
  * @returns {string} The formatted URL with parameters replaced and query string
137
84
  * parameters appended
138
85
  */
139
- export function getRequestPath<Path extends keyof paths, Method extends AllowedMethod<Path>>({
86
+ export function getRequestPath({
140
87
  path,
141
88
  pathParams,
142
89
  queryParams,
@@ -144,7 +91,7 @@ export function getRequestPath<Path extends keyof paths, Method extends AllowedM
144
91
  // method mention here so that it can be referenced in JSDoc
145
92
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
146
93
  method: _,
147
- }: GetRequestPathOptions<Path, Method>): string {
94
+ }: GetRequestPathOptions): string {
148
95
  // Step 1: Extract the path parameters (placeholders) from the path
149
96
  const placeholders = Array.from(path.matchAll(/{(.*?)}/g)).map((match) => match[1])
150
97