@ibertran/fortytwo-intra-client 4.0.0 → 5.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/README.md CHANGED
@@ -20,6 +20,26 @@ const user = await client.get("users/ibertran");
20
20
  console.log(user);
21
21
  ```
22
22
 
23
+ ## Intra V3
24
+
25
+ The library also exports `FortytwoIntraV3Client` for staff-only Intra V3 endpoints that use the 42 Auth token flow.
26
+
27
+ ```typescript
28
+ import { FortytwoIntraV3Client } from "@ibertran/fortytwo-intra-client";
29
+
30
+ const client = new FortytwoIntraV3Client({
31
+ client_id: "staff-42-campus",
32
+ client_secret: "your_client_secret",
33
+ username: "your_staff_login",
34
+ password: "your_staff_password",
35
+ });
36
+
37
+ const response = await client.get("some/v3/endpoint");
38
+ console.log(response);
39
+ ```
40
+
41
+ The V3 client automatically requests a bearer token, reuses it while valid, and falls back to the refresh token when available.
42
+
23
43
  ## Configuration
24
44
 
25
45
  The `FortytwoIntraClient` class accepts three parameters:
@@ -0,0 +1,8 @@
1
+ import { AxiosError } from "axios";
2
+ export declare class FortytwoIntraClientHttpError extends Error {
3
+ statusCode: number;
4
+ statusText: string;
5
+ data: unknown;
6
+ constructor(error: AxiosError);
7
+ }
8
+ export declare function isFortytwoIntraClientHttpError(error: unknown): error is FortytwoIntraClientHttpError;
@@ -0,0 +1,25 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.FortytwoIntraClientHttpError = void 0;
4
+ exports.isFortytwoIntraClientHttpError = isFortytwoIntraClientHttpError;
5
+ class FortytwoIntraClientHttpError extends Error {
6
+ statusCode;
7
+ statusText;
8
+ data;
9
+ constructor(error) {
10
+ const method = error.config?.method?.toUpperCase() ?? null;
11
+ const route = error.config?.url ?? null;
12
+ const statusCode = error.response?.status ?? NaN;
13
+ const statusText = error.response?.statusText ?? "Unknown";
14
+ const message = method && route ? `${method} ${route} - HTTP ${statusCode} ${statusText}` : error.message;
15
+ super(message);
16
+ this.name = "FortytwoIntraClientHttpError";
17
+ this.statusCode = statusCode;
18
+ this.statusText = statusText;
19
+ this.data = error.response?.data;
20
+ }
21
+ }
22
+ exports.FortytwoIntraClientHttpError = FortytwoIntraClientHttpError;
23
+ function isFortytwoIntraClientHttpError(error) {
24
+ return error instanceof FortytwoIntraClientHttpError;
25
+ }
@@ -0,0 +1,7 @@
1
+ import { ZodError } from "zod";
2
+ export declare class FortytwoIntraClientValidationError extends Error {
3
+ schemaErrors: ZodError;
4
+ data: unknown;
5
+ constructor(schemaErrors: ZodError, data: unknown);
6
+ }
7
+ export declare function isFortytwoIntraClientValidationError(error: unknown): error is FortytwoIntraClientValidationError;
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.FortytwoIntraClientValidationError = void 0;
4
+ exports.isFortytwoIntraClientValidationError = isFortytwoIntraClientValidationError;
5
+ class FortytwoIntraClientValidationError extends Error {
6
+ schemaErrors;
7
+ data;
8
+ constructor(schemaErrors, data) {
9
+ super("Schema validation failed");
10
+ this.name = "FortytwoIntraClientValidationError";
11
+ this.schemaErrors = schemaErrors;
12
+ this.data = data;
13
+ }
14
+ }
15
+ exports.FortytwoIntraClientValidationError = FortytwoIntraClientValidationError;
16
+ function isFortytwoIntraClientValidationError(error) {
17
+ return error instanceof FortytwoIntraClientValidationError;
18
+ }
package/dist/index.d.ts CHANGED
@@ -1,6 +1,8 @@
1
+ import z from "zod";
1
2
  import { inputOptions } from "./types";
2
- import { FortytwoIntraClientError, isFortytwoIntraClientError } from "./lib/errors";
3
- import { z } from "zod";
3
+ export { FortytwoIntraV3Client } from "./intraV3";
4
+ export * from "./errors/httpError";
5
+ export * from "./errors/validationError";
4
6
  export interface FortytwoIntraClientConf {
5
7
  redirect_uri: string | null;
6
8
  base_url: string;
@@ -37,24 +39,25 @@ export declare class FortytwoIntraClient {
37
39
  private reqHandler;
38
40
  private logSuccess;
39
41
  private logError;
40
- get(endpoint: URL | string, options?: Omit<inputOptions, "body" | "perPage" | "maxPage">): Promise<any>;
41
- get<S extends z.ZodType>(endpoint: URL | string, options: Omit<inputOptions, "body" | "perPage" | "maxPage"> & {
42
+ private validate;
43
+ get(endpoint: URL | string, options?: Omit<inputOptions, "body" | "perPage" | "maxPages">): Promise<any>;
44
+ get<S extends z.ZodType>(endpoint: URL | string, options: Omit<inputOptions, "body" | "perPage" | "maxPages"> & {
42
45
  schema: S;
43
46
  }): Promise<z.infer<S>>;
44
- post(endpoint: URL | string, options?: Omit<inputOptions, "perPage" | "maxPage">): Promise<any>;
45
- post<S extends z.ZodType>(endpoint: URL | string, options: Omit<inputOptions, "perPage" | "maxPage"> & {
47
+ post(endpoint: URL | string, options?: Omit<inputOptions, "perPage" | "maxPages">): Promise<any>;
48
+ post<S extends z.ZodType>(endpoint: URL | string, options: Omit<inputOptions, "perPage" | "maxPages"> & {
46
49
  schema: S;
47
50
  }): Promise<z.infer<S>>;
48
- put(endpoint: URL | string, options?: Omit<inputOptions, "perPage" | "maxPage">): Promise<any>;
49
- put<S extends z.ZodType>(endpoint: URL | string, options: Omit<inputOptions, "perPage" | "maxPage"> & {
51
+ put(endpoint: URL | string, options?: Omit<inputOptions, "perPage" | "maxPages">): Promise<any>;
52
+ put<S extends z.ZodType>(endpoint: URL | string, options: Omit<inputOptions, "perPage" | "maxPages"> & {
50
53
  schema: S;
51
54
  }): Promise<z.infer<S>>;
52
- patch(endpoint: URL | string, options?: Omit<inputOptions, "perPage" | "maxPage">): Promise<any>;
53
- patch<S extends z.ZodType>(endpoint: URL | string, options: Omit<inputOptions, "perPage" | "maxPage"> & {
55
+ patch(endpoint: URL | string, options?: Omit<inputOptions, "perPage" | "maxPages">): Promise<any>;
56
+ patch<S extends z.ZodType>(endpoint: URL | string, options: Omit<inputOptions, "perPage" | "maxPages"> & {
54
57
  schema: S;
55
58
  }): Promise<z.infer<S>>;
56
- delete(endpoint: URL | string, options?: Omit<inputOptions, "perPage" | "maxPage">): Promise<any>;
57
- delete<S extends z.ZodType>(endpoint: URL | string, options: Omit<inputOptions, "perPage" | "maxPage"> & {
59
+ delete(endpoint: URL | string, options?: Omit<inputOptions, "perPage" | "maxPages">): Promise<any>;
60
+ delete<S extends z.ZodType>(endpoint: URL | string, options: Omit<inputOptions, "perPage" | "maxPages"> & {
58
61
  schema: S;
59
62
  }): Promise<z.infer<S>>;
60
63
  getAll(endpoint: URL | string, options?: Omit<inputOptions, "body">): Promise<any>;
@@ -70,6 +73,5 @@ export declare class FortytwoIntraClient {
70
73
  state: string | null;
71
74
  };
72
75
  exchangeOAuthCode(code: string, redirect_uri?: string): Promise<any>;
73
- tokenInfos(options?: Omit<inputOptions, "body" | "perPage" | "maxPage">): Promise<any>;
76
+ tokenInfos(options?: Omit<inputOptions, "body" | "perPage" | "maxPages">): Promise<any>;
74
77
  }
75
- export { FortytwoIntraClientError, isFortytwoIntraClientError };
package/dist/index.js CHANGED
@@ -32,18 +32,25 @@ var __importStar = (this && this.__importStar) || (function () {
32
32
  return result;
33
33
  };
34
34
  })();
35
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
36
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
37
+ };
35
38
  var __importDefault = (this && this.__importDefault) || function (mod) {
36
39
  return (mod && mod.__esModule) ? mod : { "default": mod };
37
40
  };
38
41
  Object.defineProperty(exports, "__esModule", { value: true });
39
- exports.isFortytwoIntraClientError = exports.FortytwoIntraClientError = exports.FortytwoIntraClient = void 0;
42
+ exports.FortytwoIntraClient = exports.FortytwoIntraV3Client = void 0;
40
43
  const axios_1 = __importStar(require("axios"));
41
44
  const axios_rate_limit_1 = __importDefault(require("axios-rate-limit"));
42
- const pagination_1 = require("./lib/pagination");
45
+ const zod_1 = __importDefault(require("zod"));
43
46
  const logs_1 = require("./lib/logs");
44
- const errors_1 = require("./lib/errors");
45
- Object.defineProperty(exports, "FortytwoIntraClientError", { enumerable: true, get: function () { return errors_1.FortytwoIntraClientError; } });
46
- Object.defineProperty(exports, "isFortytwoIntraClientError", { enumerable: true, get: function () { return errors_1.isFortytwoIntraClientError; } });
47
+ const pagination_1 = require("./lib/pagination");
48
+ const httpError_1 = require("./errors/httpError");
49
+ const validationError_1 = require("./errors/validationError");
50
+ var intraV3_1 = require("./intraV3");
51
+ Object.defineProperty(exports, "FortytwoIntraV3Client", { enumerable: true, get: function () { return intraV3_1.FortytwoIntraV3Client; } });
52
+ __exportStar(require("./errors/httpError"), exports);
53
+ __exportStar(require("./errors/validationError"), exports);
47
54
  const defaultConf = {
48
55
  redirect_uri: null,
49
56
  base_url: "https://api.intra.42.fr/v2/",
@@ -113,9 +120,7 @@ class FortytwoIntraClient {
113
120
  this.access_token = await this.generateToken();
114
121
  }
115
122
  // Attach access_token
116
- const accessToken = options.token
117
- ? options.token.access_token
118
- : this.access_token;
123
+ const accessToken = options.token ? options.token.access_token : this.access_token;
119
124
  // Extract query parameters from URL and combine with options.query
120
125
  const urlParams = {};
121
126
  url.searchParams.forEach((value, key) => {
@@ -125,7 +130,7 @@ class FortytwoIntraClient {
125
130
  const combinedParams = { ...urlParams, ...query };
126
131
  // Create clean URL without query parameters
127
132
  const cleanUrl = new URL(url);
128
- cleanUrl.search = '';
133
+ cleanUrl.search = "";
129
134
  // Use the rate-limited axios instance
130
135
  return this.axiosInstance.request({
131
136
  method: method.toLowerCase(),
@@ -156,7 +161,7 @@ class FortytwoIntraClient {
156
161
  return this.reqHandler(url, options);
157
162
  }
158
163
  else {
159
- throw new errors_1.FortytwoIntraClientError(err);
164
+ throw new httpError_1.FortytwoIntraClientHttpError(err);
160
165
  }
161
166
  }
162
167
  else {
@@ -183,6 +188,20 @@ class FortytwoIntraClient {
183
188
  console.log(line, logBody ? body : "");
184
189
  }
185
190
  }
191
+ validate(data, schema) {
192
+ if (!schema) {
193
+ return data;
194
+ }
195
+ try {
196
+ return schema.parse(data);
197
+ }
198
+ catch (err) {
199
+ if (err instanceof zod_1.default.ZodError) {
200
+ throw new validationError_1.FortytwoIntraClientValidationError(err, data);
201
+ }
202
+ throw err;
203
+ }
204
+ }
186
205
  async get(endpoint, options = {}) {
187
206
  if (endpoint instanceof URL === false) {
188
207
  endpoint = new URL(endpoint, this.base_url);
@@ -195,10 +214,7 @@ class FortytwoIntraClient {
195
214
  errLogBody: this.errLogBody,
196
215
  ...options,
197
216
  });
198
- if (options.schema) {
199
- return options.schema.parse(res.data);
200
- }
201
- return res.data;
217
+ return this.validate(res.data, options.schema);
202
218
  }
203
219
  async post(endpoint, options = {}) {
204
220
  if (endpoint instanceof URL === false) {
@@ -212,10 +228,7 @@ class FortytwoIntraClient {
212
228
  errLogBody: this.errLogBody,
213
229
  ...options,
214
230
  });
215
- if (options.schema) {
216
- return options.schema.parse(res.data);
217
- }
218
- return res.data;
231
+ return this.validate(res.data, options.schema);
219
232
  }
220
233
  async put(endpoint, options = {}) {
221
234
  if (endpoint instanceof URL === false) {
@@ -229,10 +242,7 @@ class FortytwoIntraClient {
229
242
  errLogBody: this.errLogBody,
230
243
  ...options,
231
244
  });
232
- if (options.schema) {
233
- return options.schema.parse(res.data);
234
- }
235
- return res.data;
245
+ return this.validate(res.data, options.schema);
236
246
  }
237
247
  async patch(endpoint, options = {}) {
238
248
  if (endpoint instanceof URL === false) {
@@ -246,10 +256,7 @@ class FortytwoIntraClient {
246
256
  errLogBody: this.errLogBody,
247
257
  ...options,
248
258
  });
249
- if (options.schema) {
250
- return options.schema.parse(res.data);
251
- }
252
- return res.data;
259
+ return this.validate(res.data, options.schema);
253
260
  }
254
261
  async delete(endpoint, options = {}) {
255
262
  if (endpoint instanceof URL === false) {
@@ -263,10 +270,7 @@ class FortytwoIntraClient {
263
270
  errLogBody: this.errLogBody,
264
271
  ...options,
265
272
  });
266
- if (options.schema) {
267
- return options.schema.parse(res.data);
268
- }
269
- return res.data;
273
+ return this.validate(res.data, options.schema);
270
274
  }
271
275
  async getAll(endpoint, options = {}) {
272
276
  if (endpoint instanceof URL === false) {
@@ -287,26 +291,19 @@ class FortytwoIntraClient {
287
291
  ...options.query,
288
292
  page: 1,
289
293
  per_page: perPage,
290
- }
294
+ },
291
295
  });
292
296
  if (!Array.isArray(firstPage.data)) {
293
- if (options.schema) {
294
- return options.schema.parse(firstPage.data);
295
- }
296
- return firstPage.data;
297
+ return this.validate(firstPage.data, options.schema);
297
298
  }
298
299
  let lastPage;
299
300
  try {
300
301
  lastPage = Math.min((0, pagination_1.getLastPage)(firstPage.headers["link"]), options.maxPages || Infinity);
301
302
  }
302
303
  catch (err) {
303
- if (options.schema) {
304
- return options.schema.parse(firstPage.data);
305
- }
306
- return firstPage.data;
304
+ return this.validate(firstPage.data, options.schema);
307
305
  }
308
- const promises = Array.from({ length: lastPage - 1 }, (_, i) => i + 2)
309
- .map(pageNumber => this.reqHandler(url, {
306
+ const promises = Array.from({ length: lastPage - 1 }, (_, i) => i + 2).map((pageNumber) => this.reqHandler(url, {
310
307
  method: "GET",
311
308
  attempt: 0,
312
309
  currpage: pageNumber,
@@ -319,14 +316,11 @@ class FortytwoIntraClient {
319
316
  ...options.query,
320
317
  page: pageNumber,
321
318
  per_page: perPage,
322
- }
319
+ },
323
320
  }));
324
321
  const otherPages = await Promise.all(promises);
325
322
  const allData = [...firstPage.data, ...otherPages.flatMap((res) => res.data)];
326
- if (options.schema) {
327
- return options.schema.parse(allData);
328
- }
329
- return allData;
323
+ return this.validate(allData, options.schema);
330
324
  }
331
325
  URL(endpoint) {
332
326
  return new URL(endpoint, this.base_url);
@@ -0,0 +1,58 @@
1
+ import z from "zod";
2
+ import { inputOptions } from "../types";
3
+ export * from "../errors/httpError";
4
+ export * from "../errors/validationError";
5
+ export interface FortytwoIntraClientConf {
6
+ base_url: string | undefined;
7
+ token_url: string;
8
+ maxRetry: number;
9
+ logLine: boolean;
10
+ errLogBody: boolean;
11
+ }
12
+ type Credentials = {
13
+ client_id: string;
14
+ client_secret: string;
15
+ username: string;
16
+ password: string;
17
+ };
18
+ export declare class FortytwoIntraV3Client {
19
+ private credentials;
20
+ private base_url;
21
+ private token_url;
22
+ private axiosInstance;
23
+ private retryOn;
24
+ private maxRetry;
25
+ private logLine;
26
+ private errLogBody;
27
+ private authorization;
28
+ constructor(credentials: Credentials, conf?: Partial<FortytwoIntraClientConf>);
29
+ private generateToken;
30
+ private isTokenValid;
31
+ private isRefreshTokenValid;
32
+ private fetch;
33
+ private reqHandler;
34
+ private logSuccess;
35
+ private logError;
36
+ private validate;
37
+ get(endpoint: URL | string, options?: Omit<inputOptions, "body" | "perPage" | "maxPages" | "token">): Promise<any>;
38
+ get<S extends z.ZodType>(endpoint: URL | string, options: Omit<inputOptions, "body" | "perPage" | "maxPages" | "token"> & {
39
+ schema: S;
40
+ }): Promise<z.infer<S>>;
41
+ post(endpoint: URL | string, options?: Omit<inputOptions, "perPage" | "maxPages">): Promise<any>;
42
+ post<S extends z.ZodType>(endpoint: URL | string, options: Omit<inputOptions, "perPage" | "maxPages"> & {
43
+ schema: S;
44
+ }): Promise<z.infer<S>>;
45
+ put(endpoint: URL | string, options?: Omit<inputOptions, "perPage" | "maxPages">): Promise<any>;
46
+ put<S extends z.ZodType>(endpoint: URL | string, options: Omit<inputOptions, "perPage" | "maxPages"> & {
47
+ schema: S;
48
+ }): Promise<z.infer<S>>;
49
+ patch(endpoint: URL | string, options?: Omit<inputOptions, "perPage" | "maxPages">): Promise<any>;
50
+ patch<S extends z.ZodType>(endpoint: URL | string, options: Omit<inputOptions, "perPage" | "maxPages"> & {
51
+ schema: S;
52
+ }): Promise<z.infer<S>>;
53
+ delete(endpoint: URL | string, options?: Omit<inputOptions, "perPage" | "maxPages">): Promise<any>;
54
+ delete<S extends z.ZodType>(endpoint: URL | string, options: Omit<inputOptions, "perPage" | "maxPages"> & {
55
+ schema: S;
56
+ }): Promise<z.infer<S>>;
57
+ URL(endpoint: string): URL;
58
+ }
@@ -0,0 +1,266 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
36
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
37
+ };
38
+ var __importDefault = (this && this.__importDefault) || function (mod) {
39
+ return (mod && mod.__esModule) ? mod : { "default": mod };
40
+ };
41
+ Object.defineProperty(exports, "__esModule", { value: true });
42
+ exports.FortytwoIntraV3Client = void 0;
43
+ const axios_1 = __importStar(require("axios"));
44
+ const zod_1 = __importDefault(require("zod"));
45
+ const logs_1 = require("../lib/logs");
46
+ const httpError_1 = require("../errors/httpError");
47
+ const validationError_1 = require("../errors/validationError");
48
+ const schemas_1 = require("./schemas");
49
+ __exportStar(require("../errors/httpError"), exports);
50
+ __exportStar(require("../errors/validationError"), exports);
51
+ const defaultConf = {
52
+ base_url: undefined,
53
+ token_url: "https://auth.42.fr/auth/realms/staff-42/protocol/openid-connect/token",
54
+ maxRetry: 5,
55
+ logLine: true,
56
+ errLogBody: true,
57
+ };
58
+ class FortytwoIntraV3Client {
59
+ credentials;
60
+ base_url;
61
+ token_url;
62
+ axiosInstance;
63
+ retryOn;
64
+ maxRetry;
65
+ logLine;
66
+ errLogBody;
67
+ authorization;
68
+ constructor(credentials, conf = {}) {
69
+ this.credentials = credentials;
70
+ const config = { ...defaultConf, ...conf };
71
+ this.base_url = config.base_url;
72
+ this.token_url = config.token_url;
73
+ this.axiosInstance = axios_1.default.create();
74
+ this.retryOn = [401, 429, 500];
75
+ this.maxRetry = config.maxRetry;
76
+ this.logLine = config.logLine;
77
+ this.errLogBody = config.errLogBody;
78
+ this.authorization = null;
79
+ }
80
+ async generateToken() {
81
+ const data = this.authorization && this.isRefreshTokenValid()
82
+ ? new URLSearchParams({
83
+ grant_type: "refresh_token",
84
+ refresh_token: this.authorization.refresh_token,
85
+ })
86
+ : new URLSearchParams({
87
+ grant_type: "password",
88
+ username: this.credentials.username,
89
+ password: this.credentials.password,
90
+ });
91
+ const res = await this.axiosInstance.post(this.token_url, data, {
92
+ headers: {
93
+ Authorization: `Basic ${Buffer.from(`${this.credentials.client_id}:${this.credentials.client_secret}`).toString("base64")}`,
94
+ "Content-Type": "application/x-www-form-urlencoded",
95
+ },
96
+ });
97
+ return schemas_1.tokenSchema.parse(res.data);
98
+ }
99
+ isTokenValid() {
100
+ return this.authorization !== null && this.authorization.expires_at - 30000 > Date.now();
101
+ }
102
+ isRefreshTokenValid() {
103
+ return (this.authorization !== null && this.authorization.refresh_expires_at - 30000 > Date.now());
104
+ }
105
+ async fetch(url, options) {
106
+ const { method, body, query } = options;
107
+ if (!this.isTokenValid()) {
108
+ this.authorization = await this.generateToken();
109
+ }
110
+ // Attach access_token
111
+ const accessToken = this.authorization?.access_token;
112
+ // Extract query parameters from URL and combine with options.query
113
+ const urlParams = {};
114
+ url.searchParams.forEach((value, key) => {
115
+ urlParams[key] = value;
116
+ });
117
+ // Combine URL params with query params (query params take precedence)
118
+ const combinedParams = { ...urlParams, ...query };
119
+ // Create clean URL without query parameters
120
+ const cleanUrl = new URL(url);
121
+ cleanUrl.search = "";
122
+ return this.axiosInstance.request({
123
+ method: method.toLowerCase(),
124
+ url: cleanUrl.toString(),
125
+ headers: {
126
+ Authorization: `Bearer ${accessToken}`,
127
+ },
128
+ params: combinedParams,
129
+ data: body ? body : undefined,
130
+ });
131
+ }
132
+ async reqHandler(url, options) {
133
+ try {
134
+ const res = await this.fetch(url, options);
135
+ this.logSuccess(res, options);
136
+ return res;
137
+ }
138
+ catch (err) {
139
+ if ((0, axios_1.isAxiosError)(err)) {
140
+ this.logError(err, options);
141
+ const { attempt, maxRetry } = options;
142
+ const status = err.response?.status;
143
+ if (maxRetry > 0 && attempt < maxRetry && status && this.retryOn.includes(status)) {
144
+ if (status === 401) {
145
+ this.authorization = null;
146
+ }
147
+ options.attempt++;
148
+ return this.reqHandler(url, options);
149
+ }
150
+ else {
151
+ throw new httpError_1.FortytwoIntraClientHttpError(err);
152
+ }
153
+ }
154
+ else {
155
+ throw err;
156
+ }
157
+ }
158
+ }
159
+ logSuccess(res, options) {
160
+ if (!options.logLine)
161
+ return;
162
+ const line = (0, logs_1.getLogLine)(res, options);
163
+ console.log(line);
164
+ }
165
+ logError(err, options) {
166
+ if (!options.logLine)
167
+ return;
168
+ const line = (0, logs_1.getErrorLogLine)(err, options);
169
+ const body = err.response?.data;
170
+ const logBody = options.errLogBody && body && Object.keys(body).length;
171
+ try {
172
+ console.log(line, logBody ? JSON.stringify(body, null, 2) : "");
173
+ }
174
+ catch {
175
+ console.log(line, logBody ? body : "");
176
+ }
177
+ }
178
+ validate(data, schema) {
179
+ if (!schema) {
180
+ return data;
181
+ }
182
+ try {
183
+ return schema.parse(data);
184
+ }
185
+ catch (err) {
186
+ if (err instanceof zod_1.default.ZodError) {
187
+ throw new validationError_1.FortytwoIntraClientValidationError(err, data);
188
+ }
189
+ throw err;
190
+ }
191
+ }
192
+ async get(endpoint, options = {}) {
193
+ if (endpoint instanceof URL === false) {
194
+ endpoint = new URL(endpoint, this.base_url);
195
+ }
196
+ const res = await this.reqHandler(endpoint, {
197
+ method: "GET",
198
+ attempt: 0,
199
+ maxRetry: this.maxRetry,
200
+ logLine: this.logLine,
201
+ errLogBody: this.errLogBody,
202
+ ...options,
203
+ });
204
+ return this.validate(res.data, options.schema);
205
+ }
206
+ async post(endpoint, options = {}) {
207
+ if (endpoint instanceof URL === false) {
208
+ endpoint = new URL(endpoint, this.base_url);
209
+ }
210
+ const res = await this.reqHandler(endpoint, {
211
+ method: "POST",
212
+ attempt: 0,
213
+ maxRetry: this.maxRetry,
214
+ logLine: this.logLine,
215
+ errLogBody: this.errLogBody,
216
+ ...options,
217
+ });
218
+ return this.validate(res.data, options.schema);
219
+ }
220
+ async put(endpoint, options = {}) {
221
+ if (endpoint instanceof URL === false) {
222
+ endpoint = new URL(endpoint, this.base_url);
223
+ }
224
+ const res = await this.reqHandler(endpoint, {
225
+ method: "PUT",
226
+ attempt: 0,
227
+ maxRetry: this.maxRetry,
228
+ logLine: this.logLine,
229
+ errLogBody: this.errLogBody,
230
+ ...options,
231
+ });
232
+ return this.validate(res.data, options.schema);
233
+ }
234
+ async patch(endpoint, options = {}) {
235
+ if (endpoint instanceof URL === false) {
236
+ endpoint = new URL(endpoint, this.base_url);
237
+ }
238
+ const res = await this.reqHandler(endpoint, {
239
+ method: "PATCH",
240
+ attempt: 0,
241
+ maxRetry: this.maxRetry,
242
+ logLine: this.logLine,
243
+ errLogBody: this.errLogBody,
244
+ ...options,
245
+ });
246
+ return this.validate(res.data, options.schema);
247
+ }
248
+ async delete(endpoint, options = {}) {
249
+ if (endpoint instanceof URL === false) {
250
+ endpoint = new URL(endpoint, this.base_url);
251
+ }
252
+ const res = await this.reqHandler(endpoint, {
253
+ method: "DELETE",
254
+ attempt: 0,
255
+ maxRetry: this.maxRetry,
256
+ logLine: this.logLine,
257
+ errLogBody: this.errLogBody,
258
+ ...options,
259
+ });
260
+ return this.validate(res.data, options.schema);
261
+ }
262
+ URL(endpoint) {
263
+ return new URL(endpoint, this.base_url);
264
+ }
265
+ }
266
+ exports.FortytwoIntraV3Client = FortytwoIntraV3Client;
@@ -0,0 +1,27 @@
1
+ import z from "zod";
2
+ export declare const tokenSchema: z.ZodPipe<z.ZodObject<{
3
+ access_token: z.ZodString;
4
+ expires_in: z.ZodNumber;
5
+ refresh_expires_in: z.ZodNumber;
6
+ refresh_token: z.ZodString;
7
+ token_type: z.ZodString;
8
+ "not-before-policy": z.ZodNumber;
9
+ session_state: z.ZodString;
10
+ scope: z.ZodString;
11
+ }, z.core.$strict>, z.ZodTransform<{
12
+ access_token: string;
13
+ expires_at: number;
14
+ refresh_token: string;
15
+ refresh_expires_at: number;
16
+ token_type: string;
17
+ }, {
18
+ access_token: string;
19
+ expires_in: number;
20
+ refresh_expires_in: number;
21
+ refresh_token: string;
22
+ token_type: string;
23
+ "not-before-policy": number;
24
+ session_state: string;
25
+ scope: string;
26
+ }>>;
27
+ export type Token = z.infer<typeof tokenSchema>;
@@ -0,0 +1,28 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.tokenSchema = void 0;
7
+ const zod_1 = __importDefault(require("zod"));
8
+ exports.tokenSchema = zod_1.default
9
+ .strictObject({
10
+ access_token: zod_1.default.string(),
11
+ expires_in: zod_1.default.number(),
12
+ refresh_expires_in: zod_1.default.number(),
13
+ refresh_token: zod_1.default.string(),
14
+ token_type: zod_1.default.string(),
15
+ "not-before-policy": zod_1.default.number(),
16
+ session_state: zod_1.default.string(),
17
+ scope: zod_1.default.string(),
18
+ })
19
+ .transform((obj) => {
20
+ const now = Date.now();
21
+ return {
22
+ access_token: obj.access_token,
23
+ expires_at: now + obj.expires_in * 1000,
24
+ refresh_token: obj.refresh_token,
25
+ refresh_expires_at: now + obj.refresh_expires_in * 1000,
26
+ token_type: obj.token_type,
27
+ };
28
+ });
@@ -11,9 +11,7 @@ class FortytwoIntraClientError extends Error {
11
11
  const route = error.config?.url ?? null;
12
12
  const statusCode = error.response?.status ?? NaN;
13
13
  const statusText = error.response?.statusText ?? "Unknown";
14
- const message = method && route
15
- ? `${method} ${route} - HTTP ${statusCode} ${statusText}`
16
- : error.message;
14
+ const message = method && route ? `${method} ${route} - HTTP ${statusCode} ${statusText}` : error.message;
17
15
  super(message);
18
16
  this.name = "FortytwoIntraClientError";
19
17
  this.statusCode = statusCode;
package/dist/lib/logs.js CHANGED
@@ -15,7 +15,7 @@ function formatQueryParams(params) {
15
15
  depth: Infinity,
16
16
  compact: true,
17
17
  breakLength: Infinity,
18
- colors: true
18
+ colors: true,
19
19
  });
20
20
  }
21
21
  function getLogLine(res, options) {
@@ -5,9 +5,7 @@ function getLastPage(header) {
5
5
  if (!header) {
6
6
  throw Error("No pagination link header");
7
7
  }
8
- const paginationlinks = header
9
- .split(",")
10
- .reduce((links, part) => {
8
+ const paginationlinks = header.split(",").reduce((links, part) => {
11
9
  const match = part.match(/<([^>]+)>\s*;\s*rel="([^"]+)"/);
12
10
  if (match) {
13
11
  const url = match[1];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ibertran/fortytwo-intra-client",
3
- "version": "4.0.0",
3
+ "version": "5.0.0",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "author": {