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.
Files changed (53) hide show
  1. package/lib/endpoints/accountinformationv1.d.ts +92 -95
  2. package/lib/endpoints/accountsettingsv1.d.ts +88 -110
  3. package/lib/endpoints/adconfigurationv2.d.ts +108 -215
  4. package/lib/endpoints/assetdeliveryv1.d.ts +236 -237
  5. package/lib/endpoints/assetdeliveryv2.d.ts +180 -181
  6. package/lib/endpoints/authv1.d.ts +479 -521
  7. package/lib/endpoints/authv2.d.ts +209 -221
  8. package/lib/endpoints/authv3.d.ts +32 -35
  9. package/lib/endpoints/avatarv1.d.ts +112 -117
  10. package/lib/endpoints/avatarv2.d.ts +71 -76
  11. package/lib/endpoints/avatarv3.d.ts +27 -33
  12. package/lib/endpoints/badgesv1.d.ts +92 -99
  13. package/lib/endpoints/catalogv1.d.ts +207 -233
  14. package/lib/endpoints/catalogv2.d.ts +81 -119
  15. package/lib/endpoints/clientsettingsv1.d.ts +33 -35
  16. package/lib/endpoints/clientsettingsv2.d.ts +62 -68
  17. package/lib/endpoints/contactsv1.d.ts +18 -20
  18. package/lib/endpoints/developv1.d.ts +230 -293
  19. package/lib/endpoints/developv2.d.ts +84 -137
  20. package/lib/endpoints/economycreatorstatsv1.d.ts +12 -12
  21. package/lib/endpoints/economyv1.d.ts +6 -6
  22. package/lib/endpoints/engagementpayoutsv1.d.ts +9 -9
  23. package/lib/endpoints/followingsv1.d.ts +33 -34
  24. package/lib/endpoints/followingsv2.d.ts +9 -9
  25. package/lib/endpoints/friendsv1.d.ts +200 -237
  26. package/lib/endpoints/gameinternationalizationv1.d.ts +1509 -1642
  27. package/lib/endpoints/gameinternationalizationv2.d.ts +62 -64
  28. package/lib/endpoints/gamesv1.d.ts +181 -143
  29. package/lib/endpoints/gamesv2.d.ts +161 -169
  30. package/lib/endpoints/groupsv1.d.ts +938 -988
  31. package/lib/endpoints/groupsv2.d.ts +259 -278
  32. package/lib/endpoints/inventoryv1.d.ts +91 -122
  33. package/lib/endpoints/inventoryv2.d.ts +163 -180
  34. package/lib/endpoints/itemconfigurationv1.d.ts +33 -40
  35. package/lib/endpoints/localev1.d.ts +47 -48
  36. package/lib/endpoints/localizationtablesv1.d.ts +315 -353
  37. package/lib/endpoints/matchmakingv1.d.ts +184 -190
  38. package/lib/endpoints/notificationsv2.d.ts +144 -158
  39. package/lib/endpoints/premiumfeaturesv1.d.ts +14 -14
  40. package/lib/endpoints/presencev1.d.ts +7 -12
  41. package/lib/endpoints/privatemessagesv1.d.ts +56 -64
  42. package/lib/endpoints/publishv1.d.ts +32 -32
  43. package/lib/endpoints/thumbnailsresizerv1.d.ts +52 -53
  44. package/lib/endpoints/thumbnailsv1.d.ts +325 -326
  45. package/lib/endpoints/thumbnailsv1.js +24 -24
  46. package/lib/endpoints/tradesv1.d.ts +92 -104
  47. package/lib/endpoints/tradesv2.d.ts +81 -84
  48. package/lib/endpoints/translationrolesv1.d.ts +46 -51
  49. package/lib/endpoints/twostepverificationv1.d.ts +233 -237
  50. package/lib/endpoints/usersv1.d.ts +110 -114
  51. package/lib/index.d.ts +56 -1
  52. package/lib/index.js +159 -3
  53. 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
- exports.hbaClient = new roblox_bat_1.HBAClient({
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
- exports.hbaClient.isAuthenticated = headers.get('cookie')?.includes('.ROBLOSECURITY');
791
+ activeHbaClient.isAuthenticated = headers.get('cookie')?.includes('.ROBLOSECURITY');
644
792
  }
645
- const setHeaders = await exports.hbaClient.generateBaseHeaders(url, info?.method, info?.credentials === 'include', info?.body);
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.8.0",
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",