Switch to undici

This commit is contained in:
Samuel Elliott 2023-07-12 23:15:37 +01:00
parent 10a6784fa7
commit d7a64b9807
No known key found for this signature in database
GPG Key ID: 8420C7CDE43DC4D6
32 changed files with 155 additions and 75 deletions

52
package-lock.json generated
View File

@ -26,6 +26,7 @@
"splatnet3-types": "^0.2.20230601143335",
"supports-color": "^8.1.1",
"tslib": "^2.4.1",
"undici": "^5.22.1",
"uuid": "^8.3.2",
"yargs": "^17.6.2"
},
@ -1198,6 +1199,17 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/busboy": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz",
"integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==",
"dependencies": {
"streamsearch": "^1.1.0"
},
"engines": {
"node": ">=10.16.0"
}
},
"node_modules/bytes": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
@ -4069,6 +4081,14 @@
"node": ">= 0.8"
}
},
"node_modules/streamsearch": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz",
"integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==",
"engines": {
"node": ">=10.0.0"
}
},
"node_modules/string-width": {
"version": "4.2.3",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
@ -4400,6 +4420,17 @@
"node": "*"
}
},
"node_modules/undici": {
"version": "5.22.1",
"resolved": "https://registry.npmjs.org/undici/-/undici-5.22.1.tgz",
"integrity": "sha512-Ji2IJhFXZY0x/0tVBXeQwgPlLWw13GVzpsWPQ3rV50IFMMof2I55PZZxtm4P6iNq+L5znYN9nSTAq0ZyE6lSJw==",
"dependencies": {
"busboy": "^1.6.0"
},
"engines": {
"node": ">=14.0"
}
},
"node_modules/universalify": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz",
@ -5553,6 +5584,14 @@
"integrity": "sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==",
"dev": true
},
"busboy": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz",
"integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==",
"requires": {
"streamsearch": "^1.1.0"
}
},
"bytes": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
@ -7757,6 +7796,11 @@
"resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
"integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ=="
},
"streamsearch": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz",
"integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg=="
},
"string-width": {
"version": "4.2.3",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
@ -7994,6 +8038,14 @@
"integrity": "sha512-f9BESNVhzlhEFf2CHMSj40NWOjYPl1YKYbrvIr/hFTDEmLq7SRbWvm7FcdcpCYT95zrOhC7gZSxjdnnTpBcwVw==",
"dev": true
},
"undici": {
"version": "5.22.1",
"resolved": "https://registry.npmjs.org/undici/-/undici-5.22.1.tgz",
"integrity": "sha512-Ji2IJhFXZY0x/0tVBXeQwgPlLWw13GVzpsWPQ3rV50IFMMof2I55PZZxtm4P6iNq+L5znYN9nSTAq0ZyE6lSJw==",
"requires": {
"busboy": "^1.6.0"
}
},
"universalify": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz",

View File

@ -53,6 +53,7 @@
"splatnet3-types": "^0.2.20230601143335",
"supports-color": "^8.1.1",
"tslib": "^2.4.1",
"undici": "^5.22.1",
"uuid": "^8.3.2",
"yargs": "^17.6.2"
},

View File

@ -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>;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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);

View File

@ -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;

View File

@ -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);
}

View File

@ -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, {

View File

@ -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);
}

View File

@ -1,7 +1,7 @@
import { BrowserWindow, dialog, Menu, MenuItem, MessageBoxOptions, nativeImage } from './electron.js';
import path from 'node:path';
import { Buffer } from 'node:buffer';
import fetch from 'node-fetch';
import { fetch } from 'undici';
import { dir } from '../../util/product.js';
import { App, Store } from './index.js';
import { SavedToken } from '../../common/auth/coral.js';

View File

@ -4,7 +4,7 @@ import { constants } from 'node:fs';
import * as fs from 'node:fs/promises';
import { Buffer } from 'node:buffer';
import * as util from 'node:util';
import fetch from 'node-fetch';
import { fetch } from 'undici';
import { Store } from './index.js';
import { createWebServiceWindow } from './windows.js';
import { askUserForUri, showErrorDialog } from './util.js';

View File

@ -1,4 +1,4 @@
import fetch from 'node-fetch';
import { fetch } from 'undici';
import Table from '../util/table.js';
import type { Arguments as ParentArguments } from '../nso.js';
import { getToken } from '../../common/auth/coral.js';
@ -7,6 +7,7 @@ import createDebug from '../../util/debug.js';
import { Argv } from '../../util/yargs.js';
import { initStorage } from '../../util/storage.js';
import { getUserAgent } from '../../util/useragent.js';
import { ErrorResponse } from '../../api/util.js';
const debug = createDebug('cli:nso:znc-proxy-tokens');
@ -164,7 +165,7 @@ export function builder(yargs: Argv<ParentArguments>) {
debug('fetch %s %s, response %d', 'DELETE', '/token', response.status);
if (response.status !== 204) {
throw new Error('Unknown error ' + response.status);
throw await ErrorResponse.fromResponse(response, 'Non-204 status code');
}
console.warn('Deleted access token');

View File

@ -3,7 +3,7 @@ import * as os from 'node:os';
import * as fs from 'node:fs/promises';
import * as path from 'node:path';
import express, { Request, Response } from 'express';
import fetch from 'node-fetch';
import { fetch } from 'undici';
import * as persist from 'node-persist';
import mkdirp from 'mkdirp';
import { BankaraMatchSetting_schedule, CoopRule, CoopSetting_schedule, DetailFestRecordDetailResult, DetailVotingStatusResult, FestMatchSetting_schedule, FestRecordResult, FestState, FestTeam_schedule, FestTeam_votingStatus, FestVoteState, Fest_schedule, FriendListResult, FriendOnlineState, Friend_friendList, GraphQLSuccessResponse, KnownRequestId, LeagueMatchSetting_schedule, RegularMatchSetting_schedule, StageScheduleResult, XMatchSetting_schedule } from 'splatnet3-types/splatnet3';

View File

@ -1,7 +1,7 @@
import * as path from 'node:path';
import * as fs from 'node:fs/promises';
import mkdirp from 'mkdirp';
import fetch from 'node-fetch';
import { fetch } from 'undici';
import { PhotoAlbumResult } from 'splatnet3-types/splatnet3';
import type { Arguments as ParentArguments } from '../splatnet3.js';
import createDebug from '../../util/debug.js';

View File

@ -1,5 +1,5 @@
import process from 'node:process';
import fetch from 'node-fetch';
import { fetch } from 'undici';
import { getPresenceFromUrl } from '../../api/znc-proxy.js';
import { ActiveEvent, CurrentUser, Friend, Game, Presence, PresenceState } from '../../api/coral-types.js';
import type { Arguments as ParentArguments } from '../util.js';

View File

@ -1,4 +1,4 @@
import fetch from 'node-fetch';
import { fetch } from 'undici';
import type { Arguments as ParentArguments } from '../util.js';
import createDebug from '../../util/debug.js';
import { ArgumentsCamelCase, Argv, YargsArguments } from '../../util/yargs.js';

View File

@ -1,4 +1,4 @@
import { Response } from 'node-fetch';
import { Response } from 'undici';
import CoralApi, { CoralApiInterface, CoralAuthData, Result, ZNCA_CLIENT_ID } from '../api/coral.js';
import { Announcements, Friends, Friend, GetActiveEventResult, WebServices, CoralError } from '../api/coral-types.js';
import ZncProxyApi from '../api/znc-proxy.js';

View File

@ -1,4 +1,4 @@
import { Response } from 'node-fetch';
import { Response } from 'undici';
import { ConfigureAnalyticsResult, CurrentFestResult, DetailVotingStatusResult, FriendListResult, Friend_friendList, HomeResult, StageScheduleResult } from 'splatnet3-types/splatnet3';
import createDebug from '../util/debug.js';
import { ZNCA_CLIENT_ID } from '../api/coral.js';

View File

@ -1,5 +1,5 @@
import * as persist from 'node-persist';
import { Response } from 'node-fetch';
import { Response } from 'undici';
import CoralApi, { CoralAuthData, ZNCA_CLIENT_ID } from '../../api/coral.js';
import { CoralError } from '../../api/coral-types.js';
import ZncProxyApi from '../../api/znc-proxy.js';

View File

@ -1,5 +1,5 @@
import * as persist from 'node-persist';
import { Response } from 'node-fetch';
import { Response } from 'undici';
import { MoonAuthData, ZNMA_CLIENT_ID } from '../../api/moon.js';
import { NintendoAccountSessionTokenJwtPayload } from '../../api/na.js';
import createDebug from '../../util/debug.js';

View File

@ -1,5 +1,5 @@
import persist from 'node-persist';
import { Response } from 'node-fetch';
import { Response } from 'undici';
import { getToken, Login } from './coral.js';
import NooklinkApi, { NooklinkAuthData, NooklinkUserApi, NooklinkUserAuthData } from '../../api/nooklink.js';
import { Users, WebServiceError } from '../../api/nooklink-types.js';

View File

@ -1,5 +1,5 @@
import persist from 'node-persist';
import { Response } from 'node-fetch';
import { Response } from 'undici';
import { getToken, Login, SavedToken } from './coral.js';
import SplatNet3Api, { SplatNet3AuthData, SplatNet3AuthErrorCode, SplatNet3AuthErrorResponse } from '../../api/splatnet3.js';
import { checkMembershipActive, checkUseLimit, SHOULD_LIMIT_USE } from './util.js';

View File

@ -46,7 +46,9 @@ class RateLimitAttempt {
readonly storage: persist.LocalStorage,
readonly key: string, readonly user: string,
readonly time = Date.now(),
) {}
) {
Object.defineProperty(this, 'storage', {configurable: true, enumerable: false, value: storage});
}
async recordError(err: Error | unknown) {
const error_description = ErrorDescription.getErrorDescription(err);

View File

@ -1,7 +1,7 @@
import * as path from 'node:path';
import * as fs from 'node:fs/promises';
import { fileURLToPath } from 'node:url';
import fetch from 'node-fetch';
import { fetch } from 'undici';
import mkdirp from 'mkdirp';
import { ErrorResponse, ResponseSymbol } from '../api/util.js';
import createDebug from '../util/debug.js';
@ -124,7 +124,7 @@ async function loadRemoteConfig() {
version,
revision: git?.revision ?? null,
url: response.url,
headers: response.headers.raw(),
headers: Object.fromEntries(response.headers.entries()),
data: config,
};
@ -241,7 +241,7 @@ export interface RemoteConfigCacheData {
version: string;
revision: string | null;
url: string;
headers: Record<string, string[]>;
headers: Record<string, string> | Record<string, string[]>;
data: NxapiRemoteConfig;
}

View File

@ -2,7 +2,7 @@ import * as path from 'node:path';
import * as fs from 'node:fs/promises';
import * as crypto from 'node:crypto';
import { Buffer } from 'node:buffer';
import fetch from 'node-fetch';
import { fetch } from 'undici';
import SplatNet2Api, { ShareColour } from '../../api/splatnet2.js';
import { Challenge, NicknameAndIcon, Records, Stages } from '../../api/splatnet2-types.js';
import createDebug from '../../util/debug.js';

View File

@ -1,7 +1,7 @@
import * as path from 'node:path';
import * as fs from 'node:fs/promises';
import { Buffer } from 'node:buffer';
import fetch from 'node-fetch';
import { fetch } from 'undici';
import SplatNet2Api from '../../api/splatnet2.js';
import { NicknameAndIcon } from '../../api/splatnet2-types.js';
import createDebug from '../../util/debug.js';

View File

@ -1,6 +1,6 @@
import * as path from 'node:path';
import * as fs from 'node:fs/promises';
import fetch from 'node-fetch';
import { fetch } from 'undici';
import mkdirp from 'mkdirp';
import createDebug from '../util/debug.js';
import { dir, docker, version } from '../util/product.js';

View File

@ -1,7 +1,7 @@
import process from 'node:process';
import * as net from 'node:net';
import { EventEmitter } from 'node:events';
import fetch from 'node-fetch';
import { fetch } from 'undici';
import DiscordRPC from 'discord-rpc';
// @ts-expect-error
import __BaseIpcTransport from 'discord-rpc/src/transports/ipc.js';

View File

@ -1,5 +1,6 @@
import * as util from 'node:util';
import { AbortError } from 'node-fetch';
import { errors } from 'undici';
import createDebug from './debug.js';
import Loop, { LoopResult } from './loop.js';
import { TemporaryErrorSymbol } from './misc.js';
@ -75,6 +76,22 @@ export async function handleError(
} else if (err instanceof AbortError) {
debug('Request aborted (timeout?), waiting %ds before retrying', loop.update_interval, err);
return LoopResult.OK;
} else if (err instanceof errors.ConnectTimeoutError) {
debug('Request timeout (connect), waiting %ds before retrying', loop.update_interval, err);
return LoopResult.OK;
} else if (err instanceof errors.HeadersTimeoutError) {
debug('Request timeout (headers), waiting %ds before retrying', loop.update_interval, err);
return LoopResult.OK;
} else if (err instanceof errors.BodyTimeoutError) {
debug('Request timeout (body), waiting %ds before retrying', loop.update_interval, err);
return LoopResult.OK;
} else if (err instanceof errors.RequestAbortedError) {
debug('Request aborted, waiting %ds before retrying', loop.update_interval, err);
return LoopResult.OK;
} else if ('code' in err && (err as any).type === 'system' && err.code && err.code in temporary_system_errors) {
const desc = temporary_system_errors[err.code as keyof typeof temporary_system_errors];

View File

@ -1,7 +1,7 @@
import * as crypto from 'node:crypto';
import { Buffer } from 'node:buffer';
import persist from 'node-persist';
import fetch from 'node-fetch';
import { fetch } from 'undici';
import createDebug from './debug.js';
import { timeoutSignal } from './misc.js';