Update types

This commit is contained in:
Samuel Elliott 2025-07-22 02:10:00 +01:00
parent 8cac7935f2
commit e55b8dc965
No known key found for this signature in database
GPG Key ID: 8420C7CDE43DC4D6
20 changed files with 84 additions and 55 deletions

View File

@ -165,7 +165,7 @@ export default class MoonApi {
const nintendoAccountToken = await getNintendoAccountToken(token, ZNMA_CLIENT_ID);
// Nintendo Account user data
const user = await getNintendoAccountUser(nintendoAccountToken);
const user = await getNintendoAccountUser<never>(nintendoAccountToken);
return {
nintendoAccountToken,

View File

@ -1,8 +1,7 @@
import { fetch, FormData, Response } from 'undici';
import { WebServiceToken } from './coral-types.js';
import { NintendoAccountUser } from './na.js';
import { defineResponse, ErrorResponse, HasResponse } from './util.js';
import { CoralApiInterface } from './coral.js';
import { CoralApiInterface, NintendoAccountUserCoral } from './coral.js';
import { WebServiceError, Users, AuthToken, UserProfile, Newspapers, Newspaper, Emoticons, Reaction, IslandProfile } from './nooklink-types.js';
import createDebug from '../util/debug.js';
import { timeoutSignal } from '../util/misc.js';
@ -107,13 +106,13 @@ export default class NooklinkApi {
return NooklinkUserApi._createWithNooklinkApi(this, user_id);
}
async renewTokenWithCoral(coral: CoralApiInterface, user: NintendoAccountUser) {
async renewTokenWithCoral(coral: CoralApiInterface, user: NintendoAccountUserCoral) {
const data = await NooklinkApi.loginWithCoral(coral, user);
this.setTokenWithSavedToken(data);
return data;
}
async renewTokenWithWebServiceToken(webserviceToken: WebServiceToken, user: NintendoAccountUser) {
async renewTokenWithWebServiceToken(webserviceToken: WebServiceToken, user: NintendoAccountUserCoral) {
const data = await NooklinkApi.loginWithWebServiceToken(webserviceToken, user);
this.setTokenWithSavedToken(data);
return data;
@ -124,7 +123,7 @@ export default class NooklinkApi {
this._token_expired = false;
}
static async createWithCoral(coral: CoralApiInterface, user: NintendoAccountUser) {
static async createWithCoral(coral: CoralApiInterface, user: NintendoAccountUserCoral) {
const data = await this.loginWithCoral(coral, user);
return {nooklink: this.createWithSavedToken(data), data};
}
@ -133,7 +132,7 @@ export default class NooklinkApi {
return new this(data.gtoken, data.useragent);
}
static async loginWithCoral(coral: CoralApiInterface, user: NintendoAccountUser) {
static async loginWithCoral(coral: CoralApiInterface, user: NintendoAccountUserCoral) {
const { default: { coral_gws_nooklink: config } } = await import('../common/remote-config.js');
if (!config) throw new Error('Remote configuration prevents NookLink authentication');
@ -143,7 +142,7 @@ export default class NooklinkApi {
}
static async loginWithWebServiceToken(
webserviceToken: WebServiceToken, user: NintendoAccountUser
webserviceToken: WebServiceToken, user: NintendoAccountUserCoral
): Promise<NooklinkAuthData> {
const { default: { coral_gws_nooklink: config } } = await import('../common/remote-config.js');
if (!config) throw new Error('Remote configuration prevents NookLink authentication');

View File

@ -1,9 +1,8 @@
import { randomUUID } from 'node:crypto';
import { Cookie, fetch, FormData, getSetCookies } from 'undici';
import { WebServiceToken } from './coral-types.js';
import { NintendoAccountUser } from './na.js';
import { defineResponse, ErrorResponse } from './util.js';
import { CoralApiInterface } from './coral.js';
import { CoralApiInterface, NintendoAccountUserCoral } from './coral.js';
import { ActiveFestivals, CoopResult, CoopResults, CoopSchedules, HeroRecords, LeagueMatchRankings, NicknameAndIcons, PastFestivals, Records, Result, Results, Schedules, ShareResponse, ShopMerchandises, Stages, Timeline, WebServiceError, XPowerRankingRecords, XPowerRankingSummary } from './splatnet2-types.js';
import createDebug from '../util/debug.js';
import { timeoutSignal } from '../util/misc.js';
@ -240,7 +239,7 @@ ${colour}
});
}
static async createWithCoral(coral: CoralApiInterface, user: NintendoAccountUser) {
static async createWithCoral(coral: CoralApiInterface, user: NintendoAccountUserCoral) {
const data = await this.loginWithCoral(coral, user);
return {splatnet: this.createWithSavedToken(data), data};
}
@ -269,14 +268,14 @@ ${colour}
);
}
static async loginWithCoral(coral: CoralApiInterface, user: NintendoAccountUser) {
static async loginWithCoral(coral: CoralApiInterface, user: NintendoAccountUserCoral) {
const webserviceToken = await coral.getWebServiceToken(SPLATNET2_WEBSERVICE_ID);
return this.loginWithWebServiceToken(webserviceToken, user);
}
static async loginWithWebServiceToken(
webserviceToken: WebServiceToken, user: NintendoAccountUser
webserviceToken: WebServiceToken, user: NintendoAccountUserCoral
): Promise<SplatNet2AuthData> {
const url = new URL(SPLATNET2_WEBSERVICE_URL);
url.search = new URLSearchParams({

View File

@ -1,7 +1,7 @@
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';
import { CoralApiInterface, NintendoAccountUserCoral } from './coral.js';
import { NintendoAccountUser } from './na.js';
import { BulletToken } from './splatnet3-types.js';
import { defineResponse, ErrorResponse, HasResponse, ResponseSymbol } from './util.js';
@ -938,13 +938,13 @@ export default class SplatNet3Api {
//
//
async renewTokenWithCoral(coral: CoralApiInterface, user: NintendoAccountUser) {
async renewTokenWithCoral(coral: CoralApiInterface, user: NintendoAccountUserCoral) {
const data = await SplatNet3Api.loginWithCoral(coral, user);
this.setTokenWithSavedToken(data);
return data;
}
async renewTokenWithWebServiceToken(webserviceToken: WebServiceToken, user: NintendoAccountUser) {
async renewTokenWithWebServiceToken(webserviceToken: WebServiceToken, user: NintendoAccountUserCoral) {
const data = await SplatNet3Api.loginWithWebServiceToken(webserviceToken, user);
this.setTokenWithSavedToken(data);
return data;
@ -958,7 +958,7 @@ export default class SplatNet3Api {
this._token_expired = false;
}
static async createWithCoral(coral: CoralApiInterface, user: NintendoAccountUser) {
static async createWithCoral(coral: CoralApiInterface, user: NintendoAccountUserCoral) {
const data = await this.loginWithCoral(coral, user);
return {splatnet: this.createWithSavedToken(data), data};
}
@ -987,7 +987,7 @@ export default class SplatNet3Api {
);
}
static async loginWithCoral(coral: CoralApiInterface, user: NintendoAccountUser) {
static async loginWithCoral(coral: CoralApiInterface, user: NintendoAccountUserCoral) {
const { default: { coral_gws_splatnet3: config } } = await import('../common/remote-config.js');
if (!config) throw new Error('Remote configuration prevents SplatNet 3 authentication');
@ -997,7 +997,7 @@ export default class SplatNet3Api {
}
static async loginWithWebServiceToken(
webserviceToken: WebServiceToken, user: NintendoAccountUser
webserviceToken: WebServiceToken, user: NintendoAccountUserCoral
): Promise<SplatNet3AuthData> {
const { default: { coral_gws_splatnet3: config } } = await import('../common/remote-config.js');
if (!config) throw new Error('Remote configuration prevents SplatNet 3 authentication');

View File

@ -1,5 +1,5 @@
import { fetch, Response } from 'undici';
import { ActiveEvent, Announcements, CurrentUser, Event, Friend, Presence, PresencePermissions, User, WebService, WebServiceToken, CoralStatus, CoralSuccessResponse, FriendCodeUser, FriendCodeUrl, WebService_4, Media, Announcements_4, Friend_4 } from './coral-types.js';
import { ActiveEvent, CurrentUser, Event, Friend, Presence, PresencePermissions, User, WebServiceToken, CoralStatus, CoralSuccessResponse, FriendCodeUser, FriendCodeUrl, WebService_4, Media, Announcements_4, Friend_4 } from './coral-types.js';
import { defineResponse, ErrorResponse, ResponseSymbol } from './util.js';
import { AbstractCoralApi, CoralApiInterface, CoralAuthData, CorrelationIdSymbol, PartialCoralAuthData, RequestFlagAddPlatformSymbol, RequestFlagAddProductVersionSymbol, RequestFlagNoParameterSymbol, RequestFlagRequestIdSymbol, RequestFlags, ResponseDataSymbol, Result } from './coral.js';
import { NintendoAccountToken, NintendoAccountUser } from './na.js';

View File

@ -53,7 +53,7 @@ export default function Friends(props: {
function Friend(props: {
friend: Friend;
user?: User;
user?: User<true>;
}) {
const theme = useColourScheme() === 'light' ? light : dark;

View File

@ -243,9 +243,15 @@ export function useColourScheme() {
return React.useContext(ColourSchemeContext);
}
export interface User {
user: NintendoAccountUserCoral | NintendoAccountUserMoon;
nso: SavedToken | null;
export interface User<IsCoral extends boolean = boolean> {
user:
IsCoral extends true ? NintendoAccountUserCoral :
IsCoral extends false ? NintendoAccountUserMoon :
NintendoAccountUserCoral | NintendoAccountUserMoon;
nso:
IsCoral extends true ? SavedToken :
IsCoral extends true ? null :
SavedToken | null;
nsotoken: string | undefined;
moon: SavedMoonToken | null;
moontoken: string | undefined;

View File

@ -13,6 +13,8 @@ import type { NintendoAccountUser } from '../../api/na.js';
import type { DiscordSetupProps } from '../browser/discord/index.js';
import type { FriendProps } from '../browser/friend/index.js';
import type { AddFriendProps } from '../browser/add-friend/index.js';
import { NintendoAccountUserCoral } from '../../api/coral.js';
import { NintendoAccountUserMoon } from '../../api/moon.js';
// In sandboxed renderers the process object contains a very limited set of APIs
// https://www.electronjs.org/docs/latest/api/process#sandbox
@ -93,10 +95,10 @@ const ipc = {
openExternalUrl: (url: string) => inv('misc:open-url', url),
share: (item: SharingItem) => inv('misc:share', item),
showUserMenu: (user: NintendoAccountUser, nso?: CurrentUser, moon?: boolean) => inv('menu:user', user, nso, moon),
showUserMenu: (user: NintendoAccountUserCoral | NintendoAccountUserMoon, nso?: CurrentUser, moon?: boolean) => inv('menu:user', user, nso, moon),
showAddUserMenu: () => inv('menu:add-user'),
showFriendCodeMenu: (fc: CurrentUser['links']['friendCode']) => inv('menu:friend-code', fc),
showFriendMenu: (user: NintendoAccountUser, nso: CurrentUser, friend: Friend) => inv('menu:friend', user, nso, friend),
showFriendMenu: (user: NintendoAccountUserCoral, nso: CurrentUser, friend: Friend) => inv('menu:friend', user, nso, friend),
registerEventListener: (event: string, listener: (args: any[]) => void) => events.on(event, listener),
removeEventListener: (event: string, listener: (args: any[]) => void) => events.removeListener(event, listener),

View File

@ -55,20 +55,21 @@ export async function handler(argv: ArgumentsCamelCase<Arguments>) {
const table = new Table({
head: [
'ID',
'Type',
'Title',
'Priority',
'Contents',
'Date',
'Display end date',
],
});
for (const announcement of announcements) {
table.push([
announcement.announcementId,
announcement.id,
announcement.type,
announcement.title.substr(0, 60),
announcement.priority,
new Date(announcement.distributionDate * 1000).toISOString(),
new Date(announcement.forceDisplayEndDate * 1000).toISOString(),
'operation' in announcement ? announcement.operation.contents.substr(0, 40) + '...' :
'friendRequest' in announcement ? 'NSA ID: ' + announcement.friendRequest.nsaId : '',
new Date(announcement.deliversAt * 1000).toISOString(),
]);
}

View File

@ -7,7 +7,7 @@ import express, { Request, RequestHandler, Response } from 'express';
import bodyParser from 'body-parser';
import type { Arguments as ParentArguments } from './index.js';
import CoralApi, { CoralApiInterface, CoralErrorResponse, RequestFlagAddPlatformSymbol, RequestFlagAddProductVersionSymbol, RequestFlagNoParameterSymbol, RequestFlagRequestId, RequestFlagRequestIdSymbol, RequestFlags } from '../../api/coral.js';
import { Announcement, Announcement_4, CoralStatus, CurrentUser, Friend, FriendCodeUrl, FriendCodeUser, Presence } from '../../api/coral-types.js';
import { Announcement_4, CoralStatus, CurrentUser, Friend, FriendCodeUrl, FriendCodeUser, Presence } from '../../api/coral-types.js';
import ZncProxyApi, { AuthPolicy, AuthToken, ZncPresenceEventStreamEvent } from '../../api/znc-proxy.js';
import createDebug from '../../util/debug.js';
import { ArgumentsCamelCase, Argv, YargsArguments } from '../../util/yargs.js';

View File

@ -68,6 +68,10 @@ export function builder(yargs: Argv<ParentArguments>) {
describe: 'Restrict allowed actions',
type: 'boolean',
default: true,
}).option('policy-api-call', {
describe: 'Allow access to /call - this allows full access to the Coral API',
type: 'boolean',
default: false,
}).option('policy-announcements', {
describe: 'Allow access to /announcements',
type: 'boolean',
@ -124,6 +128,8 @@ export function builder(yargs: Argv<ParentArguments>) {
const {nso, data} = await getToken(storage, token, argv.zncProxyUrl);
const policy: AuthPolicy | null = argv.policy ? {
api: argv.policyApiCall,
announcements: argv.policyAnnouncements,
list_friends: argv.policyListFriends,
list_friends_presence: argv.policyListFriendsPresence,

View File

@ -51,7 +51,7 @@ export function builder(yargs: Argv<ParentArguments>) {
table.push([
user.id + (selected === user.id ? ' *' : ''),
user.screenName,
user.screenName ?? 'Unknown',
user.nickname,
user.country,
nsoCache?.nsoAccount.user.nsaId ?? 'Not signed in',

View File

@ -1,6 +1,7 @@
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 { NintendoAccountScope } from '../api/na.js';
import ZncProxyApi from '../api/znc-proxy.js';
import { NintendoAccountSession, Storage } from './storage/index.js';
import { checkUseLimit } from './util.js';
@ -12,6 +13,13 @@ import Users from './users.js';
const debug = createDebug('nxapi:client:coral');
type NintendoAccountScopeCoral = NintendoAccountScope.USER | NintendoAccountScope.USER_BIRTHDAY | NintendoAccountScope.USER_SCREENNAME;
export type NintendoAccountOIDCCoral =
NintendoAccountOIDC<NintendoAccountScopeCoral> |
// Nintendo Account session token obtained before 3.0.1
NintendoAccountOIDC<NintendoAccountScope.USER | NintendoAccountScope.USER_BIRTHDAY | NintendoAccountScope.USER_MII | NintendoAccountScope.USER_SCREENNAME>;
export interface SavedToken extends CoralAuthData {
expires_at: number;
proxy_url?: string;
@ -163,12 +171,12 @@ export default class Coral {
return this.createWithProxy(session, proxy_url);
}
const oidc = await NintendoAccountOIDC.createWithSession(session, false);
const oidc = await NintendoAccountOIDC.createWithSession<NintendoAccountScopeCoral>(session, false);
return this.createWithSession(session, oidc);
}
static async createWithSession(session: NintendoAccountSession<SavedToken>, oidc: NintendoAccountOIDC) {
static async createWithSession(session: NintendoAccountSession<SavedToken>, oidc: NintendoAccountOIDCCoral) {
const cached_auth_data = await session.getAuthenticationData();
const [coral, auth_data, skip_fetch] = cached_auth_data && cached_auth_data.expires_at > Date.now() ?
@ -179,7 +187,7 @@ export default class Coral {
}
private static async createWithSessionAuthenticate(
session: NintendoAccountSession<SavedToken>, oidc: NintendoAccountOIDC, ratelimit = true
session: NintendoAccountSession<SavedToken>, oidc: NintendoAccountOIDCCoral, ratelimit = true
) {
await checkUseLimit(session, 'coral', ratelimit);
@ -261,7 +269,7 @@ export default class Coral {
}
// const oidc = await users.get(NintendoAccountOIDC, id, false);
const oidc = await NintendoAccountOIDC.createWithSession(session, false);
const oidc = await NintendoAccountOIDC.createWithSession<NintendoAccountScopeCoral>(session, false);
return Coral.createWithSession(session, oidc);
}

View File

@ -1,6 +1,6 @@
import createDebug from '../util/debug.js';
import { NintendoAccountSession } from './storage/index.js';
import { getNintendoAccountToken, getNintendoAccountUser, NintendoAccountToken, NintendoAccountUser } from '../api/na.js';
import { getNintendoAccountToken, getNintendoAccountUser, NintendoAccountScope, NintendoAccountToken, NintendoAccountUser } from '../api/na.js';
const debug = createDebug('nxapi:client:na');
@ -10,7 +10,7 @@ export interface SavedToken {
expires_at: number;
}
export default class NintendoAccountOIDC {
export default class NintendoAccountOIDC<S extends NintendoAccountScope = never> {
created_at = Date.now();
expires_at = Infinity;
@ -22,7 +22,7 @@ export default class NintendoAccountOIDC {
};
update_interval = 10 * 1000; // 10 seconds
user: NintendoAccountUser | null = null;
user: NintendoAccountUser<S> | null = null;
onUpdateSavedToken: ((data: SavedToken) => Promise<void>) | null = null;
@ -73,17 +73,17 @@ export default class NintendoAccountOIDC {
async getUser() {
await this.update('user', async () => {
const token = await this.getToken();
this.user = await getNintendoAccountUser(token);
this.user = await getNintendoAccountUser<S>(token);
}, this.update_interval);
return this.user!;
}
static async createWithSession(session: NintendoAccountSession<unknown>, renew_token = true) {
static async createWithSession<S extends NintendoAccountScope>(session: NintendoAccountSession<unknown>, renew_token = true) {
const cached_auth_data = await session.getNintendoAccountToken();
if (cached_auth_data && (cached_auth_data.expires_at > Date.now() || !renew_token)) {
const client = new NintendoAccountOIDC(session.token, session.client_id, cached_auth_data);
const client = new NintendoAccountOIDC<S>(session.token, session.client_id, cached_auth_data);
client.onUpdateSavedToken = data => session.setNintendoAccountToken(data);
return client;
}
@ -98,7 +98,7 @@ export default class NintendoAccountOIDC {
await session.setNintendoAccountToken(auth_data);
const client = new NintendoAccountOIDC(session.token, session.client_id, auth_data);
const client = new NintendoAccountOIDC<S>(session.token, session.client_id, auth_data);
client.onUpdateSavedToken = data => session.setNintendoAccountToken(data);
return client;
}

View File

@ -3,7 +3,7 @@ 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';
import { getNintendoAccountUser, NintendoAccountSessionTokenJwtPayload } from '../../api/na.js';
import { getNintendoAccountUser, NintendoAccountScope, NintendoAccountSessionTokenJwtPayload } from '../../api/na.js';
import createDebug from '../../util/debug.js';
import { Jwt } from '../../util/jwt.js';
import { checkUseLimit, SHOULD_LIMIT_USE } from './util.js';
@ -115,7 +115,7 @@ async function createWithSessionToken(
storage: persist.LocalStorage, na_session_token: string, ratelimit = true
) {
const na_token = await getNaToken(storage, na_session_token, ZNCA_CLIENT_ID, ratelimit);
const user = await getNintendoAccountUser(na_token.token);
const user = await getNintendoAccountUser<NintendoAccountScope.USER_BIRTHDAY | NintendoAccountScope.USER_MII | NintendoAccountScope.USER_SCREENNAME>(na_token.token);
debug('Authenticating to coral');

View File

@ -1,6 +1,6 @@
import persist from 'node-persist';
import { CoralApiInterface } from '../api/coral.js';
import { ActiveEvent, Announcements, CurrentUser, Friend, Presence, PresenceState, WebServices, CoralError, GetActiveEventResult, FriendRouteChannel, PresenceGame } from '../api/coral-types.js';
import { ActiveEvent, CurrentUser, Friend, Presence, PresenceState, CoralError, GetActiveEventResult, FriendRouteChannel, PresenceGame, Announcements_4, Friend_4, WebServices_4, PresenceOnline_4, PresenceOffline } from '../api/coral-types.js';
import ZncProxyApi from '../api/znc-proxy.js';
import { ErrorResponse } from '../api/util.js';
import { SavedToken } from './auth/coral.js';
@ -37,9 +37,9 @@ export class ZncNotifications extends Loop {
'announcements' | 'friends' | {friend: string; presence?: boolean} | 'webservices' | 'event' | 'user' | null
)[]) {
const result: Partial<{
announcements: Announcements;
friends: Friend[];
webservices: WebServices;
announcements: Announcements_4;
friends: Friend_4[];
webservices: WebServices_4;
activeevent: ActiveEvent;
user: CurrentUser;
}> = {};
@ -67,7 +67,7 @@ export class ZncNotifications extends Loop {
const nso = this.nso as unknown as ZncProxyApi;
if (r.presence) {
const friend: Friend = {
const friend: Friend_4 = {
id: 0,
nsaId: r.friend,
imageUri: '',
@ -85,13 +85,14 @@ export class ZncNotifications extends Loop {
imageUri: '',
channel: FriendRouteChannel.FRIEND_CODE,
},
presence: await nso.fetchProxyApi<Presence>('/friend/' + r.friend + '/presence'),
isOnlineNotificationEnabled: false,
presence: await nso.fetchProxyApi<PresenceOnline_4 | PresenceOffline>('/friend/' + r.friend + '/presence'),
};
return friend;
}
return (await nso.fetchProxyApi<{friend: Friend}>('/friend/' + r.friend)).friend;
return (await nso.fetchProxyApi<{friend: Friend_4}>('/friend/' + r.friend)).friend;
}));
}
if (req.includes('webservices')) {

View File

@ -4,7 +4,7 @@ import { Response } from 'undici';
import createDebug from '../util/debug.js';
import CoralApi, { CoralApiInterface, NintendoAccountUserCoral, Result } from '../api/coral.js';
import ZncProxyApi from '../api/znc-proxy.js';
import { Announcements, Friends, Friend, GetActiveEventResult, CoralSuccessResponse, WebService, WebServices, CoralError, Announcements_4, Friends_4, WebServices_4, ListMedia, ListChat, WebService_4, CurrentUser } from '../api/coral-types.js';
import { Friend, GetActiveEventResult, CoralSuccessResponse, WebService, CoralError, Announcements_4, Friends_4, WebServices_4, ListMedia, ListChat, WebService_4, CurrentUser } from '../api/coral-types.js';
import { getToken, SavedToken } from './auth/coral.js';
import type { Store } from '../app/main/index.js';

View File

@ -10,6 +10,7 @@ export {
CoralErrorResponse,
NintendoAccountUserCoral,
NintendoAccountSessionAuthorisationCoral,
} from '../api/coral.js';
@ -38,5 +39,6 @@ export {
} from '../api/f.js';
export {
/** @internal Testing */
default as Coral,
} from '../client/coral.js';

View File

@ -5,12 +5,16 @@ export { addUserAgent, addUserAgentFromPackageJson } from '../util/useragent.js'
export { version, product } from '../util/product.js';
export {
/** @internal Testing */
default as Users,
} from '../client/users.js';
export {
/** @internal Testing */
Storage,
/** @internal Testing */
StorageProvider,
} from '../client/storage/index.js';
export {
/** @internal Testing */
LocalStorageProvider,
} from '../client/storage/local.js';

View File

@ -5,6 +5,7 @@ export {
MoonErrorResponse,
NintendoAccountUserMoon,
NintendoAccountSessionAuthorisationMoon,
} from '../api/moon.js';