halo-infinite-api 4.1.0 → 5.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/authentication/halo-authentication-client.d.ts +2 -1
- package/dist/authentication/halo-authentication-client.js +8 -5
- package/dist/authentication/halo-authentication-client.js.map +1 -1
- package/dist/authentication/xbox-authentication-client.d.ts +5 -4
- package/dist/authentication/xbox-authentication-client.js +43 -37
- package/dist/authentication/xbox-authentication-client.js.map +1 -1
- package/dist/core/halo-infinite-client.d.ts +4 -2
- package/dist/core/halo-infinite-client.js +18 -25
- package/dist/core/halo-infinite-client.js.map +1 -1
- package/dist/core/token-providers/auto-token-provider.d.ts +13 -0
- package/dist/core/token-providers/auto-token-provider.js +34 -0
- package/dist/core/token-providers/auto-token-provider.js.map +1 -0
- package/dist/core/token-providers/spartan-token-providers/auto-xsts-spartan-token-provider.d.ts +11 -0
- package/dist/core/token-providers/spartan-token-providers/auto-xsts-spartan-token-provider.js +29 -0
- package/dist/core/token-providers/spartan-token-providers/auto-xsts-spartan-token-provider.js.map +1 -0
- package/dist/core/token-providers/spartan-token-providers/index.d.ts +3 -0
- package/dist/core/token-providers/spartan-token-providers/index.js +2 -0
- package/dist/core/token-providers/spartan-token-providers/index.js.map +1 -0
- package/dist/core/token-providers/spartan-token-providers/static-xsts-ticket-token-spartan-token-provider.d.ts +12 -0
- package/dist/core/token-providers/spartan-token-providers/static-xsts-ticket-token-spartan-token-provider.js +25 -0
- package/dist/core/token-providers/spartan-token-providers/static-xsts-ticket-token-spartan-token-provider.js.map +1 -0
- package/dist/core/token-providers/xbox-token-provider.d.ts +3 -0
- package/dist/core/token-providers/xbox-token-provider.js +2 -0
- package/dist/core/token-providers/xbox-token-provider.js.map +1 -0
- package/dist/core/xbox-client.d.ts +14 -0
- package/dist/core/xbox-client.js +28 -0
- package/dist/core/xbox-client.js.map +1 -0
- package/dist/index.d.ts +4 -3
- package/dist/index.js +3 -2
- package/dist/index.js.map +1 -1
- package/dist/models/halo-infinite/game-variant-category.d.ts +1 -0
- package/dist/models/halo-infinite/game-variant-category.js +1 -0
- package/dist/models/halo-infinite/game-variant-category.js.map +1 -1
- package/dist/models/halo-infinite/stats.d.ts +44 -5
- package/dist/util/keyed-expiry-token-cache.d.ts +13 -0
- package/dist/util/keyed-expiry-token-cache.js +74 -0
- package/dist/util/keyed-expiry-token-cache.js.map +1 -0
- package/package.json +1 -2
- package/src/authentication/halo-authentication-client.ts +8 -7
- package/src/authentication/xbox-authentication-client.ts +52 -46
- package/src/core/halo-infinite-client.ts +39 -35
- package/src/core/{spartan-token-providers/auto-xsts-spartan-token-provider.ts → token-providers/auto-token-provider.ts} +15 -3
- package/src/core/{spartan-token-providers → token-providers/spartan-token-providers}/static-xsts-ticket-token-spartan-token-provider.ts +3 -3
- package/src/core/token-providers/xbox-token-provider.ts +3 -0
- package/src/core/xbox-client.ts +53 -0
- package/src/index.ts +7 -3
- package/src/models/halo-infinite/game-variant-category.ts +1 -0
- package/src/models/halo-infinite/stats.ts +49 -5
- package/src/util/keyed-expiry-token-cache.ts +90 -0
- /package/src/core/{spartan-token-providers → token-providers/spartan-token-providers}/index.ts +0 -0
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { XboxTokenProvider } from "./token-providers/xbox-token-provider";
|
|
2
|
+
|
|
3
|
+
export class XboxClient {
|
|
4
|
+
private readonly fetchFn: typeof fetch;
|
|
5
|
+
constructor(
|
|
6
|
+
private xboxTokenProvider: XboxTokenProvider,
|
|
7
|
+
fetchFn?: typeof fetch
|
|
8
|
+
) {
|
|
9
|
+
this.fetchFn = fetchFn ?? fetch;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
private async executeRequest<T>(url: string, method: RequestInit["method"]) {
|
|
13
|
+
const response = await this.fetchFn(url, {
|
|
14
|
+
method,
|
|
15
|
+
headers: {
|
|
16
|
+
Accept: "application/json",
|
|
17
|
+
Authorization: await this.xboxTokenProvider.getXboxLiveV3Token(),
|
|
18
|
+
"x-xbl-contract-version": "1",
|
|
19
|
+
},
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
const result = (await response.json()) as T;
|
|
23
|
+
|
|
24
|
+
return result;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
public async searchUsers(query: string) {
|
|
28
|
+
const { results } = await this.executeRequest<{
|
|
29
|
+
results: [
|
|
30
|
+
{
|
|
31
|
+
result: {
|
|
32
|
+
id: string;
|
|
33
|
+
gamertag: string;
|
|
34
|
+
displayPicUri: string;
|
|
35
|
+
score: 0.0;
|
|
36
|
+
};
|
|
37
|
+
text: string;
|
|
38
|
+
}
|
|
39
|
+
];
|
|
40
|
+
}>(
|
|
41
|
+
`https://usersearch.xboxlive.com/suggest?q=${encodeURIComponent(query)}`,
|
|
42
|
+
"GET"
|
|
43
|
+
);
|
|
44
|
+
return results.map(({ result }) => result);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
public async recentPlayers() {
|
|
48
|
+
return await this.executeRequest<unknown>(
|
|
49
|
+
"https://peoplehub.xboxlive.com/users/me/people/recentplayers",
|
|
50
|
+
"GET"
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -2,6 +2,7 @@ export {
|
|
|
2
2
|
HaloInfiniteClient,
|
|
3
3
|
AssetKindTypeMap,
|
|
4
4
|
} from "./core/halo-infinite-client";
|
|
5
|
+
export { XboxClient } from "./core/xbox-client";
|
|
5
6
|
export {
|
|
6
7
|
XboxAuthenticationClient,
|
|
7
8
|
RelyingParty,
|
|
@@ -21,8 +22,11 @@ export { MatchOutcome } from "./models/halo-infinite/match-outcome";
|
|
|
21
22
|
export { MatchSkill } from "./models/halo-infinite/match-skill";
|
|
22
23
|
export { AssetVersionLink } from "./models/halo-infinite/asset-version-link";
|
|
23
24
|
export { MatchInfo } from "./models/halo-infinite/match-info";
|
|
24
|
-
export { SpartanTokenProvider } from "./core/spartan-token-providers";
|
|
25
|
-
export {
|
|
26
|
-
|
|
25
|
+
export { SpartanTokenProvider } from "./core/token-providers/spartan-token-providers";
|
|
26
|
+
export {
|
|
27
|
+
AutoTokenProvider,
|
|
28
|
+
AutoTokenProvider as AutoXstsSpartanTokenProvider,
|
|
29
|
+
} from "./core/token-providers/auto-token-provider";
|
|
30
|
+
export { StaticXstsTicketTokenSpartanTokenProvider } from "./core/token-providers/spartan-token-providers/static-xsts-ticket-token-spartan-token-provider";
|
|
27
31
|
export { TokenPersister } from "./core/token-persisters";
|
|
28
32
|
export { PlaylistExperience } from "./models/halo-infinite/playlist-experience";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { GameVariantCategory } from "./game-variant-category";
|
|
2
2
|
|
|
3
|
-
interface OddballStats {
|
|
3
|
+
export interface OddballStats {
|
|
4
4
|
KillsAsSkullCarrier: number;
|
|
5
5
|
LongestTimeAsSkullCarrier: string;
|
|
6
6
|
SkullCarriersKilled: number;
|
|
@@ -8,7 +8,8 @@ interface OddballStats {
|
|
|
8
8
|
TimeAsSkullCarrier: string;
|
|
9
9
|
SkullScoringTicks: number;
|
|
10
10
|
}
|
|
11
|
-
|
|
11
|
+
|
|
12
|
+
export interface ZonesStats {
|
|
12
13
|
StrongholdCaptures: number;
|
|
13
14
|
StrongholdDefensiveKills: number;
|
|
14
15
|
StrongholdOffensiveKills: number;
|
|
@@ -16,7 +17,8 @@ interface ZonesStats {
|
|
|
16
17
|
StrongholdOccupationTime: string;
|
|
17
18
|
StrongholdScoringTicks: number;
|
|
18
19
|
}
|
|
19
|
-
|
|
20
|
+
|
|
21
|
+
export interface CaptureTheFlagStats {
|
|
20
22
|
FlagCaptureAssists: number;
|
|
21
23
|
FlagCaptures: number;
|
|
22
24
|
FlagCarriersKilled: number;
|
|
@@ -30,7 +32,7 @@ interface CaptureTheFlagStats {
|
|
|
30
32
|
TimeAsFlagCarrier: string;
|
|
31
33
|
}
|
|
32
34
|
|
|
33
|
-
interface ExtractionStats {
|
|
35
|
+
export interface ExtractionStats {
|
|
34
36
|
SuccessfulExtractions: number;
|
|
35
37
|
ExtractionConversionsDenied: number;
|
|
36
38
|
ExtractionConversionsCompleted: number;
|
|
@@ -38,6 +40,42 @@ interface ExtractionStats {
|
|
|
38
40
|
ExtractionInitiationsCompleted: number;
|
|
39
41
|
}
|
|
40
42
|
|
|
43
|
+
export interface EliminationStats {
|
|
44
|
+
AlliesRevived: number;
|
|
45
|
+
EliminationAssists: number;
|
|
46
|
+
Eliminations: number;
|
|
47
|
+
EnemyRevivesDenied: number;
|
|
48
|
+
Executions: number;
|
|
49
|
+
KillsAsLastPlayerStanding: number;
|
|
50
|
+
LastPlayersStandingKilled: number;
|
|
51
|
+
RoundsSurvived: number;
|
|
52
|
+
TimesRevivedByAlly: number;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export interface InfectionStats {
|
|
56
|
+
AlphasKilled: number;
|
|
57
|
+
SpartansInfected: number;
|
|
58
|
+
SpartansInfectedAsAlpha: number;
|
|
59
|
+
KillsAsLastSpartanStanding: number;
|
|
60
|
+
LastSpartansStandingInfected: number;
|
|
61
|
+
RoundsAsAlpha: number;
|
|
62
|
+
RoundsAsLastSpartanStanding: number;
|
|
63
|
+
RoundsFinishedAsInfected: number;
|
|
64
|
+
RoundsSurvivedAsSpartan: number;
|
|
65
|
+
RoundsSurvivedAsLastSpartanStanding: number;
|
|
66
|
+
TimeAsLastSpartanStanding: string;
|
|
67
|
+
InfectedKilled: number;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export interface StockpileStats {
|
|
71
|
+
KillsAsPowerSeedCarrier: number;
|
|
72
|
+
PowerSeedCarriersKilled: number;
|
|
73
|
+
PowerSeedsDeposited: number;
|
|
74
|
+
PowerSeedsStolen: number;
|
|
75
|
+
TimeAsPowerSeedCarrier: string;
|
|
76
|
+
TimeAsPowerSeedDriver: string;
|
|
77
|
+
}
|
|
78
|
+
|
|
41
79
|
type StatsMap = {
|
|
42
80
|
[GameVariantCategory.MultiplayerOddball]: { OddballStats: OddballStats };
|
|
43
81
|
[GameVariantCategory.MultiplayerStrongholds]: { ZonesStats: ZonesStats };
|
|
@@ -49,7 +87,13 @@ type StatsMap = {
|
|
|
49
87
|
ExtractionStats: ExtractionStats;
|
|
50
88
|
};
|
|
51
89
|
[GameVariantCategory.MultiplayerFirefight]: {
|
|
52
|
-
|
|
90
|
+
EliminationStats: EliminationStats;
|
|
91
|
+
};
|
|
92
|
+
[GameVariantCategory.MultiplayerInfection]: {
|
|
93
|
+
InfectionSTats: InfectionStats;
|
|
94
|
+
};
|
|
95
|
+
[GameVariantCategory.MultiplayerStockpile]: {
|
|
96
|
+
StockpileStats: StockpileStats;
|
|
53
97
|
};
|
|
54
98
|
};
|
|
55
99
|
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { DateTime } from "luxon";
|
|
2
|
+
import { ResolvablePromise } from "./resolvable-promise";
|
|
3
|
+
import { coalesceDateTime } from "./date-time";
|
|
4
|
+
|
|
5
|
+
export class KeyedExpiryTokenCache<
|
|
6
|
+
TToken extends { expiresAt: DateTime },
|
|
7
|
+
TKey extends string,
|
|
8
|
+
TArgs extends any[]
|
|
9
|
+
> {
|
|
10
|
+
private readonly tokenFetchPromiseMap = new Map<
|
|
11
|
+
TKey,
|
|
12
|
+
ResolvablePromise<TToken>
|
|
13
|
+
>();
|
|
14
|
+
|
|
15
|
+
constructor(
|
|
16
|
+
private readonly generateNewToken: (
|
|
17
|
+
key: TKey,
|
|
18
|
+
...args: TArgs
|
|
19
|
+
) => Promise<TToken>,
|
|
20
|
+
private readonly existingTokenFetcher: (
|
|
21
|
+
key: TKey
|
|
22
|
+
) => Promise<(Omit<TToken, "expiresAt"> & { expiresAt: unknown }) | null>
|
|
23
|
+
) {}
|
|
24
|
+
|
|
25
|
+
async getToken(key: TKey, ...args: TArgs): Promise<TToken> {
|
|
26
|
+
let tokenFetchPromise = this.tokenFetchPromiseMap.get(key);
|
|
27
|
+
if (tokenFetchPromise) {
|
|
28
|
+
// Someone either already has a token or is in the process of getting one
|
|
29
|
+
// Wait for them to finish, then check for validity
|
|
30
|
+
const currentToken = await tokenFetchPromise;
|
|
31
|
+
|
|
32
|
+
if (currentToken.expiresAt > DateTime.now()) {
|
|
33
|
+
// Current token is valid, return it
|
|
34
|
+
return currentToken;
|
|
35
|
+
} else {
|
|
36
|
+
// Current token expired, start a new promise
|
|
37
|
+
tokenFetchPromise = new ResolvablePromise<TToken>();
|
|
38
|
+
this.tokenFetchPromiseMap.set(key, tokenFetchPromise);
|
|
39
|
+
|
|
40
|
+
try {
|
|
41
|
+
const newToken = await this.generateNewToken(key, ...args);
|
|
42
|
+
tokenFetchPromise.resolve(newToken);
|
|
43
|
+
return newToken;
|
|
44
|
+
} catch (e) {
|
|
45
|
+
tokenFetchPromise.reject(e);
|
|
46
|
+
tokenFetchPromise = undefined;
|
|
47
|
+
throw e;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
} else {
|
|
51
|
+
// No one has a token, start a new promise
|
|
52
|
+
tokenFetchPromise = new ResolvablePromise<TToken>();
|
|
53
|
+
this.tokenFetchPromiseMap.set(key, tokenFetchPromise);
|
|
54
|
+
|
|
55
|
+
try {
|
|
56
|
+
const existingToken = await this.getExistingToken(key);
|
|
57
|
+
|
|
58
|
+
if (existingToken?.expiresAt) {
|
|
59
|
+
const expiresAt = coalesceDateTime(existingToken.expiresAt);
|
|
60
|
+
if (expiresAt && expiresAt > DateTime.now()) {
|
|
61
|
+
const newToken = { ...existingToken, expiresAt } as TToken;
|
|
62
|
+
tokenFetchPromise.resolve(newToken);
|
|
63
|
+
return newToken;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const newToken = await this.generateNewToken(key, ...args);
|
|
68
|
+
tokenFetchPromise.resolve(newToken);
|
|
69
|
+
return newToken;
|
|
70
|
+
} catch (e) {
|
|
71
|
+
tokenFetchPromise.reject(e);
|
|
72
|
+
tokenFetchPromise = undefined;
|
|
73
|
+
throw e;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
async getExistingToken(key: TKey) {
|
|
79
|
+
const existingToken = await this.existingTokenFetcher(key);
|
|
80
|
+
|
|
81
|
+
if (existingToken?.expiresAt) {
|
|
82
|
+
const expiresAt = coalesceDateTime(existingToken.expiresAt);
|
|
83
|
+
if (expiresAt && expiresAt > DateTime.now()) {
|
|
84
|
+
return { ...existingToken, expiresAt } as TToken;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
90
|
+
}
|
/package/src/core/{spartan-token-providers → token-providers/spartan-token-providers}/index.ts
RENAMED
|
File without changes
|