From 0e07a0d4bcaabfaf043f74164ecf006d920702e2 Mon Sep 17 00:00:00 2001 From: AnthonyFuller <24512050+AnthonyFuller@users.noreply.github.com> Date: Sun, 22 Mar 2026 03:24:15 +0000 Subject: [PATCH 01/11] feat: initial implementation of checking entitlements with Steam directly using the app ticket --- components/entitlementStrategies.ts | 234 +++++++++++++++++++++++++++- components/flags.ts | 20 +++ components/oauthToken.ts | 59 +++++-- package.json | 3 +- yarn.lock | 180 ++++++++++++++++++++- 5 files changed, 481 insertions(+), 15 deletions(-) diff --git a/components/entitlementStrategies.ts b/components/entitlementStrategies.ts index d3fd1ad24..a51444e84 100644 --- a/components/entitlementStrategies.ts +++ b/components/entitlementStrategies.ts @@ -16,7 +16,7 @@ * along with this program. If not, see . */ -import { AxiosError, AxiosResponse } from "axios" +import axios, { AxiosError, AxiosResponse } from "axios" import { log, LogLevel } from "./loggingInterop" import { userAuths } from "./officialServerAuth" import { @@ -28,6 +28,14 @@ import { } from "./platformEntitlements" import { GameVersion } from "./types/types" import { getRemoteService } from "./utils" +import { getFlag } from "./flags" +import { parseAppTicket } from "steam-appticket" +import { createHash } from "crypto" + +// An in-memory cache of Steam ownership tickets to entitlements (they're valid for up to 21 days) +// For most users, this won't provide any benefit since they'll be restarting Peacock often, +// but this is more here for those running it 24/7 on a server somewhere. +const STEAM_TICKET_CACHE: Map = new Map() /** * The base class for an entitlement strategy. @@ -39,6 +47,12 @@ abstract class EntitlementStrategy { accessToken: string, userId: string, ): string[] | Promise + + abstract get( + clientToken: string, + identity: string, + steamId: string, + ): string[] | Promise } /** @@ -56,6 +70,224 @@ export class EpicH3Strategy extends EntitlementStrategy { } } +/** + * Provider for any Steam-based game using the ISteamUserAuth API. + * + * @internal + */ +type SteamAuthMethod = "OFFICIAL" | "BACKEND" | "STEAM" | "STEAM_STRICT" +type SteamAuthResult = + | { + success: true + steamId: string + entitlements: string[] + } + | { + success: false + code: number + error: string + } +type SteamAuthResponse = { + response: { + error?: { + errorcode: number + errordesc: string + } + params?: { + result: string + steamid: string + ownersteamid: string + vacbanned: boolean + publisherbanned: boolean + } + } +} + +export class SteamStrategy extends EntitlementStrategy { + private readonly _apiKey: string = getFlag("steamApiKey") as SteamAuthMethod + public readonly isValid: boolean = false + + constructor(private readonly _appId: string) { + super() + this._appId = _appId + + const method = getFlag("steamAuthenticationMethod") as SteamAuthMethod + + switch (method) { + case "BACKEND": { + const host = getFlag("leaderboardsHost") as string + + if (!host) { + log( + LogLevel.WARN, + "steamAuthenticationMethod is set to 'BACKEND' but 'leaderboardsHost' is null or empty - using official!", + "SteamStrategy", + ) + break + } + + this.isValid = true + break + } + case "STEAM": + case "STEAM_STRICT": { + if (!this._apiKey) { + log( + LogLevel.WARN, + `steamAuthenticationMethod is set to '${method}' but 'steamApiKey' is null or empty${method !== "STEAM_STRICT" ? " - using official" : ""}!`, + "SteamStrategy", + ) + break + } + + this.isValid = true + break + } + case "OFFICIAL": + break + } + } + + private async _getFromBackend( + _clientToken: string, + ): Promise { + return { + success: false, + code: 500, + error: "Backend validation not implemented", + } + } + + private async _getFromSteam( + clientToken: string, + identity: string, + ): Promise { + const ticket = parseAppTicket(Buffer.from(clientToken, "hex")) + + if (!ticket?.isValid) { + return { + success: false, + code: 400, + error: "Invalid app ticket.", + } + } + + try { + const resp = await axios( + "https://api.steampowered.com/ISteamUserAuth/AuthenticateUserTicket/v1", + { + params: { + key: this._apiKey, + appid: this._appId, + ticket: clientToken, + identity, + }, + }, + ) + + if (resp.status !== 200) { + return { + success: false, + code: resp.status, + error: `${resp.statusText}`, + } + } + + const data = resp.data as SteamAuthResponse + + if (data.response.error) { + return { + success: false, + code: data.response.error.errorcode, + error: `${data.response.error.errordesc}`, + } + } + + if (data.response.params!.result !== "OK") { + return { + success: false, + code: 200, + error: `${data.response.params!.result}`, + } + } + + return { + success: true, + steamId: data.response.params!.steamid, + entitlements: [ + ticket.appID.toString(), + ...ticket.dlc.map((dlc) => dlc.appID.toString()), + ], + } + } catch (error) { + if (error instanceof AxiosError) { + return { + success: false, + code: error.response?.status ?? 400, + error: `${error.response?.statusText}`, + } + } else { + return { + success: false, + code: 400, + error: `${error}`, + } + } + } + } + + // @ts-expect-error There are two functions we can overload + override async get( + clientToken: string, + identity: string, + steamId: string, + ): Promise { + if (!this.isValid) return [] + + const hash = createHash("sha256") + .update( + clientToken.startsWith("14000000") && + clientToken.length > 52 * 2 + ? clientToken.substring(52 * 2) // Skip 52 bytes to get the ownership ticket (this part can be valid for up to 21 days) + : clientToken, + ) + .digest("hex") + + if (STEAM_TICKET_CACHE.has(hash)) { + return STEAM_TICKET_CACHE.get(hash)! + } + + const authMethod = getFlag( + "steamAuthenticationMethod", + ) as SteamAuthMethod + const res = await (authMethod === "BACKEND" + ? this._getFromBackend(clientToken) + : this._getFromSteam(clientToken, identity)) + + if (!res.success) { + log( + LogLevel.WARN, + `Failed to get entitlements from ${authMethod.split("_")[0]}. Code: ${res.code}, Error: ${res.error} `, + "SteamStrategy", + ) + return [] + } + + if (res.steamId !== steamId) { + log( + LogLevel.WARN, + `Encountered mismatched SteamID when validating authentication token! Expected: ${steamId}, Got: ${res.steamId}`, + "SteamStrategy", + ) + return [] + } + + STEAM_TICKET_CACHE.set(hash, res.entitlements) + + return res.entitlements + } +} + /** * Provider for any game using the official servers. * diff --git a/components/flags.ts b/components/flags.ts index 4cab8e440..f58d7758a 100644 --- a/components/flags.ts +++ b/components/flags.ts @@ -130,6 +130,26 @@ export const defaultFlags: Flags = { possibleValues: ["SAVEASREQUESTED", "ONLINE", "OFFLINE"], default: "SAVEASREQUESTED", }, + steamAuthenticationMethod: { + category: "Services", + title: "steamAuthenticationMethod", + desc: "How users connecting via Steam should be authenticated. OFFICIAL = Official Servers, BACKEND = Using a separate backend server (uses leaderboardsHost), STEAM = Issues requests to Steam directly from Peacock, requires 'steamApiKey' to be set, STEAM_STRICT = Same as Steam, but will never fallback to official. OFFICIAL is used as a fallback if other methods fail.", + possibleValues: [ + "OFFICIAL", + "BACKEND", + "STEAM", + "STEAM_STRICT", + ], + default: "OFFICIAL", // TODO: Change this to BACKEND when ready. + showIngame: false, + }, + steamApiKey: { + category: "Services", + title: "Steam API Key", + desc: "The Steam API key to use when 'steamAuthenticationMethod' is set to 'STEAM' or 'STEAM_STRICT'.", + default: "", + showIngame: false, + }, liveSplit: { category: "Splitter", title: "LiveSplit", diff --git a/components/oauthToken.ts b/components/oauthToken.ts index fe2f70efd..2a2268431 100644 --- a/components/oauthToken.ts +++ b/components/oauthToken.ts @@ -39,10 +39,12 @@ import { EpicH1Strategy, EpicH3Strategy, IOIStrategy, + SteamStrategy, SteamH1Strategy, SteamH2Strategy, SteamScpcStrategy, } from "./entitlementStrategies" +import { getFlag } from "./flags" export const JWT_SECRET = PEACOCK_DEV ? "secret" @@ -51,6 +53,8 @@ export const JWT_SECRET = PEACOCK_DEV export type OAuthTokenBody = { grant_type: "external_steam" | "external_epic" | "refresh_token" steam_userid?: string + steam_clienttoken?: string + steam_identity?: string epic_userid?: string access_token: string pId?: string @@ -222,17 +226,19 @@ export async function handleOAuthToken( /* Store user auth for all games except scpc */ - if (!isScpc) { - const authContainer = new OfficialServerAuth( - gameVersion, - req.body.access_token, - ) + const authUser = async () => { + if (!isScpc) { + const authContainer = new OfficialServerAuth( + gameVersion, + req.body.access_token, + ) - log(LogLevel.DEBUG, `Setting up container with ID ${req.body.pId}.`) + log(LogLevel.DEBUG, `Setting up container with ID ${req.body.pId}.`) - userAuths.set(req.body.pId, authContainer) + userAuths.set(req.body.pId!, authContainer) - await authContainer._initiallyAuthenticate(req) + await authContainer._initiallyAuthenticate(req) + } } let userData = getUserData(req.body.pId, gameVersion) @@ -266,6 +272,10 @@ export async function handleOAuthToken( return new SteamScpcStrategy().get() } + // Authenticate user with official if we're not on H3 Steam (otherwise the token will be invalidated) + if (gameVersion !== "h3" || external_platform !== "steam") + await authUser() + if (gameVersion === "h1") { if (external_platform === "steam") { return new SteamH1Strategy().get() @@ -288,10 +298,35 @@ export async function handleOAuthToken( req.body.epic_userid!, ) } else if (external_platform === "steam") { - return await new IOIStrategy( - gameVersion, - STEAM_NAMESPACE_2021, - ).get(req.body.pId!) + let ents = await new SteamStrategy(external_appid).get( + req.body.steam_clienttoken!, + req.body.steam_identity!, + req.body.steam_userid!, + ) + await authUser() + + if (ents.length === 0) { + if ( + getFlag("steamAuthenticationMethod") === "STEAM_STRICT" + ) { + log( + LogLevel.WARN, + "No entitlements returned by SteamStrategy with strict mode enabled!", + ) + return [] + } + + log( + LogLevel.WARN, + "No entitlements returned by SteamStrategy - defaulting to official!", + ) + ents = await new IOIStrategy( + gameVersion, + external_appid, + ).get(req.body.pId!) + } + + return ents } else { log(LogLevel.ERROR, "Unsupported platform.") return [] diff --git a/package.json b/package.json index a8a755c82..ae21a64b5 100644 --- a/package.json +++ b/package.json @@ -69,7 +69,8 @@ "random": "^5.3.0", "semver": "^7.7.1", "send": "^1.1.0", - "serve-static": "^2.1.0" + "serve-static": "^2.1.0", + "steam-appticket": "^2.0.1" }, "devDependencies": { "@eslint/compat": "^1.2.7", diff --git a/yarn.lock b/yarn.lock index 5c0983045..0c93008ef 100644 --- a/yarn.lock +++ b/yarn.lock @@ -39,6 +39,22 @@ __metadata: languageName: node linkType: hard +"@doctormckay/stdlib@npm:^2.10.0": + version: 2.10.0 + resolution: "@doctormckay/stdlib@npm:2.10.0" + dependencies: + psl: "npm:^1.9.0" + checksum: 10/44b0075fd9316355e7cac7bfb349a2b69e57423be88b89b6e6249ac9d69436242933fc801d0f49cf3248815bfd2a75df214622d1b85c9f027f822351423dec9e + languageName: node + linkType: hard + +"@doctormckay/steam-crypto@npm:^1.2.0": + version: 1.2.0 + resolution: "@doctormckay/steam-crypto@npm:1.2.0" + checksum: 10/b614ff92f68eebec154ec3f1ce002ad0e40158b1ea193080924dee68a7ca1b36c5831f21eefc86827f7dca3d4686877747931a27d1c4e8f13af2480388be21d1 + languageName: node + linkType: hard + "@esbuild/aix-ppc64@npm:0.25.0": version: 0.25.0 resolution: "@esbuild/aix-ppc64@npm:0.25.0" @@ -598,6 +614,7 @@ __metadata: semver: "npm:^7.7.1" send: "npm:^1.1.0" serve-static: "npm:^2.1.0" + steam-appticket: "npm:^2.0.1" terser: "npm:^5.39.0" typescript: "npm:5.8.2" winston: "npm:3.13.0" @@ -664,6 +681,79 @@ __metadata: languageName: node linkType: hard +"@protobufjs/aspromise@npm:^1.1.1, @protobufjs/aspromise@npm:^1.1.2": + version: 1.1.2 + resolution: "@protobufjs/aspromise@npm:1.1.2" + checksum: 10/8a938d84fe4889411296db66b29287bd61ea3c14c2d23e7a8325f46a2b8ce899857c5f038d65d7641805e6c1d06b495525c7faf00c44f85a7ee6476649034969 + languageName: node + linkType: hard + +"@protobufjs/base64@npm:^1.1.2": + version: 1.1.2 + resolution: "@protobufjs/base64@npm:1.1.2" + checksum: 10/c71b100daeb3c9bdccab5cbc29495b906ba0ae22ceedc200e1ba49717d9c4ab15a6256839cebb6f9c6acae4ed7c25c67e0a95e734f612b258261d1a3098fe342 + languageName: node + linkType: hard + +"@protobufjs/codegen@npm:^2.0.4": + version: 2.0.4 + resolution: "@protobufjs/codegen@npm:2.0.4" + checksum: 10/c6ee5fa172a8464f5253174d3c2353ea520c2573ad7b6476983d9b1346f4d8f2b44aa29feb17a949b83c1816bc35286a5ea265ed9d8fdd2865acfa09668c0447 + languageName: node + linkType: hard + +"@protobufjs/eventemitter@npm:^1.1.0": + version: 1.1.0 + resolution: "@protobufjs/eventemitter@npm:1.1.0" + checksum: 10/03af3e99f17ad421283d054c88a06a30a615922a817741b43ca1b13e7c6b37820a37f6eba9980fb5150c54dba6e26cb6f7b64a6f7d8afa83596fafb3afa218c3 + languageName: node + linkType: hard + +"@protobufjs/fetch@npm:^1.1.0": + version: 1.1.0 + resolution: "@protobufjs/fetch@npm:1.1.0" + dependencies: + "@protobufjs/aspromise": "npm:^1.1.1" + "@protobufjs/inquire": "npm:^1.1.0" + checksum: 10/67ae40572ad536e4ef94269199f252c024b66e3059850906bdaee161ca1d75c73d04d35cd56f147a8a5a079f5808e342b99e61942c1dae15604ff0600b09a958 + languageName: node + linkType: hard + +"@protobufjs/float@npm:^1.0.2": + version: 1.0.2 + resolution: "@protobufjs/float@npm:1.0.2" + checksum: 10/634c2c989da0ef2f4f19373d64187e2a79f598c5fb7991afb689d29a2ea17c14b796b29725945fa34b9493c17fb799e08ac0a7ccaae460ee1757d3083ed35187 + languageName: node + linkType: hard + +"@protobufjs/inquire@npm:^1.1.0": + version: 1.1.0 + resolution: "@protobufjs/inquire@npm:1.1.0" + checksum: 10/c09efa34a5465cb120775e1a482136f2340a58b4abce7e93d72b8b5a9324a0e879275016ef9fcd73d72a4731639c54f2bb755bb82f916e4a78892d1d840bb3d2 + languageName: node + linkType: hard + +"@protobufjs/path@npm:^1.1.2": + version: 1.1.2 + resolution: "@protobufjs/path@npm:1.1.2" + checksum: 10/bb709567935fd385a86ad1f575aea98131bbd719c743fb9b6edd6b47ede429ff71a801cecbd64fc72deebf4e08b8f1bd8062793178cdaed3713b8d15771f9b83 + languageName: node + linkType: hard + +"@protobufjs/pool@npm:^1.1.0": + version: 1.1.0 + resolution: "@protobufjs/pool@npm:1.1.0" + checksum: 10/b9c7047647f6af28e92aac54f6f7c1f7ff31b201b4bfcc7a415b2861528854fce3ec666d7e7e10fd744da905f7d4aef2205bbcc8944ca0ca7a82e18134d00c46 + languageName: node + linkType: hard + +"@protobufjs/utf8@npm:^1.1.0": + version: 1.1.0 + resolution: "@protobufjs/utf8@npm:1.1.0" + checksum: 10/131e289c57534c1d73a0e55782d6751dd821db1583cb2f7f7e017c9d6747addaebe79f28120b2e0185395d990aad347fb14ffa73ef4096fa38508d61a0e64602 + languageName: node + linkType: hard + "@radix-ui/primitive@npm:1.0.1": version: 1.0.1 resolution: "@radix-ui/primitive@npm:1.0.1" @@ -1288,6 +1378,15 @@ __metadata: languageName: node linkType: hard +"@types/node@npm:>=13.7.0": + version: 25.5.0 + resolution: "@types/node@npm:25.5.0" + dependencies: + undici-types: "npm:~7.18.0" + checksum: 10/b1e8116bd8c9ff62e458b76d28a59cf7631537bb17e8961464bf754dd5b07b46f1620f568b2f89970505af9eef478dd74c614651b454c1ea95949ec472c64fcb + languageName: node + linkType: hard + "@types/parseurl@npm:^1.3.3": version: 1.3.3 resolution: "@types/parseurl@npm:1.3.3" @@ -1885,6 +1984,15 @@ __metadata: languageName: node linkType: hard +"bytebuffer@npm:^5.0.1": + version: 5.0.1 + resolution: "bytebuffer@npm:5.0.1" + dependencies: + long: "npm:~3" + checksum: 10/f3e9739ed9ab30e19d985fc3dadfdbd631d030874bbb313feefddac756f21ac10957257737e630fd9959744318e6e8b7d8c35b797519693bf1897be16c560970 + languageName: node + linkType: hard + "bytes@npm:3.1.2, bytes@npm:^3.1.2": version: 3.1.2 resolution: "bytes@npm:3.1.2" @@ -3600,6 +3708,20 @@ __metadata: languageName: node linkType: hard +"long@npm:^5.0.0": + version: 5.3.2 + resolution: "long@npm:5.3.2" + checksum: 10/b6b55ddae56fcce2864d37119d6b02fe28f6dd6d9e44fd22705f86a9254b9321bd69e9ffe35263b4846d54aba197c64882adcb8c543f2383c1e41284b321ea64 + languageName: node + linkType: hard + +"long@npm:~3": + version: 3.2.0 + resolution: "long@npm:3.2.0" + checksum: 10/ffc685ec458ddf71a830d6deb62ff7dc551a736d47473350d9e077c22db96ec88c8a3554c11ffce7d7f2291b0c30da36629e4d0a97c29b5360dc977533c96d28 + languageName: node + linkType: hard + "loose-envify@npm:^1.0.0, loose-envify@npm:^1.1.0": version: 1.4.0 resolution: "loose-envify@npm:1.4.0" @@ -4305,6 +4427,26 @@ __metadata: languageName: node linkType: hard +"protobufjs@npm:^7.3.2": + version: 7.5.4 + resolution: "protobufjs@npm:7.5.4" + dependencies: + "@protobufjs/aspromise": "npm:^1.1.2" + "@protobufjs/base64": "npm:^1.1.2" + "@protobufjs/codegen": "npm:^2.0.4" + "@protobufjs/eventemitter": "npm:^1.1.0" + "@protobufjs/fetch": "npm:^1.1.0" + "@protobufjs/float": "npm:^1.0.2" + "@protobufjs/inquire": "npm:^1.1.0" + "@protobufjs/path": "npm:^1.1.2" + "@protobufjs/pool": "npm:^1.1.0" + "@protobufjs/utf8": "npm:^1.1.0" + "@types/node": "npm:>=13.7.0" + long: "npm:^5.0.0" + checksum: 10/88d677bb6f11a2ecec63fdd053dfe6d31120844d04e865efa9c8fbe0674cd077d6624ecfdf014018a20dcb114ae2a59c1b21966dd8073e920650c71370966439 + languageName: node + linkType: hard + "proxy-addr@npm:~2.0.7": version: 2.0.7 resolution: "proxy-addr@npm:2.0.7" @@ -4322,6 +4464,15 @@ __metadata: languageName: node linkType: hard +"psl@npm:^1.9.0": + version: 1.15.0 + resolution: "psl@npm:1.15.0" + dependencies: + punycode: "npm:^2.3.1" + checksum: 10/5e7467eb5196eb7900d156783d12907d445c0122f76c73203ce96b148a6ccf8c5450cc805887ffada38ff92d634afcf33720c24053cb01d5b6598d1c913c5caf + languageName: node + linkType: hard + "pump@npm:^3.0.0": version: 3.0.0 resolution: "pump@npm:3.0.0" @@ -4343,7 +4494,7 @@ __metadata: languageName: node linkType: hard -"punycode@npm:^2.1.0": +"punycode@npm:^2.1.0, punycode@npm:^2.3.1": version: 2.3.1 resolution: "punycode@npm:2.3.1" checksum: 10/febdc4362bead22f9e2608ff0171713230b57aff9dddc1c273aa2a651fbd366f94b7d6a71d78342a7c0819906750351ca7f2edd26ea41b626d87d6a13d1bd059 @@ -4996,6 +5147,26 @@ __metadata: languageName: node linkType: hard +"steam-appticket@npm:^2.0.1": + version: 2.0.1 + resolution: "steam-appticket@npm:2.0.1" + dependencies: + "@doctormckay/stdlib": "npm:^2.10.0" + "@doctormckay/steam-crypto": "npm:^1.2.0" + bytebuffer: "npm:^5.0.1" + protobufjs: "npm:^7.3.2" + steamid: "npm:^2.1.0" + checksum: 10/9643eb026b1fbf0b7b3bca2633050d59c4eebe61fba7311cf69361a5fc60cdefd42f2acf2495aa440306475e1efe782999cf9b6b32ec4ba18cf1d036217109a5 + languageName: node + linkType: hard + +"steamid@npm:^2.1.0": + version: 2.1.0 + resolution: "steamid@npm:2.1.0" + checksum: 10/e23d0eb1d3296199ff827eab9415c136fe5b94ce492e696b0c29caf33afeb74a9cfc3802437b319d008db82024b525411cf6fabb76cef61cc70744358fe870a4 + languageName: node + linkType: hard + "stream-shift@npm:^1.0.0": version: 1.0.3 resolution: "stream-shift@npm:1.0.3" @@ -5320,6 +5491,13 @@ __metadata: languageName: node linkType: hard +"undici-types@npm:~7.18.0": + version: 7.18.2 + resolution: "undici-types@npm:7.18.2" + checksum: 10/e61a5918f624d68420c3ca9d301e9f15b61cba6e97be39fe2ce266dd6151e4afe424d679372638826cb506be33952774e0424141200111a9857e464216c009af + languageName: node + linkType: hard + "unique-filename@npm:^3.0.0": version: 3.0.0 resolution: "unique-filename@npm:3.0.0" From 96dfce0f6d84d5c54863bc3475615aa35ce9b16e Mon Sep 17 00:00:00 2001 From: AnthonyFuller <24512050+AnthonyFuller@users.noreply.github.com> Date: Sat, 28 Mar 2026 23:14:58 +0000 Subject: [PATCH 02/11] chore: update yarn lockfile --- yarn.lock | 882 +++++++++++++++++++++++++++++++----------------------- 1 file changed, 505 insertions(+), 377 deletions(-) diff --git a/yarn.lock b/yarn.lock index 0c93008ef..f99adf94b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -431,6 +431,13 @@ __metadata: languageName: node linkType: hard +"@jridgewell/sourcemap-codec@npm:^1.5.5": + version: 1.5.5 + resolution: "@jridgewell/sourcemap-codec@npm:1.5.5" + checksum: 10/5d9d207b462c11e322d71911e55e21a4e2772f71ffe8d6f1221b8eb5ae6774458c1d242f897fb0814e8714ca9a6b498abfa74dfe4f434493342902b1a48b33a5 + languageName: node + linkType: hard + "@jridgewell/trace-mapping@npm:^0.3.9": version: 0.3.22 resolution: "@jridgewell/trace-mapping@npm:0.3.22" @@ -483,6 +490,18 @@ __metadata: languageName: node linkType: hard +"@napi-rs/wasm-runtime@npm:^1.1.1": + version: 1.1.2 + resolution: "@napi-rs/wasm-runtime@npm:1.1.2" + dependencies: + "@tybys/wasm-util": "npm:^0.10.1" + peerDependencies: + "@emnapi/core": ^1.7.1 + "@emnapi/runtime": ^1.7.1 + checksum: 10/fcb8a5cff65dfb6c44277a1f7a16da5a1be2ed609c83e13f4bb621db97b511129b8ccf808794c8906abd3561e10c2e66d3ba550f0a1a0db18f53f1e399a0a5f8 + languageName: node + linkType: hard + "@nodelib/fs.scandir@npm:2.1.5": version: 2.1.5 resolution: "@nodelib/fs.scandir@npm:2.1.5" @@ -532,6 +551,13 @@ __metadata: languageName: node linkType: hard +"@oxc-project/types@npm:=0.122.0": + version: 0.122.0 + resolution: "@oxc-project/types@npm:0.122.0" + checksum: 10/2b33895c7701a595d10b9c7b0927222954becc4c6cbde7a7b582e9524828937368baacba1cbb6e3c33bc9a18e0a35435ffff6c53f511762ae872d55d3e993a8c + languageName: node + linkType: hard + "@peacockproject/core@workspace:packaging/typedefs": version: 0.0.0-use.local resolution: "@peacockproject/core@workspace:packaging/typedefs" @@ -634,9 +660,9 @@ __metadata: resolution: "@peacockproject/tests@workspace:tests" dependencies: "@rdil/factorygirl": "npm:^0.2.0" - "@vitest/ui": "npm:^3.0.8" - vite: "npm:^6.2.1" - vitest: "npm:^3.0.8" + "@vitest/ui": "npm:^4.1.0" + vite: "npm:^8.0.1" + vitest: "npm:^4.1.0" languageName: unknown linkType: soft @@ -658,7 +684,7 @@ __metadata: rollup-plugin-license: "npm:^3.4.0" swr: "npm:^2.3.3" typescript: "npm:5.8.2" - vite: "npm:^6.2.1" + vite: "npm:^8.0.1" peerDependencies: rollup: "*" peerDependenciesMeta: @@ -1103,136 +1129,117 @@ __metadata: languageName: node linkType: hard -"@rollup/rollup-android-arm-eabi@npm:4.35.0": - version: 4.35.0 - resolution: "@rollup/rollup-android-arm-eabi@npm:4.35.0" - conditions: os=android & cpu=arm - languageName: node - linkType: hard - -"@rollup/rollup-android-arm64@npm:4.35.0": - version: 4.35.0 - resolution: "@rollup/rollup-android-arm64@npm:4.35.0" +"@rolldown/binding-android-arm64@npm:1.0.0-rc.12": + version: 1.0.0-rc.12 + resolution: "@rolldown/binding-android-arm64@npm:1.0.0-rc.12" conditions: os=android & cpu=arm64 languageName: node linkType: hard -"@rollup/rollup-darwin-arm64@npm:4.35.0": - version: 4.35.0 - resolution: "@rollup/rollup-darwin-arm64@npm:4.35.0" +"@rolldown/binding-darwin-arm64@npm:1.0.0-rc.12": + version: 1.0.0-rc.12 + resolution: "@rolldown/binding-darwin-arm64@npm:1.0.0-rc.12" conditions: os=darwin & cpu=arm64 languageName: node linkType: hard -"@rollup/rollup-darwin-x64@npm:4.35.0": - version: 4.35.0 - resolution: "@rollup/rollup-darwin-x64@npm:4.35.0" +"@rolldown/binding-darwin-x64@npm:1.0.0-rc.12": + version: 1.0.0-rc.12 + resolution: "@rolldown/binding-darwin-x64@npm:1.0.0-rc.12" conditions: os=darwin & cpu=x64 languageName: node linkType: hard -"@rollup/rollup-freebsd-arm64@npm:4.35.0": - version: 4.35.0 - resolution: "@rollup/rollup-freebsd-arm64@npm:4.35.0" - conditions: os=freebsd & cpu=arm64 - languageName: node - linkType: hard - -"@rollup/rollup-freebsd-x64@npm:4.35.0": - version: 4.35.0 - resolution: "@rollup/rollup-freebsd-x64@npm:4.35.0" +"@rolldown/binding-freebsd-x64@npm:1.0.0-rc.12": + version: 1.0.0-rc.12 + resolution: "@rolldown/binding-freebsd-x64@npm:1.0.0-rc.12" conditions: os=freebsd & cpu=x64 languageName: node linkType: hard -"@rollup/rollup-linux-arm-gnueabihf@npm:4.35.0": - version: 4.35.0 - resolution: "@rollup/rollup-linux-arm-gnueabihf@npm:4.35.0" - conditions: os=linux & cpu=arm & libc=glibc - languageName: node - linkType: hard - -"@rollup/rollup-linux-arm-musleabihf@npm:4.35.0": - version: 4.35.0 - resolution: "@rollup/rollup-linux-arm-musleabihf@npm:4.35.0" - conditions: os=linux & cpu=arm & libc=musl +"@rolldown/binding-linux-arm-gnueabihf@npm:1.0.0-rc.12": + version: 1.0.0-rc.12 + resolution: "@rolldown/binding-linux-arm-gnueabihf@npm:1.0.0-rc.12" + conditions: os=linux & cpu=arm languageName: node linkType: hard -"@rollup/rollup-linux-arm64-gnu@npm:4.35.0": - version: 4.35.0 - resolution: "@rollup/rollup-linux-arm64-gnu@npm:4.35.0" +"@rolldown/binding-linux-arm64-gnu@npm:1.0.0-rc.12": + version: 1.0.0-rc.12 + resolution: "@rolldown/binding-linux-arm64-gnu@npm:1.0.0-rc.12" conditions: os=linux & cpu=arm64 & libc=glibc languageName: node linkType: hard -"@rollup/rollup-linux-arm64-musl@npm:4.35.0": - version: 4.35.0 - resolution: "@rollup/rollup-linux-arm64-musl@npm:4.35.0" +"@rolldown/binding-linux-arm64-musl@npm:1.0.0-rc.12": + version: 1.0.0-rc.12 + resolution: "@rolldown/binding-linux-arm64-musl@npm:1.0.0-rc.12" conditions: os=linux & cpu=arm64 & libc=musl languageName: node linkType: hard -"@rollup/rollup-linux-loongarch64-gnu@npm:4.35.0": - version: 4.35.0 - resolution: "@rollup/rollup-linux-loongarch64-gnu@npm:4.35.0" - conditions: os=linux & cpu=loong64 & libc=glibc +"@rolldown/binding-linux-ppc64-gnu@npm:1.0.0-rc.12": + version: 1.0.0-rc.12 + resolution: "@rolldown/binding-linux-ppc64-gnu@npm:1.0.0-rc.12" + conditions: os=linux & cpu=ppc64 & libc=glibc languageName: node linkType: hard -"@rollup/rollup-linux-powerpc64le-gnu@npm:4.35.0": - version: 4.35.0 - resolution: "@rollup/rollup-linux-powerpc64le-gnu@npm:4.35.0" - conditions: os=linux & cpu=ppc64 & libc=glibc +"@rolldown/binding-linux-s390x-gnu@npm:1.0.0-rc.12": + version: 1.0.0-rc.12 + resolution: "@rolldown/binding-linux-s390x-gnu@npm:1.0.0-rc.12" + conditions: os=linux & cpu=s390x & libc=glibc languageName: node linkType: hard -"@rollup/rollup-linux-riscv64-gnu@npm:4.35.0": - version: 4.35.0 - resolution: "@rollup/rollup-linux-riscv64-gnu@npm:4.35.0" - conditions: os=linux & cpu=riscv64 & libc=glibc +"@rolldown/binding-linux-x64-gnu@npm:1.0.0-rc.12": + version: 1.0.0-rc.12 + resolution: "@rolldown/binding-linux-x64-gnu@npm:1.0.0-rc.12" + conditions: os=linux & cpu=x64 & libc=glibc languageName: node linkType: hard -"@rollup/rollup-linux-s390x-gnu@npm:4.35.0": - version: 4.35.0 - resolution: "@rollup/rollup-linux-s390x-gnu@npm:4.35.0" - conditions: os=linux & cpu=s390x & libc=glibc +"@rolldown/binding-linux-x64-musl@npm:1.0.0-rc.12": + version: 1.0.0-rc.12 + resolution: "@rolldown/binding-linux-x64-musl@npm:1.0.0-rc.12" + conditions: os=linux & cpu=x64 & libc=musl languageName: node linkType: hard -"@rollup/rollup-linux-x64-gnu@npm:4.35.0": - version: 4.35.0 - resolution: "@rollup/rollup-linux-x64-gnu@npm:4.35.0" - conditions: os=linux & cpu=x64 & libc=glibc +"@rolldown/binding-openharmony-arm64@npm:1.0.0-rc.12": + version: 1.0.0-rc.12 + resolution: "@rolldown/binding-openharmony-arm64@npm:1.0.0-rc.12" + conditions: os=openharmony & cpu=arm64 languageName: node linkType: hard -"@rollup/rollup-linux-x64-musl@npm:4.35.0": - version: 4.35.0 - resolution: "@rollup/rollup-linux-x64-musl@npm:4.35.0" - conditions: os=linux & cpu=x64 & libc=musl +"@rolldown/binding-wasm32-wasi@npm:1.0.0-rc.12": + version: 1.0.0-rc.12 + resolution: "@rolldown/binding-wasm32-wasi@npm:1.0.0-rc.12" + dependencies: + "@napi-rs/wasm-runtime": "npm:^1.1.1" + conditions: cpu=wasm32 languageName: node linkType: hard -"@rollup/rollup-win32-arm64-msvc@npm:4.35.0": - version: 4.35.0 - resolution: "@rollup/rollup-win32-arm64-msvc@npm:4.35.0" +"@rolldown/binding-win32-arm64-msvc@npm:1.0.0-rc.12": + version: 1.0.0-rc.12 + resolution: "@rolldown/binding-win32-arm64-msvc@npm:1.0.0-rc.12" conditions: os=win32 & cpu=arm64 languageName: node linkType: hard -"@rollup/rollup-win32-ia32-msvc@npm:4.35.0": - version: 4.35.0 - resolution: "@rollup/rollup-win32-ia32-msvc@npm:4.35.0" - conditions: os=win32 & cpu=ia32 +"@rolldown/binding-win32-x64-msvc@npm:1.0.0-rc.12": + version: 1.0.0-rc.12 + resolution: "@rolldown/binding-win32-x64-msvc@npm:1.0.0-rc.12" + conditions: os=win32 & cpu=x64 languageName: node linkType: hard -"@rollup/rollup-win32-x64-msvc@npm:4.35.0": - version: 4.35.0 - resolution: "@rollup/rollup-win32-x64-msvc@npm:4.35.0" - conditions: os=win32 & cpu=x64 +"@rolldown/pluginutils@npm:1.0.0-rc.12": + version: 1.0.0-rc.12 + resolution: "@rolldown/pluginutils@npm:1.0.0-rc.12" + checksum: 10/6ce1601849b3095a2b6e57074c1f8a661eba67ebf65cf9afdf894d903302318247ddb69ab6cbc621e7f582408af301ea0523ed59ddb9a4ef3ea97f3d7002683e languageName: node linkType: hard @@ -1243,6 +1250,22 @@ __metadata: languageName: node linkType: hard +"@standard-schema/spec@npm:^1.1.0": + version: 1.1.0 + resolution: "@standard-schema/spec@npm:1.1.0" + checksum: 10/a209615c9e8b2ea535d7db0a5f6aa0f962fd4ab73ee86a46c100fb78116964af1f55a27c1794d4801e534a196794223daa25ff5135021e03c7828aa3d95e1763 + languageName: node + linkType: hard + +"@tybys/wasm-util@npm:^0.10.1": + version: 0.10.1 + resolution: "@tybys/wasm-util@npm:0.10.1" + dependencies: + tslib: "npm:^2.4.0" + checksum: 10/7fe0d239397aebb002ac4855d30c197c06a05ea8df8511350a3a5b1abeefe26167c60eda8a5508337571161e4c4b53d7c1342296123f9607af8705369de9fa7f + languageName: node + linkType: hard + "@types/body-parser@npm:*": version: 1.19.5 resolution: "@types/body-parser@npm:1.19.5" @@ -1253,6 +1276,16 @@ __metadata: languageName: node linkType: hard +"@types/chai@npm:^5.2.2": + version: 5.2.3 + resolution: "@types/chai@npm:5.2.3" + dependencies: + "@types/deep-eql": "npm:*" + assertion-error: "npm:^2.0.1" + checksum: 10/e79947307dc235953622e65f83d2683835212357ca261389116ab90bed369ac862ba28b146b4fed08b503ae1e1a12cb93ce783f24bb8d562950469f4320e1c7c + languageName: node + linkType: hard + "@types/connect@npm:*": version: 3.4.38 resolution: "@types/connect@npm:3.4.38" @@ -1262,6 +1295,13 @@ __metadata: languageName: node linkType: hard +"@types/deep-eql@npm:*": + version: 4.0.2 + resolution: "@types/deep-eql@npm:4.0.2" + checksum: 10/249a27b0bb22f6aa28461db56afa21ec044fa0e303221a62dff81831b20c8530502175f1a49060f7099e7be06181078548ac47c668de79ff9880241968d43d0c + languageName: node + linkType: hard + "@types/emscripten@npm:^1.39.6": version: 1.39.10 resolution: "@types/emscripten@npm:1.39.10" @@ -1269,7 +1309,7 @@ __metadata: languageName: node linkType: hard -"@types/estree@npm:1.0.6, @types/estree@npm:^1.0.0, @types/estree@npm:^1.0.6": +"@types/estree@npm:^1.0.0, @types/estree@npm:^1.0.6": version: 1.0.6 resolution: "@types/estree@npm:1.0.6" checksum: 10/9d35d475095199c23e05b431bcdd1f6fec7380612aed068b14b2a08aa70494de8a9026765a5a91b1073f636fb0368f6d8973f518a31391d519e20c59388ed88d @@ -1602,101 +1642,102 @@ __metadata: languageName: node linkType: hard -"@vitest/expect@npm:3.0.8": - version: 3.0.8 - resolution: "@vitest/expect@npm:3.0.8" +"@vitest/expect@npm:4.1.2": + version: 4.1.2 + resolution: "@vitest/expect@npm:4.1.2" dependencies: - "@vitest/spy": "npm:3.0.8" - "@vitest/utils": "npm:3.0.8" - chai: "npm:^5.2.0" - tinyrainbow: "npm:^2.0.0" - checksum: 10/6cb8a707ff8be140f5d1a5f61a9b0622b2783af1cb591b286e20ebeab9d04081567ef0f9bd697e60b08bc5be0008ea4687b78fb1134e7f3956f2fb06c74c59f8 + "@standard-schema/spec": "npm:^1.1.0" + "@types/chai": "npm:^5.2.2" + "@vitest/spy": "npm:4.1.2" + "@vitest/utils": "npm:4.1.2" + chai: "npm:^6.2.2" + tinyrainbow: "npm:^3.1.0" + checksum: 10/536c5a8903927e324bbb66967be4e0ec2ec4ff6234f0b8fe20987841b0705c931c7e3ce2e61c7665f4ded65ba736de6cda8d2d37ee114efeedb187ca5d597ea1 languageName: node linkType: hard -"@vitest/mocker@npm:3.0.8": - version: 3.0.8 - resolution: "@vitest/mocker@npm:3.0.8" +"@vitest/mocker@npm:4.1.2": + version: 4.1.2 + resolution: "@vitest/mocker@npm:4.1.2" dependencies: - "@vitest/spy": "npm:3.0.8" + "@vitest/spy": "npm:4.1.2" estree-walker: "npm:^3.0.3" - magic-string: "npm:^0.30.17" + magic-string: "npm:^0.30.21" peerDependencies: msw: ^2.4.9 - vite: ^5.0.0 || ^6.0.0 + vite: ^6.0.0 || ^7.0.0 || ^8.0.0 peerDependenciesMeta: msw: optional: true vite: optional: true - checksum: 10/456cafc5c2701a3cfffd7549e3bef0313f96672aea1c3f3da449b0d28744b69d466e510afdf6d5ad08beb7126954c75468c8408e099a2faea8733b364c5523fd + checksum: 10/1d7976e19ef168357aba2ca41cd8db86236a98dfb2209bd3152a3a20e9a5b8cbfd8f73356c43a934b384d3b4c7a63835fb1037d3f56a7824faa838331eaa214e languageName: node linkType: hard -"@vitest/pretty-format@npm:3.0.8, @vitest/pretty-format@npm:^3.0.8": - version: 3.0.8 - resolution: "@vitest/pretty-format@npm:3.0.8" +"@vitest/pretty-format@npm:4.1.2": + version: 4.1.2 + resolution: "@vitest/pretty-format@npm:4.1.2" dependencies: - tinyrainbow: "npm:^2.0.0" - checksum: 10/255a7929e814fd8cfd8978ae6342479a8f453ccca97a0a968efbe45b5d39d2c56e1bfa3a5400816f54d3a82c944c8407f7fe2426ec57499a9210bdccf06dbc78 + tinyrainbow: "npm:^3.1.0" + checksum: 10/a07a6023c52b25be5c75fc05bb3317629390cc1b50eae6cbea91ba4c13193ec88e54abaa56b46b40ddb8a6a4558d667f2ba0e1cf2ee2d0e32b463244f3002aa7 languageName: node linkType: hard -"@vitest/runner@npm:3.0.8": - version: 3.0.8 - resolution: "@vitest/runner@npm:3.0.8" +"@vitest/runner@npm:4.1.2": + version: 4.1.2 + resolution: "@vitest/runner@npm:4.1.2" dependencies: - "@vitest/utils": "npm:3.0.8" + "@vitest/utils": "npm:4.1.2" pathe: "npm:^2.0.3" - checksum: 10/d1c3661ed1a5b2ffc3b90b99eac6133b318b2f32ff49e805e153d7128b3a824ff7906eced8d08d7a43b9f34a280432b060c59b2fcede942cde2de4c5684ae003 + checksum: 10/13fd019a63ee3225420474cbd1ca0ae7c5c2dcdd241f2a958ca45731c10de36131f15303ae8ab1196133ec4e955b7c6de658c7b5e19736d550f310c8195fa9b2 languageName: node linkType: hard -"@vitest/snapshot@npm:3.0.8": - version: 3.0.8 - resolution: "@vitest/snapshot@npm:3.0.8" +"@vitest/snapshot@npm:4.1.2": + version: 4.1.2 + resolution: "@vitest/snapshot@npm:4.1.2" dependencies: - "@vitest/pretty-format": "npm:3.0.8" - magic-string: "npm:^0.30.17" + "@vitest/pretty-format": "npm:4.1.2" + "@vitest/utils": "npm:4.1.2" + magic-string: "npm:^0.30.21" pathe: "npm:^2.0.3" - checksum: 10/61b66ca6a3362de8724fd7cfa17b27a1d59d884693e5c1a2b4edfbcdc75621a7d3314ecb207c88aabff6e6360e7d4ed08c1997ecf3f71e28c485bd590a98919e + checksum: 10/9d124412dbe44db43ca5277180bf5fe5dad7373218a177830bba631b53d225f7d4de368a20d6f5740ec07402e9e4dd179609db2b2f691d2d8b02f1bdbfd8c1a3 languageName: node linkType: hard -"@vitest/spy@npm:3.0.8": - version: 3.0.8 - resolution: "@vitest/spy@npm:3.0.8" - dependencies: - tinyspy: "npm:^3.0.2" - checksum: 10/a6be428cedd4052d44ffd90ebd0c422d389f313996e08c5a655148b7d1c5695a94a321c66acc8331e20a3988e3946d4231722a8c5040afe1fe41035e3d390297 +"@vitest/spy@npm:4.1.2": + version: 4.1.2 + resolution: "@vitest/spy@npm:4.1.2" + checksum: 10/e20e417ac430fee34e4be58802b2eb31e1c1163296a8921c0878be14e1ae77c7a7cae1b9b515d56fe623e05ee21b092aff7eb5e0d412f656650b72ecd02bb30a languageName: node linkType: hard -"@vitest/ui@npm:^3.0.8": - version: 3.0.8 - resolution: "@vitest/ui@npm:3.0.8" +"@vitest/ui@npm:^4.1.0": + version: 4.1.2 + resolution: "@vitest/ui@npm:4.1.2" dependencies: - "@vitest/utils": "npm:3.0.8" + "@vitest/utils": "npm:4.1.2" fflate: "npm:^0.8.2" - flatted: "npm:^3.3.3" + flatted: "npm:^3.4.2" pathe: "npm:^2.0.3" - sirv: "npm:^3.0.1" - tinyglobby: "npm:^0.2.12" - tinyrainbow: "npm:^2.0.0" + sirv: "npm:^3.0.2" + tinyglobby: "npm:^0.2.15" + tinyrainbow: "npm:^3.1.0" peerDependencies: - vitest: 3.0.8 - checksum: 10/d38c3ebf31b62fb6c987ce663e8a04e36642cbdcba4e521fcef13c41346435847885f180854ee20592c56d6722b6b5c5f46c211bd2d86403023b0ce99ff23f0b + vitest: 4.1.2 + checksum: 10/ce53323d553fb08a228e29c3d162aefbc78a20cb90635d92ea40f3482d7c15a2962a65c50ead858272b2871dde84f71667dc618cf160189f06238a36498b50af languageName: node linkType: hard -"@vitest/utils@npm:3.0.8": - version: 3.0.8 - resolution: "@vitest/utils@npm:3.0.8" +"@vitest/utils@npm:4.1.2": + version: 4.1.2 + resolution: "@vitest/utils@npm:4.1.2" dependencies: - "@vitest/pretty-format": "npm:3.0.8" - loupe: "npm:^3.1.3" - tinyrainbow: "npm:^2.0.0" - checksum: 10/207281dc59cd37e4aabb56db4b9bd66d281b4ef314cbed7f9642e61dfcd65bb12d29600291d676f56c3eb82b9831722a59b13f0d65b1a7af4e3ed2a5c18e98b7 + "@vitest/pretty-format": "npm:4.1.2" + convert-source-map: "npm:^2.0.0" + tinyrainbow: "npm:^3.1.0" + checksum: 10/854decf0eb639758d012c9aa53c3d7aed547e37c05ece6704d5f53035be77f704a24973ed95089926e1768c0b55902d42c4438660788e7a0f0e80d0fda1c713b languageName: node linkType: hard @@ -2000,13 +2041,6 @@ __metadata: languageName: node linkType: hard -"cac@npm:^6.7.14": - version: 6.7.14 - resolution: "cac@npm:6.7.14" - checksum: 10/002769a0fbfc51c062acd2a59df465a2a947916b02ac50b56c69ec6c018ee99ac3e7f4dd7366334ea847f1ecacf4defaa61bcd2ac283db50156ce1f1d8c8ad42 - languageName: node - linkType: hard - "cacache@npm:^18.0.0": version: 18.0.2 resolution: "cacache@npm:18.0.2" @@ -2034,16 +2068,10 @@ __metadata: languageName: node linkType: hard -"chai@npm:^5.2.0": - version: 5.2.0 - resolution: "chai@npm:5.2.0" - dependencies: - assertion-error: "npm:^2.0.1" - check-error: "npm:^2.1.1" - deep-eql: "npm:^5.0.1" - loupe: "npm:^3.1.0" - pathval: "npm:^2.0.0" - checksum: 10/2ce03671c159c6a567bf1912756daabdbb7c075f3c0078f1b59d61da8d276936367ee696dfe093b49e1479d9ba93a6074c8e55d49791dddd8061728cdcad249e +"chai@npm:^6.2.2": + version: 6.2.2 + resolution: "chai@npm:6.2.2" + checksum: 10/13cda42cc40aa46da04a41cf7e5c61df6b6ae0b4e8a8c8b40e04d6947e4d7951377ea8c14f9fa7fe5aaa9e8bd9ba414f11288dc958d4cee6f5221b9436f2778f languageName: node linkType: hard @@ -2057,13 +2085,6 @@ __metadata: languageName: node linkType: hard -"check-error@npm:^2.1.1": - version: 2.1.1 - resolution: "check-error@npm:2.1.1" - checksum: 10/d785ed17b1d4a4796b6e75c765a9a290098cf52ff9728ce0756e8ffd4293d2e419dd30c67200aee34202463b474306913f2fcfaf1890641026d9fc6966fea27a - languageName: node - linkType: hard - "chownr@npm:^2.0.0": version: 2.0.0 resolution: "chownr@npm:2.0.0" @@ -2241,6 +2262,13 @@ __metadata: languageName: node linkType: hard +"convert-source-map@npm:^2.0.0": + version: 2.0.0 + resolution: "convert-source-map@npm:2.0.0" + checksum: 10/c987be3ec061348cdb3c2bfb924bec86dea1eacad10550a85ca23edb0fe3556c3a61c7399114f3331ccb3499d7fd0285ab24566e5745929412983494c3926e15 + languageName: node + linkType: hard + "cookie-signature@npm:^1.2.1": version: 1.2.2 resolution: "cookie-signature@npm:1.2.2" @@ -2292,13 +2320,6 @@ __metadata: languageName: node linkType: hard -"deep-eql@npm:^5.0.1": - version: 5.0.2 - resolution: "deep-eql@npm:5.0.2" - checksum: 10/a529b81e2ef8821621d20a36959a0328873a3e49d393ad11f8efe8559f31239494c2eb889b80342808674c475802ba95b9d6c4c27641b9a029405104c1b59fcf - languageName: node - linkType: hard - "deep-is@npm:^0.1.3": version: 0.1.4 resolution: "deep-is@npm:0.1.4" @@ -2350,6 +2371,13 @@ __metadata: languageName: node linkType: hard +"detect-libc@npm:^2.0.3": + version: 2.1.2 + resolution: "detect-libc@npm:2.1.2" + checksum: 10/b736c8d97d5d46164c0d1bed53eb4e6a3b1d8530d460211e2d52f1c552875e706c58a5376854e4e54f8b828c9cada58c855288c968522eb93ac7696d65970766 + languageName: node + linkType: hard + "detect-node-es@npm:^1.1.0": version: 1.1.0 resolution: "detect-node-es@npm:1.1.0" @@ -2459,10 +2487,10 @@ __metadata: languageName: node linkType: hard -"es-module-lexer@npm:^1.6.0": - version: 1.6.0 - resolution: "es-module-lexer@npm:1.6.0" - checksum: 10/807ee7020cc46a9c970c78cad1f2f3fc139877e5ebad7f66dbfbb124d451189ba1c48c1c632bd5f8ce1b8af2caef3fca340ba044a410fa890d17b080a59024bb +"es-module-lexer@npm:^2.0.0": + version: 2.0.0 + resolution: "es-module-lexer@npm:2.0.0" + checksum: 10/b075855289b5f40ee496f3d7525c5c501d029c3da15c22298a0030d625bf36d1da0768b26278f7f4bada2a602459b505888e20b77c414fba5da5619b0e84dbd1 languageName: node linkType: hard @@ -2762,10 +2790,10 @@ __metadata: languageName: node linkType: hard -"expect-type@npm:^1.1.0": - version: 1.2.0 - resolution: "expect-type@npm:1.2.0" - checksum: 10/12a081159e87451a96e2e2f8a5e101509b63a4f0738590bb27988d2017c6e5aff6bf722889fe7afd96cf7e343b332b040460db41850fcd7a1392a4c8e26e51e3 +"expect-type@npm:^1.3.0": + version: 1.3.0 + resolution: "expect-type@npm:1.3.0" + checksum: 10/a5fada3d0c621649261f886e7d93e6bf80ce26d8a86e5d517e38301b8baec8450ab2cb94ba6e7a0a6bf2fc9ee55f54e1b06938ef1efa52ddcfeffbfa01acbbcc languageName: node linkType: hard @@ -2866,15 +2894,15 @@ __metadata: languageName: node linkType: hard -"fdir@npm:^6.4.3": - version: 6.4.3 - resolution: "fdir@npm:6.4.3" +"fdir@npm:^6.5.0": + version: 6.5.0 + resolution: "fdir@npm:6.5.0" peerDependencies: picomatch: ^3 || ^4 peerDependenciesMeta: picomatch: optional: true - checksum: 10/8e6d20f4590dc168de1374a9cadaa37e20ca6e0b822aa247c230e7ea1d9e9674a68cd816146435e4ecc98f9285091462ab7e5e56eebc9510931a1794e4db68b2 + checksum: 10/14ca1c9f0a0e8f4f2e9bf4e8551065a164a09545dae548c12a18d238b72e51e5a7b39bd8e5494b56463a0877672d0a6c1ef62c6fa0677db1b0c847773be939b1 languageName: node linkType: hard @@ -2953,13 +2981,20 @@ __metadata: languageName: node linkType: hard -"flatted@npm:^3.2.9, flatted@npm:^3.3.3": +"flatted@npm:^3.2.9": version: 3.3.3 resolution: "flatted@npm:3.3.3" checksum: 10/8c96c02fbeadcf4e8ffd0fa24983241e27698b0781295622591fc13585e2f226609d95e422bcf2ef044146ffacb6b68b1f20871454eddf75ab3caa6ee5f4a1fe languageName: node linkType: hard +"flatted@npm:^3.4.2": + version: 3.4.2 + resolution: "flatted@npm:3.4.2" + checksum: 10/a9e78fe5c2c1fcd98209a015ccee3a6caa953e01729778e83c1fe92e68601a63e1e69cd4e573010ca99eaf585a581b80ccf1018b99283e6cbc2117bcba1e030f + languageName: node + linkType: hard + "fn.name@npm:1.x.x": version: 1.1.0 resolution: "fn.name@npm:1.1.0" @@ -3044,7 +3079,7 @@ __metadata: languageName: node linkType: hard -"fsevents@npm:~2.3.2, fsevents@npm:~2.3.3": +"fsevents@npm:~2.3.3": version: 2.3.3 resolution: "fsevents@npm:2.3.3" dependencies: @@ -3054,7 +3089,7 @@ __metadata: languageName: node linkType: hard -"fsevents@patch:fsevents@npm%3A~2.3.2#optional!builtin, fsevents@patch:fsevents@npm%3A~2.3.3#optional!builtin": +"fsevents@patch:fsevents@npm%3A~2.3.3#optional!builtin": version: 2.3.3 resolution: "fsevents@patch:fsevents@npm%3A2.3.3#optional!builtin::version=2.3.3&hash=df0bf1" dependencies: @@ -3612,6 +3647,126 @@ __metadata: languageName: node linkType: hard +"lightningcss-android-arm64@npm:1.32.0": + version: 1.32.0 + resolution: "lightningcss-android-arm64@npm:1.32.0" + conditions: os=android & cpu=arm64 + languageName: node + linkType: hard + +"lightningcss-darwin-arm64@npm:1.32.0": + version: 1.32.0 + resolution: "lightningcss-darwin-arm64@npm:1.32.0" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + +"lightningcss-darwin-x64@npm:1.32.0": + version: 1.32.0 + resolution: "lightningcss-darwin-x64@npm:1.32.0" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + +"lightningcss-freebsd-x64@npm:1.32.0": + version: 1.32.0 + resolution: "lightningcss-freebsd-x64@npm:1.32.0" + conditions: os=freebsd & cpu=x64 + languageName: node + linkType: hard + +"lightningcss-linux-arm-gnueabihf@npm:1.32.0": + version: 1.32.0 + resolution: "lightningcss-linux-arm-gnueabihf@npm:1.32.0" + conditions: os=linux & cpu=arm + languageName: node + linkType: hard + +"lightningcss-linux-arm64-gnu@npm:1.32.0": + version: 1.32.0 + resolution: "lightningcss-linux-arm64-gnu@npm:1.32.0" + conditions: os=linux & cpu=arm64 & libc=glibc + languageName: node + linkType: hard + +"lightningcss-linux-arm64-musl@npm:1.32.0": + version: 1.32.0 + resolution: "lightningcss-linux-arm64-musl@npm:1.32.0" + conditions: os=linux & cpu=arm64 & libc=musl + languageName: node + linkType: hard + +"lightningcss-linux-x64-gnu@npm:1.32.0": + version: 1.32.0 + resolution: "lightningcss-linux-x64-gnu@npm:1.32.0" + conditions: os=linux & cpu=x64 & libc=glibc + languageName: node + linkType: hard + +"lightningcss-linux-x64-musl@npm:1.32.0": + version: 1.32.0 + resolution: "lightningcss-linux-x64-musl@npm:1.32.0" + conditions: os=linux & cpu=x64 & libc=musl + languageName: node + linkType: hard + +"lightningcss-win32-arm64-msvc@npm:1.32.0": + version: 1.32.0 + resolution: "lightningcss-win32-arm64-msvc@npm:1.32.0" + conditions: os=win32 & cpu=arm64 + languageName: node + linkType: hard + +"lightningcss-win32-x64-msvc@npm:1.32.0": + version: 1.32.0 + resolution: "lightningcss-win32-x64-msvc@npm:1.32.0" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + +"lightningcss@npm:^1.32.0": + version: 1.32.0 + resolution: "lightningcss@npm:1.32.0" + dependencies: + detect-libc: "npm:^2.0.3" + lightningcss-android-arm64: "npm:1.32.0" + lightningcss-darwin-arm64: "npm:1.32.0" + lightningcss-darwin-x64: "npm:1.32.0" + lightningcss-freebsd-x64: "npm:1.32.0" + lightningcss-linux-arm-gnueabihf: "npm:1.32.0" + lightningcss-linux-arm64-gnu: "npm:1.32.0" + lightningcss-linux-arm64-musl: "npm:1.32.0" + lightningcss-linux-x64-gnu: "npm:1.32.0" + lightningcss-linux-x64-musl: "npm:1.32.0" + lightningcss-win32-arm64-msvc: "npm:1.32.0" + lightningcss-win32-x64-msvc: "npm:1.32.0" + dependenciesMeta: + lightningcss-android-arm64: + optional: true + lightningcss-darwin-arm64: + optional: true + lightningcss-darwin-x64: + optional: true + lightningcss-freebsd-x64: + optional: true + lightningcss-linux-arm-gnueabihf: + optional: true + lightningcss-linux-arm64-gnu: + optional: true + lightningcss-linux-arm64-musl: + optional: true + lightningcss-linux-x64-gnu: + optional: true + lightningcss-linux-x64-musl: + optional: true + lightningcss-win32-arm64-msvc: + optional: true + lightningcss-win32-x64-msvc: + optional: true + checksum: 10/098e61007f0d0ec8b5c50884e33b543b551d1ff21bc7b062434b6638fd0b8596858f823b60dfc2a4aa756f3cb120ad79f2b7f4a55b1bda2c0269ab8cf476f114 + languageName: node + linkType: hard + "locate-path@npm:^6.0.0": version: 6.0.0 resolution: "locate-path@npm:6.0.0" @@ -3733,13 +3888,6 @@ __metadata: languageName: node linkType: hard -"loupe@npm:^3.1.0, loupe@npm:^3.1.3": - version: 3.1.3 - resolution: "loupe@npm:3.1.3" - checksum: 10/9e98c34daf0eba48ccc603595e51f2ae002110982d84879cf78c51de2c632f0c571dfe82ce4210af60c32203d06b443465c269bda925076fe6d9b612cc65c321 - languageName: node - linkType: hard - "lru-cache@npm:^10.0.1, lru-cache@npm:^9.1.1 || ^10.0.0": version: 10.2.0 resolution: "lru-cache@npm:10.2.0" @@ -3747,7 +3895,16 @@ __metadata: languageName: node linkType: hard -"magic-string@npm:^0.30.17, magic-string@npm:~0.30.0": +"magic-string@npm:^0.30.21": + version: 0.30.21 + resolution: "magic-string@npm:0.30.21" + dependencies: + "@jridgewell/sourcemap-codec": "npm:^1.5.5" + checksum: 10/57d5691f41ed40d962d8bd300148114f53db67fadbff336207db10a99f2bdf4a1be9cac3a68ee85dba575912ee1d4402e4396408196ec2d3afd043b076156221 + languageName: node + linkType: hard + +"magic-string@npm:~0.30.0": version: 0.30.17 resolution: "magic-string@npm:0.30.17" dependencies: @@ -4052,12 +4209,12 @@ __metadata: languageName: node linkType: hard -"nanoid@npm:^3.3.8": - version: 3.3.9 - resolution: "nanoid@npm:3.3.9" +"nanoid@npm:^3.3.11": + version: 3.3.11 + resolution: "nanoid@npm:3.3.11" bin: nanoid: bin/nanoid.cjs - checksum: 10/80ec0f2f7fe0f472f459fbeab6afd88f6739e3da94cf2c2307bc83ef0203ec3b72e6113a9e3196ac4be79540440184136ee96e77c10a965e37d8347f43b265fa + checksum: 10/73b5afe5975a307aaa3c95dfe3334c52cdf9ae71518176895229b8d65ab0d1c0417dd081426134eb7571c055720428ea5d57c645138161e7d10df80815527c48 languageName: node linkType: hard @@ -4140,6 +4297,13 @@ __metadata: languageName: node linkType: hard +"obug@npm:^2.1.1": + version: 2.1.1 + resolution: "obug@npm:2.1.1" + checksum: 10/bdcf9213361786688019345f3452b95a1dc73710e4b403c82a1994b98bad6abc31b26cb72a482128c5fd53ea9daf6fbb7d0e0e7b2b7e9c8be6d779deeccee07f + languageName: node + linkType: hard + "on-finished@npm:2.4.1, on-finished@npm:^2.4.1": version: 2.4.1 resolution: "on-finished@npm:2.4.1" @@ -4311,13 +4475,6 @@ __metadata: languageName: node linkType: hard -"pathval@npm:^2.0.0": - version: 2.0.0 - resolution: "pathval@npm:2.0.0" - checksum: 10/b91575bf9cdf01757afd7b5e521eb8a0b874a49bc972d08e0047cfea0cd3c019f5614521d4bc83d2855e3fcc331db6817dfd533dd8f3d90b16bc76fad2450fc1 - languageName: node - linkType: hard - "picocolors@npm:^1.1.1": version: 1.1.1 resolution: "picocolors@npm:1.1.1" @@ -4332,21 +4489,21 @@ __metadata: languageName: node linkType: hard -"picomatch@npm:^4.0.2": - version: 4.0.2 - resolution: "picomatch@npm:4.0.2" - checksum: 10/ce617b8da36797d09c0baacb96ca8a44460452c89362d7cb8f70ca46b4158ba8bc3606912de7c818eb4a939f7f9015cef3c766ec8a0c6bfc725fdc078e39c717 +"picomatch@npm:^4.0.3, picomatch@npm:^4.0.4": + version: 4.0.4 + resolution: "picomatch@npm:4.0.4" + checksum: 10/f6ef80a3590827ce20378ae110ac78209cc4f74d39236370f1780f957b7ee41c12acde0e4651b90f39983506fd2f5e449994716f516db2e9752924aff8de93ce languageName: node linkType: hard -"postcss@npm:^8.5.3": - version: 8.5.3 - resolution: "postcss@npm:8.5.3" +"postcss@npm:^8.5.8": + version: 8.5.8 + resolution: "postcss@npm:8.5.8" dependencies: - nanoid: "npm:^3.3.8" + nanoid: "npm:^3.3.11" picocolors: "npm:^1.1.1" source-map-js: "npm:^1.2.1" - checksum: 10/6d7e21a772e8b05bf102636918654dac097bac013f0dc8346b72ac3604fc16829646f94ea862acccd8f82e910b00e2c11c1f0ea276543565d278c7ca35516a7c + checksum: 10/cbacbfd7f767e2c820d4bf09a3a744834dd7d14f69ff08d1f57b1a7defce9ae5efcf31981890d9697a972a64e9965de677932ef28e4c8ba23a87aad45b82c459 languageName: node linkType: hard @@ -4727,94 +4884,80 @@ __metadata: languageName: node linkType: hard -"rollup-plugin-license@npm:^3.4.0": - version: 3.4.0 - resolution: "rollup-plugin-license@npm:3.4.0" - dependencies: - commenting: "npm:~1.1.0" - glob: "npm:~7.2.0" - lodash: "npm:~4.17.21" - magic-string: "npm:~0.30.0" - mkdirp: "npm:~3.0.0" - moment: "npm:~2.30.1" - package-name-regex: "npm:~2.0.6" - spdx-expression-validate: "npm:~2.0.0" - spdx-satisfies: "npm:~5.0.1" - peerDependencies: - rollup: ^1.0.0 || ^2.0.0 || ^3.0.0 || ^4.0.0 - checksum: 10/2a3accaf3966a3a55f85b9d2704d7343c884b7f9bbd37d9b96e176bb0147f264ab3acacb8110ccec20f27107e0edaa28ed38740df14073f8e3e57f1f04c0e76f - languageName: node - linkType: hard - -"rollup@npm:^4.30.1": - version: 4.35.0 - resolution: "rollup@npm:4.35.0" - dependencies: - "@rollup/rollup-android-arm-eabi": "npm:4.35.0" - "@rollup/rollup-android-arm64": "npm:4.35.0" - "@rollup/rollup-darwin-arm64": "npm:4.35.0" - "@rollup/rollup-darwin-x64": "npm:4.35.0" - "@rollup/rollup-freebsd-arm64": "npm:4.35.0" - "@rollup/rollup-freebsd-x64": "npm:4.35.0" - "@rollup/rollup-linux-arm-gnueabihf": "npm:4.35.0" - "@rollup/rollup-linux-arm-musleabihf": "npm:4.35.0" - "@rollup/rollup-linux-arm64-gnu": "npm:4.35.0" - "@rollup/rollup-linux-arm64-musl": "npm:4.35.0" - "@rollup/rollup-linux-loongarch64-gnu": "npm:4.35.0" - "@rollup/rollup-linux-powerpc64le-gnu": "npm:4.35.0" - "@rollup/rollup-linux-riscv64-gnu": "npm:4.35.0" - "@rollup/rollup-linux-s390x-gnu": "npm:4.35.0" - "@rollup/rollup-linux-x64-gnu": "npm:4.35.0" - "@rollup/rollup-linux-x64-musl": "npm:4.35.0" - "@rollup/rollup-win32-arm64-msvc": "npm:4.35.0" - "@rollup/rollup-win32-ia32-msvc": "npm:4.35.0" - "@rollup/rollup-win32-x64-msvc": "npm:4.35.0" - "@types/estree": "npm:1.0.6" - fsevents: "npm:~2.3.2" +"rolldown@npm:1.0.0-rc.12": + version: 1.0.0-rc.12 + resolution: "rolldown@npm:1.0.0-rc.12" + dependencies: + "@oxc-project/types": "npm:=0.122.0" + "@rolldown/binding-android-arm64": "npm:1.0.0-rc.12" + "@rolldown/binding-darwin-arm64": "npm:1.0.0-rc.12" + "@rolldown/binding-darwin-x64": "npm:1.0.0-rc.12" + "@rolldown/binding-freebsd-x64": "npm:1.0.0-rc.12" + "@rolldown/binding-linux-arm-gnueabihf": "npm:1.0.0-rc.12" + "@rolldown/binding-linux-arm64-gnu": "npm:1.0.0-rc.12" + "@rolldown/binding-linux-arm64-musl": "npm:1.0.0-rc.12" + "@rolldown/binding-linux-ppc64-gnu": "npm:1.0.0-rc.12" + "@rolldown/binding-linux-s390x-gnu": "npm:1.0.0-rc.12" + "@rolldown/binding-linux-x64-gnu": "npm:1.0.0-rc.12" + "@rolldown/binding-linux-x64-musl": "npm:1.0.0-rc.12" + "@rolldown/binding-openharmony-arm64": "npm:1.0.0-rc.12" + "@rolldown/binding-wasm32-wasi": "npm:1.0.0-rc.12" + "@rolldown/binding-win32-arm64-msvc": "npm:1.0.0-rc.12" + "@rolldown/binding-win32-x64-msvc": "npm:1.0.0-rc.12" + "@rolldown/pluginutils": "npm:1.0.0-rc.12" dependenciesMeta: - "@rollup/rollup-android-arm-eabi": - optional: true - "@rollup/rollup-android-arm64": - optional: true - "@rollup/rollup-darwin-arm64": - optional: true - "@rollup/rollup-darwin-x64": - optional: true - "@rollup/rollup-freebsd-arm64": - optional: true - "@rollup/rollup-freebsd-x64": + "@rolldown/binding-android-arm64": optional: true - "@rollup/rollup-linux-arm-gnueabihf": + "@rolldown/binding-darwin-arm64": optional: true - "@rollup/rollup-linux-arm-musleabihf": + "@rolldown/binding-darwin-x64": optional: true - "@rollup/rollup-linux-arm64-gnu": + "@rolldown/binding-freebsd-x64": optional: true - "@rollup/rollup-linux-arm64-musl": + "@rolldown/binding-linux-arm-gnueabihf": optional: true - "@rollup/rollup-linux-loongarch64-gnu": + "@rolldown/binding-linux-arm64-gnu": optional: true - "@rollup/rollup-linux-powerpc64le-gnu": + "@rolldown/binding-linux-arm64-musl": optional: true - "@rollup/rollup-linux-riscv64-gnu": + "@rolldown/binding-linux-ppc64-gnu": optional: true - "@rollup/rollup-linux-s390x-gnu": + "@rolldown/binding-linux-s390x-gnu": optional: true - "@rollup/rollup-linux-x64-gnu": + "@rolldown/binding-linux-x64-gnu": optional: true - "@rollup/rollup-linux-x64-musl": + "@rolldown/binding-linux-x64-musl": optional: true - "@rollup/rollup-win32-arm64-msvc": + "@rolldown/binding-openharmony-arm64": optional: true - "@rollup/rollup-win32-ia32-msvc": + "@rolldown/binding-wasm32-wasi": optional: true - "@rollup/rollup-win32-x64-msvc": + "@rolldown/binding-win32-arm64-msvc": optional: true - fsevents: + "@rolldown/binding-win32-x64-msvc": optional: true bin: - rollup: dist/bin/rollup - checksum: 10/1fd13b8cb874106727cc4241e7b09167b835247185f52a0ac0d4b302df6dd01feec32e53ee3fead757c0c033f8b15ae6f0e093854de1878ae9e5dee37ec52579 + rolldown: bin/cli.mjs + checksum: 10/b8cc0d9df80b495a57b63d69a16a5566c600162046edd407f335a6d27e5b6618a2d88d63e82c4e77a1447d18edcc6900696e041c33236ef38ab51d33cf5da2fe + languageName: node + linkType: hard + +"rollup-plugin-license@npm:^3.4.0": + version: 3.4.0 + resolution: "rollup-plugin-license@npm:3.4.0" + dependencies: + commenting: "npm:~1.1.0" + glob: "npm:~7.2.0" + lodash: "npm:~4.17.21" + magic-string: "npm:~0.30.0" + mkdirp: "npm:~3.0.0" + moment: "npm:~2.30.1" + package-name-regex: "npm:~2.0.6" + spdx-expression-validate: "npm:~2.0.0" + spdx-satisfies: "npm:~5.0.1" + peerDependencies: + rollup: ^1.0.0 || ^2.0.0 || ^3.0.0 || ^4.0.0 + checksum: 10/2a3accaf3966a3a55f85b9d2704d7343c884b7f9bbd37d9b96e176bb0147f264ab3acacb8110ccec20f27107e0edaa28ed38740df14073f8e3e57f1f04c0e76f languageName: node linkType: hard @@ -4978,14 +5121,14 @@ __metadata: languageName: node linkType: hard -"sirv@npm:^3.0.1": - version: 3.0.1 - resolution: "sirv@npm:3.0.1" +"sirv@npm:^3.0.2": + version: 3.0.2 + resolution: "sirv@npm:3.0.2" dependencies: "@polka/url": "npm:^1.0.0-next.24" mrmime: "npm:^2.0.0" totalist: "npm:^3.0.0" - checksum: 10/b110ebe28eb1740772fbbfacb6c71c58d1ec8ec17a5ae2852a5418c3ef41d52d473663613de808f8a6337ec29dd446414d0d059e75bfd13fb9630d18651c99f2 + checksum: 10/259617f4ab57664be6d963f5b27b38a6351d3e91ce70d6726985d087b40efd595fcf7f72ae010babf5e0acb63bcb3e3d6db8de34604da1011be6e28ee32aa15d languageName: node linkType: hard @@ -5140,10 +5283,10 @@ __metadata: languageName: node linkType: hard -"std-env@npm:^3.8.0": - version: 3.8.1 - resolution: "std-env@npm:3.8.1" - checksum: 10/ee119570e2e449be86aa4972f119f9086a918307cc524f6e891b7a7c1327a5c970cf1b7d5898c881777845292a7e3380cf7d80ad34aee355d2c22ac5eb628542 +"std-env@npm:^4.0.0-rc.1": + version: 4.0.0 + resolution: "std-env@npm:4.0.0" + checksum: 10/19ef21cd85da52dc1178288d1b69e242b6579c0a76ddba2374f859aa58678797ec4a34c4f1fe6b9007a032e04d6fd3fca4e27349c88809265a9cbd90d924203f languageName: node linkType: hard @@ -5329,41 +5472,27 @@ __metadata: languageName: node linkType: hard -"tinyexec@npm:^0.3.2": - version: 0.3.2 - resolution: "tinyexec@npm:0.3.2" - checksum: 10/b9d5fed3166fb1acd1e7f9a89afcd97ccbe18b9c1af0278e429455f6976d69271ba2d21797e7c36d57d6b05025e525d2882d88c2ab435b60d1ddf2fea361de57 +"tinyexec@npm:^1.0.2": + version: 1.0.4 + resolution: "tinyexec@npm:1.0.4" + checksum: 10/ccebe4044eef6fa5050929df7862fda70b4fb700f15d94aef8ae6109b9d194dbc3a990125d99944fd25b90fe2115e1927f055b909a604c571a81b647ede5757a languageName: node linkType: hard -"tinyglobby@npm:^0.2.12": - version: 0.2.12 - resolution: "tinyglobby@npm:0.2.12" +"tinyglobby@npm:^0.2.15": + version: 0.2.15 + resolution: "tinyglobby@npm:0.2.15" dependencies: - fdir: "npm:^6.4.3" - picomatch: "npm:^4.0.2" - checksum: 10/4ad28701fa9118b32ef0e27f409e0a6c5741e8b02286d50425c1f6f71e6d6c6ded9dd5bbbbb714784b08623c4ec4d150151f1d3d996cfabe0495f908ab4f7002 - languageName: node - linkType: hard - -"tinypool@npm:^1.0.2": - version: 1.0.2 - resolution: "tinypool@npm:1.0.2" - checksum: 10/6109322f14b3763f65c8fa49fddab72cd3edd96b82dd50e05e63de74867329ff5353bff4377281ec963213d9314f37f4a353e9ee34bbac85fd4c1e4a568d6076 + fdir: "npm:^6.5.0" + picomatch: "npm:^4.0.3" + checksum: 10/d72bd826a8b0fa5fa3929e7fe5ba48fceb2ae495df3a231b6c5408cd7d8c00b58ab5a9c2a76ba56a62ee9b5e083626f1f33599734bed1ffc4b792406408f0ca2 languageName: node linkType: hard -"tinyrainbow@npm:^2.0.0": - version: 2.0.0 - resolution: "tinyrainbow@npm:2.0.0" - checksum: 10/94d4e16246972614a5601eeb169ba94f1d49752426312d3cf8cc4f2cc663a2e354ffc653aa4de4eebccbf9eeebdd0caef52d1150271fdfde65d7ae7f3dcb9eb5 - languageName: node - linkType: hard - -"tinyspy@npm:^3.0.2": - version: 3.0.2 - resolution: "tinyspy@npm:3.0.2" - checksum: 10/5db671b2ff5cd309de650c8c4761ca945459d7204afb1776db9a04fb4efa28a75f08517a8620c01ee32a577748802231ad92f7d5b194dc003ee7f987a2a06337 +"tinyrainbow@npm:^3.1.0": + version: 3.1.0 + resolution: "tinyrainbow@npm:3.1.0" + checksum: 10/4c2c01dde1e5bb9a74973daaae141d4d733d246280b2f9a7f6a9e7dd8e940d48b2580a6086125278777897bc44635d6ccec5f9f563c2179dd2129f4542d0ec05 languageName: node linkType: hard @@ -5603,38 +5732,26 @@ __metadata: languageName: node linkType: hard -"vite-node@npm:3.0.8": - version: 3.0.8 - resolution: "vite-node@npm:3.0.8" - dependencies: - cac: "npm:^6.7.14" - debug: "npm:^4.4.0" - es-module-lexer: "npm:^1.6.0" - pathe: "npm:^2.0.3" - vite: "npm:^5.0.0 || ^6.0.0" - bin: - vite-node: vite-node.mjs - checksum: 10/9a06d27d9f56f17cf9586cd36f19e4c275227f55f0d9b71c0002d7dbe1a76398cac836b639e2c2537be67f16adb33c40d6d64d3640a97696ebfdfd731e5ea13f - languageName: node - linkType: hard - -"vite@npm:^5.0.0 || ^6.0.0, vite@npm:^6.2.1": - version: 6.2.1 - resolution: "vite@npm:6.2.1" +"vite@npm:^6.0.0 || ^7.0.0 || ^8.0.0, vite@npm:^8.0.1": + version: 8.0.3 + resolution: "vite@npm:8.0.3" dependencies: - esbuild: "npm:^0.25.0" fsevents: "npm:~2.3.3" - postcss: "npm:^8.5.3" - rollup: "npm:^4.30.1" + lightningcss: "npm:^1.32.0" + picomatch: "npm:^4.0.4" + postcss: "npm:^8.5.8" + rolldown: "npm:1.0.0-rc.12" + tinyglobby: "npm:^0.2.15" peerDependencies: - "@types/node": ^18.0.0 || ^20.0.0 || >=22.0.0 + "@types/node": ^20.19.0 || >=22.12.0 + "@vitejs/devtools": ^0.1.0 + esbuild: ^0.27.0 jiti: ">=1.21.0" - less: "*" - lightningcss: ^1.21.0 - sass: "*" - sass-embedded: "*" - stylus: "*" - sugarss: "*" + less: ^4.0.0 + sass: ^1.70.0 + sass-embedded: ^1.70.0 + stylus: ">=0.54.8" + sugarss: ^5.0.0 terser: ^5.16.0 tsx: ^4.8.1 yaml: ^2.4.2 @@ -5644,12 +5761,14 @@ __metadata: peerDependenciesMeta: "@types/node": optional: true + "@vitejs/devtools": + optional: true + esbuild: + optional: true jiti: optional: true less: optional: true - lightningcss: - optional: true sass: optional: true sass-embedded: @@ -5666,50 +5785,57 @@ __metadata: optional: true bin: vite: bin/vite.js - checksum: 10/a37cbc33a3d9746434e8e96614dd6d8d45d821e61e9d266f389808da290b3128ac93ba0059ab09a098cdeab8b790d6d3ae7cdd5e1ce7d55c31b8bece650b3790 + checksum: 10/745b791cb71297ac3877af061da44751d93f198413426bbb76a1f8384d76d4162a6ad739b2bcdf5fb966cd1295db59412614aee60738e40e1c99cee561e682f0 languageName: node linkType: hard -"vitest@npm:^3.0.8": - version: 3.0.8 - resolution: "vitest@npm:3.0.8" - dependencies: - "@vitest/expect": "npm:3.0.8" - "@vitest/mocker": "npm:3.0.8" - "@vitest/pretty-format": "npm:^3.0.8" - "@vitest/runner": "npm:3.0.8" - "@vitest/snapshot": "npm:3.0.8" - "@vitest/spy": "npm:3.0.8" - "@vitest/utils": "npm:3.0.8" - chai: "npm:^5.2.0" - debug: "npm:^4.4.0" - expect-type: "npm:^1.1.0" - magic-string: "npm:^0.30.17" +"vitest@npm:^4.1.0": + version: 4.1.2 + resolution: "vitest@npm:4.1.2" + dependencies: + "@vitest/expect": "npm:4.1.2" + "@vitest/mocker": "npm:4.1.2" + "@vitest/pretty-format": "npm:4.1.2" + "@vitest/runner": "npm:4.1.2" + "@vitest/snapshot": "npm:4.1.2" + "@vitest/spy": "npm:4.1.2" + "@vitest/utils": "npm:4.1.2" + es-module-lexer: "npm:^2.0.0" + expect-type: "npm:^1.3.0" + magic-string: "npm:^0.30.21" + obug: "npm:^2.1.1" pathe: "npm:^2.0.3" - std-env: "npm:^3.8.0" + picomatch: "npm:^4.0.3" + std-env: "npm:^4.0.0-rc.1" tinybench: "npm:^2.9.0" - tinyexec: "npm:^0.3.2" - tinypool: "npm:^1.0.2" - tinyrainbow: "npm:^2.0.0" - vite: "npm:^5.0.0 || ^6.0.0" - vite-node: "npm:3.0.8" + tinyexec: "npm:^1.0.2" + tinyglobby: "npm:^0.2.15" + tinyrainbow: "npm:^3.1.0" + vite: "npm:^6.0.0 || ^7.0.0 || ^8.0.0" why-is-node-running: "npm:^2.3.0" peerDependencies: "@edge-runtime/vm": "*" - "@types/debug": ^4.1.12 - "@types/node": ^18.0.0 || ^20.0.0 || >=22.0.0 - "@vitest/browser": 3.0.8 - "@vitest/ui": 3.0.8 + "@opentelemetry/api": ^1.9.0 + "@types/node": ^20.0.0 || ^22.0.0 || >=24.0.0 + "@vitest/browser-playwright": 4.1.2 + "@vitest/browser-preview": 4.1.2 + "@vitest/browser-webdriverio": 4.1.2 + "@vitest/ui": 4.1.2 happy-dom: "*" jsdom: "*" + vite: ^6.0.0 || ^7.0.0 || ^8.0.0 peerDependenciesMeta: "@edge-runtime/vm": optional: true - "@types/debug": + "@opentelemetry/api": optional: true "@types/node": optional: true - "@vitest/browser": + "@vitest/browser-playwright": + optional: true + "@vitest/browser-preview": + optional: true + "@vitest/browser-webdriverio": optional: true "@vitest/ui": optional: true @@ -5717,9 +5843,11 @@ __metadata: optional: true jsdom: optional: true + vite: + optional: false bin: vitest: vitest.mjs - checksum: 10/83b246ded7dab20db40a0dfa93a45a7a4de3d41f1860889b53d2896761db48ca42b88d1a5d8920681d6f5b96b76a46d5ab27456affb89be7ea2138d95531c87e + checksum: 10/6b037387e59d403f6570f887f6ac96b81ff6e768dbd02d32a812ddff5bdebef022dd6d9f20b84fb9535866e0c5dbdf80e6705cc428b6a8f8a8e67e1335235848 languageName: node linkType: hard From 99b38291b65a3029b48abb3104fc0c970632501f Mon Sep 17 00:00:00 2001 From: AnthonyFuller <24512050+AnthonyFuller@users.noreply.github.com> Date: Sun, 29 Mar 2026 03:04:53 +0100 Subject: [PATCH 03/11] refactor(SteamStrategy): remove dependency on steam-appticket --- components/entitlementStrategies.ts | 9 +- components/utils.ts | 87 ++++++++++++++ package.json | 3 +- yarn.lock | 180 +--------------------------- 4 files changed, 93 insertions(+), 186 deletions(-) diff --git a/components/entitlementStrategies.ts b/components/entitlementStrategies.ts index a51444e84..053e343b9 100644 --- a/components/entitlementStrategies.ts +++ b/components/entitlementStrategies.ts @@ -27,9 +27,8 @@ import { STEAM_NAMESPACE_2016, } from "./platformEntitlements" import { GameVersion } from "./types/types" -import { getRemoteService } from "./utils" +import { getRemoteService, parseAppTicket } from "./utils" import { getFlag } from "./flags" -import { parseAppTicket } from "steam-appticket" import { createHash } from "crypto" // An in-memory cache of Steam ownership tickets to entitlements (they're valid for up to 21 days) @@ -164,7 +163,7 @@ export class SteamStrategy extends EntitlementStrategy { ): Promise { const ticket = parseAppTicket(Buffer.from(clientToken, "hex")) - if (!ticket?.isValid) { + if (!ticket?.valid) { return { success: false, code: 400, @@ -215,8 +214,8 @@ export class SteamStrategy extends EntitlementStrategy { success: true, steamId: data.response.params!.steamid, entitlements: [ - ticket.appID.toString(), - ...ticket.dlc.map((dlc) => dlc.appID.toString()), + ticket.appId.toString(), + ...ticket.dlc.map((dlc) => dlc.toString()), ], } } catch (error) { diff --git a/components/utils.ts b/components/utils.ts index e90eb0474..3e171221a 100644 --- a/components/utils.ts +++ b/components/utils.ts @@ -37,6 +37,7 @@ import { getConfig, getVersionedConfig } from "./configSwizzleManager" import { compare } from "semver" import assert from "assert" import { getUnlockableById } from "./inventory" +import { createVerify } from "crypto" /** * True if the server is being run by the launcher, false otherwise. @@ -860,3 +861,89 @@ export function parsePageNumber(page: unknown): number { page = Math.max(page as number, 0) return page as number } + +// Steam Utility Functions +const STEAM_PUBLIC_KEY: string = + "-----BEGIN PUBLIC KEY-----\n" + + "MIGdMA0GCSqGSIb3DQEBAQUAA4GLADCBhwKBgQDf7BrWLBBmLBc1OhSwfFkRf53T\n" + + "2Ct64+AVzRkeRuh7h3SiGEYxqQMUeYKO6UWiSRKpI2hzic9pobFhRr3Bvr/WARvY\n" + + "gdTckPv+T1JzZsuVcNfFjrocejN1oWI0Rrtgt4Bo+hOneoo3S57G9F1fOpn5nsQ6\n" + + "6WOiu4gZKODnFMBCiQIBEQ==\n" + + "-----END PUBLIC KEY-----" + +// A subset of the full app ticket data +export interface AppTicket { + steamId: string + appId: number + expiry: Date + dlc: number[] + valid: boolean +} + +export function parseAppTicket(ticket: Buffer): AppTicket | undefined { + try { + // Adapted from the structs found at https://github.com/SteamRE/SteamKit/blob/master/Resources/Structs/steam3_appticket.hsl + const appTicket: AppTicket = { + steamId: "", + appId: 0, + expiry: new Date(), + dlc: [], + valid: false, + } + + let offset = 0 + + if (ticket.readUInt32LE() === 20) { + // This ticket includes a GCTOKEN, skip this (extra + 4 to skip the length from OWNERSHIPSECTIONWITHSIGNATURE) + offset += 52 + 4 + } + + // Invalid app ticket + if (ticket.length <= offset) return + + const ownershipTicket = ticket.subarray( + offset, + offset + ticket.readUInt32LE(offset), + ) + appTicket.steamId = ticket.readBigUInt64LE((offset += 8)).toString() + appTicket.appId = ticket.readUInt32LE((offset += 8)) + appTicket.expiry = new Date(ticket.readUInt32LE((offset += 20)) * 1000) + + // Skip past licenses + const licensesLength = ticket.readUInt16LE((offset += 4)) + offset += 2 + licensesLength * 4 + + const dlcLength = ticket.readUInt16LE(offset) + offset += 2 + + for (let i = 0; i < dlcLength; ++i) { + appTicket.dlc.push(ticket.readUInt32LE(offset)) + + // DLC Licenses array, usually empty + const licensesLength = ticket.readUInt16LE((offset += 4)) + offset += 2 + licensesLength * 4 + } + + // Skip past reserved + offset += 2 + + // We require a valid signature + let validSignature = false + + if (offset + 128 === ticket.length) { + const signature = ticket.subarray(offset, offset + 128) + + const verify = createVerify("RSA-SHA1") + verify.update(ownershipTicket) + verify.end() + + validSignature = verify.verify(STEAM_PUBLIC_KEY, signature) + } + + appTicket.valid = new Date() < appTicket.expiry && validSignature + + return appTicket + } catch { + return + } +} diff --git a/package.json b/package.json index ae21a64b5..a8a755c82 100644 --- a/package.json +++ b/package.json @@ -69,8 +69,7 @@ "random": "^5.3.0", "semver": "^7.7.1", "send": "^1.1.0", - "serve-static": "^2.1.0", - "steam-appticket": "^2.0.1" + "serve-static": "^2.1.0" }, "devDependencies": { "@eslint/compat": "^1.2.7", diff --git a/yarn.lock b/yarn.lock index f99adf94b..ee81184d0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -39,22 +39,6 @@ __metadata: languageName: node linkType: hard -"@doctormckay/stdlib@npm:^2.10.0": - version: 2.10.0 - resolution: "@doctormckay/stdlib@npm:2.10.0" - dependencies: - psl: "npm:^1.9.0" - checksum: 10/44b0075fd9316355e7cac7bfb349a2b69e57423be88b89b6e6249ac9d69436242933fc801d0f49cf3248815bfd2a75df214622d1b85c9f027f822351423dec9e - languageName: node - linkType: hard - -"@doctormckay/steam-crypto@npm:^1.2.0": - version: 1.2.0 - resolution: "@doctormckay/steam-crypto@npm:1.2.0" - checksum: 10/b614ff92f68eebec154ec3f1ce002ad0e40158b1ea193080924dee68a7ca1b36c5831f21eefc86827f7dca3d4686877747931a27d1c4e8f13af2480388be21d1 - languageName: node - linkType: hard - "@esbuild/aix-ppc64@npm:0.25.0": version: 0.25.0 resolution: "@esbuild/aix-ppc64@npm:0.25.0" @@ -640,7 +624,6 @@ __metadata: semver: "npm:^7.7.1" send: "npm:^1.1.0" serve-static: "npm:^2.1.0" - steam-appticket: "npm:^2.0.1" terser: "npm:^5.39.0" typescript: "npm:5.8.2" winston: "npm:3.13.0" @@ -707,79 +690,6 @@ __metadata: languageName: node linkType: hard -"@protobufjs/aspromise@npm:^1.1.1, @protobufjs/aspromise@npm:^1.1.2": - version: 1.1.2 - resolution: "@protobufjs/aspromise@npm:1.1.2" - checksum: 10/8a938d84fe4889411296db66b29287bd61ea3c14c2d23e7a8325f46a2b8ce899857c5f038d65d7641805e6c1d06b495525c7faf00c44f85a7ee6476649034969 - languageName: node - linkType: hard - -"@protobufjs/base64@npm:^1.1.2": - version: 1.1.2 - resolution: "@protobufjs/base64@npm:1.1.2" - checksum: 10/c71b100daeb3c9bdccab5cbc29495b906ba0ae22ceedc200e1ba49717d9c4ab15a6256839cebb6f9c6acae4ed7c25c67e0a95e734f612b258261d1a3098fe342 - languageName: node - linkType: hard - -"@protobufjs/codegen@npm:^2.0.4": - version: 2.0.4 - resolution: "@protobufjs/codegen@npm:2.0.4" - checksum: 10/c6ee5fa172a8464f5253174d3c2353ea520c2573ad7b6476983d9b1346f4d8f2b44aa29feb17a949b83c1816bc35286a5ea265ed9d8fdd2865acfa09668c0447 - languageName: node - linkType: hard - -"@protobufjs/eventemitter@npm:^1.1.0": - version: 1.1.0 - resolution: "@protobufjs/eventemitter@npm:1.1.0" - checksum: 10/03af3e99f17ad421283d054c88a06a30a615922a817741b43ca1b13e7c6b37820a37f6eba9980fb5150c54dba6e26cb6f7b64a6f7d8afa83596fafb3afa218c3 - languageName: node - linkType: hard - -"@protobufjs/fetch@npm:^1.1.0": - version: 1.1.0 - resolution: "@protobufjs/fetch@npm:1.1.0" - dependencies: - "@protobufjs/aspromise": "npm:^1.1.1" - "@protobufjs/inquire": "npm:^1.1.0" - checksum: 10/67ae40572ad536e4ef94269199f252c024b66e3059850906bdaee161ca1d75c73d04d35cd56f147a8a5a079f5808e342b99e61942c1dae15604ff0600b09a958 - languageName: node - linkType: hard - -"@protobufjs/float@npm:^1.0.2": - version: 1.0.2 - resolution: "@protobufjs/float@npm:1.0.2" - checksum: 10/634c2c989da0ef2f4f19373d64187e2a79f598c5fb7991afb689d29a2ea17c14b796b29725945fa34b9493c17fb799e08ac0a7ccaae460ee1757d3083ed35187 - languageName: node - linkType: hard - -"@protobufjs/inquire@npm:^1.1.0": - version: 1.1.0 - resolution: "@protobufjs/inquire@npm:1.1.0" - checksum: 10/c09efa34a5465cb120775e1a482136f2340a58b4abce7e93d72b8b5a9324a0e879275016ef9fcd73d72a4731639c54f2bb755bb82f916e4a78892d1d840bb3d2 - languageName: node - linkType: hard - -"@protobufjs/path@npm:^1.1.2": - version: 1.1.2 - resolution: "@protobufjs/path@npm:1.1.2" - checksum: 10/bb709567935fd385a86ad1f575aea98131bbd719c743fb9b6edd6b47ede429ff71a801cecbd64fc72deebf4e08b8f1bd8062793178cdaed3713b8d15771f9b83 - languageName: node - linkType: hard - -"@protobufjs/pool@npm:^1.1.0": - version: 1.1.0 - resolution: "@protobufjs/pool@npm:1.1.0" - checksum: 10/b9c7047647f6af28e92aac54f6f7c1f7ff31b201b4bfcc7a415b2861528854fce3ec666d7e7e10fd744da905f7d4aef2205bbcc8944ca0ca7a82e18134d00c46 - languageName: node - linkType: hard - -"@protobufjs/utf8@npm:^1.1.0": - version: 1.1.0 - resolution: "@protobufjs/utf8@npm:1.1.0" - checksum: 10/131e289c57534c1d73a0e55782d6751dd821db1583cb2f7f7e017c9d6747addaebe79f28120b2e0185395d990aad347fb14ffa73ef4096fa38508d61a0e64602 - languageName: node - linkType: hard - "@radix-ui/primitive@npm:1.0.1": version: 1.0.1 resolution: "@radix-ui/primitive@npm:1.0.1" @@ -1418,15 +1328,6 @@ __metadata: languageName: node linkType: hard -"@types/node@npm:>=13.7.0": - version: 25.5.0 - resolution: "@types/node@npm:25.5.0" - dependencies: - undici-types: "npm:~7.18.0" - checksum: 10/b1e8116bd8c9ff62e458b76d28a59cf7631537bb17e8961464bf754dd5b07b46f1620f568b2f89970505af9eef478dd74c614651b454c1ea95949ec472c64fcb - languageName: node - linkType: hard - "@types/parseurl@npm:^1.3.3": version: 1.3.3 resolution: "@types/parseurl@npm:1.3.3" @@ -2025,15 +1926,6 @@ __metadata: languageName: node linkType: hard -"bytebuffer@npm:^5.0.1": - version: 5.0.1 - resolution: "bytebuffer@npm:5.0.1" - dependencies: - long: "npm:~3" - checksum: 10/f3e9739ed9ab30e19d985fc3dadfdbd631d030874bbb313feefddac756f21ac10957257737e630fd9959744318e6e8b7d8c35b797519693bf1897be16c560970 - languageName: node - linkType: hard - "bytes@npm:3.1.2, bytes@npm:^3.1.2": version: 3.1.2 resolution: "bytes@npm:3.1.2" @@ -3863,20 +3755,6 @@ __metadata: languageName: node linkType: hard -"long@npm:^5.0.0": - version: 5.3.2 - resolution: "long@npm:5.3.2" - checksum: 10/b6b55ddae56fcce2864d37119d6b02fe28f6dd6d9e44fd22705f86a9254b9321bd69e9ffe35263b4846d54aba197c64882adcb8c543f2383c1e41284b321ea64 - languageName: node - linkType: hard - -"long@npm:~3": - version: 3.2.0 - resolution: "long@npm:3.2.0" - checksum: 10/ffc685ec458ddf71a830d6deb62ff7dc551a736d47473350d9e077c22db96ec88c8a3554c11ffce7d7f2291b0c30da36629e4d0a97c29b5360dc977533c96d28 - languageName: node - linkType: hard - "loose-envify@npm:^1.0.0, loose-envify@npm:^1.1.0": version: 1.4.0 resolution: "loose-envify@npm:1.4.0" @@ -4584,26 +4462,6 @@ __metadata: languageName: node linkType: hard -"protobufjs@npm:^7.3.2": - version: 7.5.4 - resolution: "protobufjs@npm:7.5.4" - dependencies: - "@protobufjs/aspromise": "npm:^1.1.2" - "@protobufjs/base64": "npm:^1.1.2" - "@protobufjs/codegen": "npm:^2.0.4" - "@protobufjs/eventemitter": "npm:^1.1.0" - "@protobufjs/fetch": "npm:^1.1.0" - "@protobufjs/float": "npm:^1.0.2" - "@protobufjs/inquire": "npm:^1.1.0" - "@protobufjs/path": "npm:^1.1.2" - "@protobufjs/pool": "npm:^1.1.0" - "@protobufjs/utf8": "npm:^1.1.0" - "@types/node": "npm:>=13.7.0" - long: "npm:^5.0.0" - checksum: 10/88d677bb6f11a2ecec63fdd053dfe6d31120844d04e865efa9c8fbe0674cd077d6624ecfdf014018a20dcb114ae2a59c1b21966dd8073e920650c71370966439 - languageName: node - linkType: hard - "proxy-addr@npm:~2.0.7": version: 2.0.7 resolution: "proxy-addr@npm:2.0.7" @@ -4621,15 +4479,6 @@ __metadata: languageName: node linkType: hard -"psl@npm:^1.9.0": - version: 1.15.0 - resolution: "psl@npm:1.15.0" - dependencies: - punycode: "npm:^2.3.1" - checksum: 10/5e7467eb5196eb7900d156783d12907d445c0122f76c73203ce96b148a6ccf8c5450cc805887ffada38ff92d634afcf33720c24053cb01d5b6598d1c913c5caf - languageName: node - linkType: hard - "pump@npm:^3.0.0": version: 3.0.0 resolution: "pump@npm:3.0.0" @@ -4651,7 +4500,7 @@ __metadata: languageName: node linkType: hard -"punycode@npm:^2.1.0, punycode@npm:^2.3.1": +"punycode@npm:^2.1.0": version: 2.3.1 resolution: "punycode@npm:2.3.1" checksum: 10/febdc4362bead22f9e2608ff0171713230b57aff9dddc1c273aa2a651fbd366f94b7d6a71d78342a7c0819906750351ca7f2edd26ea41b626d87d6a13d1bd059 @@ -5290,26 +5139,6 @@ __metadata: languageName: node linkType: hard -"steam-appticket@npm:^2.0.1": - version: 2.0.1 - resolution: "steam-appticket@npm:2.0.1" - dependencies: - "@doctormckay/stdlib": "npm:^2.10.0" - "@doctormckay/steam-crypto": "npm:^1.2.0" - bytebuffer: "npm:^5.0.1" - protobufjs: "npm:^7.3.2" - steamid: "npm:^2.1.0" - checksum: 10/9643eb026b1fbf0b7b3bca2633050d59c4eebe61fba7311cf69361a5fc60cdefd42f2acf2495aa440306475e1efe782999cf9b6b32ec4ba18cf1d036217109a5 - languageName: node - linkType: hard - -"steamid@npm:^2.1.0": - version: 2.1.0 - resolution: "steamid@npm:2.1.0" - checksum: 10/e23d0eb1d3296199ff827eab9415c136fe5b94ce492e696b0c29caf33afeb74a9cfc3802437b319d008db82024b525411cf6fabb76cef61cc70744358fe870a4 - languageName: node - linkType: hard - "stream-shift@npm:^1.0.0": version: 1.0.3 resolution: "stream-shift@npm:1.0.3" @@ -5620,13 +5449,6 @@ __metadata: languageName: node linkType: hard -"undici-types@npm:~7.18.0": - version: 7.18.2 - resolution: "undici-types@npm:7.18.2" - checksum: 10/e61a5918f624d68420c3ca9d301e9f15b61cba6e97be39fe2ce266dd6151e4afe424d679372638826cb506be33952774e0424141200111a9857e464216c009af - languageName: node - linkType: hard - "unique-filename@npm:^3.0.0": version: 3.0.0 resolution: "unique-filename@npm:3.0.0" From 8eb7e688f7d57fdf81e9401edce8990896d63825 Mon Sep 17 00:00:00 2001 From: AnthonyFuller <24512050+AnthonyFuller@users.noreply.github.com> Date: Mon, 30 Mar 2026 22:48:14 +0100 Subject: [PATCH 04/11] refactor(parseAppTicket): return app id and dlcs as strings, compute hash --- components/utils.ts | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/components/utils.ts b/components/utils.ts index 3e171221a..4fcf9f722 100644 --- a/components/utils.ts +++ b/components/utils.ts @@ -37,7 +37,7 @@ import { getConfig, getVersionedConfig } from "./configSwizzleManager" import { compare } from "semver" import assert from "assert" import { getUnlockableById } from "./inventory" -import { createVerify } from "crypto" +import { createHash, createVerify } from "crypto" /** * True if the server is being run by the launcher, false otherwise. @@ -874,10 +874,11 @@ const STEAM_PUBLIC_KEY: string = // A subset of the full app ticket data export interface AppTicket { steamId: string - appId: number + appId: string expiry: Date - dlc: number[] + dlc: string[] valid: boolean + hash: string } export function parseAppTicket(ticket: Buffer): AppTicket | undefined { @@ -885,10 +886,11 @@ export function parseAppTicket(ticket: Buffer): AppTicket | undefined { // Adapted from the structs found at https://github.com/SteamRE/SteamKit/blob/master/Resources/Structs/steam3_appticket.hsl const appTicket: AppTicket = { steamId: "", - appId: 0, + appId: "", expiry: new Date(), dlc: [], valid: false, + hash: "" } let offset = 0 @@ -906,8 +908,9 @@ export function parseAppTicket(ticket: Buffer): AppTicket | undefined { offset + ticket.readUInt32LE(offset), ) appTicket.steamId = ticket.readBigUInt64LE((offset += 8)).toString() - appTicket.appId = ticket.readUInt32LE((offset += 8)) + appTicket.appId = ticket.readUInt32LE((offset += 8)).toString() appTicket.expiry = new Date(ticket.readUInt32LE((offset += 20)) * 1000) + appTicket.hash = createHash("sha256").update(ownershipTicket).digest("hex") // Skip past licenses const licensesLength = ticket.readUInt16LE((offset += 4)) @@ -917,7 +920,7 @@ export function parseAppTicket(ticket: Buffer): AppTicket | undefined { offset += 2 for (let i = 0; i < dlcLength; ++i) { - appTicket.dlc.push(ticket.readUInt32LE(offset)) + appTicket.dlc.push(ticket.readUInt32LE(offset).toString()) // DLC Licenses array, usually empty const licensesLength = ticket.readUInt16LE((offset += 4)) From 89132255817b761e37b051519cbdb33079fa3854 Mon Sep 17 00:00:00 2001 From: AnthonyFuller <24512050+AnthonyFuller@users.noreply.github.com> Date: Mon, 30 Mar 2026 22:51:31 +0100 Subject: [PATCH 05/11] refactor(SteamStrategy): parse ticket before calling out to steam, update to reflect changes made to parseAppTicket, use app id from the ticket, change cache to a set --- components/entitlementStrategies.ts | 53 ++++++++++++++--------------- components/oauthToken.ts | 2 +- 2 files changed, 26 insertions(+), 29 deletions(-) diff --git a/components/entitlementStrategies.ts b/components/entitlementStrategies.ts index 053e343b9..44cae3e7e 100644 --- a/components/entitlementStrategies.ts +++ b/components/entitlementStrategies.ts @@ -27,14 +27,13 @@ import { STEAM_NAMESPACE_2016, } from "./platformEntitlements" import { GameVersion } from "./types/types" -import { getRemoteService, parseAppTicket } from "./utils" +import { AppTicket, getRemoteService, parseAppTicket } from "./utils" import { getFlag } from "./flags" -import { createHash } from "crypto" -// An in-memory cache of Steam ownership tickets to entitlements (they're valid for up to 21 days) +// An in-memory cache of valid Steam ownership ticket hashes (they're valid for up to 21 days) // For most users, this won't provide any benefit since they'll be restarting Peacock often, // but this is more here for those running it 24/7 on a server somewhere. -const STEAM_TICKET_CACHE: Map = new Map() +const STEAM_TICKET_CACHE: Set = new Set() /** * The base class for an entitlement strategy. @@ -106,9 +105,8 @@ export class SteamStrategy extends EntitlementStrategy { private readonly _apiKey: string = getFlag("steamApiKey") as SteamAuthMethod public readonly isValid: boolean = false - constructor(private readonly _appId: string) { + constructor() { super() - this._appId = _appId const method = getFlag("steamAuthenticationMethod") as SteamAuthMethod @@ -159,10 +157,10 @@ export class SteamStrategy extends EntitlementStrategy { private async _getFromSteam( clientToken: string, + ticket: AppTicket, identity: string, ): Promise { - const ticket = parseAppTicket(Buffer.from(clientToken, "hex")) - + // We already check this before calling, but it's just for sanity. if (!ticket?.valid) { return { success: false, @@ -177,7 +175,7 @@ export class SteamStrategy extends EntitlementStrategy { { params: { key: this._apiKey, - appid: this._appId, + appid: ticket.appId, ticket: clientToken, identity, }, @@ -210,13 +208,11 @@ export class SteamStrategy extends EntitlementStrategy { } } + ticket.dlc.unshift(ticket.appId) return { success: true, steamId: data.response.params!.steamid, - entitlements: [ - ticket.appId.toString(), - ...ticket.dlc.map((dlc) => dlc.toString()), - ], + entitlements: ticket.dlc, } } catch (error) { if (error instanceof AxiosError) { @@ -243,17 +239,17 @@ export class SteamStrategy extends EntitlementStrategy { ): Promise { if (!this.isValid) return [] - const hash = createHash("sha256") - .update( - clientToken.startsWith("14000000") && - clientToken.length > 52 * 2 - ? clientToken.substring(52 * 2) // Skip 52 bytes to get the ownership ticket (this part can be valid for up to 21 days) - : clientToken, - ) - .digest("hex") + const ticket = parseAppTicket(Buffer.from(clientToken, "hex")) + + if (!ticket?.valid) + { + log(LogLevel.WARN, "Invalid ticket.", "SteamStrategy") + return [] + } - if (STEAM_TICKET_CACHE.has(hash)) { - return STEAM_TICKET_CACHE.get(hash)! + if (STEAM_TICKET_CACHE.has(ticket.hash)) { + ticket.dlc.unshift(ticket.appId) + return ticket.dlc } const authMethod = getFlag( @@ -261,7 +257,7 @@ export class SteamStrategy extends EntitlementStrategy { ) as SteamAuthMethod const res = await (authMethod === "BACKEND" ? this._getFromBackend(clientToken) - : this._getFromSteam(clientToken, identity)) + : this._getFromSteam(clientToken, ticket, identity)) if (!res.success) { log( @@ -281,7 +277,7 @@ export class SteamStrategy extends EntitlementStrategy { return [] } - STEAM_TICKET_CACHE.set(hash, res.entitlements) + STEAM_TICKET_CACHE.add(ticket.hash) return res.entitlements } @@ -294,13 +290,14 @@ export class SteamStrategy extends EntitlementStrategy { */ export class IOIStrategy extends EntitlementStrategy { private readonly _remoteService: string + private readonly _issuerId: string constructor( gameVersion: GameVersion, - private readonly issuerId: string, + issuerId: string, ) { super() - this.issuerId = issuerId + this._issuerId = issuerId this._remoteService = getRemoteService(gameVersion)! } @@ -319,7 +316,7 @@ export class IOIStrategy extends EntitlementStrategy { `https://${this._remoteService}.hitman.io/authentication/api/userchannel/ProfileService/GetPlatformEntitlements`, false, { - issuerId: this.issuerId, + issuerId: this._issuerId, }, ) } catch (error) { diff --git a/components/oauthToken.ts b/components/oauthToken.ts index 2a2268431..580baa2a6 100644 --- a/components/oauthToken.ts +++ b/components/oauthToken.ts @@ -298,7 +298,7 @@ export async function handleOAuthToken( req.body.epic_userid!, ) } else if (external_platform === "steam") { - let ents = await new SteamStrategy(external_appid).get( + let ents = await new SteamStrategy().get( req.body.steam_clienttoken!, req.body.steam_identity!, req.body.steam_userid!, From 9bb00bc2c83404c54d1799289ccf2f577ed293be Mon Sep 17 00:00:00 2001 From: AnthonyFuller <24512050+AnthonyFuller@users.noreply.github.com> Date: Mon, 30 Mar 2026 23:04:04 +0100 Subject: [PATCH 06/11] feat(SteamStrategy): implement validation via the backend (and a prettier run) --- components/entitlementStrategies.ts | 86 ++++++++++++++++++++++++----- components/utils.ts | 6 +- 2 files changed, 77 insertions(+), 15 deletions(-) diff --git a/components/entitlementStrategies.ts b/components/entitlementStrategies.ts index 44cae3e7e..dcb38e0cd 100644 --- a/components/entitlementStrategies.ts +++ b/components/entitlementStrategies.ts @@ -27,7 +27,12 @@ import { STEAM_NAMESPACE_2016, } from "./platformEntitlements" import { GameVersion } from "./types/types" -import { AppTicket, getRemoteService, parseAppTicket } from "./utils" +import { + AppTicket, + getRemoteService, + parseAppTicket, + PEACOCKVERSTRING, +} from "./utils" import { getFlag } from "./flags" // An in-memory cache of valid Steam ownership ticket hashes (they're valid for up to 21 days) @@ -100,6 +105,16 @@ type SteamAuthResponse = { } } } +type SteamAuthBackendResponse = + | { + success: true + steam_id: string + entitlements: string[] + } + | { + success: false + error: string + } export class SteamStrategy extends EntitlementStrategy { private readonly _apiKey: string = getFlag("steamApiKey") as SteamAuthMethod @@ -146,12 +161,61 @@ export class SteamStrategy extends EntitlementStrategy { } private async _getFromBackend( - _clientToken: string, + clientToken: string, + identity: string, ): Promise { - return { - success: false, - code: 500, - error: "Backend validation not implemented", + try { + const host = getFlag("leaderboardsHost") as string + const resp = await axios.post( + `${host}/peacock/steam/validate_ticket`, + { + ticket: clientToken, + identity, + }, + { + headers: { + "Peacock-Version": PEACOCKVERSTRING, + }, + }, + ) + + if (resp.status !== 200 && resp.status !== 400) { + return { + success: false, + code: resp.status, + error: `${resp.statusText}`, + } + } + + const data = resp.data as SteamAuthBackendResponse + + if (!data.success) { + return { + success: false, + code: resp.status, + error: data.error, + } + } + + return { + success: true, + steamId: data.steam_id, + entitlements: data.entitlements, + } + } catch (error) { + if (error instanceof AxiosError) { + return { + success: false, + code: error.response?.status ?? 400, + error: `${error.response?.statusText}`, + } + } else { + return { + success: false, + code: 400, + error: `${error}`, + } + } } } @@ -241,8 +305,7 @@ export class SteamStrategy extends EntitlementStrategy { const ticket = parseAppTicket(Buffer.from(clientToken, "hex")) - if (!ticket?.valid) - { + if (!ticket?.valid) { log(LogLevel.WARN, "Invalid ticket.", "SteamStrategy") return [] } @@ -256,7 +319,7 @@ export class SteamStrategy extends EntitlementStrategy { "steamAuthenticationMethod", ) as SteamAuthMethod const res = await (authMethod === "BACKEND" - ? this._getFromBackend(clientToken) + ? this._getFromBackend(clientToken, identity) : this._getFromSteam(clientToken, ticket, identity)) if (!res.success) { @@ -292,10 +355,7 @@ export class IOIStrategy extends EntitlementStrategy { private readonly _remoteService: string private readonly _issuerId: string - constructor( - gameVersion: GameVersion, - issuerId: string, - ) { + constructor(gameVersion: GameVersion, issuerId: string) { super() this._issuerId = issuerId this._remoteService = getRemoteService(gameVersion)! diff --git a/components/utils.ts b/components/utils.ts index 4fcf9f722..3eaf9b618 100644 --- a/components/utils.ts +++ b/components/utils.ts @@ -890,7 +890,7 @@ export function parseAppTicket(ticket: Buffer): AppTicket | undefined { expiry: new Date(), dlc: [], valid: false, - hash: "" + hash: "", } let offset = 0 @@ -910,7 +910,9 @@ export function parseAppTicket(ticket: Buffer): AppTicket | undefined { appTicket.steamId = ticket.readBigUInt64LE((offset += 8)).toString() appTicket.appId = ticket.readUInt32LE((offset += 8)).toString() appTicket.expiry = new Date(ticket.readUInt32LE((offset += 20)) * 1000) - appTicket.hash = createHash("sha256").update(ownershipTicket).digest("hex") + appTicket.hash = createHash("sha256") + .update(ownershipTicket) + .digest("hex") // Skip past licenses const licensesLength = ticket.readUInt16LE((offset += 4)) From 678334259af3820f93aa1f6abd396329e4ed4537 Mon Sep 17 00:00:00 2001 From: AnthonyFuller <24512050+AnthonyFuller@users.noreply.github.com> Date: Mon, 30 Mar 2026 23:22:02 +0100 Subject: [PATCH 07/11] feat(parseAppTicket): validate ownership ticket version --- components/utils.ts | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/components/utils.ts b/components/utils.ts index 3eaf9b618..c43ba8837 100644 --- a/components/utils.ts +++ b/components/utils.ts @@ -907,7 +907,15 @@ export function parseAppTicket(ticket: Buffer): AppTicket | undefined { offset, offset + ticket.readUInt32LE(offset), ) - appTicket.steamId = ticket.readBigUInt64LE((offset += 8)).toString() + + const ticketVersion = ticket.readUInt32LE((offset += 4)) + if (ticketVersion !== 4) + { + log(LogLevel.ERROR, `Encountered unknown ownership ticket version! Expected: 4, Got: ${ticketVersion}`, "parseAppTicket") + return + } + + appTicket.steamId = ticket.readBigUInt64LE((offset += 4)).toString() appTicket.appId = ticket.readUInt32LE((offset += 8)).toString() appTicket.expiry = new Date(ticket.readUInt32LE((offset += 20)) * 1000) appTicket.hash = createHash("sha256") From 7eed7e53bcae4e77f16c1ca28649c15508a8e9f1 Mon Sep 17 00:00:00 2001 From: AnthonyFuller <24512050+AnthonyFuller@users.noreply.github.com> Date: Mon, 30 Mar 2026 23:24:45 +0100 Subject: [PATCH 08/11] chore: prettier run --- components/utils.ts | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/components/utils.ts b/components/utils.ts index c43ba8837..bbc51c125 100644 --- a/components/utils.ts +++ b/components/utils.ts @@ -909,9 +909,12 @@ export function parseAppTicket(ticket: Buffer): AppTicket | undefined { ) const ticketVersion = ticket.readUInt32LE((offset += 4)) - if (ticketVersion !== 4) - { - log(LogLevel.ERROR, `Encountered unknown ownership ticket version! Expected: 4, Got: ${ticketVersion}`, "parseAppTicket") + if (ticketVersion !== 4) { + log( + LogLevel.ERROR, + `Encountered unknown ownership ticket version! Expected: 4, Got: ${ticketVersion}`, + "parseAppTicket", + ) return } From 35378e1ec0655795bf47974b5e99c05f2443cab4 Mon Sep 17 00:00:00 2001 From: AnthonyFuller <24512050+AnthonyFuller@users.noreply.github.com> Date: Mon, 30 Mar 2026 23:26:29 +0100 Subject: [PATCH 09/11] chore: prettier run --- components/utils.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/components/utils.ts b/components/utils.ts index bbc51c125..0ade23419 100644 --- a/components/utils.ts +++ b/components/utils.ts @@ -909,6 +909,7 @@ export function parseAppTicket(ticket: Buffer): AppTicket | undefined { ) const ticketVersion = ticket.readUInt32LE((offset += 4)) + if (ticketVersion !== 4) { log( LogLevel.ERROR, From 263c19d6d93f4672250a4b4f358cbfa14490b031 Mon Sep 17 00:00:00 2001 From: AnthonyFuller <24512050+AnthonyFuller@users.noreply.github.com> Date: Tue, 31 Mar 2026 00:49:36 +0100 Subject: [PATCH 10/11] fix(SteamStrategy/Backend): don't throw if the response status is 400 --- components/entitlementStrategies.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/components/entitlementStrategies.ts b/components/entitlementStrategies.ts index dcb38e0cd..564bfd5d0 100644 --- a/components/entitlementStrategies.ts +++ b/components/entitlementStrategies.ts @@ -176,6 +176,7 @@ export class SteamStrategy extends EntitlementStrategy { headers: { "Peacock-Version": PEACOCKVERSTRING, }, + validateStatus: status => status === 400 || (status >= 200 && status < 300) }, ) From dbe2cb31d8801996ba9431a2ef3a55fb8922714a Mon Sep 17 00:00:00 2001 From: AnthonyFuller <24512050+AnthonyFuller@users.noreply.github.com> Date: Wed, 1 Apr 2026 19:33:10 +0100 Subject: [PATCH 11/11] chore: prettier run --- components/entitlementStrategies.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/components/entitlementStrategies.ts b/components/entitlementStrategies.ts index 564bfd5d0..9152dd03e 100644 --- a/components/entitlementStrategies.ts +++ b/components/entitlementStrategies.ts @@ -176,7 +176,8 @@ export class SteamStrategy extends EntitlementStrategy { headers: { "Peacock-Version": PEACOCKVERSTRING, }, - validateStatus: status => status === 400 || (status >= 200 && status < 300) + validateStatus: (status) => + status === 400 || (status >= 200 && status < 300), }, )