mirror of
https://github.com/samuelthomas2774/nxapi.git
synced 2026-10-26 03:21:50 -05:00
Switch to undici
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import fetch, { Response } from 'node-fetch';
|
||||
import { fetch, Response } from 'undici';
|
||||
import { v4 as uuidgen } from 'uuid';
|
||||
import createDebug from '../util/debug.js';
|
||||
import { JwtPayload } from '../util/jwt.js';
|
||||
@@ -145,7 +145,7 @@ export default class CoralApi implements CoralApiInterface {
|
||||
debug('fetch %s %s, response %s', method, url, response.status);
|
||||
|
||||
if (response.status !== 200) {
|
||||
throw new CoralErrorResponse('[znc] Non-200 status code', response, await response.text());
|
||||
throw await CoralErrorResponse.fromResponse(response, '[znc] Non-200 status code');
|
||||
}
|
||||
|
||||
const data = await response.json() as CoralResponse<T>;
|
||||
@@ -449,7 +449,7 @@ export default class CoralApi implements CoralApiInterface {
|
||||
debug('fetch %s %s, response %s', 'POST', '/v3/Account/Login', response.status);
|
||||
|
||||
if (response.status !== 200) {
|
||||
throw new CoralErrorResponse('[znc] Non-200 status code', response, await response.text());
|
||||
throw await CoralErrorResponse.fromResponse(response, '[znc] Non-200 status code');
|
||||
}
|
||||
|
||||
const data = await response.json() as CoralResponse<AccountLogin>;
|
||||
|
||||
10
src/api/f.ts
10
src/api/f.ts
@@ -1,5 +1,5 @@
|
||||
import process from 'node:process';
|
||||
import fetch, { Headers } from 'node-fetch';
|
||||
import { fetch, Headers } from 'undici';
|
||||
import { v4 as uuidgen } from 'uuid';
|
||||
import { defineResponse, ErrorResponse } from './util.js';
|
||||
import createDebug from '../util/debug.js';
|
||||
@@ -61,7 +61,7 @@ export async function flapg(
|
||||
}).finally(cancel);
|
||||
|
||||
if (response.status !== 200) {
|
||||
throw new ErrorResponse<FlapgApiError>('[flapg] Non-200 status code', response, await response.text());
|
||||
throw await ErrorResponse.fromResponse(response, '[flapg] Non-200 status code');
|
||||
}
|
||||
|
||||
const data = await response.json() as FlapgApiResponse;
|
||||
@@ -142,13 +142,13 @@ export async function iminkf(
|
||||
}).finally(cancel);
|
||||
|
||||
if (response.status !== 200) {
|
||||
throw new ErrorResponse<IminkFError>('[imink] Non-200 status code', response, await response.text());
|
||||
throw await ErrorResponse.fromResponse(response, '[imink] Non-200 status code');
|
||||
}
|
||||
|
||||
const data = await response.json() as IminkFResponse | IminkFError;
|
||||
|
||||
if ('error' in data) {
|
||||
throw new ErrorResponse<IminkFError>('[imink] ' + data.reason, response, data);
|
||||
throw new ErrorResponse('[imink] ' + data.reason, response, data);
|
||||
}
|
||||
|
||||
debugImink('Got f parameter "%s"', data.f);
|
||||
@@ -229,7 +229,7 @@ export async function genf(
|
||||
}).finally(cancel);
|
||||
|
||||
if (response.status !== 200) {
|
||||
throw new ErrorResponse<AndroidZncaFError>('[znca-api] Non-200 status code', response, await response.text());
|
||||
throw await ErrorResponse.fromResponse(response, '[znca-api] Non-200 status code');
|
||||
}
|
||||
|
||||
const data = await response.json() as AndroidZncaFResponse | AndroidZncaFError;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import fetch, { Response } from 'node-fetch';
|
||||
import { fetch, Response } from 'undici';
|
||||
import { generateAuthData, getNintendoAccountToken, getNintendoAccountUser, NintendoAccountSessionAuthorisation, NintendoAccountToken, NintendoAccountUser } from './na.js';
|
||||
import { defineResponse, ErrorResponse, HasResponse } from './util.js';
|
||||
import { DailySummaries, Devices, MonthlySummaries, MonthlySummary, MoonError, ParentalControlSettingState, SmartDevices, User } from './moon-types.js';
|
||||
@@ -86,7 +86,7 @@ export default class MoonApi {
|
||||
}
|
||||
|
||||
if (response.status !== 200) {
|
||||
throw new MoonErrorResponse('[moon] Non-200 status code', response, await response.text());
|
||||
throw await MoonErrorResponse.fromResponse(response, '[moon] Non-200 status code');
|
||||
}
|
||||
|
||||
const data = await response.json() as T | MoonError;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import * as crypto from 'node:crypto';
|
||||
import fetch, { Response } from 'node-fetch';
|
||||
import { fetch, Response } from 'undici';
|
||||
import { defineResponse, ErrorResponse, HasResponse } from './util.js';
|
||||
import createDebug from '../util/debug.js';
|
||||
import { JwtPayload } from '../util/jwt.js';
|
||||
@@ -127,7 +127,7 @@ export async function getNintendoAccountSessionToken(code: string, verifier: str
|
||||
}).finally(cancel);
|
||||
|
||||
if (response.status !== 200) {
|
||||
throw new NintendoAccountAuthErrorResponse('[na] Non-200 status code', response, await response.text());
|
||||
throw await NintendoAccountAuthErrorResponse.fromResponse(response, '[na] Non-200 status code');
|
||||
}
|
||||
|
||||
const token = await response.json() as NintendoAccountSessionToken | NintendoAccountAuthError | NintendoAccountError;
|
||||
@@ -164,7 +164,7 @@ export async function getNintendoAccountToken(token: string, client_id: string)
|
||||
}).finally(cancel);
|
||||
|
||||
if (response.status !== 200) {
|
||||
throw new NintendoAccountAuthErrorResponse('[na] Non-200 status code', response, await response.text());
|
||||
throw await NintendoAccountAuthErrorResponse.fromResponse(response, '[na] Non-200 status code');
|
||||
}
|
||||
|
||||
const nintendoAccountToken = await response.json() as NintendoAccountToken | NintendoAccountAuthError | NintendoAccountError;
|
||||
@@ -198,7 +198,7 @@ export async function getNintendoAccountUser(token: NintendoAccountToken) {
|
||||
}).finally(cancel);
|
||||
|
||||
if (response.status !== 200) {
|
||||
throw new NintendoAccountErrorResponse('[na] Non-200 status code', response, await response.text());
|
||||
throw await NintendoAccountErrorResponse.fromResponse(response, '[na] Non-200 status code');
|
||||
}
|
||||
|
||||
const user = await response.json() as NintendoAccountUser | NintendoAccountError;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import fetch, { Response } from 'node-fetch';
|
||||
import { fetch, FormData, Response } from 'undici';
|
||||
import { WebServiceToken } from './coral-types.js';
|
||||
import { NintendoAccountUser } from './na.js';
|
||||
import { defineResponse, ErrorResponse, HasResponse } from './util.js';
|
||||
@@ -81,7 +81,7 @@ export default class NooklinkApi {
|
||||
}
|
||||
|
||||
if (!response.ok) {
|
||||
throw new NooklinkErrorResponse('[nooklink] Non-2xx status code', response, await response.text());
|
||||
throw await NooklinkErrorResponse.fromResponse(response, '[nooklink] Non-2xx status code');
|
||||
}
|
||||
|
||||
const data = await response.json() as T | WebServiceError;
|
||||
@@ -172,12 +172,12 @@ export default class NooklinkApi {
|
||||
|
||||
debug('fetch %s %s, response %s', 'GET', url, response.status);
|
||||
|
||||
const body = await response.text();
|
||||
|
||||
if (response.status !== 200) {
|
||||
throw new NooklinkErrorResponse('[nooklink] Non-200 status code', response, body);
|
||||
throw await NooklinkErrorResponse.fromResponse(response, '[nooklink] Non-200 status code');
|
||||
}
|
||||
|
||||
const body = await response.text();
|
||||
|
||||
const cookies = response.headers.get('Set-Cookie');
|
||||
const match = cookies?.match(/\b_gtoken=([^;]*)(;(\s*((?!expires)[a-z]+=([^;]*));?)*(\s*(expires=([^;]*));?)?|$)/i);
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import fetch from 'node-fetch';
|
||||
import { Cookie, fetch, FormData, getSetCookies } from 'undici';
|
||||
import { v4 as uuidgen } from 'uuid';
|
||||
import { WebServiceToken } from './coral-types.js';
|
||||
import { NintendoAccountUser } from './na.js';
|
||||
@@ -63,7 +63,7 @@ export default class SplatNet2Api {
|
||||
}
|
||||
|
||||
if (response.status !== 200) {
|
||||
throw new SplatNet2ErrorResponse('[splatnet2] Non-200 status code', response, await response.text());
|
||||
throw await SplatNet2ErrorResponse.fromResponse(response, '[splatnet2] Non-200 status code');
|
||||
}
|
||||
|
||||
updateIksmSessionLastUsed.handler?.call(null, this.iksm_session);
|
||||
@@ -302,27 +302,22 @@ ${colour}
|
||||
|
||||
debug('fetch %s %s, response %s', 'GET', url, response.status);
|
||||
|
||||
const body = await response.text();
|
||||
|
||||
if (response.status !== 200) {
|
||||
throw new SplatNet2ErrorResponse('[splatnet2] Non-200 status code', response, body);
|
||||
throw await SplatNet2ErrorResponse.fromResponse(response, '[splatnet2] Non-200 status code');
|
||||
}
|
||||
|
||||
const cookies = response.headers.get('Set-Cookie');
|
||||
const match = cookies?.match(/\biksm_session=([^;]*)(;(\s*((?!expires)[a-z]+=([^;]*));?)*(\s*(expires=([^;]*));?)?|$)/i);
|
||||
const body = await response.text();
|
||||
|
||||
if (!match) {
|
||||
const cookies = getSetCookies(response.headers);
|
||||
const iksm_session = cookies.find(c => c.name === 'iksm_session');
|
||||
|
||||
if (!iksm_session) {
|
||||
throw new SplatNet2ErrorResponse('[splatnet2] Response didn\'t include iksm_session cookie', response, body);
|
||||
}
|
||||
|
||||
const iksm_session = decodeURIComponent(match[1]);
|
||||
// Nintendo sets the expires field to an invalid timestamp - browsers don't care but Data.parse does
|
||||
const expires = decodeURIComponent(match[8] || '')
|
||||
.replace(/(\b)(\d{1,2})-([a-z]{3})-(\d{4})(\b)/gi, '$1$2 $3 $4$5');
|
||||
const expires_at: number = (iksm_session.expires as Date)?.getTime() ?? Date.now() + 24 * 60 * 60 * 1000;
|
||||
|
||||
debug('iksm_session %s, expires %s', iksm_session.replace(/^(.{6}).*/, '$1****'), expires);
|
||||
|
||||
const expires_at = expires ? Date.parse(expires) : Date.now() + 24 * 60 * 60 * 1000;
|
||||
debug('iksm_session %s, expires %s', iksm_session.value.replace(/^(.{6}).*/, '$1****'), iksm_session.expires);
|
||||
|
||||
const ml = body.match(/<html(?:\s+[a-z0-9-]+(?:=(?:"[^"]*"|[^\s>]*))?)*\s+lang=(?:"([^"]*)"|([^\s>]*))/i);
|
||||
const mr = body.match(/<html(?:\s+[a-z0-9-]+(?:=(?:"[^"]*"|[^\s>]*))?)*\s+data-region=(?:"([^"]*)"|([^\s>]*))/i);
|
||||
@@ -345,14 +340,14 @@ ${colour}
|
||||
return {
|
||||
webserviceToken,
|
||||
url: url.toString(),
|
||||
cookies: cookies!,
|
||||
cookies,
|
||||
body,
|
||||
language,
|
||||
region,
|
||||
user_id,
|
||||
nsa_id,
|
||||
|
||||
iksm_session,
|
||||
iksm_session: iksm_session.value,
|
||||
expires_at,
|
||||
useragent: SPLATNET2_WEBSERVICE_USERAGENT,
|
||||
};
|
||||
@@ -364,7 +359,7 @@ export class SplatNet2ErrorResponse extends ErrorResponse<WebServiceError> {}
|
||||
export interface SplatNet2AuthData {
|
||||
webserviceToken: WebServiceToken;
|
||||
url: string;
|
||||
cookies: string;
|
||||
cookies: string | Cookie[];
|
||||
body: string;
|
||||
|
||||
language: string;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import fetch, { Response } from 'node-fetch';
|
||||
import { fetch, Response } from 'undici';
|
||||
import { BankaraBattleHistoriesRefetchResult, BankaraBattleHistoriesRefetchVariables, GraphQLError, GraphQLErrorResponse, GraphQLRequest, GraphQLResponse, GraphQLSuccessResponse, KnownRequestId, LatestBattleHistoriesRefetchResult, LatestBattleHistoriesRefetchVariables, MyOutfitInput, PagerUpdateBattleHistoriesByVsModeResult, PagerUpdateBattleHistoriesByVsModeVariables, PrivateBattleHistoriesRefetchResult, PrivateBattleHistoriesRefetchVariables, RegularBattleHistoriesRefetchResult, RegularBattleHistoriesRefetchVariables, RequestId, ResultTypes, VariablesTypes, XBattleHistoriesRefetchResult, XBattleHistoriesRefetchVariables } from 'splatnet3-types/splatnet3';
|
||||
import { WebServiceToken } from './coral-types.js';
|
||||
import { CoralApiInterface } from './coral.js';
|
||||
@@ -102,7 +102,7 @@ export default class SplatNet3Api {
|
||||
) {}
|
||||
|
||||
async fetch<T = unknown>(
|
||||
url: string, method = 'GET', body?: string | FormData, headers?: object,
|
||||
url: string, method = 'GET', body?: string, headers?: object,
|
||||
/** @internal */ _log?: string,
|
||||
/** @internal */ _attempt = 0,
|
||||
): Promise<HasResponse<T, Response>> {
|
||||
@@ -153,7 +153,7 @@ export default class SplatNet3Api {
|
||||
}
|
||||
|
||||
if (response.status !== 200) {
|
||||
throw new SplatNet3ErrorResponse('[splatnet3] Non-200 status code', response, await response.text());
|
||||
throw await SplatNet3ErrorResponse.fromResponse(response, '[splatnet3] Non-200 status code');
|
||||
}
|
||||
|
||||
const remaining = parseInt(response.headers.get('x-bullettoken-remaining') ?? '0');
|
||||
@@ -1029,12 +1029,12 @@ export default class SplatNet3Api {
|
||||
|
||||
debug('fetch %s %s, response %s', 'GET', url, response.status);
|
||||
|
||||
const body = await response.text();
|
||||
|
||||
if (response.status !== 200) {
|
||||
throw new SplatNet3ErrorResponse('[splatnet3] Non-200 status code', response, body);
|
||||
throw await SplatNet3ErrorResponse.fromResponse(response, '[splatnet3] Non-200 status code');
|
||||
}
|
||||
|
||||
const body = await response.text();
|
||||
|
||||
const cookies = response.headers.get('Set-Cookie');
|
||||
|
||||
const [signal2, cancel2] = timeoutSignal();
|
||||
@@ -1058,8 +1058,8 @@ export default class SplatNet3Api {
|
||||
debug('fetch %s %s, response %s', 'POST', '/bullet_tokens', response.status);
|
||||
|
||||
const error: SplatNet3AuthErrorCode | undefined = AUTH_ERROR_CODES[tr.status as keyof typeof AUTH_ERROR_CODES];
|
||||
if (error) throw new SplatNet3AuthErrorResponse('[splatnet3] ' + error, tr, await tr.text(), error);
|
||||
if (tr.status !== 201) throw new SplatNet3ErrorResponse('[splatnet3] Non-201 status code', tr, await tr.text());
|
||||
if (error) throw await SplatNet3AuthErrorResponse.fromResponse(tr, '[splatnet3] ' + error);
|
||||
if (tr.status !== 201) throw await SplatNet3ErrorResponse.fromResponse(tr, '[splatnet3] Non-201 status code');
|
||||
|
||||
const bullet_token = await tr.json() as BulletToken;
|
||||
const created_at = Date.now();
|
||||
@@ -1099,7 +1099,8 @@ export class SplatNet3AuthErrorResponse extends SplatNet3ErrorResponse {
|
||||
constructor(
|
||||
message: string, response: Response | globalThis.Response,
|
||||
body?: string | unknown | undefined,
|
||||
readonly code = SplatNet3AuthErrorCode.ERROR_SERVER,
|
||||
readonly code = AUTH_ERROR_CODES[response.status as keyof typeof AUTH_ERROR_CODES] ??
|
||||
SplatNet3AuthErrorCode.ERROR_SERVER,
|
||||
) {
|
||||
super(message, response, body);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import * as util from 'node:util';
|
||||
import { Response as NodeFetchResponse } from 'node-fetch';
|
||||
import { Response as UndiciResponse } from 'undici';
|
||||
|
||||
export const ResponseSymbol = Symbol('Response');
|
||||
const ErrorResponseSymbol = Symbol('IsErrorResponse');
|
||||
@@ -20,13 +21,17 @@ export class ErrorResponse<T = unknown> extends Error {
|
||||
|
||||
constructor(
|
||||
message: string,
|
||||
readonly response: Response | NodeFetchResponse,
|
||||
body?: string | T
|
||||
readonly response: Response | NodeFetchResponse | UndiciResponse,
|
||||
body?: string | ArrayBuffer | T
|
||||
) {
|
||||
super(message);
|
||||
|
||||
Object.defineProperty(this, ErrorResponseSymbol, {enumerable: false, value: ErrorResponseSymbol});
|
||||
|
||||
if (body instanceof ArrayBuffer) {
|
||||
body = (new TextDecoder()).decode(body);
|
||||
}
|
||||
|
||||
if (typeof body === 'string') {
|
||||
this.body = body;
|
||||
try {
|
||||
@@ -50,6 +55,12 @@ export class ErrorResponse<T = unknown> extends Error {
|
||||
(lines.length ? '\n' + lines.join('\n') : ''),
|
||||
});
|
||||
}
|
||||
|
||||
static async fromResponse(response: UndiciResponse, message: string) {
|
||||
const body = await response.arrayBuffer();
|
||||
|
||||
return new this(message, response, body);
|
||||
}
|
||||
}
|
||||
|
||||
Object.defineProperty(ErrorResponse, Symbol.hasInstance, {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import fetch, { Response } from 'node-fetch';
|
||||
import { fetch, Response } from 'undici';
|
||||
import { ActiveEvent, Announcements, CurrentUser, Event, Friend, Presence, PresencePermissions, User, WebService, WebServiceToken, CoralStatus, CoralSuccessResponse, FriendCodeUser, FriendCodeUrl } from './coral-types.js';
|
||||
import { defineResponse, ErrorResponse, ResponseSymbol } from './util.js';
|
||||
import { CoralApiInterface, CoralAuthData, CorrelationIdSymbol, PartialCoralAuthData, ResponseDataSymbol, Result } from './coral.js';
|
||||
@@ -34,7 +34,7 @@ export default class ZncProxyApi implements CoralApiInterface {
|
||||
debug('fetch %s %s, response %s', method, url, response.status);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new ZncProxyErrorResponse('[zncproxy] Non-2xx status code', response, await response.text());
|
||||
throw await ZncProxyErrorResponse.fromResponse(response, '[zncproxy] Non-2xx status code');
|
||||
}
|
||||
|
||||
const data = (response.status === 204 ? {} : await response.json()) as T;
|
||||
@@ -229,11 +229,11 @@ export async function getPresenceFromUrl(presence_url: string, useragent?: strin
|
||||
debug('fetch %s %s, response %s', 'GET', presence_url, response.status);
|
||||
|
||||
if (response.status !== 200) {
|
||||
throw new ZncProxyErrorResponse('[zncproxy] Unknown error', response, await response.text());
|
||||
throw await ZncProxyErrorResponse.fromResponse(response, '[zncproxy] Non-200 status code');
|
||||
}
|
||||
|
||||
if (!response.headers.get('Content-Type')?.match(/^application\/json(;|$)$/)) {
|
||||
controller.abort();
|
||||
response.body?.cancel();
|
||||
throw new ZncProxyErrorResponse('[zncproxy] Unacceptable content type', response);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user