@bedrock-rbx/ocale 0.1.0-beta.13 → 0.1.0-beta.15
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/README.md +128 -0
- package/dist/badges.d.mts +1 -1
- package/dist/badges.mjs +3 -2
- package/dist/badges.mjs.map +1 -1
- package/dist/developer-products.d.mts +1 -1
- package/dist/developer-products.mjs +4 -3
- package/dist/developer-products.mjs.map +1 -1
- package/dist/game-passes.d.mts +1 -1
- package/dist/game-passes.mjs +4 -3
- package/dist/game-passes.mjs.map +1 -1
- package/dist/index.d.mts +3 -12
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +5 -5
- package/dist/luau-execution.d.mts +3 -3
- package/dist/luau-execution.mjs +5 -4
- package/dist/luau-execution.mjs.map +1 -1
- package/dist/places.d.mts +2 -2
- package/dist/places.mjs +5 -4
- package/dist/places.mjs.map +1 -1
- package/dist/{poll-timeout-Dg_QFEqi.mjs → poll-timeout-DMS4UPro.mjs} +2 -2
- package/dist/{poll-timeout-Dg_QFEqi.mjs.map → poll-timeout-DMS4UPro.mjs.map} +1 -1
- package/dist/{polling-BMrYajok.d.mts → polling-Vn5MT-fh.d.mts} +38 -9
- package/dist/{polling-BMrYajok.d.mts.map → polling-Vn5MT-fh.d.mts.map} +1 -1
- package/dist/{polling-helpers-QGjvYq3c.mjs → polling-helpers-B35M3ViY.mjs} +166 -54
- package/dist/{polling-helpers-QGjvYq3c.mjs.map → polling-helpers-B35M3ViY.mjs.map} +1 -1
- package/dist/{price-information-DIrvwCmd.mjs → price-information-DT7_QJN-.mjs} +2 -2
- package/dist/{price-information-DIrvwCmd.mjs.map → price-information-DT7_QJN-.mjs.map} +1 -1
- package/dist/{rate-limit-D1q2Js-z.mjs → rate-limit-nY4BF079.mjs} +19 -2
- package/dist/rate-limit-nY4BF079.mjs.map +1 -0
- package/dist/{resource-client-D6Efj9fU.mjs → resource-client-CG9-BG81.mjs} +67 -237
- package/dist/resource-client-CG9-BG81.mjs.map +1 -0
- package/dist/retry-BzX29aw_.mjs +333 -0
- package/dist/retry-BzX29aw_.mjs.map +1 -0
- package/dist/retry-CXnj3gXI.d.mts +163 -0
- package/dist/retry-CXnj3gXI.d.mts.map +1 -0
- package/dist/storage.d.mts +2 -27
- package/dist/storage.d.mts.map +1 -1
- package/dist/storage.mjs +3 -2
- package/dist/storage.mjs.map +1 -1
- package/dist/testing.d.mts +1 -1
- package/dist/testing.mjs +1 -1
- package/dist/{types-CwtZT1ek.d.mts → types-rzs1NB-j.d.mts} +12 -2
- package/dist/{types-CwtZT1ek.d.mts.map → types-rzs1NB-j.d.mts.map} +1 -1
- package/dist/universes.d.mts +1 -1
- package/dist/universes.mjs +4 -3
- package/dist/universes.mjs.map +1 -1
- package/dist/{validation-DkL5KQqz.mjs → validation-CGsK8aey.mjs} +2 -2
- package/dist/{validation-DkL5KQqz.mjs.map → validation-CGsK8aey.mjs.map} +1 -1
- package/package.json +3 -3
- package/dist/permission-error-DOVtNq3A.mjs +0 -46
- package/dist/permission-error-DOVtNq3A.mjs.map +0 -1
- package/dist/rate-limit-BYuizHoD.d.mts +0 -92
- package/dist/rate-limit-BYuizHoD.d.mts.map +0 -1
- package/dist/rate-limit-D1q2Js-z.mjs.map +0 -1
- package/dist/resource-client-D6Efj9fU.mjs.map +0 -1
|
@@ -0,0 +1,333 @@
|
|
|
1
|
+
import { n as NetworkError, r as ApiError, t as RateLimitError } from "./rate-limit-nY4BF079.mjs";
|
|
2
|
+
//#region src/errors/permission-error.ts
|
|
3
|
+
/**
|
|
4
|
+
* Thrown when the Roblox Open Cloud API returns a 401 or 403 for an operation
|
|
5
|
+
* whose required scopes are known. Subclass of {@link ApiError} carrying the
|
|
6
|
+
* scope strings the caller's credential is missing plus the operation key, so
|
|
7
|
+
* a CLI consumer can tell the user exactly which scope to grant on their API
|
|
8
|
+
* key.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
*
|
|
12
|
+
* ```ts
|
|
13
|
+
* import { PermissionError } from "@bedrock-rbx/ocale";
|
|
14
|
+
*
|
|
15
|
+
* const error = new PermissionError("HTTP 403", {
|
|
16
|
+
* operationKey: "developer-products.create",
|
|
17
|
+
* requiredScopes: ["creator-store-product:write"],
|
|
18
|
+
* statusCode: 403,
|
|
19
|
+
* });
|
|
20
|
+
*
|
|
21
|
+
* expect(error).toBeInstanceOf(PermissionError);
|
|
22
|
+
* expect(error.requiredScopes).toStrictEqual(["creator-store-product:write"]);
|
|
23
|
+
* expect(error.operationKey).toBe("developer-products.create");
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
var PermissionError = class extends ApiError {
|
|
27
|
+
name = "PermissionError";
|
|
28
|
+
operationKey;
|
|
29
|
+
requiredScopes;
|
|
30
|
+
/**
|
|
31
|
+
* Creates a new PermissionError.
|
|
32
|
+
*
|
|
33
|
+
* @param message - Human-readable error description.
|
|
34
|
+
* @param options - Error options including status code, the operation key,
|
|
35
|
+
* and the scopes the caller's credential must carry.
|
|
36
|
+
*/
|
|
37
|
+
constructor(message, options) {
|
|
38
|
+
super(message, options);
|
|
39
|
+
this.operationKey = options.operationKey;
|
|
40
|
+
this.requiredScopes = options.requiredScopes;
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
//#endregion
|
|
44
|
+
//#region src/internal/utils/find-error-code.ts
|
|
45
|
+
/**
|
|
46
|
+
* Maximum cause-chain depth walked by {@link findErrorCode}. Caps pathological
|
|
47
|
+
* self-referential or deeply nested chains; transport failures surface as
|
|
48
|
+
* `NetworkError → TypeError("fetch failed") → OS Error{code}`, so three
|
|
49
|
+
* levels is the expected shape and five leaves headroom.
|
|
50
|
+
*/
|
|
51
|
+
const MAX_DEPTH = 5;
|
|
52
|
+
/**
|
|
53
|
+
* Walks an error's `cause` chain and returns the first node-style string
|
|
54
|
+
* `code` it finds (for example `"ECONNRESET"`, `"ETIMEDOUT"`). Native `fetch`
|
|
55
|
+
* surfaces a transport reset as a `NetworkError` wrapping a
|
|
56
|
+
* `TypeError("fetch failed")` whose own cause carries the OS-level `code`, so
|
|
57
|
+
* the code lives several links down the chain.
|
|
58
|
+
*
|
|
59
|
+
* @example
|
|
60
|
+
*
|
|
61
|
+
* ```ts
|
|
62
|
+
* import { findErrorCode } from "./find-error-code";
|
|
63
|
+
*
|
|
64
|
+
* const root = Object.assign(new Error("read ECONNRESET"), { code: "ECONNRESET" });
|
|
65
|
+
* const outer = new Error("Network request failed", {
|
|
66
|
+
* cause: new TypeError("fetch failed", { cause: root }),
|
|
67
|
+
* });
|
|
68
|
+
*
|
|
69
|
+
* expect(findErrorCode(outer)).toBe("ECONNRESET");
|
|
70
|
+
* ```
|
|
71
|
+
*
|
|
72
|
+
* @param error - The error to inspect; typically a `NetworkError`.
|
|
73
|
+
* @returns The first string `code` in the chain, or `undefined` if none.
|
|
74
|
+
*/
|
|
75
|
+
function findErrorCode(error) {
|
|
76
|
+
let current = error;
|
|
77
|
+
for (let depth = 0; depth < MAX_DEPTH && current instanceof Error; depth += 1) {
|
|
78
|
+
const code = readCode(current);
|
|
79
|
+
if (code !== void 0) return code;
|
|
80
|
+
current = current.cause;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
function readCode(error) {
|
|
84
|
+
const code = Reflect.get(error, "code");
|
|
85
|
+
return typeof code === "string" ? code : void 0;
|
|
86
|
+
}
|
|
87
|
+
//#endregion
|
|
88
|
+
//#region src/internal/http/retry.ts
|
|
89
|
+
/**
|
|
90
|
+
* Transient transport error codes that are safe to retry for idempotent
|
|
91
|
+
* operations. Connection resets, timeouts, and DNS hiccups are recoverable on
|
|
92
|
+
* a retry; a self-aborted request timeout carries no `code` and so is excluded
|
|
93
|
+
* by construction.
|
|
94
|
+
*/
|
|
95
|
+
const TRANSIENT_TRANSPORT_CODES = Object.freeze([
|
|
96
|
+
"ECONNRESET",
|
|
97
|
+
"ECONNREFUSED",
|
|
98
|
+
"ETIMEDOUT",
|
|
99
|
+
"EPIPE",
|
|
100
|
+
"ENETUNREACH",
|
|
101
|
+
"EHOSTDOWN",
|
|
102
|
+
"EAI_AGAIN",
|
|
103
|
+
"UND_ERR_SOCKET"
|
|
104
|
+
]);
|
|
105
|
+
/**
|
|
106
|
+
* Default retry policy for idempotent operations (read, list, update,
|
|
107
|
+
* delete). Safe to retry on rate limits, transient server errors, and
|
|
108
|
+
* transient transport failures.
|
|
109
|
+
*/
|
|
110
|
+
const IDEMPOTENT_METHOD_DEFAULTS = Object.freeze({
|
|
111
|
+
retryableStatuses: Object.freeze([
|
|
112
|
+
429,
|
|
113
|
+
500,
|
|
114
|
+
502,
|
|
115
|
+
503,
|
|
116
|
+
504
|
|
117
|
+
]),
|
|
118
|
+
retryableTransportCodes: TRANSIENT_TRANSPORT_CODES
|
|
119
|
+
});
|
|
120
|
+
/**
|
|
121
|
+
* Default retry policy for create operations. Retries rate limits only (no
|
|
122
|
+
* 5xx and no transport-error retries) to prevent duplicate resources, since
|
|
123
|
+
* Roblox Open Cloud has no idempotency-key support. Consumers who can tolerate
|
|
124
|
+
* a duplicate opt in per request.
|
|
125
|
+
*/
|
|
126
|
+
const CREATE_METHOD_DEFAULTS = Object.freeze({
|
|
127
|
+
retryableStatuses: Object.freeze([429]),
|
|
128
|
+
retryableTransportCodes: Object.freeze([])
|
|
129
|
+
});
|
|
130
|
+
/**
|
|
131
|
+
* Default exponential backoff: 1s → 2s → 4s → 8s → 16s → 30s (capped).
|
|
132
|
+
*
|
|
133
|
+
* @example
|
|
134
|
+
*
|
|
135
|
+
* ```ts
|
|
136
|
+
* import { defaultRetryDelay } from "./retry";
|
|
137
|
+
*
|
|
138
|
+
* expect(defaultRetryDelay(0)).toBe(1000);
|
|
139
|
+
* expect(defaultRetryDelay(4)).toBe(16_000);
|
|
140
|
+
* expect(defaultRetryDelay(10)).toBe(30_000);
|
|
141
|
+
* ```
|
|
142
|
+
*
|
|
143
|
+
* @param attempt - Zero-indexed retry attempt number.
|
|
144
|
+
* @returns Wait duration in milliseconds.
|
|
145
|
+
*/
|
|
146
|
+
function defaultRetryDelay(attempt) {
|
|
147
|
+
return Math.min(1e3 * 2 ** attempt, 3e4);
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Computes how long to wait before the next retry. Prefers the server's
|
|
151
|
+
* suggested delay when the error is a {@link RateLimitError} with a positive
|
|
152
|
+
* `retryAfterSeconds`; otherwise falls through to `retryDelay(attempt)`.
|
|
153
|
+
*
|
|
154
|
+
* @example
|
|
155
|
+
*
|
|
156
|
+
* ```ts
|
|
157
|
+
* import { RateLimitError } from "../../errors/rate-limit.ts";
|
|
158
|
+
* import { computeRetryWaitMs, defaultRetryDelay } from "./retry";
|
|
159
|
+
*
|
|
160
|
+
* const error = new RateLimitError("slow down", { retryAfterSeconds: 3 });
|
|
161
|
+
*
|
|
162
|
+
* expect(computeRetryWaitMs(error, { attempt: 0, retryDelay: defaultRetryDelay })).toBe(
|
|
163
|
+
* 3000,
|
|
164
|
+
* );
|
|
165
|
+
* ```
|
|
166
|
+
*
|
|
167
|
+
* @example
|
|
168
|
+
*
|
|
169
|
+
* ```ts
|
|
170
|
+
* import { ApiError } from "../../errors/api-error.ts";
|
|
171
|
+
* import { computeRetryWaitMs, defaultRetryDelay } from "./retry";
|
|
172
|
+
*
|
|
173
|
+
* const error = new ApiError("server error", { statusCode: 503 });
|
|
174
|
+
*
|
|
175
|
+
* expect(computeRetryWaitMs(error, { attempt: 2, retryDelay: defaultRetryDelay })).toBe(
|
|
176
|
+
* 4000,
|
|
177
|
+
* );
|
|
178
|
+
* ```
|
|
179
|
+
*
|
|
180
|
+
* @param error - The error returned by the failing request.
|
|
181
|
+
* @param options - Retry attempt index and fallback delay function.
|
|
182
|
+
* @returns Wait duration in milliseconds before the next attempt.
|
|
183
|
+
*/
|
|
184
|
+
function computeRetryWaitMs(error, options) {
|
|
185
|
+
if (error instanceof RateLimitError && error.retryAfterSeconds > 0) return error.retryAfterSeconds * 1e3;
|
|
186
|
+
return options.retryDelay(options.attempt);
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* Decides whether a failed request is eligible for retry. {@link RateLimitError}
|
|
190
|
+
* (checked against 429) and {@link ApiError} (checked against its `statusCode`)
|
|
191
|
+
* are retryable when their status is in `retryableStatuses`. A
|
|
192
|
+
* {@link NetworkError} is retryable when its transport code
|
|
193
|
+
* ({@link findErrorCode}) is in `retryableTransportCodes`. This is how
|
|
194
|
+
* transient connection resets recover. All other failures return `false`.
|
|
195
|
+
*
|
|
196
|
+
* @example
|
|
197
|
+
*
|
|
198
|
+
* ```ts
|
|
199
|
+
* import { RateLimitError } from "../../errors/rate-limit.ts";
|
|
200
|
+
* import { shouldRetry } from "./retry";
|
|
201
|
+
*
|
|
202
|
+
* const error = new RateLimitError("", { retryAfterSeconds: 1 });
|
|
203
|
+
*
|
|
204
|
+
* expect(shouldRetry(error, { retryableStatuses: [429], retryableTransportCodes: [] })).toBe(
|
|
205
|
+
* true,
|
|
206
|
+
* );
|
|
207
|
+
* ```
|
|
208
|
+
*
|
|
209
|
+
* @example
|
|
210
|
+
*
|
|
211
|
+
* ```ts
|
|
212
|
+
* import { NetworkError } from "../../errors/network-error.ts";
|
|
213
|
+
* import { shouldRetry } from "./retry";
|
|
214
|
+
*
|
|
215
|
+
* const reset = Object.assign(new Error("read ECONNRESET"), { code: "ECONNRESET" });
|
|
216
|
+
* const error = new NetworkError("Network request failed", { cause: reset });
|
|
217
|
+
*
|
|
218
|
+
* expect(
|
|
219
|
+
* shouldRetry(error, { retryableStatuses: [], retryableTransportCodes: ["ECONNRESET"] }),
|
|
220
|
+
* ).toBe(true);
|
|
221
|
+
* ```
|
|
222
|
+
*
|
|
223
|
+
* @param error - The error returned by the failing request.
|
|
224
|
+
* @param config - Object carrying the retry-eligible status and transport-code lists.
|
|
225
|
+
* @returns `true` if the error should be retried, `false` otherwise.
|
|
226
|
+
*/
|
|
227
|
+
function shouldRetry(error, config) {
|
|
228
|
+
if (error instanceof RateLimitError) return config.retryableStatuses.includes(429);
|
|
229
|
+
if (error instanceof ApiError) return config.retryableStatuses.includes(error.statusCode);
|
|
230
|
+
if (error instanceof NetworkError) {
|
|
231
|
+
const code = findErrorCode(error);
|
|
232
|
+
return code !== void 0 && config.retryableTransportCodes.includes(code);
|
|
233
|
+
}
|
|
234
|
+
return false;
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* Resolves the effective config for a single request by shallow-merging the
|
|
238
|
+
* client config, method defaults, and per-request options. Precedence depends
|
|
239
|
+
* on `methodKind`:
|
|
240
|
+
*
|
|
241
|
+
* - `"create"`: method defaults override client config, so client-level
|
|
242
|
+
* settings cannot silently relax create-method safety. Only explicit
|
|
243
|
+
* per-request `requestOptions` can.
|
|
244
|
+
* - `"idempotent"`: client config overrides method defaults, so consumers
|
|
245
|
+
* can loosen or tighten retry policy globally. `requestOptions` still wins
|
|
246
|
+
* when provided.
|
|
247
|
+
*
|
|
248
|
+
* Array-valued fields like `retryableStatuses` are *replaced*, not extended.
|
|
249
|
+
*
|
|
250
|
+
* @template T - Concrete `RetryResolvable` subtype being merged.
|
|
251
|
+
*
|
|
252
|
+
* @example
|
|
253
|
+
*
|
|
254
|
+
* ```ts
|
|
255
|
+
* import {
|
|
256
|
+
* CREATE_METHOD_DEFAULTS,
|
|
257
|
+
* defaultRetryDelay,
|
|
258
|
+
* mergeConfig,
|
|
259
|
+
* type RetryResolvable,
|
|
260
|
+
* } from "./retry";
|
|
261
|
+
*
|
|
262
|
+
* const clientConfig: RetryResolvable = {
|
|
263
|
+
* apiKey: "k",
|
|
264
|
+
* baseUrl: "https://apis.roblox.com",
|
|
265
|
+
* maxRetries: 3,
|
|
266
|
+
* retryableStatuses: [429, 500],
|
|
267
|
+
* retryableTransportCodes: [],
|
|
268
|
+
* retryDelay: defaultRetryDelay,
|
|
269
|
+
* timeout: 30_000,
|
|
270
|
+
* };
|
|
271
|
+
*
|
|
272
|
+
* const merged = mergeConfig(clientConfig, {
|
|
273
|
+
* methodDefaults: CREATE_METHOD_DEFAULTS,
|
|
274
|
+
* methodKind: "create",
|
|
275
|
+
* });
|
|
276
|
+
*
|
|
277
|
+
* expect(merged.retryableStatuses).toStrictEqual([429]);
|
|
278
|
+
* ```
|
|
279
|
+
*
|
|
280
|
+
* @example
|
|
281
|
+
*
|
|
282
|
+
* ```ts
|
|
283
|
+
* import {
|
|
284
|
+
* defaultRetryDelay,
|
|
285
|
+
* IDEMPOTENT_METHOD_DEFAULTS,
|
|
286
|
+
* mergeConfig,
|
|
287
|
+
* type RetryResolvable,
|
|
288
|
+
* } from "./retry";
|
|
289
|
+
*
|
|
290
|
+
* const clientConfig: RetryResolvable = {
|
|
291
|
+
* apiKey: "k",
|
|
292
|
+
* baseUrl: "https://apis.roblox.com",
|
|
293
|
+
* maxRetries: 3,
|
|
294
|
+
* retryableStatuses: [429],
|
|
295
|
+
* retryableTransportCodes: [],
|
|
296
|
+
* retryDelay: defaultRetryDelay,
|
|
297
|
+
* timeout: 30_000,
|
|
298
|
+
* };
|
|
299
|
+
*
|
|
300
|
+
* const merged = mergeConfig(clientConfig, {
|
|
301
|
+
* methodDefaults: IDEMPOTENT_METHOD_DEFAULTS,
|
|
302
|
+
* methodKind: "idempotent",
|
|
303
|
+
* requestOptions: { timeout: 10_000 },
|
|
304
|
+
* });
|
|
305
|
+
*
|
|
306
|
+
* expect(merged.retryableStatuses).toStrictEqual([429]);
|
|
307
|
+
* expect(merged.timeout).toBe(10_000);
|
|
308
|
+
* ```
|
|
309
|
+
*
|
|
310
|
+
* @param clientConfig - Config frozen at client construction.
|
|
311
|
+
* @param options - Method defaults, method kind, and optional per-request overrides.
|
|
312
|
+
* @returns A new merged config object. Inputs are not mutated.
|
|
313
|
+
*/
|
|
314
|
+
function mergeConfig(clientConfig, options) {
|
|
315
|
+
const { methodDefaults, methodKind, requestOptions } = options;
|
|
316
|
+
switch (methodKind) {
|
|
317
|
+
case "create": return {
|
|
318
|
+
...clientConfig,
|
|
319
|
+
...methodDefaults,
|
|
320
|
+
...requestOptions
|
|
321
|
+
};
|
|
322
|
+
case "idempotent": return {
|
|
323
|
+
...methodDefaults,
|
|
324
|
+
...clientConfig,
|
|
325
|
+
...requestOptions
|
|
326
|
+
};
|
|
327
|
+
default: throw new Error(`Unexpected methodKind: ${String(methodKind)}`);
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
//#endregion
|
|
331
|
+
export { defaultRetryDelay as a, findErrorCode as c, computeRetryWaitMs as i, PermissionError as l, IDEMPOTENT_METHOD_DEFAULTS as n, mergeConfig as o, TRANSIENT_TRANSPORT_CODES as r, shouldRetry as s, CREATE_METHOD_DEFAULTS as t };
|
|
332
|
+
|
|
333
|
+
//# sourceMappingURL=retry-BzX29aw_.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"retry-BzX29aw_.mjs","names":[],"sources":["../src/errors/permission-error.ts","../src/internal/utils/find-error-code.ts","../src/internal/http/retry.ts"],"sourcesContent":["import { ApiError, type ApiErrorOptions } from \"./api-error.ts\";\n\n/**\n * Options for constructing a {@link PermissionError}.\n */\nexport interface PermissionErrorOptions extends ApiErrorOptions {\n\t/**\n\t * Stable identifier of the Open Cloud operation that returned the\n\t * permission failure (matches `OperationLimit.operationKey`, e.g.\n\t * `\"developer-products.create\"`).\n\t */\n\toperationKey: string;\n\t/**\n\t * Scope strings the API key or OAuth token must carry for the failing\n\t * operation, sourced from the vendored OpenAPI schema's `x-roblox-scopes`\n\t * for that operationId.\n\t */\n\trequiredScopes: ReadonlyArray<string>;\n}\n\n/**\n * Thrown when the Roblox Open Cloud API returns a 401 or 403 for an operation\n * whose required scopes are known. Subclass of {@link ApiError} carrying the\n * scope strings the caller's credential is missing plus the operation key, so\n * a CLI consumer can tell the user exactly which scope to grant on their API\n * key.\n *\n * @example\n *\n * ```ts\n * import { PermissionError } from \"@bedrock-rbx/ocale\";\n *\n * const error = new PermissionError(\"HTTP 403\", {\n * operationKey: \"developer-products.create\",\n * requiredScopes: [\"creator-store-product:write\"],\n * statusCode: 403,\n * });\n *\n * expect(error).toBeInstanceOf(PermissionError);\n * expect(error.requiredScopes).toStrictEqual([\"creator-store-product:write\"]);\n * expect(error.operationKey).toBe(\"developer-products.create\");\n * ```\n */\nexport class PermissionError extends ApiError {\n\tpublic override readonly name: string = \"PermissionError\";\n\tpublic readonly operationKey: string;\n\tpublic readonly requiredScopes: ReadonlyArray<string>;\n\n\t/**\n\t * Creates a new PermissionError.\n\t *\n\t * @param message - Human-readable error description.\n\t * @param options - Error options including status code, the operation key,\n\t * and the scopes the caller's credential must carry.\n\t */\n\tconstructor(message: string, options: PermissionErrorOptions) {\n\t\tsuper(message, options);\n\t\tthis.operationKey = options.operationKey;\n\t\tthis.requiredScopes = options.requiredScopes;\n\t}\n}\n","/**\n * Maximum cause-chain depth walked by {@link findErrorCode}. Caps pathological\n * self-referential or deeply nested chains; transport failures surface as\n * `NetworkError → TypeError(\"fetch failed\") → OS Error{code}`, so three\n * levels is the expected shape and five leaves headroom.\n */\nconst MAX_DEPTH = 5;\n\n/**\n * Walks an error's `cause` chain and returns the first node-style string\n * `code` it finds (for example `\"ECONNRESET\"`, `\"ETIMEDOUT\"`). Native `fetch`\n * surfaces a transport reset as a `NetworkError` wrapping a\n * `TypeError(\"fetch failed\")` whose own cause carries the OS-level `code`, so\n * the code lives several links down the chain.\n *\n * @example\n *\n * ```ts\n * import { findErrorCode } from \"./find-error-code\";\n *\n * const root = Object.assign(new Error(\"read ECONNRESET\"), { code: \"ECONNRESET\" });\n * const outer = new Error(\"Network request failed\", {\n * cause: new TypeError(\"fetch failed\", { cause: root }),\n * });\n *\n * expect(findErrorCode(outer)).toBe(\"ECONNRESET\");\n * ```\n *\n * @param error - The error to inspect; typically a `NetworkError`.\n * @returns The first string `code` in the chain, or `undefined` if none.\n */\nexport function findErrorCode(error: unknown): string | undefined {\n\tlet current: unknown = error;\n\tfor (let depth = 0; depth < MAX_DEPTH && current instanceof Error; depth += 1) {\n\t\tconst code = readCode(current);\n\t\tif (code !== undefined) {\n\t\t\treturn code;\n\t\t}\n\n\t\tcurrent = current.cause;\n\t}\n\n\treturn undefined;\n}\n\nfunction readCode(error: Error): string | undefined {\n\tconst code = Reflect.get(error, \"code\");\n\treturn typeof code === \"string\" ? code : undefined;\n}\n","import { ApiError } from \"../../errors/api-error.ts\";\nimport { NetworkError } from \"../../errors/network-error.ts\";\nimport { RateLimitError } from \"../../errors/rate-limit.ts\";\nimport { findErrorCode } from \"../utils/find-error-code.ts\";\n\n/**\n * Fully-resolved retry config shape that {@link mergeConfig} and\n * {@link shouldRetry} operate on. Fields are required because this represents\n * the post-defaulting, internal view; callers should supply every field (or\n * resolve them via a test factory / client constructor). The partial,\n * user-facing type lives on client construction options; method defaults and\n * per-request overrides use `Partial<RetryResolvable>`.\n */\nexport interface RetryResolvable {\n\t/** Roblox Open Cloud API key. */\n\treadonly apiKey: string;\n\t/** Base URL for the Open Cloud API. */\n\treadonly baseUrl: string;\n\t/** Maximum retry attempts before giving up. */\n\treadonly maxRetries: number;\n\t/** Status codes that are eligible for retry. */\n\treadonly retryableStatuses: ReadonlyArray<number>;\n\t/**\n\t * Node-style transport error codes ({@link findErrorCode}) eligible for\n\t * retry when surfaced as a {@link NetworkError}. Empty for create\n\t * operations by default; consumers opt a create in via a per-request\n\t * override.\n\t */\n\treadonly retryableTransportCodes: ReadonlyArray<string>;\n\t/** Fallback delay function when no server hint is available. */\n\treadonly retryDelay: (attempt: number) => number;\n\t/** Per-request timeout in milliseconds. */\n\treadonly timeout: number;\n}\n\n/**\n * Transient transport error codes that are safe to retry for idempotent\n * operations. Connection resets, timeouts, and DNS hiccups are recoverable on\n * a retry; a self-aborted request timeout carries no `code` and so is excluded\n * by construction.\n */\nexport const TRANSIENT_TRANSPORT_CODES: ReadonlyArray<string> = Object.freeze([\n\t\"ECONNRESET\",\n\t\"ECONNREFUSED\",\n\t\"ETIMEDOUT\",\n\t\"EPIPE\",\n\t\"ENETUNREACH\",\n\t\"EHOSTDOWN\",\n\t\"EAI_AGAIN\",\n\t\"UND_ERR_SOCKET\",\n]);\n\n/** Method-level retry defaults, keyed by {@link MethodKind}. */\ntype MethodDefaults = Readonly<\n\tPick<RetryResolvable, \"retryableStatuses\" | \"retryableTransportCodes\">\n>;\n\n/**\n * Default retry policy for idempotent operations (read, list, update,\n * delete). Safe to retry on rate limits, transient server errors, and\n * transient transport failures.\n */\nexport const IDEMPOTENT_METHOD_DEFAULTS: MethodDefaults = Object.freeze({\n\tretryableStatuses: Object.freeze([429, 500, 502, 503, 504] as const),\n\tretryableTransportCodes: TRANSIENT_TRANSPORT_CODES,\n});\n\n/**\n * Default retry policy for create operations. Retries rate limits only (no\n * 5xx and no transport-error retries) to prevent duplicate resources, since\n * Roblox Open Cloud has no idempotency-key support. Consumers who can tolerate\n * a duplicate opt in per request.\n */\nexport const CREATE_METHOD_DEFAULTS: MethodDefaults = Object.freeze({\n\tretryableStatuses: Object.freeze([429] as const),\n\tretryableTransportCodes: Object.freeze([] as const),\n});\n\n/** Kind of HTTP method the merge is being performed for. */\nexport type MethodKind = \"create\" | \"idempotent\";\n\n/**\n * Options for {@link mergeConfig}.\n *\n * @template T - Concrete `RetryResolvable` subtype being merged.\n */\ninterface MergeConfigOptions<T> {\n\t/** Method-level defaults (e.g. {@link CREATE_METHOD_DEFAULTS}). */\n\treadonly methodDefaults: Partial<T>;\n\t/** Whether the method is a create or idempotent operation. */\n\treadonly methodKind: MethodKind;\n\t/** Optional per-request overrides; always win when provided. */\n\treadonly requestOptions?: Partial<T>;\n}\n\n/**\n * Options for {@link computeRetryWaitMs}.\n */\ninterface ComputeRetryWaitMsOptions {\n\t/** Zero-indexed retry attempt number. */\n\treadonly attempt: number;\n\t/** Fallback delay function when no server hint is available. */\n\treadonly retryDelay: (attempt: number) => number;\n}\n\n/**\n * Default exponential backoff: 1s → 2s → 4s → 8s → 16s → 30s (capped).\n *\n * @example\n *\n * ```ts\n * import { defaultRetryDelay } from \"./retry\";\n *\n * expect(defaultRetryDelay(0)).toBe(1000);\n * expect(defaultRetryDelay(4)).toBe(16_000);\n * expect(defaultRetryDelay(10)).toBe(30_000);\n * ```\n *\n * @param attempt - Zero-indexed retry attempt number.\n * @returns Wait duration in milliseconds.\n */\nexport function defaultRetryDelay(attempt: number): number {\n\treturn Math.min(1000 * 2 ** attempt, 30_000);\n}\n\n/**\n * Computes how long to wait before the next retry. Prefers the server's\n * suggested delay when the error is a {@link RateLimitError} with a positive\n * `retryAfterSeconds`; otherwise falls through to `retryDelay(attempt)`.\n *\n * @example\n *\n * ```ts\n * import { RateLimitError } from \"../../errors/rate-limit.ts\";\n * import { computeRetryWaitMs, defaultRetryDelay } from \"./retry\";\n *\n * const error = new RateLimitError(\"slow down\", { retryAfterSeconds: 3 });\n *\n * expect(computeRetryWaitMs(error, { attempt: 0, retryDelay: defaultRetryDelay })).toBe(\n * 3000,\n * );\n * ```\n *\n * @example\n *\n * ```ts\n * import { ApiError } from \"../../errors/api-error.ts\";\n * import { computeRetryWaitMs, defaultRetryDelay } from \"./retry\";\n *\n * const error = new ApiError(\"server error\", { statusCode: 503 });\n *\n * expect(computeRetryWaitMs(error, { attempt: 2, retryDelay: defaultRetryDelay })).toBe(\n * 4000,\n * );\n * ```\n *\n * @param error - The error returned by the failing request.\n * @param options - Retry attempt index and fallback delay function.\n * @returns Wait duration in milliseconds before the next attempt.\n */\nexport function computeRetryWaitMs(\n\terror: ApiError | NetworkError | RateLimitError,\n\toptions: ComputeRetryWaitMsOptions,\n): number {\n\tif (error instanceof RateLimitError && error.retryAfterSeconds > 0) {\n\t\treturn error.retryAfterSeconds * 1000;\n\t}\n\n\treturn options.retryDelay(options.attempt);\n}\n\n/**\n * Decides whether a failed request is eligible for retry. {@link RateLimitError}\n * (checked against 429) and {@link ApiError} (checked against its `statusCode`)\n * are retryable when their status is in `retryableStatuses`. A\n * {@link NetworkError} is retryable when its transport code\n * ({@link findErrorCode}) is in `retryableTransportCodes`. This is how\n * transient connection resets recover. All other failures return `false`.\n *\n * @example\n *\n * ```ts\n * import { RateLimitError } from \"../../errors/rate-limit.ts\";\n * import { shouldRetry } from \"./retry\";\n *\n * const error = new RateLimitError(\"\", { retryAfterSeconds: 1 });\n *\n * expect(shouldRetry(error, { retryableStatuses: [429], retryableTransportCodes: [] })).toBe(\n * true,\n * );\n * ```\n *\n * @example\n *\n * ```ts\n * import { NetworkError } from \"../../errors/network-error.ts\";\n * import { shouldRetry } from \"./retry\";\n *\n * const reset = Object.assign(new Error(\"read ECONNRESET\"), { code: \"ECONNRESET\" });\n * const error = new NetworkError(\"Network request failed\", { cause: reset });\n *\n * expect(\n * shouldRetry(error, { retryableStatuses: [], retryableTransportCodes: [\"ECONNRESET\"] }),\n * ).toBe(true);\n * ```\n *\n * @param error - The error returned by the failing request.\n * @param config - Object carrying the retry-eligible status and transport-code lists.\n * @returns `true` if the error should be retried, `false` otherwise.\n */\nexport function shouldRetry(\n\terror: unknown,\n\tconfig: {\n\t\treadonly retryableStatuses: ReadonlyArray<number>;\n\t\treadonly retryableTransportCodes: ReadonlyArray<string>;\n\t},\n): error is ApiError | NetworkError | RateLimitError {\n\tif (error instanceof RateLimitError) {\n\t\treturn config.retryableStatuses.includes(429);\n\t}\n\n\tif (error instanceof ApiError) {\n\t\treturn config.retryableStatuses.includes(error.statusCode);\n\t}\n\n\tif (error instanceof NetworkError) {\n\t\tconst code = findErrorCode(error);\n\t\treturn code !== undefined && config.retryableTransportCodes.includes(code);\n\t}\n\n\treturn false;\n}\n\n/**\n * Resolves the effective config for a single request by shallow-merging the\n * client config, method defaults, and per-request options. Precedence depends\n * on `methodKind`:\n *\n * - `\"create\"`: method defaults override client config, so client-level\n * settings cannot silently relax create-method safety. Only explicit\n * per-request `requestOptions` can.\n * - `\"idempotent\"`: client config overrides method defaults, so consumers\n * can loosen or tighten retry policy globally. `requestOptions` still wins\n * when provided.\n *\n * Array-valued fields like `retryableStatuses` are *replaced*, not extended.\n *\n * @template T - Concrete `RetryResolvable` subtype being merged.\n *\n * @example\n *\n * ```ts\n * import {\n * CREATE_METHOD_DEFAULTS,\n * defaultRetryDelay,\n * mergeConfig,\n * type RetryResolvable,\n * } from \"./retry\";\n *\n * const clientConfig: RetryResolvable = {\n * apiKey: \"k\",\n * baseUrl: \"https://apis.roblox.com\",\n * maxRetries: 3,\n * retryableStatuses: [429, 500],\n * retryableTransportCodes: [],\n * retryDelay: defaultRetryDelay,\n * timeout: 30_000,\n * };\n *\n * const merged = mergeConfig(clientConfig, {\n * methodDefaults: CREATE_METHOD_DEFAULTS,\n * methodKind: \"create\",\n * });\n *\n * expect(merged.retryableStatuses).toStrictEqual([429]);\n * ```\n *\n * @example\n *\n * ```ts\n * import {\n * defaultRetryDelay,\n * IDEMPOTENT_METHOD_DEFAULTS,\n * mergeConfig,\n * type RetryResolvable,\n * } from \"./retry\";\n *\n * const clientConfig: RetryResolvable = {\n * apiKey: \"k\",\n * baseUrl: \"https://apis.roblox.com\",\n * maxRetries: 3,\n * retryableStatuses: [429],\n * retryableTransportCodes: [],\n * retryDelay: defaultRetryDelay,\n * timeout: 30_000,\n * };\n *\n * const merged = mergeConfig(clientConfig, {\n * methodDefaults: IDEMPOTENT_METHOD_DEFAULTS,\n * methodKind: \"idempotent\",\n * requestOptions: { timeout: 10_000 },\n * });\n *\n * expect(merged.retryableStatuses).toStrictEqual([429]);\n * expect(merged.timeout).toBe(10_000);\n * ```\n *\n * @param clientConfig - Config frozen at client construction.\n * @param options - Method defaults, method kind, and optional per-request overrides.\n * @returns A new merged config object. Inputs are not mutated.\n */\nexport function mergeConfig<T extends RetryResolvable>(\n\tclientConfig: T,\n\toptions: MergeConfigOptions<T>,\n): T {\n\tconst { methodDefaults, methodKind, requestOptions } = options;\n\n\tswitch (methodKind) {\n\t\tcase \"create\": {\n\t\t\treturn { ...clientConfig, ...methodDefaults, ...requestOptions };\n\t\t}\n\t\tcase \"idempotent\": {\n\t\t\treturn { ...methodDefaults, ...clientConfig, ...requestOptions };\n\t\t}\n\t\tdefault: {\n\t\t\tconst exhaustive: never = methodKind;\n\t\t\tthrow new Error(`Unexpected methodKind: ${String(exhaustive)}`);\n\t\t}\n\t}\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;AA2CA,IAAa,kBAAb,cAAqC,SAAS;CAC7C,OAAwC;CACxC;CACA;;;;;;;;CASA,YAAY,SAAiB,SAAiC;AAC7D,QAAM,SAAS,QAAQ;AACvB,OAAK,eAAe,QAAQ;AAC5B,OAAK,iBAAiB,QAAQ;;;;;;;;;;;ACpDhC,MAAM,YAAY;;;;;;;;;;;;;;;;;;;;;;;;AAyBlB,SAAgB,cAAc,OAAoC;CACjE,IAAI,UAAmB;AACvB,MAAK,IAAI,QAAQ,GAAG,QAAQ,aAAa,mBAAmB,OAAO,SAAS,GAAG;EAC9E,MAAM,OAAO,SAAS,QAAQ;AAC9B,MAAI,SAAS,KAAA,EACZ,QAAO;AAGR,YAAU,QAAQ;;;AAMpB,SAAS,SAAS,OAAkC;CACnD,MAAM,OAAO,QAAQ,IAAI,OAAO,OAAO;AACvC,QAAO,OAAO,SAAS,WAAW,OAAO,KAAA;;;;;;;;;;ACN1C,MAAa,4BAAmD,OAAO,OAAO;CAC7E;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA,CAAC;;;;;;AAYF,MAAa,6BAA6C,OAAO,OAAO;CACvE,mBAAmB,OAAO,OAAO;EAAC;EAAK;EAAK;EAAK;EAAK;EAAI,CAAU;CACpE,yBAAyB;CACzB,CAAC;;;;;;;AAQF,MAAa,yBAAyC,OAAO,OAAO;CACnE,mBAAmB,OAAO,OAAO,CAAC,IAAI,CAAU;CAChD,yBAAyB,OAAO,OAAO,EAAE,CAAU;CACnD,CAAC;;;;;;;;;;;;;;;;;AA6CF,SAAgB,kBAAkB,SAAyB;AAC1D,QAAO,KAAK,IAAI,MAAO,KAAK,SAAS,IAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAsC7C,SAAgB,mBACf,OACA,SACS;AACT,KAAI,iBAAiB,kBAAkB,MAAM,oBAAoB,EAChE,QAAO,MAAM,oBAAoB;AAGlC,QAAO,QAAQ,WAAW,QAAQ,QAAQ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA0C3C,SAAgB,YACf,OACA,QAIoD;AACpD,KAAI,iBAAiB,eACpB,QAAO,OAAO,kBAAkB,SAAS,IAAI;AAG9C,KAAI,iBAAiB,SACpB,QAAO,OAAO,kBAAkB,SAAS,MAAM,WAAW;AAG3D,KAAI,iBAAiB,cAAc;EAClC,MAAM,OAAO,cAAc,MAAM;AACjC,SAAO,SAAS,KAAA,KAAa,OAAO,wBAAwB,SAAS,KAAK;;AAG3E,QAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAiFR,SAAgB,YACf,cACA,SACI;CACJ,MAAM,EAAE,gBAAgB,YAAY,mBAAmB;AAEvD,SAAQ,YAAR;EACC,KAAK,SACJ,QAAO;GAAE,GAAG;GAAc,GAAG;GAAgB,GAAG;GAAgB;EAEjE,KAAK,aACJ,QAAO;GAAE,GAAG;GAAgB,GAAG;GAAc,GAAG;GAAgB;EAEjE,QAEC,OAAM,IAAI,MAAM,0BAA0B,OADhB,WACkC,GAAG"}
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import { d as OpenCloudError } from "./types-rzs1NB-j.mjs";
|
|
2
|
+
|
|
3
|
+
//#region src/errors/api-error.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* Options for constructing an {@link ApiError}.
|
|
6
|
+
*/
|
|
7
|
+
interface ApiErrorOptions extends ErrorOptions {
|
|
8
|
+
/** Optional machine-readable error code from the API. */
|
|
9
|
+
code?: string | undefined;
|
|
10
|
+
/** Parsed response body, when present. */
|
|
11
|
+
details?: JSONValue | undefined;
|
|
12
|
+
/** HTTP status code from the API response. */
|
|
13
|
+
statusCode: number;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Thrown when the Roblox Open Cloud API returns a non-2xx response
|
|
17
|
+
* that is not a rate limit (429).
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
*
|
|
21
|
+
* ```ts
|
|
22
|
+
* import { ApiError } from "@bedrock-rbx/ocale";
|
|
23
|
+
*
|
|
24
|
+
* const error = new ApiError("HTTP 404: Pass not found (code NotFound)", {
|
|
25
|
+
* code: "NotFound",
|
|
26
|
+
* details: { errorCode: "NotFound", message: "Pass not found" },
|
|
27
|
+
* statusCode: 404,
|
|
28
|
+
* });
|
|
29
|
+
*
|
|
30
|
+
* expect(error).toBeInstanceOf(ApiError);
|
|
31
|
+
* expect(error.statusCode).toBe(404);
|
|
32
|
+
* expect(error.code).toBe("NotFound");
|
|
33
|
+
* expect(error.details).toEqual({
|
|
34
|
+
* errorCode: "NotFound",
|
|
35
|
+
* message: "Pass not found",
|
|
36
|
+
* });
|
|
37
|
+
* ```
|
|
38
|
+
*/
|
|
39
|
+
declare class ApiError extends OpenCloudError {
|
|
40
|
+
readonly code: string | undefined;
|
|
41
|
+
readonly details: JSONValue | undefined;
|
|
42
|
+
override readonly name: string;
|
|
43
|
+
readonly statusCode: number;
|
|
44
|
+
/**
|
|
45
|
+
* Creates a new ApiError.
|
|
46
|
+
*
|
|
47
|
+
* @param message - Human-readable error description.
|
|
48
|
+
* @param options - Error options including status code, optional error
|
|
49
|
+
* code, and the parsed response body when present.
|
|
50
|
+
*/
|
|
51
|
+
constructor(message: string, options: ApiErrorOptions);
|
|
52
|
+
}
|
|
53
|
+
//#endregion
|
|
54
|
+
//#region src/errors/network-error.d.ts
|
|
55
|
+
/**
|
|
56
|
+
* Options for constructing a {@link NetworkError}.
|
|
57
|
+
*/
|
|
58
|
+
interface NetworkErrorOptions extends ErrorOptions {
|
|
59
|
+
/** HTTP method of the request that failed. */
|
|
60
|
+
method?: string | undefined;
|
|
61
|
+
/** Fully-qualified URL of the request that failed. */
|
|
62
|
+
url?: string | undefined;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Thrown when a network-level failure prevents the request from reaching
|
|
66
|
+
* the Roblox Open Cloud API (e.g., DNS resolution failure, connection reset).
|
|
67
|
+
* The `method` and `url` name the failing call so a transport failure that
|
|
68
|
+
* survives every retry can be diagnosed; the underlying transport error is
|
|
69
|
+
* carried on `cause`.
|
|
70
|
+
*/
|
|
71
|
+
declare class NetworkError extends OpenCloudError {
|
|
72
|
+
readonly method: string | undefined;
|
|
73
|
+
override readonly name: string;
|
|
74
|
+
readonly url: string | undefined;
|
|
75
|
+
/**
|
|
76
|
+
* Creates a new NetworkError.
|
|
77
|
+
*
|
|
78
|
+
* @param message - Human-readable error description.
|
|
79
|
+
* @param options - Error options including the optional `cause` and the
|
|
80
|
+
* `method` / `url` of the request that failed.
|
|
81
|
+
*/
|
|
82
|
+
constructor(message: string, options?: NetworkErrorOptions);
|
|
83
|
+
}
|
|
84
|
+
//#endregion
|
|
85
|
+
//#region src/errors/rate-limit.d.ts
|
|
86
|
+
/**
|
|
87
|
+
* Options for constructing a {@link RateLimitError}.
|
|
88
|
+
*/
|
|
89
|
+
interface RateLimitErrorOptions extends ErrorOptions {
|
|
90
|
+
/** Seconds to wait before retrying the request. */
|
|
91
|
+
retryAfterSeconds: number;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Thrown when the Roblox Open Cloud API returns a 429 Too Many Requests response.
|
|
95
|
+
* Contains the server-suggested retry delay.
|
|
96
|
+
*
|
|
97
|
+
* @example
|
|
98
|
+
*
|
|
99
|
+
* ```ts
|
|
100
|
+
* import { RateLimitError } from "@bedrock-rbx/ocale";
|
|
101
|
+
*
|
|
102
|
+
* const error = new RateLimitError("Too many requests", {
|
|
103
|
+
* retryAfterSeconds: 30,
|
|
104
|
+
* });
|
|
105
|
+
*
|
|
106
|
+
* expect(error).toBeInstanceOf(RateLimitError);
|
|
107
|
+
* expect(error.retryAfterSeconds).toBe(30);
|
|
108
|
+
* ```
|
|
109
|
+
*/
|
|
110
|
+
declare class RateLimitError extends OpenCloudError {
|
|
111
|
+
override readonly name = "RateLimitError";
|
|
112
|
+
readonly retryAfterSeconds: number;
|
|
113
|
+
/**
|
|
114
|
+
* Creates a new RateLimitError.
|
|
115
|
+
*
|
|
116
|
+
* @param message - Human-readable error description.
|
|
117
|
+
* @param options - Error options including the retry delay.
|
|
118
|
+
*/
|
|
119
|
+
constructor(message: string, options: RateLimitErrorOptions);
|
|
120
|
+
}
|
|
121
|
+
//#endregion
|
|
122
|
+
//#region src/internal/http/retry.d.ts
|
|
123
|
+
/**
|
|
124
|
+
* Fully-resolved retry config shape that {@link mergeConfig} and
|
|
125
|
+
* {@link shouldRetry} operate on. Fields are required because this represents
|
|
126
|
+
* the post-defaulting, internal view; callers should supply every field (or
|
|
127
|
+
* resolve them via a test factory / client constructor). The partial,
|
|
128
|
+
* user-facing type lives on client construction options; method defaults and
|
|
129
|
+
* per-request overrides use `Partial<RetryResolvable>`.
|
|
130
|
+
*/
|
|
131
|
+
interface RetryResolvable {
|
|
132
|
+
/** Roblox Open Cloud API key. */
|
|
133
|
+
readonly apiKey: string;
|
|
134
|
+
/** Base URL for the Open Cloud API. */
|
|
135
|
+
readonly baseUrl: string;
|
|
136
|
+
/** Maximum retry attempts before giving up. */
|
|
137
|
+
readonly maxRetries: number;
|
|
138
|
+
/** Status codes that are eligible for retry. */
|
|
139
|
+
readonly retryableStatuses: ReadonlyArray<number>;
|
|
140
|
+
/**
|
|
141
|
+
* Node-style transport error codes ({@link findErrorCode}) eligible for
|
|
142
|
+
* retry when surfaced as a {@link NetworkError}. Empty for create
|
|
143
|
+
* operations by default; consumers opt a create in via a per-request
|
|
144
|
+
* override.
|
|
145
|
+
*/
|
|
146
|
+
readonly retryableTransportCodes: ReadonlyArray<string>;
|
|
147
|
+
/** Fallback delay function when no server hint is available. */
|
|
148
|
+
readonly retryDelay: (attempt: number) => number;
|
|
149
|
+
/** Per-request timeout in milliseconds. */
|
|
150
|
+
readonly timeout: number;
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Transient transport error codes that are safe to retry for idempotent
|
|
154
|
+
* operations. Connection resets, timeouts, and DNS hiccups are recoverable on
|
|
155
|
+
* a retry; a self-aborted request timeout carries no `code` and so is excluded
|
|
156
|
+
* by construction.
|
|
157
|
+
*/
|
|
158
|
+
declare const TRANSIENT_TRANSPORT_CODES: ReadonlyArray<string>;
|
|
159
|
+
/** Kind of HTTP method the merge is being performed for. */
|
|
160
|
+
type MethodKind = "create" | "idempotent";
|
|
161
|
+
//#endregion
|
|
162
|
+
export { RateLimitErrorOptions as a, ApiError as c, RateLimitError as i, ApiErrorOptions as l, RetryResolvable as n, NetworkError as o, TRANSIENT_TRANSPORT_CODES as r, NetworkErrorOptions as s, MethodKind as t };
|
|
163
|
+
//# sourceMappingURL=retry-CXnj3gXI.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"retry-CXnj3gXI.d.mts","names":[],"sources":["../src/errors/api-error.ts","../src/errors/network-error.ts","../src/errors/rate-limit.ts","../src/internal/http/retry.ts"],"mappings":";;;;;AAKA;UAAiB,eAAA,SAAwB,YAAA;;EAExC,IAAA;;EAEA,OAAA,GAAU,SAAA;;EAEV,UAAA;AAAA;;;AA2BD;;;;;;;;;;;;;;;;;;;;;;cAAa,QAAA,SAAiB,cAAA;EAAA,SACb,IAAA;EAAA,SACA,OAAA,EAAS,SAAA;EAAA,kBACA,IAAA;EAAA,SACT,UAAA;;;;;;ACvBjB;;EDgCC,WAAA,CAAY,OAAA,UAAiB,OAAA,EAAS,eAAA;AAAA;;;;;AA9CvC;UCAiB,mBAAA,SAA4B,YAAA;;EAE5C,MAAA;;EAEA,GAAA;AAAA;;;;;AD6BD;;;cCnBa,YAAA,SAAqB,cAAA;EAAA,SACjB,MAAA;EAAA,kBACS,IAAA;EAAA,SACT,GAAA;EDgBa;;;;;;;ECP7B,WAAA,CAAY,OAAA,UAAiB,OAAA,GAAU,mBAAA;AAAA;;;;;AD1BxC;UEAiB,qBAAA,SAA8B,YAAA;;EAE9C,iBAAA;AAAA;;;;;;;AF+BD;;;;;;;;;;;cEXa,cAAA,SAAuB,cAAA;EAAA,kBACV,IAAA;EAAA,SACT,iBAAA;;;;;;;EAQhB,WAAA,CAAY,OAAA,UAAiB,OAAA,EAAS,qBAAA;AAAA;;;AFhCvC;;;;;;;;AAAA,UGQiB,eAAA;;WAEP,MAAA;EHuBV;EAAA,SGrBU,OAAA;;WAEA,UAAA;;WAEA,iBAAA,EAAmB,aAAA;;;;;;;WAOnB,uBAAA,EAAyB,aAAA;;WAEzB,UAAA,GAAa,OAAA;;WAEb,OAAA;AAAA;;;;;;AF3BV;cEoCa,yBAAA,EAA2B,aAAA;;KAsC5B,UAAA"}
|
package/dist/storage.d.mts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { d as OpenCloudError, i as OpenCloudClientOptions, l as Result, n as HttpRequest, r as HttpResponse, s as RequestOptions, u as SleepFunc } from "./types-
|
|
1
|
+
import { d as OpenCloudError, i as OpenCloudClientOptions, l as Result, n as HttpRequest, r as HttpResponse, s as RequestOptions, u as SleepFunc } from "./types-rzs1NB-j.mjs";
|
|
2
|
+
import { n as RetryResolvable, t as MethodKind } from "./retry-CXnj3gXI.mjs";
|
|
2
3
|
|
|
3
4
|
//#region src/domains/cloud-v2/memory-store-queues/types.d.ts
|
|
4
5
|
/**
|
|
@@ -308,32 +309,6 @@ interface OperationLimit {
|
|
|
308
309
|
readonly operationKey: string;
|
|
309
310
|
}
|
|
310
311
|
//#endregion
|
|
311
|
-
//#region src/internal/http/retry.d.ts
|
|
312
|
-
/**
|
|
313
|
-
* Fully-resolved retry config shape that {@link mergeConfig} and
|
|
314
|
-
* {@link shouldRetry} operate on. Fields are required because this represents
|
|
315
|
-
* the post-defaulting, internal view — callers should supply every field (or
|
|
316
|
-
* resolve them via a test factory / client constructor). The partial,
|
|
317
|
-
* user-facing type lives on client construction options; method defaults and
|
|
318
|
-
* per-request overrides use `Partial<RetryResolvable>`.
|
|
319
|
-
*/
|
|
320
|
-
interface RetryResolvable {
|
|
321
|
-
/** Roblox Open Cloud API key. */
|
|
322
|
-
readonly apiKey: string;
|
|
323
|
-
/** Base URL for the Open Cloud API. */
|
|
324
|
-
readonly baseUrl: string;
|
|
325
|
-
/** Maximum retry attempts before giving up. */
|
|
326
|
-
readonly maxRetries: number;
|
|
327
|
-
/** Status codes that are eligible for retry. */
|
|
328
|
-
readonly retryableStatuses: ReadonlyArray<number>;
|
|
329
|
-
/** Fallback delay function when no server hint is available. */
|
|
330
|
-
readonly retryDelay: (attempt: number) => number;
|
|
331
|
-
/** Per-request timeout in milliseconds. */
|
|
332
|
-
readonly timeout: number;
|
|
333
|
-
}
|
|
334
|
-
/** Kind of HTTP method the merge is being performed for. */
|
|
335
|
-
type MethodKind = "create" | "idempotent";
|
|
336
|
-
//#endregion
|
|
337
312
|
//#region src/internal/resource-client.d.ts
|
|
338
313
|
/**
|
|
339
314
|
* Describes a single resource method's shape for dispatch through
|
package/dist/storage.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"storage.d.mts","names":[],"sources":["../src/domains/cloud-v2/memory-store-queues/types.ts","../src/domains/cloud-v2/memory-store-sorted-maps/types.ts","../src/internal/http/rate-limit-queue.ts","../src/internal/
|
|
1
|
+
{"version":3,"file":"storage.d.mts","names":[],"sources":["../src/domains/cloud-v2/memory-store-queues/types.ts","../src/domains/cloud-v2/memory-store-sorted-maps/types.ts","../src/internal/http/rate-limit-queue.ts","../src/internal/resource-client.ts","../src/resources/storage/queues-group.ts","../src/resources/storage/sorted-maps-group.ts","../src/resources/storage/client.ts"],"mappings":"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"}
|
package/dist/storage.mjs
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { r as ApiError } from "./rate-limit-
|
|
2
|
-
import {
|
|
1
|
+
import { r as ApiError } from "./rate-limit-nY4BF079.mjs";
|
|
2
|
+
import { n as IDEMPOTENT_METHOD_DEFAULTS, t as CREATE_METHOD_DEFAULTS } from "./retry-BzX29aw_.mjs";
|
|
3
|
+
import { a as isDateTimeString, i as isRecord, n as okRequest, r as parseEmptyResponse, t as ResourceClient } from "./resource-client-CG9-BG81.mjs";
|
|
3
4
|
//#region src/domains/cloud-v2/memory-store-queues/builders.ts
|
|
4
5
|
/**
|
|
5
6
|
* Builds a `POST` request for the Open Cloud
|