@ibertran/fortytwo-intra-client 2.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 ibertran
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,264 @@
1
+ # FortytwoIntraClient
2
+
3
+ A TypeScript library for interacting with the 42API with built-in rate limiting, automatic token management, and retry logic.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @ibertran/fortytwo-intra-client
9
+ ```
10
+
11
+ ## Quick Start
12
+
13
+ ```typescript
14
+ import { FortytwoIntraClient } from '@ibertran/fortytwo-intra-client';
15
+
16
+ const client = new FortytwoIntraClient('your_client_id', 'your_client_secret');
17
+
18
+
19
+ // Get a specific user by login
20
+ const user = await client.get('users/jdoe');
21
+ console.log(user);
22
+ ```
23
+
24
+ ## Configuration
25
+
26
+ The `FortytwoIntraClient` class accepts three parameters:
27
+
28
+ 1. `client_id` (required): Your 42 API client ID
29
+ 2. `client_secret` (required): Your 42 API client secret
30
+ 3. `config` (optional): Configuration object
31
+
32
+ ### Configuration Options
33
+
34
+ ```typescript
35
+ interface Conf {
36
+ redirect_uri: string | null; // OAuth redirect URI (default: null)
37
+ base_url: string; // API base URL (default: "https://api.intra.42.fr/v2/")
38
+ token_url: string; // Token endpoint (default: "https://api.intra.42.fr/oauth/token")
39
+ oauth_url: string; // OAuth endpoint (default: "https://api.intra.42.fr/oauth/authorize")
40
+ scopes: string[]; // OAuth scopes (default: ["public"])
41
+ rate: number; // Requests per second (default: 2)
42
+ maxRetry: number; // Max retry attempts (default: 5)
43
+ logs: boolean; // Enable logging (default: true)
44
+ errors: boolean; // Enable error logging (default: true)
45
+ }
46
+ ```
47
+
48
+ ### Example with Custom Configuration
49
+
50
+ ```typescript
51
+ const client = new FortytwoIntraClient('your_client_id', 'your_client_secret', {
52
+ rate: 1, // 1 request per second
53
+ maxRetry: 3, // Retry up to 3 times
54
+ logs: false, // Disable logging
55
+ scopes: ['public', 'projects'] // Extended scopes
56
+ });
57
+ ```
58
+
59
+ ## API Methods
60
+
61
+ ### GET Requests
62
+
63
+ ```typescript
64
+ // Get all campuses
65
+ const campuses = await client.get('campus');
66
+ console.log(`Found ${campuses.length} campuses`);
67
+
68
+ // Get a specific campus
69
+ const lyonCampus = await client.get('campus/9');
70
+ console.log(`${lyonCampus.name}: ${lyonCampus.users_count} users`);
71
+ ```
72
+
73
+ ### POST Requests
74
+
75
+ ```typescript
76
+ // Create a team for a project
77
+ const team = await client.post('teams', {
78
+ body: {
79
+ name: 'awesome-team',
80
+ project_id: 42
81
+ }
82
+ });
83
+ ```
84
+
85
+ ### PATCH Requests
86
+
87
+ ```typescript
88
+ // Update a team name
89
+ const updatedTeam = await client.patch('teams/123', {
90
+ body: {
91
+ team: {
92
+ name: "updated-team-name"
93
+ }
94
+ }
95
+ });
96
+ ```
97
+
98
+ ### DELETE Requests
99
+
100
+ ```typescript
101
+ // Delete a team
102
+ await client.delete('teams/123');
103
+ ```
104
+
105
+ ### Get All Pages (Pagination Helper)
106
+
107
+ The `getAll` method automatically handles pagination and returns all results:
108
+
109
+ ```typescript
110
+ // Get all users from 42 Lyon (handles pagination automatically)
111
+ const lyonUsers = await client.getAll('campus/9/users');
112
+ console.log(`Paris campus has ${lyonUsers.length} users`);
113
+
114
+ // Get all projects with custom page size
115
+ const allProjects = await client.getAll('projects', {
116
+ perPage: 50 // 50 items per page (default: 100)
117
+ });
118
+ ```
119
+
120
+ ## OAuth Authentication
121
+
122
+ ### Authorization Flow
123
+
124
+ ```typescript
125
+ // 1. Get OAuth authorization URL
126
+ const authUrl = client.getOAuthUrl('http://localhost:3000/callback');
127
+ // Redirect user to authUrl
128
+
129
+ // 2. Exchange authorization code for tokens
130
+ const tokens = await client.exchangeOAuthCode(code, 'http://localhost:3000/callback');
131
+ // tokens contains: access_token, refresh_token, expires_in, etc.
132
+
133
+ // 3. Use tokens for authenticated requests (USER-SPECIFIC DATA)
134
+ const currentUser = await client.get('users/me', {
135
+ token: tokens
136
+ });
137
+ console.log(`Welcome ${currentUser.displayname}!`);
138
+ ```
139
+
140
+ ### Using Custom Redirect URI
141
+
142
+ ```typescript
143
+ // Set redirect URI in constructor
144
+ const client = new IntraApiProxy('client_id', 'client_secret', {
145
+ redirect_uri: 'http://localhost:3000/callback'
146
+ });
147
+
148
+ // Or pass it to getOAuthUrl
149
+ const authUrl = client.getOAuthUrl('http://localhost:3000/callback');
150
+ ```
151
+
152
+ ## Utility Methods
153
+
154
+ ### URL Builder
155
+
156
+ ```typescript
157
+ // Create a URL object for the API
158
+ const userUrl = client.URL('users/jdoe');
159
+ console.log(userUrl.toString()); // "https://api.intra.42.fr/v2/users/jdoe"
160
+
161
+ // Build URLs for specific endpoints
162
+ const campusUrl = client.URL('campus/1/users');
163
+ const projectUrl = client.URL('projects/libft');
164
+ const coalitionUrl = client.URL('coalitions/1/users');
165
+ ```
166
+
167
+ ## Error Handling
168
+
169
+ The library includes automatic retry logic for rate limiting (429) and authentication (401) errors:
170
+
171
+ ```typescript
172
+ try {
173
+ const user = await client.get('users/jdoe');
174
+ console.log(`Found user: ${user.displayname}`);
175
+ } catch (error) {
176
+ if (error.status === 404) {
177
+ console.error('User not found');
178
+ } else if (error.status === 403) {
179
+ console.error('Access forbidden - check your token permissions');
180
+ } else {
181
+ console.error('Request failed after retries:', error.message);
182
+ }
183
+ }
184
+ ```
185
+
186
+ ## Rate Limiting
187
+
188
+ Requests are automatically throttled based on the configured rate limit. The default is 2 requests per second to comply with 42's API limits.
189
+
190
+ ## Logging
191
+
192
+ By default, the library logs all requests with status codes:
193
+
194
+ ```
195
+ ✅ 200 GET https://api.intra.42.fr/v2/users/jdoe
196
+ ✅ 200 GET https://api.intra.42.fr/v2/campus/1
197
+ 🔄 429 GET https://api.intra.42.fr/v2/projects retry 1/5
198
+ ✅ 200 GET https://api.intra.42.fr/v2/projects retry 1/5
199
+ ```
200
+
201
+ Disable logging by setting `logs: false` in the configuration.
202
+
203
+ ## TypeScript Support
204
+
205
+ This library is written in TypeScript and includes full type definitions:
206
+
207
+ ```typescript
208
+ import { FortytwoIntraClient } from '@ibertran/fortytwo-intra-client';
209
+
210
+ // Types are automatically inferred
211
+ const client = new FortytwoIntraClient('client_id', 'client_secret');
212
+ ```
213
+
214
+ ### OAuth Flow Example
215
+
216
+ ```typescript
217
+ import express from 'express';
218
+ import { FortytwoIntraClient } from '@ibertran/fortytwo-intra-client';
219
+
220
+ const app = express();
221
+ const client = new FortytwoIntraClient(
222
+ process.env.INTRA_CLIENT_ID!,
223
+ process.env.INTRA_CLIENT_SECRET!,
224
+ {
225
+ redirect_uri: 'http://localhost:3000/callback',
226
+ oauth_scope: ['public']
227
+ }
228
+ );
229
+
230
+ // Redirect to 42 OAuth
231
+ app.get('/login', (req, res) => {
232
+ const authUrl = client.getOAuthUrl();
233
+ res.redirect(authUrl);
234
+ });
235
+
236
+ // Handle OAuth callback
237
+ app.get('/callback', async (req, res) => {
238
+ try {
239
+ const { code } = req.query;
240
+ const token = await client.exchangeOAuthCode(code as string);
241
+
242
+ // Use tokens to make authenticated requests (accessing personal data)
243
+ const userData = await client.get('users/me', { token: token });
244
+
245
+ res.json({
246
+ user: {
247
+ login: userData.login,
248
+ displayname: userData.displayname,
249
+ email: userData.email,
250
+ level: userData.cursus_users[0]?.level || 0,
251
+ wallet: userData.wallet,
252
+ campus: userData.campus[0]?.name
253
+ },
254
+ });
255
+ } catch (error) {
256
+ res.status(500).json({ error: error.message });
257
+ }
258
+ });
259
+
260
+ app.listen(3000, () => {
261
+ console.log('Server running on http://localhost:3000');
262
+ console.log('Visit http://localhost:3000/login to start OAuth flow');
263
+ });
264
+ ```
@@ -0,0 +1,9 @@
1
+ declare class HttpError extends Error {
2
+ status: number | null;
3
+ url: string | null;
4
+ details: any;
5
+ constructor(err: any);
6
+ }
7
+ export declare function simplifySuperagentError(err: any): HttpError;
8
+ export declare function isSuperAgentError(err: any): boolean;
9
+ export {};
@@ -0,0 +1,25 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.simplifySuperagentError = simplifySuperagentError;
4
+ exports.isSuperAgentError = isSuperAgentError;
5
+ class HttpError extends Error {
6
+ status;
7
+ url;
8
+ details;
9
+ constructor(err) {
10
+ super(err.message || "Unknown API error");
11
+ this.name = "HttpError";
12
+ this.status = err.status || null;
13
+ this.url = err.response?.request?.url || null;
14
+ this.details = err.response?.body || null;
15
+ }
16
+ }
17
+ function simplifySuperagentError(err) {
18
+ if (!err) {
19
+ return new HttpError({ message: "Unknown error" });
20
+ }
21
+ return new HttpError(err);
22
+ }
23
+ function isSuperAgentError(err) {
24
+ return (err && typeof err === "object" && ("status" in err || "response" in err));
25
+ }
@@ -0,0 +1,43 @@
1
+ import { inputOptions, perPage } from "./types";
2
+ export interface FortytwoIntraClientConf {
3
+ redirect_uri: string | null;
4
+ base_url: string;
5
+ token_url: string;
6
+ oauth_url: string;
7
+ token_info_url: string;
8
+ scopes: string[];
9
+ rate: number;
10
+ maxRetry: number;
11
+ logs: boolean;
12
+ errors: boolean;
13
+ }
14
+ export declare class FortytwoIntraClient {
15
+ private client_id;
16
+ private client_secret;
17
+ private redirect_uri;
18
+ private base_url;
19
+ private token_url;
20
+ private oauth_url;
21
+ private token_info_url;
22
+ private scopes;
23
+ private throttler;
24
+ private retryOn;
25
+ private maxRetry;
26
+ private logs;
27
+ private access_token;
28
+ constructor(client_id: string, client_secret: string, conf: Partial<FortytwoIntraClientConf>);
29
+ private generateToken;
30
+ private fetch;
31
+ private reqHandler;
32
+ private logSuccess;
33
+ private logError;
34
+ get(endpoint: URL | string, options?: Omit<inputOptions, "body">): Promise<any>;
35
+ post(endpoint: URL | string, options?: inputOptions): Promise<any>;
36
+ patch(endpoint: URL | string, options: inputOptions): Promise<any>;
37
+ delete(endpoint: URL | string, options: inputOptions): Promise<any>;
38
+ getAll(endpoint: URL | string, options?: inputOptions & perPage): Promise<any>;
39
+ URL(endpoint: string): URL;
40
+ getOAuthUrl(redirect_uri?: string | null): string;
41
+ exchangeOAuthCode(code: string, redirect_uri?: string): Promise<any>;
42
+ tokenInfos(): Promise<any>;
43
+ }
package/dist/index.js ADDED
@@ -0,0 +1,280 @@
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 __importDefault = (this && this.__importDefault) || function (mod) {
36
+ return (mod && mod.__esModule) ? mod : { "default": mod };
37
+ };
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.FortytwoIntraClient = void 0;
40
+ const superagent = __importStar(require("superagent"));
41
+ // @ts-ignore
42
+ const superagent_throttle_1 = __importDefault(require("superagent-throttle"));
43
+ const utils_1 = require("./utils");
44
+ const HttpError_1 = require("./HttpError");
45
+ const defaultConf = {
46
+ redirect_uri: null,
47
+ base_url: "https://api.intra.42.fr/v2/",
48
+ token_url: "https://api.intra.42.fr/oauth/token",
49
+ oauth_url: "https://api.intra.42.fr/oauth/authorize",
50
+ token_info_url: "https://api.intra.42.fr/oauth/token/info",
51
+ scopes: ["public"],
52
+ rate: 2,
53
+ maxRetry: 5,
54
+ logs: true,
55
+ errors: true,
56
+ };
57
+ class FortytwoIntraClient {
58
+ client_id;
59
+ client_secret;
60
+ redirect_uri;
61
+ base_url;
62
+ token_url;
63
+ oauth_url;
64
+ token_info_url;
65
+ scopes;
66
+ throttler;
67
+ retryOn;
68
+ maxRetry;
69
+ logs;
70
+ access_token;
71
+ constructor(client_id, client_secret, conf) {
72
+ this.client_id = client_id;
73
+ this.client_secret = client_secret;
74
+ const config = { ...defaultConf, ...conf };
75
+ this.redirect_uri = config.redirect_uri;
76
+ this.base_url = config.base_url;
77
+ this.token_url = config.token_url;
78
+ this.oauth_url = config.oauth_url;
79
+ this.token_info_url = config.token_info_url;
80
+ this.scopes = config.scopes;
81
+ this.throttler = new superagent_throttle_1.default({
82
+ rate: config.rate,
83
+ ratePer: 1100,
84
+ concurrent: config.rate - 1 > 0 ? config.rate - 1 : 1,
85
+ });
86
+ this.retryOn = [401, 429, 500];
87
+ this.maxRetry = config.maxRetry;
88
+ this.logs = config.logs;
89
+ this.access_token = null;
90
+ }
91
+ async generateToken() {
92
+ const res = await superagent.post(this.token_url).send({
93
+ grant_type: "client_credentials",
94
+ client_id: this.client_id,
95
+ client_secret: this.client_secret,
96
+ scope: this.scopes.join(" "),
97
+ });
98
+ return res.body.access_token;
99
+ }
100
+ async fetch(url, options) {
101
+ const { method, body } = options;
102
+ const req = (0, utils_1.initRequest)(method, url);
103
+ if (!options.token && !this.access_token) {
104
+ this.access_token = await this.generateToken();
105
+ }
106
+ // Attach access_token
107
+ const accessToken = options.token
108
+ ? options.token.access_token
109
+ : this.access_token;
110
+ req.set("Authorization", `Bearer ${accessToken}`);
111
+ // Add query string
112
+ if (options.query)
113
+ req.query(options.query);
114
+ // Send body if any is provided
115
+ if (body)
116
+ req.send(body);
117
+ // Throttle resquest based on rate limit
118
+ req.use(this.throttler.plugin());
119
+ return req;
120
+ }
121
+ async reqHandler(url, options) {
122
+ try {
123
+ const res = await this.fetch(url, options);
124
+ this.logSuccess(res, options);
125
+ return res;
126
+ }
127
+ catch (err) {
128
+ if ((0, HttpError_1.isSuperAgentError)(err)) {
129
+ const { attempt, maxRetry } = options;
130
+ this.logError(err, url, options);
131
+ if (maxRetry > 0 &&
132
+ attempt < maxRetry &&
133
+ this.retryOn.includes(err.status)) {
134
+ if (err.status === 401) {
135
+ this.access_token = null;
136
+ }
137
+ options.attempt++;
138
+ return this.reqHandler(url, options);
139
+ }
140
+ else {
141
+ throw (0, HttpError_1.simplifySuperagentError)(err);
142
+ }
143
+ }
144
+ else {
145
+ throw err;
146
+ }
147
+ }
148
+ }
149
+ logSuccess(res, options) {
150
+ const method = options.method.padEnd(6, " ");
151
+ const color = "\x1b[42m";
152
+ const reset = "\x1b[0m";
153
+ const msg = `${color + res.status + reset} ${method} ${res.request.url}${options.attempt ? ` retry ${options.attempt}/${options.maxRetry}` : ``}`;
154
+ console.log(msg);
155
+ }
156
+ logError(err, url, options) {
157
+ const method = options.method.padEnd(6, " ");
158
+ const color = "\x1b[41m";
159
+ const reset = "\x1b[0m";
160
+ const msg = `${color + err.status + reset} ${method} ${err.response.request.url}${options.attempt ? ` retry ${options.attempt}/${options.maxRetry}` : ``}`;
161
+ console.log(msg, JSON.stringify(err.response.body, null, 2));
162
+ }
163
+ // Public methods
164
+ async get(endpoint, options = {}) {
165
+ if (endpoint instanceof URL === false) {
166
+ endpoint = new URL(endpoint, this.base_url);
167
+ }
168
+ const res = await this.reqHandler(endpoint, {
169
+ method: "GET",
170
+ attempt: 0,
171
+ maxRetry: this.maxRetry,
172
+ ...options,
173
+ });
174
+ return res.body;
175
+ }
176
+ async post(endpoint, options = {}) {
177
+ if (endpoint instanceof URL === false) {
178
+ endpoint = new URL(endpoint, this.base_url);
179
+ }
180
+ const res = await this.reqHandler(endpoint, {
181
+ method: "POST",
182
+ attempt: 0,
183
+ maxRetry: this.maxRetry,
184
+ ...options,
185
+ });
186
+ return res.body;
187
+ }
188
+ async patch(endpoint, options) {
189
+ if (endpoint instanceof URL === false) {
190
+ endpoint = new URL(endpoint, this.base_url);
191
+ }
192
+ const res = await this.reqHandler(endpoint, {
193
+ method: "PATCH",
194
+ attempt: 0,
195
+ maxRetry: this.maxRetry,
196
+ ...options,
197
+ });
198
+ return res.body;
199
+ }
200
+ async delete(endpoint, options) {
201
+ if (endpoint instanceof URL === false) {
202
+ endpoint = new URL(endpoint, this.base_url);
203
+ }
204
+ const res = await this.reqHandler(endpoint, {
205
+ method: "DELETE",
206
+ attempt: 0,
207
+ maxRetry: this.maxRetry,
208
+ ...options,
209
+ });
210
+ return res.body;
211
+ }
212
+ async getAll(endpoint, options = {}) {
213
+ if (endpoint instanceof URL === false) {
214
+ endpoint = new URL(endpoint, this.base_url);
215
+ }
216
+ const perPage = options.perPage || 100;
217
+ let url = new URL(endpoint);
218
+ const initialRes = await this.reqHandler(url, {
219
+ method: "GET",
220
+ attempt: 0,
221
+ maxRetry: this.maxRetry,
222
+ ...options,
223
+ query: {
224
+ page: 1,
225
+ per_page: 100,
226
+ },
227
+ });
228
+ let lastPage;
229
+ try {
230
+ lastPage = (0, utils_1.getLastPage)(initialRes.header["link"]);
231
+ }
232
+ catch (err) {
233
+ return initialRes.body;
234
+ }
235
+ const promises = [];
236
+ for (let i = 2; i <= lastPage; i++) {
237
+ url = new URL(endpoint);
238
+ url.searchParams.append("per_page", perPage.toString());
239
+ url.searchParams.append("page", i.toString());
240
+ promises.push(this.reqHandler(url, {
241
+ method: "GET",
242
+ attempt: 0,
243
+ maxRetry: this.maxRetry,
244
+ ...options,
245
+ }));
246
+ }
247
+ return Promise.all(promises).then((values) => {
248
+ return initialRes.body.concat(...values.map((value) => value.body));
249
+ });
250
+ }
251
+ URL(endpoint) {
252
+ return new URL(endpoint, this.base_url);
253
+ }
254
+ getOAuthUrl(redirect_uri = null) {
255
+ redirect_uri ??= this.redirect_uri;
256
+ if (!redirect_uri) {
257
+ throw new Error(`undefined redirect_uri`);
258
+ }
259
+ const url = new URL(this.oauth_url);
260
+ url.searchParams.set("client_id", this.client_id);
261
+ url.searchParams.set("redirect_uri", redirect_uri);
262
+ url.searchParams.set("response_type", "code");
263
+ url.searchParams.set("scope", this.scopes.join(" "));
264
+ return url.toString();
265
+ }
266
+ async exchangeOAuthCode(code, redirect_uri) {
267
+ const res = await superagent.post(this.token_url).send({
268
+ grant_type: "authorization_code",
269
+ client_id: this.client_id,
270
+ client_secret: this.client_secret,
271
+ redirect_uri: redirect_uri ? redirect_uri : this.redirect_uri,
272
+ code: code,
273
+ });
274
+ return res.body;
275
+ }
276
+ async tokenInfos() {
277
+ return this.get(this.token_info_url);
278
+ }
279
+ }
280
+ exports.FortytwoIntraClient = FortytwoIntraClient;
@@ -0,0 +1,20 @@
1
+ export type Method = "GET" | "POST" | "PATCH" | "DELETE";
2
+ export type userToken = {
3
+ access_token: string;
4
+ };
5
+ export type reqOptions = {
6
+ method: Method;
7
+ maxRetry: number;
8
+ attempt: number;
9
+ body?: any;
10
+ token?: userToken;
11
+ query?: querystring;
12
+ };
13
+ export type querystring = Record<string, string | number | boolean | Array<string | number | boolean>>;
14
+ export type perPage = {
15
+ perPage?: number;
16
+ };
17
+ type internal = "method" | "attempt";
18
+ type optional = "maxRetry";
19
+ export type inputOptions = Omit<reqOptions, internal | optional> & Partial<Pick<reqOptions, optional>>;
20
+ export {};
package/dist/types.js ADDED
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,4 @@
1
+ import * as superagent from "superagent";
2
+ import { Method } from "./types";
3
+ export declare function initRequest(method: Method, url: URL): superagent.Request;
4
+ export declare function getLastPage(header: string | undefined): number;
package/dist/utils.js ADDED
@@ -0,0 +1,79 @@
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
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.initRequest = initRequest;
37
+ exports.getLastPage = getLastPage;
38
+ const superagent = __importStar(require("superagent"));
39
+ function initRequest(method, url) {
40
+ const endpoint = url.toString();
41
+ switch (method) {
42
+ case "GET":
43
+ return superagent.get(endpoint);
44
+ case "POST":
45
+ return superagent.post(endpoint);
46
+ case "PATCH":
47
+ return superagent.patch(endpoint);
48
+ case "DELETE":
49
+ return superagent.delete(endpoint);
50
+ default:
51
+ throw Error("Invalid HTTP method");
52
+ }
53
+ }
54
+ function getLastPage(header) {
55
+ if (!header) {
56
+ throw Error("No pagination link header");
57
+ }
58
+ const paginationlinks = header
59
+ .split(",")
60
+ .reduce((links, part) => {
61
+ const match = part.match(/<([^>]+)>\s*;\s*rel="([^"]+)"/);
62
+ if (match) {
63
+ const url = match[1];
64
+ const rel = match[2];
65
+ links[rel] = url;
66
+ }
67
+ return links;
68
+ }, {});
69
+ if (!paginationlinks.last) {
70
+ throw Error("Missing last page link");
71
+ }
72
+ const lastPageUrl = new URL(paginationlinks.last);
73
+ const rawLastPage = lastPageUrl.searchParams.get("page");
74
+ const lastPage = rawLastPage ? parseInt(rawLastPage, 10) : undefined;
75
+ if (!lastPage || isNaN(lastPage)) {
76
+ throw Error("Invalid last page link");
77
+ }
78
+ return lastPage;
79
+ }
package/package.json ADDED
@@ -0,0 +1,23 @@
1
+ {
2
+ "name": "@ibertran/fortytwo-intra-client",
3
+ "version": "2.0.0",
4
+ "main": "dist/index.js",
5
+ "types": "dist/index.d.ts",
6
+ "scripts": {
7
+ "build": "tsc",
8
+ "prepare": "npm run build"
9
+ },
10
+ "dependencies": {
11
+ "superagent": "^10.2.1",
12
+ "superagent-throttle": "^1.0.1"
13
+ },
14
+ "devDependencies": {
15
+ "@types/superagent": "^8.1.9",
16
+ "ts-node": "^10.9.2",
17
+ "tsc": "^2.0.4"
18
+ },
19
+ "files": [
20
+ "dist",
21
+ "README.md"
22
+ ]
23
+ }