diff --git a/README.md b/README.md index da05ac8..1370572 100644 --- a/README.md +++ b/README.md @@ -59,14 +59,15 @@ It returns a boolean that is `true` when the server is a demo. - [x] parser: able to get attachment's URL - [x] Find Pronote instances with longitude and latitude - [x] `findPronoteInstances({ longitude, latitude })` -- Periods +- [x] Periods - [x] client: `.periods` (property) - [x] parser: `Period` -- Grades & Averages +- [x] Grades & Averages - [x] client: `.getGradesOverviewForPeriod(period)` - [x] parser: `Period.getGradesOverview()` - [x] parser: `StudentGrade` - [x] parser: `StudentAverage` +- [x] Custom `fetcher` to call the API with another API than [`fetch`](https://developer.mozilla.org/docs/Web/API/Fetch_API) ## Installation diff --git a/examples/custom-fetcher.ts b/examples/custom-fetcher.ts new file mode 100644 index 0000000..faec969 --- /dev/null +++ b/examples/custom-fetcher.ts @@ -0,0 +1,67 @@ +import { authenticatePronoteCredentials, PronoteApiAccountId, findPronoteInstances, type PawnoteFetcher } from "../src"; + +const customFetcher: PawnoteFetcher = async (url, options) => { + console.time("request from fetcher"); + + const response = await fetch(url, { + method: options.method, + headers: options.headers, + body: options.method !== "GET" ? options.body : void 0, + redirect: options.redirect + }); + + console.timeEnd("request from fetcher"); + + return { + json: async () => { + const data = await response.json() as T; + + // We can add stuff in those methods too ! + console.info("-> Parsing a JSON in fetcher !"); + + return data; + }, + + text: async () => { + const data = await response.text(); + + // We can add stuff in those methods too ! + console.info("-> Reading the response as text..."); + + return data; + }, + + // We can even write a function on the headers getter. + get headers () { + console.info("-> Reading headers from fetcher !"); + return response.headers; + } + }; +}; + +(async () => { + console.group("findPronoteInstances"); + const geolocationResults = await findPronoteInstances(customFetcher, { + latitude: 45.849998, + longitude: 1.25 + }); + + console.info("\nThere's", geolocationResults.length, "instances in the given location."); + console.groupEnd(); + + console.group("authenticate"); + const pronote = await authenticatePronoteCredentials("https://demo.index-education.net/pronote", { + accountTypeID: PronoteApiAccountId.Eleve, + username: "demonstration", + password: "pronotevs", + + // Because this is just an example, don't forget to change this. + deviceUUID: "my-device-uuid", + + // We use our custom fetcher here ! + fetcher: customFetcher + }); + + console.info("\nThere's", pronote.periods.length, "periods in the given pronote account."); + console.groupEnd(); +})(); diff --git a/examples/geolocation.ts b/examples/geolocation.ts index 0d386e4..a1cceba 100644 --- a/examples/geolocation.ts +++ b/examples/geolocation.ts @@ -1,7 +1,9 @@ -import { findPronoteInstances, type PronoteInstance } from "../src"; +import { findPronoteInstances, defaultPawnoteFetcher, type PronoteInstance } from "../src"; (async () => { - const instances: Array = await findPronoteInstances({ + // We need to provide explicitly the fetcher here, since + // it's a direct API call. + const instances: Array = await findPronoteInstances(defaultPawnoteFetcher, { latitude: 45.849998, longitude: 1.25 }); diff --git a/src/api/geolocation/index.ts b/src/api/geolocation/index.ts index 48e36fb..df5c591 100644 --- a/src/api/geolocation/index.ts +++ b/src/api/geolocation/index.ts @@ -8,24 +8,23 @@ import { makeApiHandler } from "~/utils/api"; import { MOBILE_CHROME_USER_AGENT } from "~/constants/user-agent"; /** Gives every Pronote instance in a 20km radius of the given `longitude` and `latitude`. */ -export const callApiGeolocation = makeApiHandler(async (input) => { +export const callApiGeolocation = makeApiHandler(async (fetcher, input) => { const request_body: PronoteApiGeolocation["request"] = { nomFonction: "geoLoc", lat: input.latitude.toString(), long: input.longitude.toString() }; - const body = new URLSearchParams(); - body.set("data", JSON.stringify(request_body)); + const searchParamsBody = new URLSearchParams(); + searchParamsBody.set("data", JSON.stringify(request_body)); - const headers = new Headers(); - headers.set("Content-Type", "application/x-www-form-urlencoded;charset=UTF-8"); - headers.set("User-Agent", MOBILE_CHROME_USER_AGENT); - - const response = await fetch(PRONOTE_GEOLOCATION_URL, { + const response = await fetcher(PRONOTE_GEOLOCATION_URL, { method: "POST", - headers, - body + headers: { + "Content-Type": "application/x-www-form-urlencoded;charset=UTF-8", + "User-Agent": MOBILE_CHROME_USER_AGENT + }, + body: searchParamsBody.toString() }); let data = await response.json() as PronoteApiGeolocation["response"]; diff --git a/src/api/login/authenticate/index.ts b/src/api/login/authenticate/index.ts index 4949ca6..86e3e8b 100644 --- a/src/api/login/authenticate/index.ts +++ b/src/api/login/authenticate/index.ts @@ -4,7 +4,7 @@ import { PronoteApiFunctions } from "~/constants/functions"; import { createPronoteAPICall } from "~/pronote/requestAPI"; import { makeApiHandler } from "~/utils/api"; -export const callApiLoginAuthenticate = makeApiHandler(async (input) => { +export const callApiLoginAuthenticate = makeApiHandler(async (fetcher, input) => { const request_payload = input.session.writePronoteFunctionPayload({ donnees: { connexion: 0, @@ -13,7 +13,7 @@ export const callApiLoginAuthenticate = makeApiHandler(asy } }); - const response = await createPronoteAPICall(PronoteApiFunctions.Authenticate, { + const response = await createPronoteAPICall(fetcher, PronoteApiFunctions.Authenticate, { session_instance: input.session.instance, cookies: input.cookies ?? [], payload: request_payload diff --git a/src/api/login/identify/index.ts b/src/api/login/identify/index.ts index a3f40d4..6831d06 100644 --- a/src/api/login/identify/index.ts +++ b/src/api/login/identify/index.ts @@ -4,7 +4,7 @@ import { PronoteApiFunctions } from "~/constants/functions"; import { createPronoteAPICall } from "~/pronote/requestAPI"; import { makeApiHandler } from "~/utils/api"; -export const callApiLoginIdentify = makeApiHandler(async (input) => { +export const callApiLoginIdentify = makeApiHandler(async (fetcher, input) => { const requestPayload = input.session.writePronoteFunctionPayload({ donnees: { genreConnexion: 0, @@ -21,7 +21,7 @@ export const callApiLoginIdentify = makeApiHandler(async (inpu } }); - const response = await createPronoteAPICall(PronoteApiFunctions.Identify, { + const response = await createPronoteAPICall(fetcher, PronoteApiFunctions.Identify, { session_instance: input.session.instance, cookies: input.cookies ?? [], payload: requestPayload diff --git a/src/api/login/informations/index.ts b/src/api/login/informations/index.ts index 1452bc3..598e7cf 100644 --- a/src/api/login/informations/index.ts +++ b/src/api/login/informations/index.ts @@ -10,7 +10,7 @@ import { PronoteApiFunctions } from "~/constants/functions"; import { createPronoteAPICall } from "~/pronote/requestAPI"; import forge from "node-forge"; -export const callApiLoginInformations = makeApiHandler(async (input) => { +export const callApiLoginInformations = makeApiHandler(async (fetcher, input) => { const accountType = PRONOTE_ACCOUNT_TYPES.find((entry) => entry.id === input.accountTypeID); if (!accountType) throw new Error(`Invalid account type ID: ${input.accountTypeID}`); @@ -66,7 +66,7 @@ export const callApiLoginInformations = makeApiHandler(asy } }); - const response = await createPronoteAPICall(PronoteApiFunctions.Informations, { + const response = await createPronoteAPICall(fetcher, PronoteApiFunctions.Informations, { cookies, payload: request_payload, session_instance: session.instance diff --git a/src/api/user/data/index.ts b/src/api/user/data/index.ts index d26e6f1..36d8056 100644 --- a/src/api/user/data/index.ts +++ b/src/api/user/data/index.ts @@ -4,9 +4,9 @@ import { PronoteApiFunctions } from "~/constants/functions"; import { createPronoteAPICall } from "~/pronote/requestAPI"; import { makeApiHandler } from "~/utils/api"; -export const callApiUserData = makeApiHandler(async (input) => { +export const callApiUserData = makeApiHandler(async (fetcher, input) => { const request_payload = input.session.writePronoteFunctionPayload({}); - const response = await createPronoteAPICall(PronoteApiFunctions.UserData, { + const response = await createPronoteAPICall(fetcher, PronoteApiFunctions.UserData, { session_instance: input.session.instance, payload: request_payload }); diff --git a/src/api/user/grades/index.ts b/src/api/user/grades/index.ts index 4b41b61..df6af20 100644 --- a/src/api/user/grades/index.ts +++ b/src/api/user/grades/index.ts @@ -4,7 +4,7 @@ import { makeApiHandler } from "~/utils/api"; import { createPronoteAPICall } from "~/pronote/requestAPI"; import { PronoteApiFunctions } from "~/constants/functions"; -export const callApiUserGrades = makeApiHandler(async (input) => { +export const callApiUserGrades = makeApiHandler(async (fetcher, input) => { const request_payload = input.session.writePronoteFunctionPayload({ donnees: { Periode: { @@ -15,7 +15,7 @@ export const callApiUserGrades = makeApiHandler(async (input) => _Signature_: { onglet: PronoteApiOnglets.Grades } }); - const response = await createPronoteAPICall(PronoteApiFunctions.Grades, { + const response = await createPronoteAPICall(fetcher, PronoteApiFunctions.Grades, { session_instance: input.session.instance, payload: request_payload }); diff --git a/src/api/user/homework/index.ts b/src/api/user/homework/index.ts index f795a07..f4d34fd 100644 --- a/src/api/user/homework/index.ts +++ b/src/api/user/homework/index.ts @@ -5,7 +5,7 @@ import { PronoteApiOnglets } from "~/constants/onglets"; import { createPronoteAPICall } from "~/pronote/requestAPI"; import { makeApiHandler } from "~/utils/api"; -export const callApiUserHomework = makeApiHandler(async (input) => { +export const callApiUserHomework = makeApiHandler(async (fetcher, input) => { if (input.fromWeekNumber <= 0) { throw new Error(`Invalid input on callApiUserHomework, "fromWeekNumber" should be a strictly positive number, got ${input.fromWeekNumber}`); } @@ -27,7 +27,7 @@ export const callApiUserHomework = makeApiHandler(async (input) } }); - const response = await createPronoteAPICall(PronoteApiFunctions.Homework, { + const response = await createPronoteAPICall(fetcher, PronoteApiFunctions.Homework, { session_instance: input.session.instance, payload: request_payload }); diff --git a/src/api/user/homeworkStatus/index.ts b/src/api/user/homeworkStatus/index.ts index 2a8700d..051cb59 100644 --- a/src/api/user/homeworkStatus/index.ts +++ b/src/api/user/homeworkStatus/index.ts @@ -5,7 +5,7 @@ import { PronoteApiOnglets } from "~/constants/onglets"; import { createPronoteAPICall } from "~/pronote/requestAPI"; import { makeApiHandler } from "~/utils/api"; -export const callApiUserHomeworkStatus = makeApiHandler(async (input) => { +export const callApiUserHomeworkStatus = makeApiHandler(async (fetcher, input) => { const request_payload = input.session.writePronoteFunctionPayload({ donnees: { listeTAF: [{ @@ -19,7 +19,7 @@ export const callApiUserHomeworkStatus = makeApiHandler(a } }); - const response = await createPronoteAPICall(PronoteApiFunctions.HomeworkDone, { + const response = await createPronoteAPICall(fetcher, PronoteApiFunctions.HomeworkDone, { session_instance: input.session.instance, payload: request_payload }); diff --git a/src/api/user/timetable/index.ts b/src/api/user/timetable/index.ts index 273bc03..e0605d9 100644 --- a/src/api/user/timetable/index.ts +++ b/src/api/user/timetable/index.ts @@ -5,7 +5,7 @@ import { PronoteApiOnglets } from "~/constants/onglets"; import { createPronoteAPICall } from "~/pronote/requestAPI"; import { makeApiHandler } from "~/utils/api"; -export const callApiUserTimetable = makeApiHandler(async (input) => { +export const callApiUserTimetable = makeApiHandler(async (fetcher, input) => { if (input.weekNumber <= 0) { throw new Error(`Invalid input on callApiUserTimetable, "weekNumber" should be a strictly positive number, got ${input.weekNumber}`); } @@ -32,7 +32,7 @@ export const callApiUserTimetable = makeApiHandler(async (inpu _Signature_: { onglet: PronoteApiOnglets.Timetable } }); - const response = await createPronoteAPICall(PronoteApiFunctions.Timetable, { + const response = await createPronoteAPICall(fetcher, PronoteApiFunctions.Timetable, { session_instance: input.session.instance, payload: request_payload }); diff --git a/src/authenticate/index.ts b/src/authenticate/index.ts index 77b78c5..d635712 100644 --- a/src/authenticate/index.ts +++ b/src/authenticate/index.ts @@ -1,12 +1,16 @@ import type { AuthenticatePronoteCredentialsOptions, AuthenticateTokenOptions, NextAuthenticationCredentials } from "./types"; import { callApiLoginInformations, callApiLoginIdentify, callApiLoginAuthenticate, callApiUserData } from "~/api"; import { PRONOTE_ACCOUNT_TYPES } from "~/constants/accounts"; +import { defaultPawnoteFetcher } from "~/utils/fetcher"; import aes from "~/utils/aes"; import Pronote from "~/client/Pronote"; import forge from "node-forge"; export const authenticatePronoteCredentials = async (pronoteStringURL: string, options: AuthenticatePronoteCredentialsOptions): Promise => { + // Use default fetcher if not provided. + const fetcher = options.fetcher ?? defaultPawnoteFetcher; + const pronoteURL = new URL(pronoteStringURL); const accountType = PRONOTE_ACCOUNT_TYPES.find((entry) => entry.id === options.accountTypeID); @@ -22,13 +26,13 @@ export const authenticatePronoteCredentials = async (pronoteStringURL: string, o const pronoteCookies = ["ielang=fr"]; - const { createdSession: session, data: loginInformations } = await callApiLoginInformations({ + const { createdSession: session, data: loginInformations } = await callApiLoginInformations(fetcher, { accountTypeID: accountType.id, pronoteURL: pronoteURL.href, cookies: pronoteCookies }); - const { data: loginIdentifier } = await callApiLoginIdentify({ + const { data: loginIdentifier } = await callApiLoginIdentify(fetcher, { cookies: pronoteCookies, username: options.username, session: session, @@ -91,7 +95,7 @@ export const authenticatePronoteCredentials = async (pronoteStringURL: string, o } // Send the resolved challenge. - const { data: authenticationReply } = await callApiLoginAuthenticate({ + const { data: authenticationReply } = await callApiLoginAuthenticate(fetcher, { solvedChallenge: resolved_challenge, cookies: pronoteCookies, session @@ -109,7 +113,7 @@ export const authenticatePronoteCredentials = async (pronoteStringURL: string, o session.encryption.aes.key = authKey; // Retrieve the user data. - const { data: user } = await callApiUserData({ session }); + const { data: user } = await callApiUserData(fetcher, { session }); const credentials: NextAuthenticationCredentials = { username: loginIdentifier.donnees.login ?? options.username, @@ -117,10 +121,13 @@ export const authenticatePronoteCredentials = async (pronoteStringURL: string, o }; // Return the new Pronote instance. - return new Pronote(session, credentials, user.donnees, loginInformations); + return new Pronote(fetcher, session, credentials, user.donnees, loginInformations); }; export const authenticateToken = async (pronoteStringURL: string, options: AuthenticateTokenOptions): Promise => { + // Use default fetcher if not provided. + const fetcher = options.fetcher ?? defaultPawnoteFetcher; + const pronoteURL = new URL(pronoteStringURL); const accountType = PRONOTE_ACCOUNT_TYPES.find((entry) => entry.id === options.accountTypeID); @@ -133,13 +140,13 @@ export const authenticateToken = async (pronoteStringURL: string, options: Authe const pronoteCookies = ["ielang=fr", "appliMobile=1"]; - const { createdSession: session, data: loginInformations } = await callApiLoginInformations({ + const { createdSession: session, data: loginInformations } = await callApiLoginInformations(fetcher, { accountTypeID: accountType.id, pronoteURL: pronoteURL.href, cookies: pronoteCookies }); - const { data: loginIdentifier } = await callApiLoginIdentify({ + const { data: loginIdentifier } = await callApiLoginIdentify(fetcher, { cookies: pronoteCookies, username: options.username, session: session, @@ -202,7 +209,7 @@ export const authenticateToken = async (pronoteStringURL: string, options: Authe } // Send the resolved challenge. - const { data: authenticationReply } = await callApiLoginAuthenticate({ + const { data: authenticationReply } = await callApiLoginAuthenticate(fetcher, { solvedChallenge: resolved_challenge, cookies: pronoteCookies, session @@ -220,7 +227,7 @@ export const authenticateToken = async (pronoteStringURL: string, options: Authe session.encryption.aes.key = authKey; // Retrieve the user data. - const { data: user } = await callApiUserData({ session }); + const { data: user } = await callApiUserData(fetcher, { session }); const credentials: NextAuthenticationCredentials = { username: loginIdentifier.donnees.login ?? options.username, @@ -228,5 +235,5 @@ export const authenticateToken = async (pronoteStringURL: string, options: Authe }; // Return the new Pronote instance. - return new Pronote(session, credentials, user.donnees, loginInformations); + return new Pronote(fetcher, session, credentials, user.donnees, loginInformations); }; diff --git a/src/authenticate/types.ts b/src/authenticate/types.ts index 45d924b..7fed976 100644 --- a/src/authenticate/types.ts +++ b/src/authenticate/types.ts @@ -1,4 +1,5 @@ import type { PronoteApiAccountId } from "~/constants/accounts"; +import type { PawnoteFetcher } from "~/utils/fetcher"; interface AuthenticateBaseOptions { /** @@ -6,6 +7,13 @@ interface AuthenticateBaseOptions { * that will authenticate to Pronote. */ deviceUUID: string + + /** + * By default, Pawnote is going to use `fetch` (Fetch API). + * If, for some reason, you need to use another method to make an + * HTTP request, you'll have to provide it here. + */ + fetcher?: PawnoteFetcher } export interface AuthenticatePronoteCredentialsOptions extends AuthenticateBaseOptions { diff --git a/src/client/Pronote.ts b/src/client/Pronote.ts index dfee1d4..ab6976c 100644 --- a/src/client/Pronote.ts +++ b/src/client/Pronote.ts @@ -1,5 +1,6 @@ import type { NextAuthenticationCredentials } from "~/authenticate/types"; import type { ApiLoginInformations } from "~/api/login/informations/types"; +import type { PawnoteFetcher } from "~/utils/fetcher"; import { callApiUserHomework, @@ -66,6 +67,7 @@ export default class Pronote { public periods: Array; constructor ( + public fetcher: PawnoteFetcher, private session: Session, credentials: NextAuthenticationCredentials, @@ -142,7 +144,7 @@ export default class Pronote { * @returns */ public async getTimetableForWeek (weekNumber: number): Promise { - const { data: { donnees: data } } = await callApiUserTimetable({ + const { data: { donnees: data } } = await callApiUserTimetable(this.fetcher, { resource: this.user.ressource, session: this.session, weekNumber @@ -163,7 +165,7 @@ export default class Pronote { const fromWeekNumber = translateToPronoteWeekNumber(from, this.startDay); const toWeekNumber = translateToPronoteWeekNumber(to, this.startDay); - const { data: { donnees: data } } = await callApiUserHomework({ + const { data: { donnees: data } } = await callApiUserHomework(this.fetcher, { session: this.session, fromWeekNumber, toWeekNumber @@ -175,7 +177,7 @@ export default class Pronote { } public async getHomeworkForWeek (weekNumber: number): Promise { - const { data: { donnees: data } } = await callApiUserHomework({ + const { data: { donnees: data } } = await callApiUserHomework(this.fetcher, { session: this.session, fromWeekNumber: weekNumber }); @@ -185,7 +187,7 @@ export default class Pronote { } public async patchHomeworkStatus (homeworkID: string, done: boolean): Promise { - await callApiUserHomeworkStatus({ + await callApiUserHomeworkStatus(this.fetcher, { session: this.session, id: homeworkID, status: done @@ -199,7 +201,7 @@ export default class Pronote { * @param period - Period the grades overview will be from. */ public async getGradesOverviewForPeriod (period: Period) { - const { data: { donnees: data } } = await callApiUserGrades({ + const { data: { donnees: data } } = await callApiUserGrades(this.fetcher, { session: this.session, periodID: period.id, periodName: period.name diff --git a/src/index.ts b/src/index.ts index 18f9ec0..f46687a 100644 --- a/src/index.ts +++ b/src/index.ts @@ -12,3 +12,5 @@ import type { ApiGeolocation } from "~/api"; * This type is a single item from the array returned by the function. */ export type PronoteInstance = ApiGeolocation["output"][number]; + +export { type PawnoteFetcher, defaultPawnoteFetcher } from "~/utils/fetcher"; diff --git a/src/parser/period.ts b/src/parser/period.ts index 445ec49..cf62f69 100644 --- a/src/parser/period.ts +++ b/src/parser/period.ts @@ -1,7 +1,7 @@ -import { PronoteApiLoginInformations } from "~/api"; import type Pronote from "~/client/Pronote"; +import type { PronoteApiLoginInformations } from "~/api"; + import { readPronoteApiDate } from "~/pronote/dates"; -import type { StudentGrade } from "./grade"; export class Period { public id: string; diff --git a/src/pronote/requestAPI.ts b/src/pronote/requestAPI.ts index 71f996a..fe1a458 100644 --- a/src/pronote/requestAPI.ts +++ b/src/pronote/requestAPI.ts @@ -1,4 +1,5 @@ import type { SessionInstance, Session } from "~/session"; +import type { PawnoteFetcher } from "~/utils/fetcher"; import { PronoteApiFunctions } from "~/constants/functions"; import { retrieveResponseCookies } from "~/utils/headers"; @@ -14,6 +15,7 @@ export interface PronoteApiFunctionPayload { } export const createPronoteAPICall = async ( + fetcher: PawnoteFetcher, apiFunctionName: PronoteApiFunctions, request: { payload: ReturnType @@ -24,7 +26,7 @@ export const createPronoteAPICall = async ( try { const pronote_url = request.session_instance.pronote_url; const function_url = pronote_url + `/appelfonction/${request.session_instance.account_type_id}/${request.session_instance.session_id}/${request.payload.order}`; - const response = await fetch(function_url, { + const response = await fetcher(function_url, { method: "POST", headers: { "Content-Type": "application/json", diff --git a/src/utils/api.ts b/src/utils/api.ts index c018a34..edafe2e 100644 --- a/src/utils/api.ts +++ b/src/utils/api.ts @@ -1,5 +1,7 @@ +import type { PawnoteFetcher } from "./fetcher"; + type ApiType = { input: any, output: any }; -type ApiHandler = (input: T["input"]) => Promise; +type ApiHandler = (fetcher: PawnoteFetcher, input: T["input"]) => Promise; export const makeApiHandler = (api: ApiHandler): ApiHandler => { - return (input) => api(input); + return (fetcher, input) => api(fetcher, input); }; diff --git a/src/utils/fetcher.ts b/src/utils/fetcher.ts index ae76ab0..444b33b 100644 --- a/src/utils/fetcher.ts +++ b/src/utils/fetcher.ts @@ -1,13 +1,35 @@ /** - * A simple `fetcher` function with built-in - * typings used to fetch external resources. + * A fetcher that looks like the Fetch API + * so every fetcher applied to Pawnote will have the + * same API and should output the same thing. + * + * @example + * import type { PawnoteFetcher } from "pawnote"; + * + * // With the `fetch()` builtin, in TypeScript. + * // This is actually the code for the default fetcher. + * const fetcher: PawnoteFetcher = async (url, options) => { + * const response = await fetch(url, { + * method: options.method, + * headers: options.headers, + * redirect: options.redirect, + * // Setting a body is not allowed on GET requests. + * body: (options.method === "GET") ? void 0 : options.body + * }); + * + * return { + * headers: response.headers, + * text: () => response.text(), + * json: () => response.json() as T + * }; + * }; */ -export type HttpCallFunction = (url: string, options: { +export type PawnoteFetcher = (url: string, options: { method: "GET" | "POST" /** Headers that should be appended to the request. */ headers?: Record | Headers /** Body of the request of type given in the "Content-Type" header. */ - body?: unknown + body?: string /** Whether we should automatically handle the redirections or do it by hand. */ redirect?: "follow" | "manual" }) => Promise<{ @@ -15,3 +37,23 @@ export type HttpCallFunction = (url: string, options: { text: () => Promise json: () => Promise }>; + +/** + * Simple and default fetcher using `fetch` if none was given + * in the authentication function. + */ +export const defaultPawnoteFetcher: PawnoteFetcher = async (url, options) => { + const response = await fetch(url, { + method: options.method, + headers: options.headers, + redirect: options.redirect, + // Setting a body is not allowed on GET requests. + body: (options.method === "GET") ? void 0 : options.body + }); + + return { + headers: response.headers, + text: () => response.text(), + json: () => response.json() as T + }; +}; diff --git a/src/utils/headers.ts b/src/utils/headers.ts index a21487c..764d64a 100644 --- a/src/utils/headers.ts +++ b/src/utils/headers.ts @@ -1,7 +1,7 @@ import cookieParser from "set-cookie-parser"; export const retrieveResponseCookies = (headers: Record | Headers): string[] => { - const setCookieHeader = headers instanceof Headers ? headers.get("set-cookie") : headers["set-cookie"]; + const setCookieHeader = getHeaderFromFetcherResponse(headers, "set-cookie"); if (setCookieHeader === null) return []; const splittedCookies = cookieParser.splitCookiesString(setCookieHeader);