rozod 6.8.0 → 6.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/endpoints/accountinformationv1.d.ts +92 -95
- package/lib/endpoints/accountsettingsv1.d.ts +88 -110
- package/lib/endpoints/adconfigurationv2.d.ts +108 -215
- package/lib/endpoints/assetdeliveryv1.d.ts +236 -237
- package/lib/endpoints/assetdeliveryv2.d.ts +180 -181
- package/lib/endpoints/authv1.d.ts +479 -521
- package/lib/endpoints/authv2.d.ts +209 -221
- package/lib/endpoints/authv3.d.ts +32 -35
- package/lib/endpoints/avatarv1.d.ts +112 -117
- package/lib/endpoints/avatarv2.d.ts +71 -76
- package/lib/endpoints/avatarv3.d.ts +27 -33
- package/lib/endpoints/badgesv1.d.ts +92 -99
- package/lib/endpoints/catalogv1.d.ts +207 -233
- package/lib/endpoints/catalogv2.d.ts +81 -119
- package/lib/endpoints/clientsettingsv1.d.ts +33 -35
- package/lib/endpoints/clientsettingsv2.d.ts +62 -68
- package/lib/endpoints/contactsv1.d.ts +18 -20
- package/lib/endpoints/developv1.d.ts +230 -293
- package/lib/endpoints/developv2.d.ts +84 -137
- package/lib/endpoints/economycreatorstatsv1.d.ts +12 -12
- package/lib/endpoints/economyv1.d.ts +6 -6
- package/lib/endpoints/engagementpayoutsv1.d.ts +9 -9
- package/lib/endpoints/followingsv1.d.ts +33 -34
- package/lib/endpoints/followingsv2.d.ts +9 -9
- package/lib/endpoints/friendsv1.d.ts +200 -237
- package/lib/endpoints/gameinternationalizationv1.d.ts +1509 -1642
- package/lib/endpoints/gameinternationalizationv2.d.ts +62 -64
- package/lib/endpoints/gamesv1.d.ts +181 -143
- package/lib/endpoints/gamesv2.d.ts +161 -169
- package/lib/endpoints/groupsv1.d.ts +938 -988
- package/lib/endpoints/groupsv2.d.ts +259 -278
- package/lib/endpoints/inventoryv1.d.ts +91 -122
- package/lib/endpoints/inventoryv2.d.ts +163 -180
- package/lib/endpoints/itemconfigurationv1.d.ts +33 -40
- package/lib/endpoints/localev1.d.ts +47 -48
- package/lib/endpoints/localizationtablesv1.d.ts +315 -353
- package/lib/endpoints/matchmakingv1.d.ts +184 -190
- package/lib/endpoints/notificationsv2.d.ts +144 -158
- package/lib/endpoints/premiumfeaturesv1.d.ts +14 -14
- package/lib/endpoints/presencev1.d.ts +7 -12
- package/lib/endpoints/privatemessagesv1.d.ts +56 -64
- package/lib/endpoints/publishv1.d.ts +32 -32
- package/lib/endpoints/thumbnailsresizerv1.d.ts +52 -53
- package/lib/endpoints/thumbnailsv1.d.ts +325 -326
- package/lib/endpoints/thumbnailsv1.js +24 -24
- package/lib/endpoints/tradesv1.d.ts +92 -104
- package/lib/endpoints/tradesv2.d.ts +81 -84
- package/lib/endpoints/translationrolesv1.d.ts +46 -51
- package/lib/endpoints/twostepverificationv1.d.ts +233 -237
- package/lib/endpoints/usersv1.d.ts +110 -114
- package/lib/index.d.ts +56 -1
- package/lib/index.js +159 -3
- package/package.json +2 -2
package/lib/index.js
CHANGED
|
@@ -135,9 +135,119 @@ function prepareRequestBody(method, requestFormat, body, headers) {
|
|
|
135
135
|
return body;
|
|
136
136
|
}
|
|
137
137
|
const onRobloxSite = 'document' in globalThis && globalThis.location.href.includes('.roblox.com');
|
|
138
|
-
|
|
138
|
+
/** How long a failed token metadata lookup is remembered before retrying. */
|
|
139
|
+
const HBA_METADATA_FAILURE_TTL = 5 * 60 * 1000;
|
|
140
|
+
/** Upper bound on the token metadata page fetch. */
|
|
141
|
+
const HBA_METADATA_FETCH_TIMEOUT = 10_000;
|
|
142
|
+
/**
|
|
143
|
+
* HBAClient hardened for server (Node/Bun) usage:
|
|
144
|
+
*
|
|
145
|
+
* - `generateBaseHeaders` short-circuits when no crypto key source exists
|
|
146
|
+
* (no supplied key pair and no IndexedDB): a BAT can never be signed, so
|
|
147
|
+
* the token metadata page must not be fetched at all.
|
|
148
|
+
* - `getTokenMetadata` remembers failed lookups for a TTL. The base class
|
|
149
|
+
* only caches successes, so an unparseable metadata page (e.g. a bot
|
|
150
|
+
* challenge served to a datacenter IP) would otherwise be refetched on
|
|
151
|
+
* every request — unbounded network and memory churn on hot paths.
|
|
152
|
+
* - The metadata page fetch is bounded by a timeout and sent with the
|
|
153
|
+
* configured server user agent instead of the runtime default, which is
|
|
154
|
+
* far more likely to receive a challenge page.
|
|
155
|
+
*/
|
|
156
|
+
class ServerSafeHBAClient extends roblox_bat_1.HBAClient {
|
|
157
|
+
metadataFailureAt = 0;
|
|
158
|
+
metadataInFlight;
|
|
159
|
+
metadataSource;
|
|
160
|
+
constructor(props) {
|
|
161
|
+
super(props);
|
|
162
|
+
this.metadataSource = props?.metadataSource;
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Forget a remembered metadata failure so the next lookup retries
|
|
166
|
+
* immediately. Called when HBA keys are (re)configured — a lookup that
|
|
167
|
+
* failed under the old configuration says nothing about the new one.
|
|
168
|
+
*/
|
|
169
|
+
resetMetadataFailure() {
|
|
170
|
+
this.metadataFailureAt = 0;
|
|
171
|
+
}
|
|
172
|
+
async generateBaseHeaders(requestUrl, requestMethod, includeCredentials, body) {
|
|
173
|
+
if (!this.suppliedCryptoKeyPair && !('indexedDB' in globalThis)) {
|
|
174
|
+
return {};
|
|
175
|
+
}
|
|
176
|
+
return super.generateBaseHeaders(requestUrl, requestMethod, includeCredentials, body);
|
|
177
|
+
}
|
|
178
|
+
async getTokenMetadata(uncached) {
|
|
179
|
+
// Token metadata is account-agnostic; per-cookie clients delegate to
|
|
180
|
+
// one shared client so its caches (success, failure TTL, in-flight)
|
|
181
|
+
// apply pool-wide instead of once per account.
|
|
182
|
+
if (this.metadataSource) {
|
|
183
|
+
return this.metadataSource.getTokenMetadata(uncached);
|
|
184
|
+
}
|
|
185
|
+
if (!uncached) {
|
|
186
|
+
if (this.metadataFailureAt && Date.now() - this.metadataFailureAt < HBA_METADATA_FAILURE_TTL) {
|
|
187
|
+
return null;
|
|
188
|
+
}
|
|
189
|
+
// The base class only shares in-flight lookups that resolve
|
|
190
|
+
// successfully; sharing here makes concurrent callers ride one
|
|
191
|
+
// lookup even when it fails, instead of each retrying in turn.
|
|
192
|
+
if (this.metadataInFlight) {
|
|
193
|
+
return this.metadataInFlight;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
let promise;
|
|
197
|
+
promise = (async () => {
|
|
198
|
+
try {
|
|
199
|
+
const metadata = await super.getTokenMetadata(uncached);
|
|
200
|
+
this.metadataFailureAt = metadata ? 0 : Date.now();
|
|
201
|
+
return metadata;
|
|
202
|
+
}
|
|
203
|
+
finally {
|
|
204
|
+
// Guarded so an overlapping lookup's reference is not clobbered
|
|
205
|
+
if (this.metadataInFlight === promise) {
|
|
206
|
+
this.metadataInFlight = undefined;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
})();
|
|
210
|
+
this.metadataInFlight = promise;
|
|
211
|
+
return promise;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
exports.hbaClient = new ServerSafeHBAClient({
|
|
139
215
|
onSite: onRobloxSite,
|
|
216
|
+
// Applies to every non-on-site context, including extension background
|
|
217
|
+
// and popup pages: the timeout is desirable everywhere, and browsers
|
|
218
|
+
// silently drop the user-agent header (it is fetch-forbidden), so this
|
|
219
|
+
// only takes effect in Node/Bun.
|
|
220
|
+
...(onRobloxSite
|
|
221
|
+
? {}
|
|
222
|
+
: {
|
|
223
|
+
fetch: (url, params) => {
|
|
224
|
+
const headers = new Headers(params?.headers);
|
|
225
|
+
if (!headers.has('user-agent')) {
|
|
226
|
+
const userAgent = getServerUserAgent();
|
|
227
|
+
if (userAgent) {
|
|
228
|
+
headers.set('user-agent', userAgent);
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
return globalThis.fetch(url, {
|
|
232
|
+
...params,
|
|
233
|
+
headers,
|
|
234
|
+
signal: AbortSignal.timeout(HBA_METADATA_FETCH_TIMEOUT),
|
|
235
|
+
});
|
|
236
|
+
},
|
|
237
|
+
}),
|
|
140
238
|
});
|
|
239
|
+
/**
|
|
240
|
+
* BAT signing clients aligned index-for-index with the configured cookie
|
|
241
|
+
* pool. `null` marks cookies whose sessions have no registered key. Unset
|
|
242
|
+
* when hbaKeys was not configured as an array.
|
|
243
|
+
*/
|
|
244
|
+
let perCookieHbaClients;
|
|
245
|
+
function hbaClientForCookie(cookieIndex) {
|
|
246
|
+
if (cookieIndex === undefined || !perCookieHbaClients) {
|
|
247
|
+
return exports.hbaClient;
|
|
248
|
+
}
|
|
249
|
+
return perCookieHbaClients[cookieIndex] ?? exports.hbaClient;
|
|
250
|
+
}
|
|
141
251
|
// ============================================================================
|
|
142
252
|
// Server/Node.js Configuration
|
|
143
253
|
// ============================================================================
|
|
@@ -200,6 +310,38 @@ function configureServer(config) {
|
|
|
200
310
|
serverConfig.userAgents = config.userAgents;
|
|
201
311
|
serverConfig.userAgentRotation = config.userAgentRotation;
|
|
202
312
|
serverConfig.onCookieRefresh = config.onCookieRefresh;
|
|
313
|
+
// Keys live on the HBA clients, not in serverConfig — they are the single
|
|
314
|
+
// source of truth that getServerConfig() reads back from. A single shared
|
|
315
|
+
// pair is kept when hbaKeys is omitted so changeHBAKeys() callers keep
|
|
316
|
+
// theirs, but per-cookie keys are positionally bound to the cookies array
|
|
317
|
+
// passed in the same call: reconfiguring without them must drop them, or
|
|
318
|
+
// a changed pool would sign with other sessions' keys.
|
|
319
|
+
if (config.hbaKeys === undefined) {
|
|
320
|
+
perCookieHbaClients = undefined;
|
|
321
|
+
}
|
|
322
|
+
else {
|
|
323
|
+
if (Array.isArray(config.hbaKeys)) {
|
|
324
|
+
const poolSize = Array.isArray(config.cookies) ? config.cookies.length : config.cookies ? 1 : 0;
|
|
325
|
+
if (config.hbaKeys.length !== poolSize) {
|
|
326
|
+
throw new Error(`hbaKeys array length (${config.hbaKeys.length}) must match the cookie pool length (${poolSize}); use null for cookies without registered keys`);
|
|
327
|
+
}
|
|
328
|
+
perCookieHbaClients = config.hbaKeys.map((keys) => keys
|
|
329
|
+
? new ServerSafeHBAClient({
|
|
330
|
+
onSite: onRobloxSite,
|
|
331
|
+
keys,
|
|
332
|
+
metadataSource: exports.hbaClient,
|
|
333
|
+
})
|
|
334
|
+
: null);
|
|
335
|
+
// The shared client must not sign: requests that bypass the cookie
|
|
336
|
+
// pool have no session to match a key to.
|
|
337
|
+
exports.hbaClient.suppliedCryptoKeyPair = undefined;
|
|
338
|
+
}
|
|
339
|
+
else {
|
|
340
|
+
exports.hbaClient.suppliedCryptoKeyPair = config.hbaKeys;
|
|
341
|
+
perCookieHbaClients = undefined;
|
|
342
|
+
}
|
|
343
|
+
exports.hbaClient.resetMetadataFailure();
|
|
344
|
+
}
|
|
203
345
|
// Reset indices and session values when config changes
|
|
204
346
|
serverConfig._cookieIndex = 0;
|
|
205
347
|
serverConfig._userAgentIndex = 0;
|
|
@@ -216,6 +358,8 @@ function clearServerConfig() {
|
|
|
216
358
|
serverConfig.userAgents = undefined;
|
|
217
359
|
serverConfig.userAgentRotation = undefined;
|
|
218
360
|
serverConfig.onCookieRefresh = undefined;
|
|
361
|
+
exports.hbaClient.suppliedCryptoKeyPair = undefined;
|
|
362
|
+
perCookieHbaClients = undefined;
|
|
219
363
|
serverConfig._cookieIndex = 0;
|
|
220
364
|
serverConfig._userAgentIndex = 0;
|
|
221
365
|
serverConfig._sessionUserAgent = undefined;
|
|
@@ -232,6 +376,9 @@ function getServerConfig() {
|
|
|
232
376
|
userAgents: serverConfig.userAgents,
|
|
233
377
|
userAgentRotation: serverConfig.userAgentRotation,
|
|
234
378
|
onCookieRefresh: serverConfig.onCookieRefresh,
|
|
379
|
+
hbaKeys: perCookieHbaClients
|
|
380
|
+
? perCookieHbaClients.map((client) => client?.suppliedCryptoKeyPair ?? null)
|
|
381
|
+
: exports.hbaClient.suppliedCryptoKeyPair,
|
|
235
382
|
};
|
|
236
383
|
}
|
|
237
384
|
function selectFromPool(pool, rotation, indexKey, sessionKey) {
|
|
@@ -639,10 +786,11 @@ async function fetch(url, info, challengeData, csrfRetries = 0, challengeRetries
|
|
|
639
786
|
// Normal flow: apply all defaults including cookie selection
|
|
640
787
|
cookieSelection = applyServerDefaults(headers, url);
|
|
641
788
|
}
|
|
789
|
+
const activeHbaClient = hbaClientForCookie(cookieSelection?.index);
|
|
642
790
|
if (!onRobloxSite) {
|
|
643
|
-
|
|
791
|
+
activeHbaClient.isAuthenticated = headers.get('cookie')?.includes('.ROBLOSECURITY');
|
|
644
792
|
}
|
|
645
|
-
const setHeaders = await
|
|
793
|
+
const setHeaders = await activeHbaClient.generateBaseHeaders(url, info?.method, info?.credentials === 'include', info?.body);
|
|
646
794
|
for (const key in setHeaders) {
|
|
647
795
|
headers.set(key, setHeaders[key]);
|
|
648
796
|
}
|
|
@@ -694,10 +842,18 @@ async function fetch(url, info, challengeData, csrfRetries = 0, challengeRetries
|
|
|
694
842
|
/**
|
|
695
843
|
* Allows you to change the Crypto Key pair used by the internal hardware-based authentication signatures. This should only be used in a NodeJS context.
|
|
696
844
|
*
|
|
845
|
+
* The pair applies to every request: any per-cookie keys configured via
|
|
846
|
+
* `configureServer({ hbaKeys: [...] })` are discarded. To key individual
|
|
847
|
+
* cookies in a pool, use `configureServer` instead.
|
|
848
|
+
*
|
|
697
849
|
* @param keys The crypto key pair.
|
|
698
850
|
*/
|
|
699
851
|
function changeHBAKeys(keys) {
|
|
700
852
|
exports.hbaClient.suppliedCryptoKeyPair = keys;
|
|
853
|
+
perCookieHbaClients = undefined;
|
|
854
|
+
if (keys) {
|
|
855
|
+
exports.hbaClient.resetMetadataFailure();
|
|
856
|
+
}
|
|
701
857
|
}
|
|
702
858
|
async function handleRetryFetch(url, requestOptions, retries, retryDelay, body, method) {
|
|
703
859
|
let response = undefined;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "rozod",
|
|
3
|
-
"version": "6.
|
|
3
|
+
"version": "6.9.0",
|
|
4
4
|
"description": "A TypeScript wrapper for the Roblox API",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"types": "lib/index.d.ts",
|
|
@@ -50,7 +50,7 @@
|
|
|
50
50
|
"scripts": {
|
|
51
51
|
"test": "jest --config jestconfig.json",
|
|
52
52
|
"test:exports": "npm run build && node scripts/smoke-test-exports.mjs",
|
|
53
|
-
"build": "tsc && tsc --project tsconfig.declarations.json",
|
|
53
|
+
"build": "tsc && tsc --project tsconfig.declarations.json && node scripts/copy-endpoint-declarations.mjs",
|
|
54
54
|
"format": "prettier --write \"src/**/*.ts\"",
|
|
55
55
|
"prepare": "npm run build",
|
|
56
56
|
"prepublishOnly": "npm test && npm run test:exports",
|