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.
Files changed (50) hide show
  1. package/dist/authentication/halo-authentication-client.d.ts +2 -1
  2. package/dist/authentication/halo-authentication-client.js +8 -5
  3. package/dist/authentication/halo-authentication-client.js.map +1 -1
  4. package/dist/authentication/xbox-authentication-client.d.ts +5 -4
  5. package/dist/authentication/xbox-authentication-client.js +43 -37
  6. package/dist/authentication/xbox-authentication-client.js.map +1 -1
  7. package/dist/core/halo-infinite-client.d.ts +4 -2
  8. package/dist/core/halo-infinite-client.js +18 -25
  9. package/dist/core/halo-infinite-client.js.map +1 -1
  10. package/dist/core/token-providers/auto-token-provider.d.ts +13 -0
  11. package/dist/core/token-providers/auto-token-provider.js +34 -0
  12. package/dist/core/token-providers/auto-token-provider.js.map +1 -0
  13. package/dist/core/token-providers/spartan-token-providers/auto-xsts-spartan-token-provider.d.ts +11 -0
  14. package/dist/core/token-providers/spartan-token-providers/auto-xsts-spartan-token-provider.js +29 -0
  15. package/dist/core/token-providers/spartan-token-providers/auto-xsts-spartan-token-provider.js.map +1 -0
  16. package/dist/core/token-providers/spartan-token-providers/index.d.ts +3 -0
  17. package/dist/core/token-providers/spartan-token-providers/index.js +2 -0
  18. package/dist/core/token-providers/spartan-token-providers/index.js.map +1 -0
  19. package/dist/core/token-providers/spartan-token-providers/static-xsts-ticket-token-spartan-token-provider.d.ts +12 -0
  20. package/dist/core/token-providers/spartan-token-providers/static-xsts-ticket-token-spartan-token-provider.js +25 -0
  21. package/dist/core/token-providers/spartan-token-providers/static-xsts-ticket-token-spartan-token-provider.js.map +1 -0
  22. package/dist/core/token-providers/xbox-token-provider.d.ts +3 -0
  23. package/dist/core/token-providers/xbox-token-provider.js +2 -0
  24. package/dist/core/token-providers/xbox-token-provider.js.map +1 -0
  25. package/dist/core/xbox-client.d.ts +14 -0
  26. package/dist/core/xbox-client.js +28 -0
  27. package/dist/core/xbox-client.js.map +1 -0
  28. package/dist/index.d.ts +4 -3
  29. package/dist/index.js +3 -2
  30. package/dist/index.js.map +1 -1
  31. package/dist/models/halo-infinite/game-variant-category.d.ts +1 -0
  32. package/dist/models/halo-infinite/game-variant-category.js +1 -0
  33. package/dist/models/halo-infinite/game-variant-category.js.map +1 -1
  34. package/dist/models/halo-infinite/stats.d.ts +44 -5
  35. package/dist/util/keyed-expiry-token-cache.d.ts +13 -0
  36. package/dist/util/keyed-expiry-token-cache.js +74 -0
  37. package/dist/util/keyed-expiry-token-cache.js.map +1 -0
  38. package/package.json +1 -2
  39. package/src/authentication/halo-authentication-client.ts +8 -7
  40. package/src/authentication/xbox-authentication-client.ts +52 -46
  41. package/src/core/halo-infinite-client.ts +39 -35
  42. package/src/core/{spartan-token-providers/auto-xsts-spartan-token-provider.ts → token-providers/auto-token-provider.ts} +15 -3
  43. package/src/core/{spartan-token-providers → token-providers/spartan-token-providers}/static-xsts-ticket-token-spartan-token-provider.ts +3 -3
  44. package/src/core/token-providers/xbox-token-provider.ts +3 -0
  45. package/src/core/xbox-client.ts +53 -0
  46. package/src/index.ts +7 -3
  47. package/src/models/halo-infinite/game-variant-category.ts +1 -0
  48. package/src/models/halo-infinite/stats.ts +49 -5
  49. package/src/util/keyed-expiry-token-cache.ts +90 -0
  50. /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 { AutoXstsSpartanTokenProvider } from "./core/spartan-token-providers/auto-xsts-spartan-token-provider";
26
- export { StaticXstsTicketTokenSpartanTokenProvider } from "./core/spartan-token-providers/static-xsts-ticket-token-spartan-token-provider";
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";
@@ -7,6 +7,7 @@ export enum GameVariantCategory {
7
7
  MultiplayerTotalControl = 14,
8
8
  MultiplayerCtf = 15,
9
9
  MultiplayerOddball = 18,
10
+ MultiplayerStockpile = 19,
10
11
  MultiplayerInfection = 22,
11
12
  MultiplayerGrifball = 25,
12
13
  MultiplayerLandGrab = 39,
@@ -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
- interface ZonesStats {
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
- interface CaptureTheFlagStats {
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
- ExtractionStats: ExtractionStats;
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
+ }