@bedrock-rbx/ocale 0.1.0-beta.1
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/LICENSE +21 -0
- package/dist/badges.d.mts +186 -0
- package/dist/badges.d.mts.map +1 -0
- package/dist/badges.mjs +309 -0
- package/dist/badges.mjs.map +1 -0
- package/dist/developer-products.d.mts +245 -0
- package/dist/developer-products.d.mts.map +1 -0
- package/dist/developer-products.mjs +388 -0
- package/dist/developer-products.mjs.map +1 -0
- package/dist/game-passes.d.mts +210 -0
- package/dist/game-passes.d.mts.map +1 -0
- package/dist/game-passes.mjs +397 -0
- package/dist/game-passes.mjs.map +1 -0
- package/dist/index.d.mts +191 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +3 -0
- package/dist/places.d.mts +161 -0
- package/dist/places.d.mts.map +1 -0
- package/dist/places.mjs +403 -0
- package/dist/places.mjs.map +1 -0
- package/dist/price-information-CmpscMc4.mjs +42 -0
- package/dist/price-information-CmpscMc4.mjs.map +1 -0
- package/dist/rate-limit-BBU_4xnZ.mjs +135 -0
- package/dist/rate-limit-BBU_4xnZ.mjs.map +1 -0
- package/dist/resource-client-CaS_j3yg.mjs +652 -0
- package/dist/resource-client-CaS_j3yg.mjs.map +1 -0
- package/dist/to-blob-1BtHsDGK.mjs +18 -0
- package/dist/to-blob-1BtHsDGK.mjs.map +1 -0
- package/dist/types-YCTsM8Qd.d.mts +214 -0
- package/dist/types-YCTsM8Qd.d.mts.map +1 -0
- package/dist/universes.d.mts +387 -0
- package/dist/universes.d.mts.map +1 -0
- package/dist/universes.mjs +705 -0
- package/dist/universes.mjs.map +1 -0
- package/dist/validation-CTZzJhmd.mjs +38 -0
- package/dist/validation-CTZzJhmd.mjs.map +1 -0
- package/package.json +70 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Christopher Buss
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
import { d as OpenCloudError, i as OpenCloudClientOptions, l as Result, s as RequestOptions } from "./types-YCTsM8Qd.mjs";
|
|
2
|
+
|
|
3
|
+
//#region src/domains/badges/badges/types.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* Awarder kind for a badge. The Roblox API only awards badges from a
|
|
6
|
+
* `Place`, so the wire enum carries a single value; the public type
|
|
7
|
+
* preserves the string label so future awarder kinds can be added
|
|
8
|
+
* without a breaking change.
|
|
9
|
+
*/
|
|
10
|
+
type BadgeAwarderType = "Place";
|
|
11
|
+
/**
|
|
12
|
+
* The entity that awards a badge. Always a place at present; the
|
|
13
|
+
* `id` is the awarding place's ID and `name` its display name.
|
|
14
|
+
*/
|
|
15
|
+
interface BadgeAwarder {
|
|
16
|
+
/** Stringified ID of the awarding entity. */
|
|
17
|
+
readonly id: string;
|
|
18
|
+
/** Display name of the awarding entity. */
|
|
19
|
+
readonly name: string;
|
|
20
|
+
/** Kind of awarding entity. Currently always `"Place"`. */
|
|
21
|
+
readonly type: BadgeAwarderType;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Award statistics for a badge.
|
|
25
|
+
*/
|
|
26
|
+
interface BadgeStatistics {
|
|
27
|
+
/** Total number of times the badge has been awarded. */
|
|
28
|
+
readonly awardedCount: number;
|
|
29
|
+
/** Number of times the badge has been awarded in the past day. */
|
|
30
|
+
readonly pastDayAwardedCount: number;
|
|
31
|
+
/** Win rate, as a percentage between 0 and 100. */
|
|
32
|
+
readonly winRatePercentage: number;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Source of funds for the Robux fee charged on badge creation.
|
|
36
|
+
*/
|
|
37
|
+
type BadgePaymentSource = "Group" | "User";
|
|
38
|
+
/**
|
|
39
|
+
* Parameters for creating a new badge under a universe.
|
|
40
|
+
*/
|
|
41
|
+
interface CreateBadgeParameters {
|
|
42
|
+
/** Display name of the new badge. */
|
|
43
|
+
readonly name: string;
|
|
44
|
+
/** Optional source-language description. */
|
|
45
|
+
readonly description?: string;
|
|
46
|
+
/** Optional confirmation of the Robux cost the caller expects to pay. */
|
|
47
|
+
readonly expectedCost?: number;
|
|
48
|
+
/** Icon image to upload as the badge's source-language icon. */
|
|
49
|
+
readonly icon: Blob | Uint8Array;
|
|
50
|
+
/** Whether the badge should be created in the active state. */
|
|
51
|
+
readonly isActive?: boolean;
|
|
52
|
+
/** Account that funds the badge creation fee. Defaults to user funds server-side. */
|
|
53
|
+
readonly paymentSource?: BadgePaymentSource;
|
|
54
|
+
/** Stringified ID of the universe that owns the badge. */
|
|
55
|
+
readonly universeId: string;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Parameters for partially updating an existing badge. Every field
|
|
59
|
+
* except the identifier is optional; omitted fields are not included
|
|
60
|
+
* in the JSON body so the server leaves their current values untouched.
|
|
61
|
+
*/
|
|
62
|
+
interface UpdateBadgeParameters {
|
|
63
|
+
/** Optional new source-language display name. */
|
|
64
|
+
readonly name?: string;
|
|
65
|
+
/** Stringified ID of the badge to update. */
|
|
66
|
+
readonly badgeId: string;
|
|
67
|
+
/** Optional new source-language description. */
|
|
68
|
+
readonly description?: string;
|
|
69
|
+
/** Optional new enabled flag. */
|
|
70
|
+
readonly enabled?: boolean;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* A Roblox badge as exposed to SDK consumers. Fields use DX-friendly
|
|
74
|
+
* names and types (stringified IDs, `Date` timestamps) rather than the
|
|
75
|
+
* raw wire representation.
|
|
76
|
+
*/
|
|
77
|
+
interface Badge {
|
|
78
|
+
/** Stringified badge ID. The API returns an int64; always use this. */
|
|
79
|
+
readonly id: string;
|
|
80
|
+
/** Source-language name shown when no localized override applies. */
|
|
81
|
+
readonly name: string;
|
|
82
|
+
/** Awarding entity for the badge. */
|
|
83
|
+
readonly awarder: BadgeAwarder;
|
|
84
|
+
/** ISO timestamp at which the badge was created, as a `Date`. */
|
|
85
|
+
readonly createdAt: Date;
|
|
86
|
+
/** Source-language description shown when no localized override applies. */
|
|
87
|
+
readonly description: string;
|
|
88
|
+
/** Resolved description for the requesting locale, or the source description. */
|
|
89
|
+
readonly displayDescription: string;
|
|
90
|
+
/** Resolved icon image asset ID for the requesting locale; `undefined` when no icon is uploaded. */
|
|
91
|
+
readonly displayIconImageId: string | undefined;
|
|
92
|
+
/** Resolved name for the requesting locale, or the source name. */
|
|
93
|
+
readonly displayName: string;
|
|
94
|
+
/** Whether the badge is currently active. Disabled badges cannot be awarded. */
|
|
95
|
+
readonly enabled: boolean;
|
|
96
|
+
/** Source-language icon image asset ID; `undefined` when no icon is uploaded. */
|
|
97
|
+
readonly iconImageId: string | undefined;
|
|
98
|
+
/** Award statistics for the badge. */
|
|
99
|
+
readonly statistics: BadgeStatistics;
|
|
100
|
+
/** ISO timestamp of the most recent update, as a `Date`. */
|
|
101
|
+
readonly updatedAt: Date;
|
|
102
|
+
}
|
|
103
|
+
//#endregion
|
|
104
|
+
//#region src/domains/publish/badge-icon/types.d.ts
|
|
105
|
+
/**
|
|
106
|
+
* Parameters for uploading or replacing the source-language icon
|
|
107
|
+
* registered against a badge. A subsequent upload for the same badge
|
|
108
|
+
* replaces the existing source icon; per-locale icon overlays live
|
|
109
|
+
* under a separate `legacy-game-internationalization` endpoint and
|
|
110
|
+
* are not exposed on the badges client today.
|
|
111
|
+
*/
|
|
112
|
+
interface UploadBadgeIconParameters {
|
|
113
|
+
/** Stringified ID of the badge whose icon is being uploaded. */
|
|
114
|
+
readonly badgeId: string;
|
|
115
|
+
/** Image bytes to upload. PNG and JPEG are accepted by the server. */
|
|
116
|
+
readonly icon: Blob | Uint8Array;
|
|
117
|
+
}
|
|
118
|
+
//#endregion
|
|
119
|
+
//#region src/resources/badges/client.d.ts
|
|
120
|
+
/**
|
|
121
|
+
* Public client for the Roblox Open Cloud Badges API. Covers programmatic
|
|
122
|
+
* badge creation under a universe, partial updates of badge configuration
|
|
123
|
+
* (`name`, `description`, `enabled`), and source-language icon uploads.
|
|
124
|
+
*
|
|
125
|
+
* Wires the request builders, the injected
|
|
126
|
+
* {@link OpenCloudClientOptions.httpClient}, and response parsers into a
|
|
127
|
+
* single ergonomic surface. Every method returns a {@link Result} so
|
|
128
|
+
* callers handle failure explicitly; no thrown {@link OpenCloudError}
|
|
129
|
+
* ever escapes the client.
|
|
130
|
+
*
|
|
131
|
+
* @example
|
|
132
|
+
*
|
|
133
|
+
* ```ts
|
|
134
|
+
* import { BadgesClient } from "@bedrock-rbx/ocale/badges";
|
|
135
|
+
*
|
|
136
|
+
* const client = new BadgesClient({ apiKey: "your-key" });
|
|
137
|
+
* expect(client).toBeInstanceOf(BadgesClient);
|
|
138
|
+
* ```
|
|
139
|
+
*/
|
|
140
|
+
declare class BadgesClient {
|
|
141
|
+
#private;
|
|
142
|
+
/**
|
|
143
|
+
* Creates a new {@link BadgesClient}. Configuration is frozen on
|
|
144
|
+
* construction; per-request overrides are accepted on each method.
|
|
145
|
+
*
|
|
146
|
+
* @param options - Client-level configuration including the API key.
|
|
147
|
+
*/
|
|
148
|
+
constructor(options: OpenCloudClientOptions);
|
|
149
|
+
/**
|
|
150
|
+
* Creates a new badge under the supplied universe.
|
|
151
|
+
*
|
|
152
|
+
* @param parameters - Creation fields including the universe, name, and
|
|
153
|
+
* icon image.
|
|
154
|
+
* @param options - Optional per-request overrides.
|
|
155
|
+
* @returns A {@link Result} wrapping the parsed {@link Badge} or the
|
|
156
|
+
* {@link OpenCloudError} that caused the request to fail.
|
|
157
|
+
*/
|
|
158
|
+
create(parameters: CreateBadgeParameters, options?: RequestOptions): Promise<Result<Badge, OpenCloudError>>;
|
|
159
|
+
/**
|
|
160
|
+
* Partially updates a badge's configuration. Mirrors the upstream
|
|
161
|
+
* `200 OK` empty response: a successful update yields `undefined`
|
|
162
|
+
* data. Only fields explicitly provided are forwarded to the server,
|
|
163
|
+
* so omitted fields keep their current values.
|
|
164
|
+
*
|
|
165
|
+
* @param parameters - Identifier plus the fields to update.
|
|
166
|
+
* @param options - Optional per-request overrides.
|
|
167
|
+
* @returns A success {@link Result} with no payload, or the
|
|
168
|
+
* {@link OpenCloudError} that caused the request to fail.
|
|
169
|
+
*/
|
|
170
|
+
update(parameters: UpdateBadgeParameters, options?: RequestOptions): Promise<Result<undefined, OpenCloudError>>;
|
|
171
|
+
/**
|
|
172
|
+
* Uploads or replaces the source-language icon registered against a
|
|
173
|
+
* badge. A subsequent upload for the same badge replaces the
|
|
174
|
+
* existing source icon. Does not retry on 5xx so a duplicate icon
|
|
175
|
+
* upload cannot be created if the server fails mid-write.
|
|
176
|
+
*
|
|
177
|
+
* @param parameters - Identifier plus the image bytes to upload.
|
|
178
|
+
* @param options - Optional per-request overrides.
|
|
179
|
+
* @returns A success {@link Result} with no payload, or the
|
|
180
|
+
* {@link OpenCloudError} that caused the request to fail.
|
|
181
|
+
*/
|
|
182
|
+
uploadIcon(parameters: UploadBadgeIconParameters, options?: RequestOptions): Promise<Result<undefined, OpenCloudError>>;
|
|
183
|
+
}
|
|
184
|
+
//#endregion
|
|
185
|
+
export { type Badge, type BadgeAwarder, type BadgeAwarderType, type BadgePaymentSource, type BadgeStatistics, BadgesClient, type CreateBadgeParameters, type UpdateBadgeParameters, type UploadBadgeIconParameters };
|
|
186
|
+
//# sourceMappingURL=badges.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"badges.d.mts","names":[],"sources":["../src/domains/badges/badges/types.ts","../src/domains/publish/badge-icon/types.ts","../src/resources/badges/client.ts"],"mappings":";;;;;;AAMA;;;KAAY,gBAAA;;AAMZ;;;UAAiB,YAAA;;WAEP,EAAA;;WAEA,IAAA;;WAEA,IAAA,EAAM,gBAAA;AAAA;;;;UAMC,eAAA;;WAEP,YAAA;;WAEA,mBAAA;EAQV;EAAA,SANU,iBAAA;AAAA;;;AAWV;KALY,kBAAA;;;;UAKK,qBAAA;;WAEP,IAAA;;WAEA,WAAA;;WAEA,YAAA;;WAEA,IAAA,EAAM,IAAA,GAAO,UAAA;;WAEb,QAAA;;WAEA,aAAA,GAAgB,kBAAA;;WAEhB,UAAA;AAAA;;;;;;UAQO,qBAAA;;WAEP,IAAA;EAMA;EAAA,SAJA,OAAA;EAYO;EAAA,SAVP,WAAA;;WAEA,OAAA;AAAA;;;;;;UAQO,KAAA;;WAEP,EAAA;;WAEA,IAAA;;WAEA,OAAA,EAAS,YAAA;;WAET,SAAA,EAAW,IAAA;;WAEX,WAAA;;WAEA,kBAAA;;WAEA,kBAAA;EAUW;EAAA,SARX,WAAA;;WAEA,OAAA;EC1FV;EAAA,SD4FU,WAAA;;WAEA,UAAA,EAAY,eAAA;;WAEZ,SAAA,EAAW,IAAA;AAAA;;;;;;AAjGrB;;;;UCCiB,yBAAA;EDKjB;EAAA,SCHU,OAAA;;WAEA,IAAA,EAAM,IAAA,GAAO,UAAA;AAAA;;;;;;ADCvB;;;;;;;;;;AAYA;;;;;;;cEyDa,YAAA;EAAA;EF7Cb;;;;;AAKA;EEiDC,WAAA,CAAY,OAAA,EAAS,sBAAA;;;;;;;;;;EAarB,MAAA,CACC,UAAA,EAAY,qBAAA,EACZ,OAAA,GAAU,cAAA,GACR,OAAA,CAAQ,MAAA,CAAO,KAAA,EAAO,cAAA;;;;;;;;;AF3C1B;;;EE0DC,MAAA,CACC,UAAA,EAAY,qBAAA,EACZ,OAAA,GAAU,cAAA,GACR,OAAA,CAAQ,MAAA,YAAkB,cAAA;;;;;;;AF7C9B;;;;;EE4DC,UAAA,CACC,UAAA,EAAY,yBAAA,EACZ,OAAA,GAAU,cAAA,GACR,OAAA,CAAQ,MAAA,YAAkB,cAAA;AAAA"}
|
package/dist/badges.mjs
ADDED
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
import { i as ApiError } from "./rate-limit-BBU_4xnZ.mjs";
|
|
2
|
+
import { t as toBlob } from "./to-blob-1BtHsDGK.mjs";
|
|
3
|
+
import { a as IDEMPOTENT_METHOD_DEFAULTS, i as CREATE_METHOD_DEFAULTS, n as okRequest, o as isRecord, r as parseEmptyResponse, t as ResourceClient } from "./resource-client-CaS_j3yg.mjs";
|
|
4
|
+
//#region src/domains/badges/badges/builders.ts
|
|
5
|
+
/**
|
|
6
|
+
* Builds a `POST` request for the legacy "create badge" endpoint.
|
|
7
|
+
*
|
|
8
|
+
* @param parameters - Fields describing the new badge; optional values
|
|
9
|
+
* omitted here are left off the multipart payload entirely.
|
|
10
|
+
* @returns A pure {@link HttpRequest} describing the create call.
|
|
11
|
+
*/
|
|
12
|
+
function buildCreateRequest(parameters) {
|
|
13
|
+
const body = new FormData();
|
|
14
|
+
body.append("name", parameters.name);
|
|
15
|
+
body.append("files", toBlob(parameters.icon));
|
|
16
|
+
if (parameters.description !== void 0) body.append("description", parameters.description);
|
|
17
|
+
if (parameters.isActive !== void 0) body.append("isActive", String(parameters.isActive));
|
|
18
|
+
if (parameters.expectedCost !== void 0) body.append("expectedCost", String(parameters.expectedCost));
|
|
19
|
+
if (parameters.paymentSource !== void 0) body.append("paymentSourceType", toPaymentSourceWire(parameters.paymentSource));
|
|
20
|
+
return {
|
|
21
|
+
body,
|
|
22
|
+
method: "POST",
|
|
23
|
+
url: `/legacy-badges/v1/universes/${parameters.universeId}/badges`
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Builds a `PATCH` request for the legacy "update badge" endpoint. Every
|
|
28
|
+
* field on `parameters` except the identifier is optional; omitted fields
|
|
29
|
+
* are not appended to the JSON body so the server leaves them unchanged.
|
|
30
|
+
*
|
|
31
|
+
* @param parameters - Identifier plus fields to update.
|
|
32
|
+
* @returns A pure {@link HttpRequest} describing the update call.
|
|
33
|
+
*/
|
|
34
|
+
function buildUpdateRequest(parameters) {
|
|
35
|
+
const body = {};
|
|
36
|
+
if (parameters.name !== void 0) body["name"] = parameters.name;
|
|
37
|
+
if (parameters.description !== void 0) body["description"] = parameters.description;
|
|
38
|
+
if (parameters.enabled !== void 0) body["enabled"] = parameters.enabled;
|
|
39
|
+
return {
|
|
40
|
+
body,
|
|
41
|
+
method: "PATCH",
|
|
42
|
+
url: `/legacy-badges/v1/badges/${parameters.badgeId}`
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
function toPaymentSourceWire(source) {
|
|
46
|
+
return source === "User" ? "1" : "2";
|
|
47
|
+
}
|
|
48
|
+
//#endregion
|
|
49
|
+
//#region src/domains/badges/badges/operations.ts
|
|
50
|
+
/**
|
|
51
|
+
* Per-second request ceiling for creating a badge, sourced from
|
|
52
|
+
* `x-roblox-rate-limits` on the legacy badges create operation in the
|
|
53
|
+
* vendored OpenAPI schema (100 per minute, per API key owner).
|
|
54
|
+
*/
|
|
55
|
+
const CREATE_OPERATION_LIMIT = Object.freeze({
|
|
56
|
+
maxPerSecond: 100 / 60,
|
|
57
|
+
operationKey: "badges.create"
|
|
58
|
+
});
|
|
59
|
+
/**
|
|
60
|
+
* Per-second request ceiling for updating a badge, sourced from
|
|
61
|
+
* `x-roblox-rate-limits` on the legacy badges update operation in the
|
|
62
|
+
* vendored OpenAPI schema. Keyed independently from
|
|
63
|
+
* {@link CREATE_OPERATION_LIMIT} so create and update do not share a
|
|
64
|
+
* queue, since Roblox does not document the per-minute quota as shared
|
|
65
|
+
* between the POST and PATCH endpoints.
|
|
66
|
+
*/
|
|
67
|
+
const UPDATE_OPERATION_LIMIT = Object.freeze({
|
|
68
|
+
maxPerSecond: 100 / 60,
|
|
69
|
+
operationKey: "badges.update"
|
|
70
|
+
});
|
|
71
|
+
/**
|
|
72
|
+
* Scopes the API key or OAuth token must carry to create a badge,
|
|
73
|
+
* sourced from `x-roblox-scopes` on the legacy badges create operation
|
|
74
|
+
* in the vendored OpenAPI schema. The trailing
|
|
75
|
+
* `:manage-and-spend-robux` reflects that badge creation may charge a
|
|
76
|
+
* Robux fee.
|
|
77
|
+
*/
|
|
78
|
+
const CREATE_REQUIRED_SCOPES = Object.freeze(["legacy-universe.badge:manage-and-spend-robux"]);
|
|
79
|
+
/**
|
|
80
|
+
* Scopes the API key or OAuth token must carry to update a badge,
|
|
81
|
+
* sourced from `x-roblox-scopes` on the legacy badges update operation
|
|
82
|
+
* in the vendored OpenAPI schema.
|
|
83
|
+
*/
|
|
84
|
+
const UPDATE_REQUIRED_SCOPES = Object.freeze(["legacy-universe.badge:write"]);
|
|
85
|
+
//#endregion
|
|
86
|
+
//#region src/domains/badges/badges/parsers.ts
|
|
87
|
+
/**
|
|
88
|
+
* Parses a successful Badges API response into the public `Badge` shape,
|
|
89
|
+
* returning a `Result` so callers can handle malformed payloads without
|
|
90
|
+
* exceptions.
|
|
91
|
+
*
|
|
92
|
+
* @param response - The full {@link HttpResponse} from the Open Cloud API.
|
|
93
|
+
* The status code is included on the returned `ApiError` when validation
|
|
94
|
+
* fails; the headers are available for future parsers that need them.
|
|
95
|
+
* @returns A success result wrapping the converted `Badge`, or an
|
|
96
|
+
* `ApiError` when the body does not match the wire schema.
|
|
97
|
+
*/
|
|
98
|
+
function parseBadgeResponse(response) {
|
|
99
|
+
const { body, status: statusCode } = response;
|
|
100
|
+
if (!isBadgeResponseV2Wire(body)) return {
|
|
101
|
+
err: new ApiError("Malformed badge response", { statusCode }),
|
|
102
|
+
success: false
|
|
103
|
+
};
|
|
104
|
+
return {
|
|
105
|
+
data: {
|
|
106
|
+
id: String(body.id),
|
|
107
|
+
name: body.name,
|
|
108
|
+
awarder: copyAwarder(body.awarder),
|
|
109
|
+
createdAt: new Date(body.created),
|
|
110
|
+
description: body.description,
|
|
111
|
+
displayDescription: body.displayDescription,
|
|
112
|
+
displayIconImageId: body.displayIconImageId === 0 ? void 0 : String(body.displayIconImageId),
|
|
113
|
+
displayName: body.displayName,
|
|
114
|
+
enabled: body.enabled,
|
|
115
|
+
iconImageId: body.iconImageId === 0 ? void 0 : String(body.iconImageId),
|
|
116
|
+
statistics: copyStatistics(body.statistics),
|
|
117
|
+
updatedAt: new Date(body.updated)
|
|
118
|
+
},
|
|
119
|
+
success: true
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
function copyAwarder(awarder) {
|
|
123
|
+
return {
|
|
124
|
+
id: String(awarder.id),
|
|
125
|
+
name: awarder.name,
|
|
126
|
+
type: "Place"
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
function copyStatistics(statistics) {
|
|
130
|
+
return {
|
|
131
|
+
awardedCount: statistics.awardedCount,
|
|
132
|
+
pastDayAwardedCount: statistics.pastDayAwardedCount,
|
|
133
|
+
winRatePercentage: statistics.winRatePercentage
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
function hasRequiredPrimitiveFields(body) {
|
|
137
|
+
return typeof body["id"] === "number" && typeof body["name"] === "string" && typeof body["description"] === "string" && typeof body["displayName"] === "string" && typeof body["displayDescription"] === "string" && typeof body["enabled"] === "boolean" && typeof body["iconImageId"] === "number" && typeof body["displayIconImageId"] === "number" && typeof body["created"] === "string" && typeof body["updated"] === "string";
|
|
138
|
+
}
|
|
139
|
+
function isBadgeAwarderWire(value) {
|
|
140
|
+
if (!isRecord(value)) return false;
|
|
141
|
+
return typeof value["id"] === "number" && typeof value["name"] === "string" && value["type"] === 1;
|
|
142
|
+
}
|
|
143
|
+
function isBadgeStatisticsWire(value) {
|
|
144
|
+
if (!isRecord(value)) return false;
|
|
145
|
+
return typeof value["awardedCount"] === "number" && typeof value["pastDayAwardedCount"] === "number" && typeof value["winRatePercentage"] === "number";
|
|
146
|
+
}
|
|
147
|
+
function isBadgeResponseV2Wire(body) {
|
|
148
|
+
if (!isRecord(body)) return false;
|
|
149
|
+
if (!hasRequiredPrimitiveFields(body)) return false;
|
|
150
|
+
if (!isBadgeAwarderWire(body["awarder"])) return false;
|
|
151
|
+
if (!isBadgeStatisticsWire(body["statistics"])) return false;
|
|
152
|
+
return true;
|
|
153
|
+
}
|
|
154
|
+
//#endregion
|
|
155
|
+
//#region src/domains/publish/badge-icon/builders.ts
|
|
156
|
+
/**
|
|
157
|
+
* Builds a `POST` request for the legacy "upload badge icon" endpoint. A
|
|
158
|
+
* successful upload replaces any existing source-language icon on the
|
|
159
|
+
* badge.
|
|
160
|
+
*
|
|
161
|
+
* @param parameters - Badge identifier plus the image bytes to upload.
|
|
162
|
+
* @returns A pure {@link HttpRequest} describing the upload call.
|
|
163
|
+
*/
|
|
164
|
+
function buildUploadIconRequest(parameters) {
|
|
165
|
+
const body = new FormData();
|
|
166
|
+
body.append("Files", toBlob(parameters.icon));
|
|
167
|
+
return {
|
|
168
|
+
body,
|
|
169
|
+
method: "POST",
|
|
170
|
+
url: `/legacy-publish/v1/badges/${parameters.badgeId}/icon`
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
//#endregion
|
|
174
|
+
//#region src/domains/publish/badge-icon/operations.ts
|
|
175
|
+
/**
|
|
176
|
+
* Per-second request ceiling for uploading a badge icon, sourced from
|
|
177
|
+
* `x-roblox-rate-limits` on the legacy badge icon upload operation in
|
|
178
|
+
* the vendored OpenAPI schema (100 per minute, per API key owner). Keyed
|
|
179
|
+
* separately from the badges create and update buckets because Roblox
|
|
180
|
+
* does not document the legacy-publish quota as shared with the
|
|
181
|
+
* legacy-badges quota.
|
|
182
|
+
*/
|
|
183
|
+
const UPLOAD_ICON_OPERATION_LIMIT = Object.freeze({
|
|
184
|
+
maxPerSecond: 100 / 60,
|
|
185
|
+
operationKey: "badges.upload-icon"
|
|
186
|
+
});
|
|
187
|
+
/**
|
|
188
|
+
* Scopes the API key or OAuth token must carry to upload a badge icon,
|
|
189
|
+
* sourced from `x-roblox-scopes` on the legacy badge icon upload
|
|
190
|
+
* operation in the vendored OpenAPI schema.
|
|
191
|
+
*/
|
|
192
|
+
const UPLOAD_ICON_REQUIRED_SCOPES = Object.freeze(["legacy-badge:manage"]);
|
|
193
|
+
//#endregion
|
|
194
|
+
//#region src/resources/badges/client.ts
|
|
195
|
+
function makeSpec(spec) {
|
|
196
|
+
return Object.freeze(spec);
|
|
197
|
+
}
|
|
198
|
+
const CREATE_SPEC = makeSpec({
|
|
199
|
+
buildRequest: (parameters) => okRequest(buildCreateRequest(parameters)),
|
|
200
|
+
methodDefaults: CREATE_METHOD_DEFAULTS,
|
|
201
|
+
methodKind: "create",
|
|
202
|
+
operationLimit: CREATE_OPERATION_LIMIT,
|
|
203
|
+
parse: parseBadgeResponse,
|
|
204
|
+
requiredScopes: CREATE_REQUIRED_SCOPES
|
|
205
|
+
});
|
|
206
|
+
const UPDATE_SPEC = makeSpec({
|
|
207
|
+
buildRequest: (parameters) => okRequest(buildUpdateRequest(parameters)),
|
|
208
|
+
methodDefaults: IDEMPOTENT_METHOD_DEFAULTS,
|
|
209
|
+
methodKind: "idempotent",
|
|
210
|
+
operationLimit: UPDATE_OPERATION_LIMIT,
|
|
211
|
+
parse: parseEmptyResponse,
|
|
212
|
+
requiredScopes: UPDATE_REQUIRED_SCOPES
|
|
213
|
+
});
|
|
214
|
+
const UPLOAD_ICON_SPEC = makeSpec({
|
|
215
|
+
buildRequest: (parameters) => okRequest(buildUploadIconRequest(parameters)),
|
|
216
|
+
methodDefaults: CREATE_METHOD_DEFAULTS,
|
|
217
|
+
methodKind: "create",
|
|
218
|
+
operationLimit: UPLOAD_ICON_OPERATION_LIMIT,
|
|
219
|
+
parse: parseEmptyResponse,
|
|
220
|
+
requiredScopes: UPLOAD_ICON_REQUIRED_SCOPES
|
|
221
|
+
});
|
|
222
|
+
/**
|
|
223
|
+
* Public client for the Roblox Open Cloud Badges API. Covers programmatic
|
|
224
|
+
* badge creation under a universe, partial updates of badge configuration
|
|
225
|
+
* (`name`, `description`, `enabled`), and source-language icon uploads.
|
|
226
|
+
*
|
|
227
|
+
* Wires the request builders, the injected
|
|
228
|
+
* {@link OpenCloudClientOptions.httpClient}, and response parsers into a
|
|
229
|
+
* single ergonomic surface. Every method returns a {@link Result} so
|
|
230
|
+
* callers handle failure explicitly; no thrown {@link OpenCloudError}
|
|
231
|
+
* ever escapes the client.
|
|
232
|
+
*
|
|
233
|
+
* @example
|
|
234
|
+
*
|
|
235
|
+
* ```ts
|
|
236
|
+
* import { BadgesClient } from "@bedrock-rbx/ocale/badges";
|
|
237
|
+
*
|
|
238
|
+
* const client = new BadgesClient({ apiKey: "your-key" });
|
|
239
|
+
* expect(client).toBeInstanceOf(BadgesClient);
|
|
240
|
+
* ```
|
|
241
|
+
*/
|
|
242
|
+
var BadgesClient = class {
|
|
243
|
+
#inner;
|
|
244
|
+
/**
|
|
245
|
+
* Creates a new {@link BadgesClient}. Configuration is frozen on
|
|
246
|
+
* construction; per-request overrides are accepted on each method.
|
|
247
|
+
*
|
|
248
|
+
* @param options - Client-level configuration including the API key.
|
|
249
|
+
*/
|
|
250
|
+
constructor(options) {
|
|
251
|
+
this.#inner = new ResourceClient(options);
|
|
252
|
+
}
|
|
253
|
+
/**
|
|
254
|
+
* Creates a new badge under the supplied universe.
|
|
255
|
+
*
|
|
256
|
+
* @param parameters - Creation fields including the universe, name, and
|
|
257
|
+
* icon image.
|
|
258
|
+
* @param options - Optional per-request overrides.
|
|
259
|
+
* @returns A {@link Result} wrapping the parsed {@link Badge} or the
|
|
260
|
+
* {@link OpenCloudError} that caused the request to fail.
|
|
261
|
+
*/
|
|
262
|
+
async create(parameters, options) {
|
|
263
|
+
return this.#inner.execute({
|
|
264
|
+
options,
|
|
265
|
+
parameters,
|
|
266
|
+
spec: CREATE_SPEC
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
/**
|
|
270
|
+
* Partially updates a badge's configuration. Mirrors the upstream
|
|
271
|
+
* `200 OK` empty response: a successful update yields `undefined`
|
|
272
|
+
* data. Only fields explicitly provided are forwarded to the server,
|
|
273
|
+
* so omitted fields keep their current values.
|
|
274
|
+
*
|
|
275
|
+
* @param parameters - Identifier plus the fields to update.
|
|
276
|
+
* @param options - Optional per-request overrides.
|
|
277
|
+
* @returns A success {@link Result} with no payload, or the
|
|
278
|
+
* {@link OpenCloudError} that caused the request to fail.
|
|
279
|
+
*/
|
|
280
|
+
async update(parameters, options) {
|
|
281
|
+
return this.#inner.execute({
|
|
282
|
+
options,
|
|
283
|
+
parameters,
|
|
284
|
+
spec: UPDATE_SPEC
|
|
285
|
+
});
|
|
286
|
+
}
|
|
287
|
+
/**
|
|
288
|
+
* Uploads or replaces the source-language icon registered against a
|
|
289
|
+
* badge. A subsequent upload for the same badge replaces the
|
|
290
|
+
* existing source icon. Does not retry on 5xx so a duplicate icon
|
|
291
|
+
* upload cannot be created if the server fails mid-write.
|
|
292
|
+
*
|
|
293
|
+
* @param parameters - Identifier plus the image bytes to upload.
|
|
294
|
+
* @param options - Optional per-request overrides.
|
|
295
|
+
* @returns A success {@link Result} with no payload, or the
|
|
296
|
+
* {@link OpenCloudError} that caused the request to fail.
|
|
297
|
+
*/
|
|
298
|
+
async uploadIcon(parameters, options) {
|
|
299
|
+
return this.#inner.execute({
|
|
300
|
+
options,
|
|
301
|
+
parameters,
|
|
302
|
+
spec: UPLOAD_ICON_SPEC
|
|
303
|
+
});
|
|
304
|
+
}
|
|
305
|
+
};
|
|
306
|
+
//#endregion
|
|
307
|
+
export { BadgesClient };
|
|
308
|
+
|
|
309
|
+
//# sourceMappingURL=badges.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"badges.mjs","names":["#inner"],"sources":["../src/domains/badges/badges/builders.ts","../src/domains/badges/badges/operations.ts","../src/domains/badges/badges/parsers.ts","../src/domains/publish/badge-icon/builders.ts","../src/domains/publish/badge-icon/operations.ts","../src/resources/badges/client.ts"],"sourcesContent":["import type { HttpRequest } from \"../../../internal/http/types.ts\";\nimport { toBlob } from \"../../../internal/utils/to-blob.ts\";\nimport type { BadgePaymentSource, CreateBadgeParameters, UpdateBadgeParameters } from \"./types.ts\";\n\n/**\n * Builds a `POST` request for the legacy \"create badge\" endpoint.\n *\n * @param parameters - Fields describing the new badge; optional values\n * omitted here are left off the multipart payload entirely.\n * @returns A pure {@link HttpRequest} describing the create call.\n */\nexport function buildCreateRequest(parameters: CreateBadgeParameters): HttpRequest {\n\tconst body = new FormData();\n\tbody.append(\"name\", parameters.name);\n\tbody.append(\"files\", toBlob(parameters.icon));\n\tif (parameters.description !== undefined) {\n\t\tbody.append(\"description\", parameters.description);\n\t}\n\n\tif (parameters.isActive !== undefined) {\n\t\tbody.append(\"isActive\", String(parameters.isActive));\n\t}\n\n\tif (parameters.expectedCost !== undefined) {\n\t\tbody.append(\"expectedCost\", String(parameters.expectedCost));\n\t}\n\n\tif (parameters.paymentSource !== undefined) {\n\t\tbody.append(\"paymentSourceType\", toPaymentSourceWire(parameters.paymentSource));\n\t}\n\n\treturn {\n\t\tbody,\n\t\tmethod: \"POST\",\n\t\turl: `/legacy-badges/v1/universes/${parameters.universeId}/badges`,\n\t};\n}\n\n/**\n * Builds a `PATCH` request for the legacy \"update badge\" endpoint. Every\n * field on `parameters` except the identifier is optional; omitted fields\n * are not appended to the JSON body so the server leaves them unchanged.\n *\n * @param parameters - Identifier plus fields to update.\n * @returns A pure {@link HttpRequest} describing the update call.\n */\nexport function buildUpdateRequest(parameters: UpdateBadgeParameters): HttpRequest {\n\tconst body: Record<string, boolean | string> = {};\n\tif (parameters.name !== undefined) {\n\t\tbody[\"name\"] = parameters.name;\n\t}\n\n\tif (parameters.description !== undefined) {\n\t\tbody[\"description\"] = parameters.description;\n\t}\n\n\tif (parameters.enabled !== undefined) {\n\t\tbody[\"enabled\"] = parameters.enabled;\n\t}\n\n\treturn {\n\t\tbody,\n\t\tmethod: \"PATCH\",\n\t\turl: `/legacy-badges/v1/badges/${parameters.badgeId}`,\n\t};\n}\n\nfunction toPaymentSourceWire(source: BadgePaymentSource): \"1\" | \"2\" {\n\treturn source === \"User\" ? \"1\" : \"2\";\n}\n","import type { OperationLimit } from \"../../../internal/http/rate-limit-queue.ts\";\n\n/**\n * Per-second request ceiling for creating a badge, sourced from\n * `x-roblox-rate-limits` on the legacy badges create operation in the\n * vendored OpenAPI schema (100 per minute, per API key owner).\n */\nexport const CREATE_OPERATION_LIMIT: OperationLimit = Object.freeze({\n\tmaxPerSecond: 100 / 60,\n\toperationKey: \"badges.create\",\n});\n\n/**\n * Per-second request ceiling for updating a badge, sourced from\n * `x-roblox-rate-limits` on the legacy badges update operation in the\n * vendored OpenAPI schema. Keyed independently from\n * {@link CREATE_OPERATION_LIMIT} so create and update do not share a\n * queue, since Roblox does not document the per-minute quota as shared\n * between the POST and PATCH endpoints.\n */\nexport const UPDATE_OPERATION_LIMIT: OperationLimit = Object.freeze({\n\tmaxPerSecond: 100 / 60,\n\toperationKey: \"badges.update\",\n});\n\n/**\n * Scopes the API key or OAuth token must carry to create a badge,\n * sourced from `x-roblox-scopes` on the legacy badges create operation\n * in the vendored OpenAPI schema. The trailing\n * `:manage-and-spend-robux` reflects that badge creation may charge a\n * Robux fee.\n */\nexport const CREATE_REQUIRED_SCOPES: ReadonlyArray<string> = Object.freeze([\n\t\"legacy-universe.badge:manage-and-spend-robux\",\n]);\n\n/**\n * Scopes the API key or OAuth token must carry to update a badge,\n * sourced from `x-roblox-scopes` on the legacy badges update operation\n * in the vendored OpenAPI schema.\n */\nexport const UPDATE_REQUIRED_SCOPES: ReadonlyArray<string> = Object.freeze([\n\t\"legacy-universe.badge:write\",\n]);\n","import type { HttpResponse } from \"../../../client/types.ts\";\nimport { ApiError } from \"../../../errors/api-error.ts\";\nimport { isRecord } from \"../../../internal/utils/is-record.ts\";\nimport type { Result } from \"../../../types.ts\";\nimport type { Badge, BadgeAwarder, BadgeStatistics } from \"./types.ts\";\nimport type { BadgeAwarderWire, BadgeResponseV2Wire, BadgeStatisticsWire } from \"./wire.ts\";\n\n/**\n * Parses a successful Badges API response into the public `Badge` shape,\n * returning a `Result` so callers can handle malformed payloads without\n * exceptions.\n *\n * @param response - The full {@link HttpResponse} from the Open Cloud API.\n * The status code is included on the returned `ApiError` when validation\n * fails; the headers are available for future parsers that need them.\n * @returns A success result wrapping the converted `Badge`, or an\n * `ApiError` when the body does not match the wire schema.\n */\nexport function parseBadgeResponse(response: HttpResponse): Result<Badge, ApiError> {\n\tconst { body, status: statusCode } = response;\n\n\tif (!isBadgeResponseV2Wire(body)) {\n\t\treturn {\n\t\t\terr: new ApiError(\"Malformed badge response\", { statusCode }),\n\t\t\tsuccess: false,\n\t\t};\n\t}\n\n\treturn {\n\t\tdata: {\n\t\t\tid: String(body.id),\n\t\t\tname: body.name,\n\t\t\tawarder: copyAwarder(body.awarder),\n\t\t\tcreatedAt: new Date(body.created),\n\t\t\tdescription: body.description,\n\t\t\tdisplayDescription: body.displayDescription,\n\t\t\tdisplayIconImageId:\n\t\t\t\tbody.displayIconImageId === 0 ? undefined : String(body.displayIconImageId),\n\t\t\tdisplayName: body.displayName,\n\t\t\tenabled: body.enabled,\n\t\t\ticonImageId: body.iconImageId === 0 ? undefined : String(body.iconImageId),\n\t\t\tstatistics: copyStatistics(body.statistics),\n\t\t\tupdatedAt: new Date(body.updated),\n\t\t},\n\t\tsuccess: true,\n\t};\n}\n\nfunction copyAwarder(awarder: BadgeAwarderWire): BadgeAwarder {\n\treturn { id: String(awarder.id), name: awarder.name, type: \"Place\" };\n}\n\nfunction copyStatistics(statistics: BadgeStatisticsWire): BadgeStatistics {\n\treturn {\n\t\tawardedCount: statistics.awardedCount,\n\t\tpastDayAwardedCount: statistics.pastDayAwardedCount,\n\t\twinRatePercentage: statistics.winRatePercentage,\n\t};\n}\n\nfunction hasRequiredPrimitiveFields(body: Record<string, unknown>): boolean {\n\treturn (\n\t\ttypeof body[\"id\"] === \"number\" &&\n\t\ttypeof body[\"name\"] === \"string\" &&\n\t\ttypeof body[\"description\"] === \"string\" &&\n\t\ttypeof body[\"displayName\"] === \"string\" &&\n\t\ttypeof body[\"displayDescription\"] === \"string\" &&\n\t\ttypeof body[\"enabled\"] === \"boolean\" &&\n\t\ttypeof body[\"iconImageId\"] === \"number\" &&\n\t\ttypeof body[\"displayIconImageId\"] === \"number\" &&\n\t\ttypeof body[\"created\"] === \"string\" &&\n\t\ttypeof body[\"updated\"] === \"string\"\n\t);\n}\n\nfunction isBadgeAwarderWire(value: unknown): value is BadgeAwarderWire {\n\tif (!isRecord(value)) {\n\t\treturn false;\n\t}\n\n\treturn (\n\t\ttypeof value[\"id\"] === \"number\" && typeof value[\"name\"] === \"string\" && value[\"type\"] === 1\n\t);\n}\n\nfunction isBadgeStatisticsWire(value: unknown): value is BadgeStatisticsWire {\n\tif (!isRecord(value)) {\n\t\treturn false;\n\t}\n\n\treturn (\n\t\ttypeof value[\"awardedCount\"] === \"number\" &&\n\t\ttypeof value[\"pastDayAwardedCount\"] === \"number\" &&\n\t\ttypeof value[\"winRatePercentage\"] === \"number\"\n\t);\n}\n\nfunction isBadgeResponseV2Wire(body: unknown): body is BadgeResponseV2Wire {\n\tif (!isRecord(body)) {\n\t\treturn false;\n\t}\n\n\tif (!hasRequiredPrimitiveFields(body)) {\n\t\treturn false;\n\t}\n\n\tif (!isBadgeAwarderWire(body[\"awarder\"])) {\n\t\treturn false;\n\t}\n\n\tif (!isBadgeStatisticsWire(body[\"statistics\"])) {\n\t\treturn false;\n\t}\n\n\treturn true;\n}\n","import type { HttpRequest } from \"../../../internal/http/types.ts\";\nimport { toBlob } from \"../../../internal/utils/to-blob.ts\";\nimport type { UploadBadgeIconParameters } from \"./types.ts\";\n\n/**\n * Builds a `POST` request for the legacy \"upload badge icon\" endpoint. A\n * successful upload replaces any existing source-language icon on the\n * badge.\n *\n * @param parameters - Badge identifier plus the image bytes to upload.\n * @returns A pure {@link HttpRequest} describing the upload call.\n */\nexport function buildUploadIconRequest(parameters: UploadBadgeIconParameters): HttpRequest {\n\tconst body = new FormData();\n\tbody.append(\"Files\", toBlob(parameters.icon));\n\n\treturn {\n\t\tbody,\n\t\tmethod: \"POST\",\n\t\turl: `/legacy-publish/v1/badges/${parameters.badgeId}/icon`,\n\t};\n}\n","import type { OperationLimit } from \"../../../internal/http/rate-limit-queue.ts\";\n\n/**\n * Per-second request ceiling for uploading a badge icon, sourced from\n * `x-roblox-rate-limits` on the legacy badge icon upload operation in\n * the vendored OpenAPI schema (100 per minute, per API key owner). Keyed\n * separately from the badges create and update buckets because Roblox\n * does not document the legacy-publish quota as shared with the\n * legacy-badges quota.\n */\nexport const UPLOAD_ICON_OPERATION_LIMIT: OperationLimit = Object.freeze({\n\tmaxPerSecond: 100 / 60,\n\toperationKey: \"badges.upload-icon\",\n});\n\n/**\n * Scopes the API key or OAuth token must carry to upload a badge icon,\n * sourced from `x-roblox-scopes` on the legacy badge icon upload\n * operation in the vendored OpenAPI schema.\n */\nexport const UPLOAD_ICON_REQUIRED_SCOPES: ReadonlyArray<string> = Object.freeze([\n\t\"legacy-badge:manage\",\n]);\n","import type { OpenCloudClientOptions, RequestOptions } from \"../../client/types.ts\";\nimport { buildCreateRequest, buildUpdateRequest } from \"../../domains/badges/badges/builders.ts\";\nimport {\n\tCREATE_OPERATION_LIMIT,\n\tCREATE_REQUIRED_SCOPES,\n\tUPDATE_OPERATION_LIMIT,\n\tUPDATE_REQUIRED_SCOPES,\n} from \"../../domains/badges/badges/operations.ts\";\nimport { parseBadgeResponse } from \"../../domains/badges/badges/parsers.ts\";\nimport type {\n\tBadge,\n\tCreateBadgeParameters,\n\tUpdateBadgeParameters,\n} from \"../../domains/badges/badges/types.ts\";\nimport { buildUploadIconRequest } from \"../../domains/publish/badge-icon/builders.ts\";\nimport {\n\tUPLOAD_ICON_OPERATION_LIMIT,\n\tUPLOAD_ICON_REQUIRED_SCOPES,\n} from \"../../domains/publish/badge-icon/operations.ts\";\nimport type { UploadBadgeIconParameters } from \"../../domains/publish/badge-icon/types.ts\";\nimport type { OpenCloudError } from \"../../errors/base.ts\";\nimport { CREATE_METHOD_DEFAULTS, IDEMPOTENT_METHOD_DEFAULTS } from \"../../internal/http/retry.ts\";\nimport {\n\tokRequest,\n\tparseEmptyResponse,\n\tResourceClient,\n\ttype ResourceMethodSpec,\n} from \"../../internal/resource-client.ts\";\nimport type { Result } from \"../../types.ts\";\n\nfunction makeSpec<P, R>(spec: ResourceMethodSpec<P, R>): ResourceMethodSpec<P, R> {\n\treturn Object.freeze(spec);\n}\n\nconst CREATE_SPEC = makeSpec<CreateBadgeParameters, Badge>({\n\tbuildRequest: (parameters) => okRequest(buildCreateRequest(parameters)),\n\tmethodDefaults: CREATE_METHOD_DEFAULTS,\n\tmethodKind: \"create\",\n\toperationLimit: CREATE_OPERATION_LIMIT,\n\tparse: parseBadgeResponse,\n\trequiredScopes: CREATE_REQUIRED_SCOPES,\n});\n\nconst UPDATE_SPEC = makeSpec<UpdateBadgeParameters, undefined>({\n\tbuildRequest: (parameters) => okRequest(buildUpdateRequest(parameters)),\n\tmethodDefaults: IDEMPOTENT_METHOD_DEFAULTS,\n\tmethodKind: \"idempotent\",\n\toperationLimit: UPDATE_OPERATION_LIMIT,\n\tparse: parseEmptyResponse,\n\trequiredScopes: UPDATE_REQUIRED_SCOPES,\n});\n\nconst UPLOAD_ICON_SPEC = makeSpec<UploadBadgeIconParameters, undefined>({\n\tbuildRequest: (parameters) => okRequest(buildUploadIconRequest(parameters)),\n\tmethodDefaults: CREATE_METHOD_DEFAULTS,\n\tmethodKind: \"create\",\n\toperationLimit: UPLOAD_ICON_OPERATION_LIMIT,\n\tparse: parseEmptyResponse,\n\trequiredScopes: UPLOAD_ICON_REQUIRED_SCOPES,\n});\n\n/**\n * Public client for the Roblox Open Cloud Badges API. Covers programmatic\n * badge creation under a universe, partial updates of badge configuration\n * (`name`, `description`, `enabled`), and source-language icon uploads.\n *\n * Wires the request builders, the injected\n * {@link OpenCloudClientOptions.httpClient}, and response parsers into a\n * single ergonomic surface. Every method returns a {@link Result} so\n * callers handle failure explicitly; no thrown {@link OpenCloudError}\n * ever escapes the client.\n *\n * @example\n *\n * ```ts\n * import { BadgesClient } from \"@bedrock-rbx/ocale/badges\";\n *\n * const client = new BadgesClient({ apiKey: \"your-key\" });\n * expect(client).toBeInstanceOf(BadgesClient);\n * ```\n */\nexport class BadgesClient {\n\treadonly #inner: ResourceClient;\n\n\t/**\n\t * Creates a new {@link BadgesClient}. Configuration is frozen on\n\t * construction; per-request overrides are accepted on each method.\n\t *\n\t * @param options - Client-level configuration including the API key.\n\t */\n\tconstructor(options: OpenCloudClientOptions) {\n\t\tthis.#inner = new ResourceClient(options);\n\t}\n\n\t/**\n\t * Creates a new badge under the supplied universe.\n\t *\n\t * @param parameters - Creation fields including the universe, name, and\n\t * icon image.\n\t * @param options - Optional per-request overrides.\n\t * @returns A {@link Result} wrapping the parsed {@link Badge} or the\n\t * {@link OpenCloudError} that caused the request to fail.\n\t */\n\tpublic async create(\n\t\tparameters: CreateBadgeParameters,\n\t\toptions?: RequestOptions,\n\t): Promise<Result<Badge, OpenCloudError>> {\n\t\treturn this.#inner.execute({ options, parameters, spec: CREATE_SPEC });\n\t}\n\n\t/**\n\t * Partially updates a badge's configuration. Mirrors the upstream\n\t * `200 OK` empty response: a successful update yields `undefined`\n\t * data. Only fields explicitly provided are forwarded to the server,\n\t * so omitted fields keep their current values.\n\t *\n\t * @param parameters - Identifier plus the fields to update.\n\t * @param options - Optional per-request overrides.\n\t * @returns A success {@link Result} with no payload, or the\n\t * {@link OpenCloudError} that caused the request to fail.\n\t */\n\tpublic async update(\n\t\tparameters: UpdateBadgeParameters,\n\t\toptions?: RequestOptions,\n\t): Promise<Result<undefined, OpenCloudError>> {\n\t\treturn this.#inner.execute({ options, parameters, spec: UPDATE_SPEC });\n\t}\n\n\t/**\n\t * Uploads or replaces the source-language icon registered against a\n\t * badge. A subsequent upload for the same badge replaces the\n\t * existing source icon. Does not retry on 5xx so a duplicate icon\n\t * upload cannot be created if the server fails mid-write.\n\t *\n\t * @param parameters - Identifier plus the image bytes to upload.\n\t * @param options - Optional per-request overrides.\n\t * @returns A success {@link Result} with no payload, or the\n\t * {@link OpenCloudError} that caused the request to fail.\n\t */\n\tpublic async uploadIcon(\n\t\tparameters: UploadBadgeIconParameters,\n\t\toptions?: RequestOptions,\n\t): Promise<Result<undefined, OpenCloudError>> {\n\t\treturn this.#inner.execute({ options, parameters, spec: UPLOAD_ICON_SPEC });\n\t}\n}\n"],"mappings":"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"}
|