import { Axios } from "axios" const axios = new Axios({ transformResponse(str: string) { return JSON.parse(str) } }) type Fields2Encode = { [key: string]: true } & { fields?: Fields2Encode } function encodeFields(fields: Fields2Encode): string { const res = Object.entries(fields).map(([key, item]) => { if (item === true) return key const { fields, ...other } = item const encodedParams = Object.entries(other).map(([i, e]) => { return `.${i}(${e})` }).join('') let r = key + encodedParams if (fields) r += `{${encodeFields(fields)}}` return r }) // return Object.entries(fields).map(([i, e]) => `${i}${e !== true ? encodeFields(e.fields) : ''}`).join(',') return res.join(',') } function encodeParams({ fields, ...params }: Fields2Encode) { // let newFields = encodeFields(fields) return { ...params, fields: fields ? encodeFields(fields) : undefined } } function magicPaging(input: any) { for (const key in input) { if (Object.prototype.hasOwnProperty.call(input, key)) { // const { paging, data } = input[key] if (input[key]['data']) { magicPaging(input[key]['data']) } if (input[key]?.paging?.next) { input[key]['paging']['nextFn'] = () => makeRequest("get", {}, input[key]['paging']['next']['next']).then(res => { return new Promise(resolve => { // input[key][data].push(...res.data) resolve(res) }) }) } } } } export async function makeRequest(method: 'get' | 'post' | 'delete' | 'update', params: any, path: string) { const result = await axios.request({ url: `https://graph.facebook.com/v16.0/${path}`, method, params: encodeParams(params) // @ts-ignore }).catch(reason => { throw Object.assign(reason.response.data.error, { usage: JSON.parse(reason.response.headers['x-app-usage']) }) }) magicPaging(result.data) // console.log(result.headers) const usage = JSON.parse(result.headers['x-business-use-case-usage']) as businessUseCaseUsage const r = { ...result.data, usage } // Object.defineProperty(r, 'usage', { value: usage, enumerable: false }) return r } interface businessUseCaseUsage { [key: string | number]: Array<{ type: string, call_count: number, total_cputime: number, total_time: number, estimated_time_to_regain_access: number }> } export type datetime = string export type int = number export type float = number export type bool = boolean export type map = Record export type list = Array export type uint = number export interface defaultParamsNodes { limit?: number filtering?: { field: string, operator: "EQUAL" | "IN", value: string }[] // alias?: string summary?: ReadonlyArray<"total_count" | string> } export interface rootParams { readonly metadata?: true readonly pretty?: boolean readonly debug?: "all" readonly access_token: string } export type rootParamsAnd = rootParams & T // Input type Params = rootParamsAnd type FormatValue = T extends Connection ? FormatParams4Input : T extends Primitive ? true : never // @ts-expect-error export type FormatParams4Input> = Omit & defaultParamsNodes & { fields?: { [key in keyof T['fields']]?: FormatValue } } export type Primitive = T export type Field = T export type Connection = T interface BaseConnection { fields?: { [key: string]: any } summary?: ReadonlyArray<"total_count" | string> } export interface paging { next?: string, cursors: { before: string, after: string } getNext?: () => {} } // Output type formatConnection | undefined> = key extends "picture" ? { data: T } : { data: T[], paging: paging } & (summary extends ReadonlyArray ? { summary: { [key in summary[number]]: any } } : {}) // @ts-expect-error type useKeysFromLeft = { [key in keyof left]: left[key] extends Params ? formatConnection, key, left[key]['summary']> : right[key] } // @ts-expect-error type formatReturn, ogParams extends Params, left = inParams['fields'], right = ogParams['fields']> = useKeysFromLeft export class BaseQuery { static async get, ogParams extends Params, returnType = formatReturn>(params: inParams, path: string): Promise { return await makeRequest('get', params, path) } static async delete, ogParams extends Params, returnType = formatReturn>(params: inParams, path: string): Promise { return await makeRequest('delete', params, path) } static async update, ogParams extends Params, returnType = formatReturn>(params: inParams, path: string): Promise { return await makeRequest('update', params, path) } static async post, ogParams extends Params, returnType = formatReturn>(params: inParams, path: string): Promise { return await makeRequest('post', params, path) } id: string constructor(id: string) { this.id = id } }