@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
package/README.md
ADDED
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
# @bedrock-rbx/ocale
|
|
2
|
+
|
|
3
|
+
Zero-dependency TypeScript SDK for Roblox Open Cloud.
|
|
4
|
+
|
|
5
|
+
[](https://npmx.dev/package/@bedrock-rbx/ocale)
|
|
6
|
+
[](https://github.com/christopher-buss/bedrock/blob/main/LICENSE)
|
|
7
|
+
[](https://github.com/christopher-buss/bedrock/actions/workflows/ci.yaml)
|
|
8
|
+
|
|
9
|
+
> **Status: 0.1 beta.** The public API is stabilizing. Breaking changes may land in minor releases (0.1 to 0.2) until 1.0.
|
|
10
|
+
|
|
11
|
+
## What is `@bedrock-rbx/ocale`?
|
|
12
|
+
|
|
13
|
+
A typed HTTP client for [Roblox Open Cloud](https://create.roblox.com/docs/cloud). Each Roblox feature (universes, places, game passes, developer products, badges, storage, Luau execution) is exposed as its own client class with the methods, request types, and response types you would otherwise build by hand against the raw REST endpoints.
|
|
14
|
+
|
|
15
|
+
The SDK has zero runtime dependencies and runs on Node >= 24.12 or Bun >= 1.3 using the standard `fetch`, `FormData`, and `TextEncoder` web APIs. Rate-limit queueing and retry handling are built in: requests are queued per-operation against Roblox's published limits, and idempotent calls automatically retry on 429 and 5xx responses without consumer code reaching for `try`/`catch` or `setTimeout`.
|
|
16
|
+
|
|
17
|
+
`@bedrock-rbx/ocale` is the foundation that [`@bedrock-rbx/core`](https://github.com/christopher-buss/bedrock/tree/main/packages/bedrock) uses internally to talk to Roblox, and it is publishable as a standalone package for anyone building tooling, scripts, or services that need typed Open Cloud calls without bedrock's full IaC engine. If you already depend on `@bedrock-rbx/core`, ocale is installed transitively; reach for it directly when you need HTTP-level control that core's IaC engine does not expose.
|
|
18
|
+
|
|
19
|
+
## Install
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
pnpm add @bedrock-rbx/ocale
|
|
23
|
+
# or: npm install @bedrock-rbx/ocale
|
|
24
|
+
# or: bun add @bedrock-rbx/ocale
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
**Runtime:** Node >= 24.12 or Bun >= 1.3.
|
|
28
|
+
|
|
29
|
+
**Authentication:** every client takes an `apiKey` at construction. Generate one at [Creator Hub > Credentials > API Keys](https://create.roblox.com/dashboard/credentials) and grant it the scopes for the operations you plan to call (each method's JSDoc lists its required scopes).
|
|
30
|
+
|
|
31
|
+
## Quick start
|
|
32
|
+
|
|
33
|
+
```ts
|
|
34
|
+
import { GamePassesClient } from "@bedrock-rbx/ocale/game-passes";
|
|
35
|
+
|
|
36
|
+
const client = new GamePassesClient({ apiKey: "your-open-cloud-api-key" });
|
|
37
|
+
|
|
38
|
+
const result = await client.get({
|
|
39
|
+
gamePassId: "9876543210",
|
|
40
|
+
universeId: "1234567890", // from your experience URL in Creator Hub
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
if (!result.success) {
|
|
44
|
+
console.error(`Open Cloud call failed: ${result.err.message}`);
|
|
45
|
+
process.exit(1);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
console.log(`${result.data.name}: ${result.data.priceInRobux} Robux`);
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Every method returns `Promise<Result<T, OpenCloudError>>`. `Result` is a discriminated union: `result.success` is `true` with the parsed response on `result.data`, or `false` with a structured error on `result.err`. No exception ever escapes a client method.
|
|
52
|
+
|
|
53
|
+
## Available clients
|
|
54
|
+
|
|
55
|
+
Resource clients live on subpaths so unused features tree-shake out of your bundle.
|
|
56
|
+
|
|
57
|
+
| Subpath | Client | What it covers |
|
|
58
|
+
|---|---|---|
|
|
59
|
+
| `@bedrock-rbx/ocale/universes` | `UniversesClient` | Universe metadata, social links, icon and thumbnail uploads, badges and game-passes scoped to a universe. |
|
|
60
|
+
| `@bedrock-rbx/ocale/places` | `PlacesClient` | Place metadata, `.rbxl` publishing, Luau execution tasks scoped to a place. |
|
|
61
|
+
| `@bedrock-rbx/ocale/game-passes` | `GamePassesClient` | Game pass CRUD, icon upload, localized name and description updates. |
|
|
62
|
+
| `@bedrock-rbx/ocale/developer-products` | `DeveloperProductsClient` | Developer product CRUD, icon upload, localized name and description updates. |
|
|
63
|
+
| `@bedrock-rbx/ocale/badges` | `BadgesClient` | Badge CRUD and localized icon uploads. |
|
|
64
|
+
| `@bedrock-rbx/ocale/storage` | `StorageClient` | Memory stores (sorted maps, queues) for live game state. |
|
|
65
|
+
| `@bedrock-rbx/ocale/luau-execution` | `LuauExecutionClient` | Standalone Luau execution tasks with binary inputs and log streaming. |
|
|
66
|
+
| `@bedrock-rbx/ocale/locales` | `ROBLOX_CREATOR_LOCALES` (data) | Reference list of locales Roblox supports for localized fields. |
|
|
67
|
+
|
|
68
|
+
Additional Open Cloud features (messaging, data stores, OAuth, groups, analytics) are not yet wrapped; they are tracked on the [roadmap](https://github.com/christopher-buss/bedrock/projects).
|
|
69
|
+
|
|
70
|
+
The package root (`@bedrock-rbx/ocale`) deliberately re-exports only shared utilities: `Result`, `Page`, `OpenCloudError` and its subclasses (`ApiError`, `RateLimitError`, `NetworkError`, `ValidationError`, and similar), and the `OpenCloudClientOptions` type. Resource clients are not on the root barrel.
|
|
71
|
+
|
|
72
|
+
## Rate limiting and retries
|
|
73
|
+
|
|
74
|
+
The SDK queues every request behind a per-operation rate-limit bucket sourced from Roblox's published OpenAPI schema. You fire calls at whatever rate your code needs; the SDK paces them.
|
|
75
|
+
|
|
76
|
+
Retry behavior is idempotency-aware:
|
|
77
|
+
|
|
78
|
+
| Method kind | 429 (rate limit) | 5xx (server error) |
|
|
79
|
+
|---|---|---|
|
|
80
|
+
| Create | Retry | Do not retry |
|
|
81
|
+
| Read / List | Retry | Retry |
|
|
82
|
+
| Update | Retry | Retry |
|
|
83
|
+
| Delete | Retry | Retry |
|
|
84
|
+
|
|
85
|
+
Create operations skip 5xx retries because Roblox does not support idempotency keys and a duplicate retry would silently produce a second resource. If you can detect duplicates externally, opt back into 5xx retry on a per-call basis via `retryableStatuses` on the request options argument.
|
|
86
|
+
|
|
87
|
+
After retry attempts are exhausted, the final failure surfaces on `result.err` as the typed error that caused it (`RateLimitError` for 429s, `ApiError` for 5xx, `NetworkError` for transport-level faults, and so on).
|
|
88
|
+
|
|
89
|
+
Observability hooks (`onRequest`, `onRetry`, `onRateLimit`) accept callbacks on the client constructor for logging, metrics, or tracing integration.
|
|
90
|
+
|
|
91
|
+
## Per-request configuration overrides
|
|
92
|
+
|
|
93
|
+
Client-level config is frozen on construction. Every method accepts an optional second `RequestOptions` argument that overrides config for a single call:
|
|
94
|
+
|
|
95
|
+
```ts
|
|
96
|
+
const client = new GamePassesClient({ apiKey: "main-key" });
|
|
97
|
+
|
|
98
|
+
const result = await client.create(parameters, {
|
|
99
|
+
apiKey: "asset-upload-key", // different key for moderation safety
|
|
100
|
+
timeout: 60_000,
|
|
101
|
+
});
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
This pattern fits multi-tenant tooling (different API keys per workspace), credential rotation (swap mid-batch), and isolating retry / timeout policies to specific calls.
|
|
105
|
+
|
|
106
|
+
## Testing helpers
|
|
107
|
+
|
|
108
|
+
A `@bedrock-rbx/ocale/testing` subpath exports the same fakes the SDK's own integration tests use:
|
|
109
|
+
|
|
110
|
+
- `createFakeHttpClient(...)` and `createFakeSend(...)` for swapping the HTTP transport with a recorded fake that validates request bodies against the vendored OpenAPI schema.
|
|
111
|
+
- `createFakeSleep(...)` for deterministic retry and rate-limit timing.
|
|
112
|
+
- `valid*Body` fixtures (`validGamePassBody`, `validPlaceBody`, `validUniverseBody`, and similar) for synthesising realistic response payloads.
|
|
113
|
+
|
|
114
|
+
Pass the fake into any client via the `httpClient` and `sleep` parameters on `OpenCloudClientOptions` and assert against the captured requests.
|
|
115
|
+
|
|
116
|
+
## Status, docs, and contributing
|
|
117
|
+
|
|
118
|
+
This package is in active development ahead of a first public release. Track scope and timing on the [project board](https://github.com/christopher-buss/bedrock/projects).
|
|
119
|
+
|
|
120
|
+
- [Documentation site](https://bedrock-livid.vercel.app/) (work in progress)
|
|
121
|
+
- [Source repository](https://github.com/christopher-buss/bedrock/tree/main/packages/open-cloud)
|
|
122
|
+
- [Issues](https://github.com/christopher-buss/bedrock/issues) (maintainer-only; external feedback runs through [Discussions](https://github.com/christopher-buss/bedrock/discussions) as prompt requests)
|
|
123
|
+
- [Contributing guide](https://github.com/christopher-buss/bedrock/blob/main/CONTRIBUTING.md)
|
|
124
|
+
- [Security policy](https://github.com/christopher-buss/bedrock/blob/main/SECURITY.md)
|
|
125
|
+
|
|
126
|
+
## License
|
|
127
|
+
|
|
128
|
+
[MIT](https://github.com/christopher-buss/bedrock/blob/main/LICENSE) (c) Christopher Buss.
|
package/dist/badges.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { d as OpenCloudError, i as OpenCloudClientOptions, l as Result, s as RequestOptions } from "./types-
|
|
1
|
+
import { d as OpenCloudError, i as OpenCloudClientOptions, l as Result, s as RequestOptions } from "./types-rzs1NB-j.mjs";
|
|
2
2
|
import { i as RobloxLocale, r as RobloxLanguageCode } from "./data.generated-Cb6g6asv.mjs";
|
|
3
3
|
|
|
4
4
|
//#region src/domains/badges/badges/types.d.ts
|
package/dist/badges.mjs
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { r as ApiError } from "./rate-limit-
|
|
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";
|
|
2
3
|
import { t as toBlob } from "./to-blob-B27VhoRp.mjs";
|
|
3
|
-
import { a as
|
|
4
|
+
import { a as isDateTimeString, i as isRecord, n as okRequest, r as parseEmptyResponse, t as ResourceClient } from "./resource-client-CG9-BG81.mjs";
|
|
4
5
|
//#region src/domains/badges/badges/builders.ts
|
|
5
6
|
/**
|
|
6
7
|
* Builds a `POST` request for the legacy "create badge" endpoint.
|
package/dist/badges.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"badges.mjs","names":["buildUpdateRequest","buildUploadIconRequest","buildUpdateRequest","buildLocaleNameDescRequest","buildLocaleUploadIconRequest","#inner"],"sources":["../src/domains/badges/badges/builders.ts","../src/domains/badges/badges/operations.ts","../src/domains/badges/badges/parsers.ts","../src/domains/game-internationalization/badge-icon/builders.ts","../src/domains/game-internationalization/badge-name-description/builders.ts","../src/domains/game-internationalization/badge-name-description/operations.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 { isDateTimeString } from \"../../../internal/utils/is-date-time-string.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\tisDateTimeString(body[\"created\"]) &&\n\t\tisDateTimeString(body[\"updated\"])\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 { UploadBadgeIconLocalizationParameters } from \"./types.ts\";\n\n/**\n * Builds a `POST` request for the localized \"upload badge icon\" endpoint. A\n * successful upload replaces any existing icon for the same\n * `(badgeId, languageCode)` pair.\n *\n * @param parameters - Badge and language identifiers plus the image bytes\n * to upload.\n * @returns A pure {@link HttpRequest} describing the upload call.\n */\nexport function buildUploadIconRequest(\n\tparameters: UploadBadgeIconLocalizationParameters,\n): HttpRequest {\n\tconst body = new FormData();\n\tbody.append(\"Files\", toBlob(parameters.image));\n\n\treturn {\n\t\tbody,\n\t\tmethod: \"POST\",\n\t\turl: `/legacy-game-internationalization/v1/badges/${parameters.badgeId}/icons/language-codes/${parameters.languageCode}`,\n\t};\n}\n","import type { HttpRequest } from \"../../../internal/http/types.ts\";\nimport type { UpdateBadgeNameDescriptionParameters } from \"./types.ts\";\n\n/**\n * Builds a `PATCH` request for the localized \"update badge name/description\"\n * endpoint. Either `name`, `description`, or both may be supplied; omitted\n * fields are not included in the JSON body so the server leaves the existing\n * value for that locale untouched.\n *\n * @param parameters - Badge and language identifiers plus the optional\n * replacement values.\n * @returns A pure {@link HttpRequest} describing the update call.\n */\nexport function buildUpdateRequest(parameters: UpdateBadgeNameDescriptionParameters): HttpRequest {\n\tconst body: Record<string, 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\treturn {\n\t\tbody,\n\t\tmethod: \"PATCH\",\n\t\turl: `/legacy-game-internationalization/v1/badges/${parameters.badgeId}/name-description/language-codes/${parameters.languageCode}`,\n\t};\n}\n","import type { OperationLimit } from \"../../../internal/http/rate-limit-queue.ts\";\n\n/**\n * Per-second request ceiling for every badge localization Operation. The\n * legacy `gameinternationalization` service caps each API key at 100\n * requests per minute *shared across the entire service* (see the\n * `x-roblox-rate-limits` extension on every operation in the vendored\n * Open Cloud spec), so all badge localization methods queue against the\n * same operation key.\n */\nexport const LOCALIZATION_OPERATION_LIMIT: OperationLimit = Object.freeze({\n\tmaxPerSecond: 100 / 60,\n\toperationKey: \"badge-localization\",\n});\n\n/**\n * Scopes required for every badge localization operation, sourced from\n * `x-roblox-scopes` on the legacy `gameinternationalization` badge\n * endpoints in the vendored OpenAPI schema.\n */\nexport const LOCALIZATION_REQUIRED_SCOPES: ReadonlyArray<string> = Object.freeze([\n\t\"legacy-badge:manage\",\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 as buildLocaleUploadIconRequest } from \"../../domains/game-internationalization/badge-icon/builders.ts\";\nimport type { UploadBadgeIconLocalizationParameters } from \"../../domains/game-internationalization/badge-icon/types.ts\";\nimport { buildUpdateRequest as buildLocaleNameDescRequest } from \"../../domains/game-internationalization/badge-name-description/builders.ts\";\nimport {\n\tLOCALIZATION_OPERATION_LIMIT,\n\tLOCALIZATION_REQUIRED_SCOPES,\n} from \"../../domains/game-internationalization/badge-name-description/operations.ts\";\nimport type { UpdateBadgeNameDescriptionParameters } from \"../../domains/game-internationalization/badge-name-description/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\nconst UPDATE_NAME_DESCRIPTION_SPEC = makeSpec<UpdateBadgeNameDescriptionParameters, undefined>({\n\tbuildRequest: (parameters) => okRequest(buildLocaleNameDescRequest(parameters)),\n\tmethodDefaults: IDEMPOTENT_METHOD_DEFAULTS,\n\tmethodKind: \"idempotent\",\n\toperationLimit: LOCALIZATION_OPERATION_LIMIT,\n\tparse: parseEmptyResponse,\n\trequiredScopes: LOCALIZATION_REQUIRED_SCOPES,\n});\n\nconst UPLOAD_LOCALIZED_ICON_SPEC = makeSpec<UploadBadgeIconLocalizationParameters, undefined>({\n\tbuildRequest: (parameters) => okRequest(buildLocaleUploadIconRequest(parameters)),\n\tmethodDefaults: CREATE_METHOD_DEFAULTS,\n\tmethodKind: \"create\",\n\toperationLimit: LOCALIZATION_OPERATION_LIMIT,\n\tparse: parseEmptyResponse,\n\trequiredScopes: LOCALIZATION_REQUIRED_SCOPES,\n});\n\ninterface BadgeLocalizationHandle {\n\t/**\n\t * Updates the per-locale display name and/or description registered against\n\t * a badge. Either `name`, `description`, or both may be supplied; omitted\n\t * fields are not forwarded so the server leaves the existing value for\n\t * that locale untouched. Mirrors the upstream `200 OK` echo body as\n\t * `undefined` data.\n\t *\n\t * @param parameters - Badge and language identifiers plus the optional\n\t * replacement values.\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\tupdateNameDescription: (\n\t\tparameters: UpdateBadgeNameDescriptionParameters,\n\t\toptions?: RequestOptions,\n\t) => Promise<Result<undefined, OpenCloudError>>;\n\t/**\n\t * Uploads or replaces the per-locale icon for a badge. A subsequent\n\t * upload for the same `(badgeId, languageCode)` pair replaces the\n\t * existing icon for that locale. Does not retry on 5xx so a duplicate\n\t * upload cannot be created if the server fails mid-write. Source-language\n\t * icons remain on {@link BadgesClient.uploadIcon}.\n\t *\n\t * @param parameters - Badge and language identifiers plus the image bytes\n\t * 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\tuploadIcon: (\n\t\tparameters: UploadBadgeIconLocalizationParameters,\n\t\toptions?: RequestOptions,\n\t) => Promise<Result<undefined, OpenCloudError>>;\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 * Operation Group exposing per-locale localization Operations\n\t * (`updateNameDescription`, `uploadIcon`) backed by the\n\t * `legacy-game-internationalization` domain. Source-language values\n\t * remain on {@link BadgesClient.update} and\n\t * {@link BadgesClient.uploadIcon}; methods on this group set per-locale\n\t * overlays on top. Shares the parent client's HTTP, rate-limit, and\n\t * retry plumbing.\n\t */\n\tpublic readonly localization: BadgeLocalizationHandle;\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\tthis.localization = createLocalizationHandle(this.#inner);\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\nfunction createLocalizationHandle(inner: ResourceClient): BadgeLocalizationHandle {\n\treturn {\n\t\tasync updateNameDescription(parameters, options) {\n\t\t\treturn inner.execute({ options, parameters, spec: UPDATE_NAME_DESCRIPTION_SPEC });\n\t\t},\n\t\tasync uploadIcon(parameters, options) {\n\t\t\treturn inner.execute({ options, parameters, spec: UPLOAD_LOCALIZED_ICON_SPEC });\n\t\t},\n\t};\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"badges.mjs","names":["buildUpdateRequest","buildUploadIconRequest","buildUpdateRequest","buildLocaleNameDescRequest","buildLocaleUploadIconRequest","#inner"],"sources":["../src/domains/badges/badges/builders.ts","../src/domains/badges/badges/operations.ts","../src/domains/badges/badges/parsers.ts","../src/domains/game-internationalization/badge-icon/builders.ts","../src/domains/game-internationalization/badge-name-description/builders.ts","../src/domains/game-internationalization/badge-name-description/operations.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 { isDateTimeString } from \"../../../internal/utils/is-date-time-string.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\tisDateTimeString(body[\"created\"]) &&\n\t\tisDateTimeString(body[\"updated\"])\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 { UploadBadgeIconLocalizationParameters } from \"./types.ts\";\n\n/**\n * Builds a `POST` request for the localized \"upload badge icon\" endpoint. A\n * successful upload replaces any existing icon for the same\n * `(badgeId, languageCode)` pair.\n *\n * @param parameters - Badge and language identifiers plus the image bytes\n * to upload.\n * @returns A pure {@link HttpRequest} describing the upload call.\n */\nexport function buildUploadIconRequest(\n\tparameters: UploadBadgeIconLocalizationParameters,\n): HttpRequest {\n\tconst body = new FormData();\n\tbody.append(\"Files\", toBlob(parameters.image));\n\n\treturn {\n\t\tbody,\n\t\tmethod: \"POST\",\n\t\turl: `/legacy-game-internationalization/v1/badges/${parameters.badgeId}/icons/language-codes/${parameters.languageCode}`,\n\t};\n}\n","import type { HttpRequest } from \"../../../internal/http/types.ts\";\nimport type { UpdateBadgeNameDescriptionParameters } from \"./types.ts\";\n\n/**\n * Builds a `PATCH` request for the localized \"update badge name/description\"\n * endpoint. Either `name`, `description`, or both may be supplied; omitted\n * fields are not included in the JSON body so the server leaves the existing\n * value for that locale untouched.\n *\n * @param parameters - Badge and language identifiers plus the optional\n * replacement values.\n * @returns A pure {@link HttpRequest} describing the update call.\n */\nexport function buildUpdateRequest(parameters: UpdateBadgeNameDescriptionParameters): HttpRequest {\n\tconst body: Record<string, 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\treturn {\n\t\tbody,\n\t\tmethod: \"PATCH\",\n\t\turl: `/legacy-game-internationalization/v1/badges/${parameters.badgeId}/name-description/language-codes/${parameters.languageCode}`,\n\t};\n}\n","import type { OperationLimit } from \"../../../internal/http/rate-limit-queue.ts\";\n\n/**\n * Per-second request ceiling for every badge localization Operation. The\n * legacy `gameinternationalization` service caps each API key at 100\n * requests per minute *shared across the entire service* (see the\n * `x-roblox-rate-limits` extension on every operation in the vendored\n * Open Cloud spec), so all badge localization methods queue against the\n * same operation key.\n */\nexport const LOCALIZATION_OPERATION_LIMIT: OperationLimit = Object.freeze({\n\tmaxPerSecond: 100 / 60,\n\toperationKey: \"badge-localization\",\n});\n\n/**\n * Scopes required for every badge localization operation, sourced from\n * `x-roblox-scopes` on the legacy `gameinternationalization` badge\n * endpoints in the vendored OpenAPI schema.\n */\nexport const LOCALIZATION_REQUIRED_SCOPES: ReadonlyArray<string> = Object.freeze([\n\t\"legacy-badge:manage\",\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 as buildLocaleUploadIconRequest } from \"../../domains/game-internationalization/badge-icon/builders.ts\";\nimport type { UploadBadgeIconLocalizationParameters } from \"../../domains/game-internationalization/badge-icon/types.ts\";\nimport { buildUpdateRequest as buildLocaleNameDescRequest } from \"../../domains/game-internationalization/badge-name-description/builders.ts\";\nimport {\n\tLOCALIZATION_OPERATION_LIMIT,\n\tLOCALIZATION_REQUIRED_SCOPES,\n} from \"../../domains/game-internationalization/badge-name-description/operations.ts\";\nimport type { UpdateBadgeNameDescriptionParameters } from \"../../domains/game-internationalization/badge-name-description/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\nconst UPDATE_NAME_DESCRIPTION_SPEC = makeSpec<UpdateBadgeNameDescriptionParameters, undefined>({\n\tbuildRequest: (parameters) => okRequest(buildLocaleNameDescRequest(parameters)),\n\tmethodDefaults: IDEMPOTENT_METHOD_DEFAULTS,\n\tmethodKind: \"idempotent\",\n\toperationLimit: LOCALIZATION_OPERATION_LIMIT,\n\tparse: parseEmptyResponse,\n\trequiredScopes: LOCALIZATION_REQUIRED_SCOPES,\n});\n\nconst UPLOAD_LOCALIZED_ICON_SPEC = makeSpec<UploadBadgeIconLocalizationParameters, undefined>({\n\tbuildRequest: (parameters) => okRequest(buildLocaleUploadIconRequest(parameters)),\n\tmethodDefaults: CREATE_METHOD_DEFAULTS,\n\tmethodKind: \"create\",\n\toperationLimit: LOCALIZATION_OPERATION_LIMIT,\n\tparse: parseEmptyResponse,\n\trequiredScopes: LOCALIZATION_REQUIRED_SCOPES,\n});\n\ninterface BadgeLocalizationHandle {\n\t/**\n\t * Updates the per-locale display name and/or description registered against\n\t * a badge. Either `name`, `description`, or both may be supplied; omitted\n\t * fields are not forwarded so the server leaves the existing value for\n\t * that locale untouched. Mirrors the upstream `200 OK` echo body as\n\t * `undefined` data.\n\t *\n\t * @param parameters - Badge and language identifiers plus the optional\n\t * replacement values.\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\tupdateNameDescription: (\n\t\tparameters: UpdateBadgeNameDescriptionParameters,\n\t\toptions?: RequestOptions,\n\t) => Promise<Result<undefined, OpenCloudError>>;\n\t/**\n\t * Uploads or replaces the per-locale icon for a badge. A subsequent\n\t * upload for the same `(badgeId, languageCode)` pair replaces the\n\t * existing icon for that locale. Does not retry on 5xx so a duplicate\n\t * upload cannot be created if the server fails mid-write. Source-language\n\t * icons remain on {@link BadgesClient.uploadIcon}.\n\t *\n\t * @param parameters - Badge and language identifiers plus the image bytes\n\t * 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\tuploadIcon: (\n\t\tparameters: UploadBadgeIconLocalizationParameters,\n\t\toptions?: RequestOptions,\n\t) => Promise<Result<undefined, OpenCloudError>>;\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 * Operation Group exposing per-locale localization Operations\n\t * (`updateNameDescription`, `uploadIcon`) backed by the\n\t * `legacy-game-internationalization` domain. Source-language values\n\t * remain on {@link BadgesClient.update} and\n\t * {@link BadgesClient.uploadIcon}; methods on this group set per-locale\n\t * overlays on top. Shares the parent client's HTTP, rate-limit, and\n\t * retry plumbing.\n\t */\n\tpublic readonly localization: BadgeLocalizationHandle;\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\tthis.localization = createLocalizationHandle(this.#inner);\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\nfunction createLocalizationHandle(inner: ResourceClient): BadgeLocalizationHandle {\n\treturn {\n\t\tasync updateNameDescription(parameters, options) {\n\t\t\treturn inner.execute({ options, parameters, spec: UPDATE_NAME_DESCRIPTION_SPEC });\n\t\t},\n\t\tasync uploadIcon(parameters, options) {\n\t\t\treturn inner.execute({ options, parameters, spec: UPLOAD_LOCALIZED_ICON_SPEC });\n\t\t},\n\t};\n}\n"],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { d as OpenCloudError, i as OpenCloudClientOptions, l as Result, s as RequestOptions } from "./types-
|
|
1
|
+
import { d as OpenCloudError, i as OpenCloudClientOptions, l as Result, s as RequestOptions } from "./types-rzs1NB-j.mjs";
|
|
2
2
|
import { i as RobloxLocale, r as RobloxLanguageCode } from "./data.generated-Cb6g6asv.mjs";
|
|
3
3
|
|
|
4
4
|
//#region src/domains/developer-products/products/types.d.ts
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { r as ApiError } from "./rate-limit-
|
|
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";
|
|
2
3
|
import { t as toBlob } from "./to-blob-B27VhoRp.mjs";
|
|
3
|
-
import { a as
|
|
4
|
-
import { n as isPriceInformationLike, t as copyPriceInformation } from "./price-information-
|
|
4
|
+
import { a as isDateTimeString, i as isRecord, n as okRequest, r as parseEmptyResponse, t as ResourceClient } from "./resource-client-CG9-BG81.mjs";
|
|
5
|
+
import { n as isPriceInformationLike, t as copyPriceInformation } from "./price-information-DT7_QJN-.mjs";
|
|
5
6
|
//#region src/domains/developer-products/products/builders.ts
|
|
6
7
|
/**
|
|
7
8
|
* Builds a `GET` request for the Open Cloud "read developer product"
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"developer-products.mjs","names":["buildUpdateRequest","buildUpdateRequest","buildLocaleNameDescRequest","#inner"],"sources":["../src/domains/developer-products/products/builders.ts","../src/domains/developer-products/products/operations.ts","../src/domains/developer-products/products/parsers.ts","../src/domains/game-internationalization/developer-product-icon/builders.ts","../src/domains/game-internationalization/developer-product-name-description/builders.ts","../src/domains/game-internationalization/developer-product-name-description/operations.ts","../src/resources/developer-products/client.ts"],"sourcesContent":["import type { HttpRequest } from \"../../../internal/http/types.ts\";\nimport { toBlob } from \"../../../internal/utils/to-blob.ts\";\nimport type {\n\tCreateDeveloperProductParameters,\n\tGetDeveloperProductParameters,\n\tUpdateDeveloperProductParameters,\n} from \"./types.ts\";\n\n/**\n * Builds a `GET` request for the Open Cloud \"read developer product\"\n * endpoint.\n *\n * @param parameters - Universe and product identifiers to interpolate into\n * the URL.\n * @returns A pure {@link HttpRequest} describing the read call.\n */\nexport function buildGetRequest(parameters: GetDeveloperProductParameters): HttpRequest {\n\treturn {\n\t\tmethod: \"GET\",\n\t\turl: `/developer-products/v2/universes/${parameters.universeId}/developer-products/${parameters.productId}/creator`,\n\t};\n}\n\n/**\n * Builds a `POST` request for the Open Cloud \"create developer product\"\n * endpoint.\n *\n * @param parameters - Fields describing the new developer product; optional\n * values omitted here are left off the multipart payload entirely.\n * @returns A pure {@link HttpRequest} describing the create call.\n */\nexport function buildCreateRequest(parameters: CreateDeveloperProductParameters): HttpRequest {\n\tconst body = new FormData();\n\tbody.append(\"name\", parameters.name);\n\tif (parameters.description !== undefined) {\n\t\tbody.append(\"description\", parameters.description);\n\t}\n\n\tif (parameters.isForSale !== undefined) {\n\t\tbody.append(\"isForSale\", String(parameters.isForSale));\n\t}\n\n\tif (parameters.price !== undefined) {\n\t\tbody.append(\"price\", String(parameters.price));\n\t}\n\n\tif (parameters.isRegionalPricingEnabled !== undefined) {\n\t\tbody.append(\"isRegionalPricingEnabled\", String(parameters.isRegionalPricingEnabled));\n\t}\n\n\tif (parameters.imageFile !== undefined) {\n\t\tbody.append(\"imageFile\", toBlob(parameters.imageFile));\n\t}\n\n\treturn {\n\t\tbody,\n\t\tmethod: \"POST\",\n\t\turl: `/developer-products/v2/universes/${parameters.universeId}/developer-products`,\n\t};\n}\n\n/**\n * Builds a `PATCH` request for the Open Cloud \"update developer product\"\n * endpoint. Every field on `parameters` except the identifiers is optional;\n * omitted fields are not appended to the multipart body so the server leaves\n * them unchanged.\n *\n * @param parameters - Identifiers plus fields to update.\n * @returns A pure {@link HttpRequest} describing the update call.\n */\nexport function buildUpdateRequest(parameters: UpdateDeveloperProductParameters): HttpRequest {\n\tconst body = new FormData();\n\tif (parameters.name !== undefined) {\n\t\tbody.append(\"name\", parameters.name);\n\t}\n\n\tif (parameters.description !== undefined) {\n\t\tbody.append(\"description\", parameters.description);\n\t}\n\n\tif (parameters.isForSale !== undefined) {\n\t\tbody.append(\"isForSale\", String(parameters.isForSale));\n\t}\n\n\tif (parameters.price !== undefined) {\n\t\tbody.append(\"price\", String(parameters.price));\n\t}\n\n\tif (parameters.isRegionalPricingEnabled !== undefined) {\n\t\tbody.append(\"isRegionalPricingEnabled\", String(parameters.isRegionalPricingEnabled));\n\t}\n\n\tif (parameters.storePageEnabled !== undefined) {\n\t\tbody.append(\"storePageEnabled\", String(parameters.storePageEnabled));\n\t}\n\n\tif (parameters.imageFile !== undefined) {\n\t\tbody.append(\"imageFile\", toBlob(parameters.imageFile));\n\t}\n\n\treturn {\n\t\tbody,\n\t\tmethod: \"PATCH\",\n\t\turl: `/developer-products/v2/universes/${parameters.universeId}/developer-products/${parameters.productId}`,\n\t};\n}\n","import type { OperationLimit } from \"../../../internal/http/rate-limit-queue.ts\";\n\n/**\n * Per-second request ceiling for reading a single developer product, from\n * the Open Cloud OpenAPI schema.\n */\nexport const GET_OPERATION_LIMIT: OperationLimit = Object.freeze({\n\tmaxPerSecond: 10,\n\toperationKey: \"developer-products.get\",\n});\n\n/**\n * Per-second request ceiling for creating a developer product, from the\n * Open Cloud OpenAPI schema.\n */\nexport const CREATE_OPERATION_LIMIT: OperationLimit = Object.freeze({\n\tmaxPerSecond: 3,\n\toperationKey: \"developer-products.create\",\n});\n\n/**\n * Per-second request ceiling for updating a developer product, from the\n * Open Cloud OpenAPI schema. Keyed independently from\n * {@link CREATE_OPERATION_LIMIT} so create and update do not share a queue,\n * since Roblox does not document the per-minute quota as shared between\n * the POST and PATCH endpoints.\n */\nexport const UPDATE_OPERATION_LIMIT: OperationLimit = Object.freeze({\n\tmaxPerSecond: 3,\n\toperationKey: \"developer-products.update\",\n});\n\n/**\n * Scopes the API key or OAuth token must carry to read a developer product,\n * sourced from `x-roblox-scopes` on the `DeveloperProducts_GetDeveloperProductConfigV2`\n * operation in the vendored OpenAPI schema.\n */\nexport const GET_REQUIRED_SCOPES: ReadonlyArray<string> = Object.freeze([\"developer-product:read\"]);\n\n/**\n * Scopes the API key or OAuth token must carry to create or update a developer\n * product, sourced from `x-roblox-scopes` on the\n * `DeveloperProducts_CreateDeveloperProductV2` and\n * `DeveloperProducts_UpdateDeveloperProductV2` operations in the vendored\n * OpenAPI schema.\n */\nexport const WRITE_REQUIRED_SCOPES: ReadonlyArray<string> = Object.freeze([\n\t\"developer-product:write\",\n]);\n","import type { HttpResponse } from \"../../../client/types.ts\";\nimport { ApiError } from \"../../../errors/api-error.ts\";\nimport {\n\tcopyPriceInformation,\n\tisPriceInformationLike,\n} from \"../../../internal/price-information.ts\";\nimport { isDateTimeString } from \"../../../internal/utils/is-date-time-string.ts\";\nimport { isRecord } from \"../../../internal/utils/is-record.ts\";\nimport type { Result } from \"../../../types.ts\";\nimport type { DeveloperProduct } from \"./types.ts\";\nimport type { DeveloperProductConfigV2, DeveloperProductPricingFeatureWire } from \"./wire.ts\";\n\n/**\n * Parses a successful Developer Products API response into the public\n * `DeveloperProduct` shape, returning a `Result` so callers can handle\n * malformed payloads without 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 `DeveloperProduct`, or\n * an `ApiError` when the body does not match the wire schema.\n */\nexport function parseDeveloperProductResponse(\n\tresponse: HttpResponse,\n): Result<DeveloperProduct, ApiError> {\n\tconst { body, status: statusCode } = response;\n\n\tif (!isDeveloperProductConfigV2(body)) {\n\t\treturn {\n\t\t\terr: new ApiError(\"Malformed developer product response\", { statusCode }),\n\t\t\tsuccess: false,\n\t\t};\n\t}\n\n\tconst priceWire = body.priceInformation ?? undefined;\n\tconst iconAssetId = body.iconImageAssetId ?? undefined;\n\n\treturn {\n\t\tdata: {\n\t\t\tid: String(body.productId),\n\t\t\tname: body.name,\n\t\t\tcreatedAt: new Date(body.createdTimestamp),\n\t\t\tdescription: body.description,\n\t\t\ticonImageAssetId: iconAssetId === undefined ? undefined : String(iconAssetId),\n\t\t\tisForSale: body.isForSale,\n\t\t\tisImmutable: body.isImmutable,\n\t\t\tprice: priceWire === undefined ? undefined : copyPriceInformation(priceWire),\n\t\t\tstorePageEnabled: body.storePageEnabled,\n\t\t\tuniverseId: String(body.universeId),\n\t\t\tupdatedAt: new Date(body.updatedTimestamp),\n\t\t},\n\t\tsuccess: true,\n\t};\n}\n\nfunction hasRequiredPrimitiveFields(body: Record<string, unknown>): boolean {\n\treturn (\n\t\ttypeof body[\"productId\"] === \"number\" &&\n\t\t// Roblox never assigns asset ID 0; a zero productId signals a\n\t\t// malformed response, not a legitimate product.\n\t\tbody[\"productId\"] !== 0 &&\n\t\ttypeof body[\"universeId\"] === \"number\" &&\n\t\ttypeof body[\"name\"] === \"string\" &&\n\t\ttypeof body[\"description\"] === \"string\" &&\n\t\ttypeof body[\"isForSale\"] === \"boolean\" &&\n\t\ttypeof body[\"isImmutable\"] === \"boolean\" &&\n\t\ttypeof body[\"storePageEnabled\"] === \"boolean\" &&\n\t\tisDateTimeString(body[\"createdTimestamp\"]) &&\n\t\tisDateTimeString(body[\"updatedTimestamp\"])\n\t);\n}\n\nfunction isPricingFeatureWire(value: unknown): value is DeveloperProductPricingFeatureWire {\n\treturn (\n\t\tvalue === \"Invalid\" ||\n\t\tvalue === \"PriceOptimization\" ||\n\t\tvalue === \"RegionalPricing\" ||\n\t\tvalue === \"UserFixedPrice\"\n\t);\n}\n\nfunction isDeveloperProductConfigV2(body: unknown): body is DeveloperProductConfigV2 {\n\tif (!isRecord(body)) {\n\t\treturn false;\n\t}\n\n\tif (!hasRequiredPrimitiveFields(body)) {\n\t\treturn false;\n\t}\n\n\tconst iconImageAssetId = body[\"iconImageAssetId\"] ?? undefined;\n\tif (iconImageAssetId !== undefined && typeof iconImageAssetId !== \"number\") {\n\t\treturn false;\n\t}\n\n\tconst price = body[\"priceInformation\"] ?? undefined;\n\tif (price !== undefined && !isPriceInformationLike(price, isPricingFeatureWire)) {\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 { UploadDeveloperProductIconParameters } from \"./types.ts\";\n\n/**\n * Builds a `POST` request for the localized \"upload developer-product icon\"\n * endpoint. A successful upload replaces any existing icon for the same\n * `(productId, languageCode)` pair.\n *\n * @param parameters - Product and language identifiers plus the image bytes\n * to upload.\n * @returns A pure {@link HttpRequest} describing the upload call.\n */\nexport function buildUploadIconRequest(\n\tparameters: UploadDeveloperProductIconParameters,\n): HttpRequest {\n\tconst body = new FormData();\n\tbody.append(\"Files\", toBlob(parameters.image));\n\n\treturn {\n\t\tbody,\n\t\tmethod: \"POST\",\n\t\turl: `/legacy-game-internationalization/v1/developer-products/${parameters.productId}/icons/language-codes/${parameters.languageCode}`,\n\t};\n}\n","import type { HttpRequest } from \"../../../internal/http/types.ts\";\nimport type { UpdateDeveloperProductNameDescriptionParameters } from \"./types.ts\";\n\n/**\n * Builds a `PATCH` request for the localized \"update developer-product\n * name/description\" endpoint. Either `name`, `description`, or both may be\n * supplied; omitted fields are not included in the JSON body so the server\n * leaves the existing value for that locale untouched.\n *\n * @param parameters - Product and language identifiers plus the optional\n * replacement values.\n * @returns A pure {@link HttpRequest} describing the update call.\n */\nexport function buildUpdateRequest(\n\tparameters: UpdateDeveloperProductNameDescriptionParameters,\n): HttpRequest {\n\tconst body: Record<string, 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\treturn {\n\t\tbody,\n\t\tmethod: \"PATCH\",\n\t\turl: `/legacy-game-internationalization/v1/developer-products/${parameters.productId}/name-description/language-codes/${parameters.languageCode}`,\n\t};\n}\n","import type { OperationLimit } from \"../../../internal/http/rate-limit-queue.ts\";\n\n/**\n * Per-second request ceiling for every developer-product localization\n * Operation. The legacy `gameinternationalization` service caps each API key\n * at 100 requests per minute *shared across the entire service* (see the\n * `x-roblox-rate-limits` extension on every operation in the vendored Open\n * Cloud spec), so all developer-product localization methods queue against\n * the same operation key.\n */\nexport const LOCALIZATION_OPERATION_LIMIT: OperationLimit = Object.freeze({\n\tmaxPerSecond: 100 / 60,\n\toperationKey: \"developer-product-localization\",\n});\n\n/**\n * Scopes required for every developer-product localization operation, sourced\n * from `x-roblox-scopes` on the legacy `gameinternationalization`\n * developer-product endpoints in the vendored OpenAPI schema.\n */\nexport const LOCALIZATION_REQUIRED_SCOPES: ReadonlyArray<string> = Object.freeze([\n\t\"legacy-developer-product:manage\",\n]);\n","import type { OpenCloudClientOptions, RequestOptions } from \"../../client/types.ts\";\nimport {\n\tbuildCreateRequest,\n\tbuildGetRequest,\n\tbuildUpdateRequest,\n} from \"../../domains/developer-products/products/builders.ts\";\nimport {\n\tCREATE_OPERATION_LIMIT,\n\tGET_OPERATION_LIMIT,\n\tGET_REQUIRED_SCOPES,\n\tUPDATE_OPERATION_LIMIT,\n\tWRITE_REQUIRED_SCOPES,\n} from \"../../domains/developer-products/products/operations.ts\";\nimport { parseDeveloperProductResponse } from \"../../domains/developer-products/products/parsers.ts\";\nimport type {\n\tCreateDeveloperProductParameters,\n\tDeveloperProduct,\n\tGetDeveloperProductParameters,\n\tUpdateDeveloperProductParameters,\n} from \"../../domains/developer-products/products/types.ts\";\nimport { buildUploadIconRequest } from \"../../domains/game-internationalization/developer-product-icon/builders.ts\";\nimport type { UploadDeveloperProductIconParameters } from \"../../domains/game-internationalization/developer-product-icon/types.ts\";\nimport { buildUpdateRequest as buildLocaleNameDescRequest } from \"../../domains/game-internationalization/developer-product-name-description/builders.ts\";\nimport {\n\tLOCALIZATION_OPERATION_LIMIT,\n\tLOCALIZATION_REQUIRED_SCOPES,\n} from \"../../domains/game-internationalization/developer-product-name-description/operations.ts\";\nimport type { UpdateDeveloperProductNameDescriptionParameters } from \"../../domains/game-internationalization/developer-product-name-description/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 = DeveloperProduct>(\n\tspec: ResourceMethodSpec<P, R>,\n): ResourceMethodSpec<P, R> {\n\treturn Object.freeze(spec);\n}\n\nconst CREATE_SPEC = makeSpec<CreateDeveloperProductParameters>({\n\tbuildRequest: (parameters) => okRequest(buildCreateRequest(parameters)),\n\tmethodDefaults: CREATE_METHOD_DEFAULTS,\n\tmethodKind: \"create\",\n\toperationLimit: CREATE_OPERATION_LIMIT,\n\tparse: parseDeveloperProductResponse,\n\trequiredScopes: WRITE_REQUIRED_SCOPES,\n});\n\nconst GET_SPEC = makeSpec<GetDeveloperProductParameters>({\n\tbuildRequest: (parameters) => okRequest(buildGetRequest(parameters)),\n\tmethodDefaults: IDEMPOTENT_METHOD_DEFAULTS,\n\tmethodKind: \"idempotent\",\n\toperationLimit: GET_OPERATION_LIMIT,\n\tparse: parseDeveloperProductResponse,\n\trequiredScopes: GET_REQUIRED_SCOPES,\n});\n\nconst UPDATE_SPEC = makeSpec<UpdateDeveloperProductParameters, 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: WRITE_REQUIRED_SCOPES,\n});\n\nconst UPDATE_NAME_DESCRIPTION_SPEC = makeSpec<\n\tUpdateDeveloperProductNameDescriptionParameters,\n\tundefined\n>({\n\tbuildRequest: (parameters) => okRequest(buildLocaleNameDescRequest(parameters)),\n\tmethodDefaults: IDEMPOTENT_METHOD_DEFAULTS,\n\tmethodKind: \"idempotent\",\n\toperationLimit: LOCALIZATION_OPERATION_LIMIT,\n\tparse: parseEmptyResponse,\n\trequiredScopes: LOCALIZATION_REQUIRED_SCOPES,\n});\n\nconst UPLOAD_ICON_SPEC = makeSpec<UploadDeveloperProductIconParameters, undefined>({\n\tbuildRequest: (parameters) => okRequest(buildUploadIconRequest(parameters)),\n\tmethodDefaults: CREATE_METHOD_DEFAULTS,\n\tmethodKind: \"create\",\n\toperationLimit: LOCALIZATION_OPERATION_LIMIT,\n\tparse: parseEmptyResponse,\n\trequiredScopes: LOCALIZATION_REQUIRED_SCOPES,\n});\n\ninterface DeveloperProductLocalizationHandle {\n\t/**\n\t * Updates the per-locale display name and/or description registered against\n\t * a developer product. Either `name`, `description`, or both may be\n\t * supplied; omitted fields are not forwarded so the server leaves the\n\t * existing value for that locale untouched. Mirrors the upstream `200 OK`\n\t * echo body as `undefined` data.\n\t *\n\t * @param parameters - Product and language identifiers plus the optional\n\t * replacement values.\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\tupdateNameDescription: (\n\t\tparameters: UpdateDeveloperProductNameDescriptionParameters,\n\t\toptions?: RequestOptions,\n\t) => Promise<Result<undefined, OpenCloudError>>;\n\t/**\n\t * Uploads or replaces the per-locale icon for a developer product. A\n\t * subsequent upload for the same `(productId, languageCode)` pair replaces\n\t * the existing icon for that locale. Does not retry on 5xx so a duplicate\n\t * upload cannot be created if the server fails mid-write.\n\t *\n\t * @param parameters - Product and language identifiers plus the image\n\t * 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\tuploadIcon: (\n\t\tparameters: UploadDeveloperProductIconParameters,\n\t\toptions?: RequestOptions,\n\t) => Promise<Result<undefined, OpenCloudError>>;\n}\n\n/**\n * Public client for the Roblox Open Cloud Developer Products API.\n *\n * Wires request builders, the injected {@link OpenCloudClientOptions.httpClient}, and response\n * parsers into a single ergonomic surface. Every method returns a\n * {@link Result} so callers handle failure explicitly; no thrown\n * `OpenCloudError` ever escapes the client.\n *\n * ```ts\n * import { DeveloperProductsClient } from \"@bedrock-rbx/ocale/developer-products\";\n *\n * const client = new DeveloperProductsClient({ apiKey: process.env.ROBLOX_API_KEY! });\n *\n * const result = await client.get({\n * universeId: \"1234567890\",\n * productId: \"9876543210\",\n * });\n *\n * if (result.success) {\n * console.log(`${result.data.name} (${result.data.id})`);\n * } else {\n * console.error(result.err.message);\n * }\n * ```\n */\nexport class DeveloperProductsClient {\n\treadonly #inner: ResourceClient;\n\n\t/**\n\t * Operation Group exposing per-locale localization Operations\n\t * (`updateNameDescription`, `uploadIcon`) backed by the\n\t * `legacy-game-internationalization` domain. Source-language values\n\t * remain on {@link DeveloperProductsClient.update}; methods on this\n\t * group set per-locale overlays on top. Shares the parent client's\n\t * HTTP, rate-limit, and retry plumbing.\n\t */\n\tpublic readonly localization: DeveloperProductLocalizationHandle;\n\n\t/**\n\t * Creates a new {@link DeveloperProductsClient}. Configuration is frozen\n\t * on 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\tthis.localization = createLocalizationHandle(this.#inner);\n\t}\n\n\t/**\n\t * Creates a new developer product under the supplied universe.\n\t *\n\t * @param parameters - Creation fields including the universe and product name.\n\t * @param options - Optional per-request overrides.\n\t * @returns A {@link Result} wrapping the parsed {@link DeveloperProduct} or\n\t * the {@link OpenCloudError} that caused the request to fail.\n\t */\n\tpublic async create(\n\t\tparameters: CreateDeveloperProductParameters,\n\t\toptions?: RequestOptions,\n\t): Promise<Result<DeveloperProduct, OpenCloudError>> {\n\t\treturn this.#inner.execute({ options, parameters, spec: CREATE_SPEC });\n\t}\n\n\t/**\n\t * Reads a single developer product by ID.\n\t *\n\t * @param parameters - Universe and product identifiers.\n\t * @param options - Optional per-request overrides (e.g. A different\n\t * {@link OpenCloudClientOptions.apiKey} for this call only).\n\t * @returns A {@link Result} wrapping the parsed {@link DeveloperProduct} or\n\t * the {@link OpenCloudError} that caused the request to fail.\n\t */\n\tpublic async get(\n\t\tparameters: GetDeveloperProductParameters,\n\t\toptions?: RequestOptions,\n\t): Promise<Result<DeveloperProduct, OpenCloudError>> {\n\t\treturn this.#inner.execute({ options, parameters, spec: GET_SPEC });\n\t}\n\n\t/**\n\t * Partially updates an existing developer product. Mirrors the upstream\n\t * `204 No Content` response: a successful update yields `undefined` data.\n\t * Callers that need the post-update state (for example to observe a\n\t * server-derived `updatedTimestamp`) chain {@link DeveloperProductsClient.get}\n\t * themselves so the GET only fires when actually needed.\n\t *\n\t * @param parameters - The universe and product identifiers and the\n\t * fields to update. Only fields explicitly provided are forwarded.\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: UpdateDeveloperProductParameters,\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\nfunction createLocalizationHandle(inner: ResourceClient): DeveloperProductLocalizationHandle {\n\treturn {\n\t\tasync updateNameDescription(parameters, options) {\n\t\t\treturn inner.execute({ options, parameters, spec: UPDATE_NAME_DESCRIPTION_SPEC });\n\t\t},\n\t\tasync uploadIcon(parameters, options) {\n\t\t\treturn inner.execute({ options, parameters, spec: UPLOAD_ICON_SPEC });\n\t\t},\n\t};\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"developer-products.mjs","names":["buildUpdateRequest","buildUpdateRequest","buildLocaleNameDescRequest","#inner"],"sources":["../src/domains/developer-products/products/builders.ts","../src/domains/developer-products/products/operations.ts","../src/domains/developer-products/products/parsers.ts","../src/domains/game-internationalization/developer-product-icon/builders.ts","../src/domains/game-internationalization/developer-product-name-description/builders.ts","../src/domains/game-internationalization/developer-product-name-description/operations.ts","../src/resources/developer-products/client.ts"],"sourcesContent":["import type { HttpRequest } from \"../../../internal/http/types.ts\";\nimport { toBlob } from \"../../../internal/utils/to-blob.ts\";\nimport type {\n\tCreateDeveloperProductParameters,\n\tGetDeveloperProductParameters,\n\tUpdateDeveloperProductParameters,\n} from \"./types.ts\";\n\n/**\n * Builds a `GET` request for the Open Cloud \"read developer product\"\n * endpoint.\n *\n * @param parameters - Universe and product identifiers to interpolate into\n * the URL.\n * @returns A pure {@link HttpRequest} describing the read call.\n */\nexport function buildGetRequest(parameters: GetDeveloperProductParameters): HttpRequest {\n\treturn {\n\t\tmethod: \"GET\",\n\t\turl: `/developer-products/v2/universes/${parameters.universeId}/developer-products/${parameters.productId}/creator`,\n\t};\n}\n\n/**\n * Builds a `POST` request for the Open Cloud \"create developer product\"\n * endpoint.\n *\n * @param parameters - Fields describing the new developer product; optional\n * values omitted here are left off the multipart payload entirely.\n * @returns A pure {@link HttpRequest} describing the create call.\n */\nexport function buildCreateRequest(parameters: CreateDeveloperProductParameters): HttpRequest {\n\tconst body = new FormData();\n\tbody.append(\"name\", parameters.name);\n\tif (parameters.description !== undefined) {\n\t\tbody.append(\"description\", parameters.description);\n\t}\n\n\tif (parameters.isForSale !== undefined) {\n\t\tbody.append(\"isForSale\", String(parameters.isForSale));\n\t}\n\n\tif (parameters.price !== undefined) {\n\t\tbody.append(\"price\", String(parameters.price));\n\t}\n\n\tif (parameters.isRegionalPricingEnabled !== undefined) {\n\t\tbody.append(\"isRegionalPricingEnabled\", String(parameters.isRegionalPricingEnabled));\n\t}\n\n\tif (parameters.imageFile !== undefined) {\n\t\tbody.append(\"imageFile\", toBlob(parameters.imageFile));\n\t}\n\n\treturn {\n\t\tbody,\n\t\tmethod: \"POST\",\n\t\turl: `/developer-products/v2/universes/${parameters.universeId}/developer-products`,\n\t};\n}\n\n/**\n * Builds a `PATCH` request for the Open Cloud \"update developer product\"\n * endpoint. Every field on `parameters` except the identifiers is optional;\n * omitted fields are not appended to the multipart body so the server leaves\n * them unchanged.\n *\n * @param parameters - Identifiers plus fields to update.\n * @returns A pure {@link HttpRequest} describing the update call.\n */\nexport function buildUpdateRequest(parameters: UpdateDeveloperProductParameters): HttpRequest {\n\tconst body = new FormData();\n\tif (parameters.name !== undefined) {\n\t\tbody.append(\"name\", parameters.name);\n\t}\n\n\tif (parameters.description !== undefined) {\n\t\tbody.append(\"description\", parameters.description);\n\t}\n\n\tif (parameters.isForSale !== undefined) {\n\t\tbody.append(\"isForSale\", String(parameters.isForSale));\n\t}\n\n\tif (parameters.price !== undefined) {\n\t\tbody.append(\"price\", String(parameters.price));\n\t}\n\n\tif (parameters.isRegionalPricingEnabled !== undefined) {\n\t\tbody.append(\"isRegionalPricingEnabled\", String(parameters.isRegionalPricingEnabled));\n\t}\n\n\tif (parameters.storePageEnabled !== undefined) {\n\t\tbody.append(\"storePageEnabled\", String(parameters.storePageEnabled));\n\t}\n\n\tif (parameters.imageFile !== undefined) {\n\t\tbody.append(\"imageFile\", toBlob(parameters.imageFile));\n\t}\n\n\treturn {\n\t\tbody,\n\t\tmethod: \"PATCH\",\n\t\turl: `/developer-products/v2/universes/${parameters.universeId}/developer-products/${parameters.productId}`,\n\t};\n}\n","import type { OperationLimit } from \"../../../internal/http/rate-limit-queue.ts\";\n\n/**\n * Per-second request ceiling for reading a single developer product, from\n * the Open Cloud OpenAPI schema.\n */\nexport const GET_OPERATION_LIMIT: OperationLimit = Object.freeze({\n\tmaxPerSecond: 10,\n\toperationKey: \"developer-products.get\",\n});\n\n/**\n * Per-second request ceiling for creating a developer product, from the\n * Open Cloud OpenAPI schema.\n */\nexport const CREATE_OPERATION_LIMIT: OperationLimit = Object.freeze({\n\tmaxPerSecond: 3,\n\toperationKey: \"developer-products.create\",\n});\n\n/**\n * Per-second request ceiling for updating a developer product, from the\n * Open Cloud OpenAPI schema. Keyed independently from\n * {@link CREATE_OPERATION_LIMIT} so create and update do not share a queue,\n * since Roblox does not document the per-minute quota as shared between\n * the POST and PATCH endpoints.\n */\nexport const UPDATE_OPERATION_LIMIT: OperationLimit = Object.freeze({\n\tmaxPerSecond: 3,\n\toperationKey: \"developer-products.update\",\n});\n\n/**\n * Scopes the API key or OAuth token must carry to read a developer product,\n * sourced from `x-roblox-scopes` on the `DeveloperProducts_GetDeveloperProductConfigV2`\n * operation in the vendored OpenAPI schema.\n */\nexport const GET_REQUIRED_SCOPES: ReadonlyArray<string> = Object.freeze([\"developer-product:read\"]);\n\n/**\n * Scopes the API key or OAuth token must carry to create or update a developer\n * product, sourced from `x-roblox-scopes` on the\n * `DeveloperProducts_CreateDeveloperProductV2` and\n * `DeveloperProducts_UpdateDeveloperProductV2` operations in the vendored\n * OpenAPI schema.\n */\nexport const WRITE_REQUIRED_SCOPES: ReadonlyArray<string> = Object.freeze([\n\t\"developer-product:write\",\n]);\n","import type { HttpResponse } from \"../../../client/types.ts\";\nimport { ApiError } from \"../../../errors/api-error.ts\";\nimport {\n\tcopyPriceInformation,\n\tisPriceInformationLike,\n} from \"../../../internal/price-information.ts\";\nimport { isDateTimeString } from \"../../../internal/utils/is-date-time-string.ts\";\nimport { isRecord } from \"../../../internal/utils/is-record.ts\";\nimport type { Result } from \"../../../types.ts\";\nimport type { DeveloperProduct } from \"./types.ts\";\nimport type { DeveloperProductConfigV2, DeveloperProductPricingFeatureWire } from \"./wire.ts\";\n\n/**\n * Parses a successful Developer Products API response into the public\n * `DeveloperProduct` shape, returning a `Result` so callers can handle\n * malformed payloads without 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 `DeveloperProduct`, or\n * an `ApiError` when the body does not match the wire schema.\n */\nexport function parseDeveloperProductResponse(\n\tresponse: HttpResponse,\n): Result<DeveloperProduct, ApiError> {\n\tconst { body, status: statusCode } = response;\n\n\tif (!isDeveloperProductConfigV2(body)) {\n\t\treturn {\n\t\t\terr: new ApiError(\"Malformed developer product response\", { statusCode }),\n\t\t\tsuccess: false,\n\t\t};\n\t}\n\n\tconst priceWire = body.priceInformation ?? undefined;\n\tconst iconAssetId = body.iconImageAssetId ?? undefined;\n\n\treturn {\n\t\tdata: {\n\t\t\tid: String(body.productId),\n\t\t\tname: body.name,\n\t\t\tcreatedAt: new Date(body.createdTimestamp),\n\t\t\tdescription: body.description,\n\t\t\ticonImageAssetId: iconAssetId === undefined ? undefined : String(iconAssetId),\n\t\t\tisForSale: body.isForSale,\n\t\t\tisImmutable: body.isImmutable,\n\t\t\tprice: priceWire === undefined ? undefined : copyPriceInformation(priceWire),\n\t\t\tstorePageEnabled: body.storePageEnabled,\n\t\t\tuniverseId: String(body.universeId),\n\t\t\tupdatedAt: new Date(body.updatedTimestamp),\n\t\t},\n\t\tsuccess: true,\n\t};\n}\n\nfunction hasRequiredPrimitiveFields(body: Record<string, unknown>): boolean {\n\treturn (\n\t\ttypeof body[\"productId\"] === \"number\" &&\n\t\t// Roblox never assigns asset ID 0; a zero productId signals a\n\t\t// malformed response, not a legitimate product.\n\t\tbody[\"productId\"] !== 0 &&\n\t\ttypeof body[\"universeId\"] === \"number\" &&\n\t\ttypeof body[\"name\"] === \"string\" &&\n\t\ttypeof body[\"description\"] === \"string\" &&\n\t\ttypeof body[\"isForSale\"] === \"boolean\" &&\n\t\ttypeof body[\"isImmutable\"] === \"boolean\" &&\n\t\ttypeof body[\"storePageEnabled\"] === \"boolean\" &&\n\t\tisDateTimeString(body[\"createdTimestamp\"]) &&\n\t\tisDateTimeString(body[\"updatedTimestamp\"])\n\t);\n}\n\nfunction isPricingFeatureWire(value: unknown): value is DeveloperProductPricingFeatureWire {\n\treturn (\n\t\tvalue === \"Invalid\" ||\n\t\tvalue === \"PriceOptimization\" ||\n\t\tvalue === \"RegionalPricing\" ||\n\t\tvalue === \"UserFixedPrice\"\n\t);\n}\n\nfunction isDeveloperProductConfigV2(body: unknown): body is DeveloperProductConfigV2 {\n\tif (!isRecord(body)) {\n\t\treturn false;\n\t}\n\n\tif (!hasRequiredPrimitiveFields(body)) {\n\t\treturn false;\n\t}\n\n\tconst iconImageAssetId = body[\"iconImageAssetId\"] ?? undefined;\n\tif (iconImageAssetId !== undefined && typeof iconImageAssetId !== \"number\") {\n\t\treturn false;\n\t}\n\n\tconst price = body[\"priceInformation\"] ?? undefined;\n\tif (price !== undefined && !isPriceInformationLike(price, isPricingFeatureWire)) {\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 { UploadDeveloperProductIconParameters } from \"./types.ts\";\n\n/**\n * Builds a `POST` request for the localized \"upload developer-product icon\"\n * endpoint. A successful upload replaces any existing icon for the same\n * `(productId, languageCode)` pair.\n *\n * @param parameters - Product and language identifiers plus the image bytes\n * to upload.\n * @returns A pure {@link HttpRequest} describing the upload call.\n */\nexport function buildUploadIconRequest(\n\tparameters: UploadDeveloperProductIconParameters,\n): HttpRequest {\n\tconst body = new FormData();\n\tbody.append(\"Files\", toBlob(parameters.image));\n\n\treturn {\n\t\tbody,\n\t\tmethod: \"POST\",\n\t\turl: `/legacy-game-internationalization/v1/developer-products/${parameters.productId}/icons/language-codes/${parameters.languageCode}`,\n\t};\n}\n","import type { HttpRequest } from \"../../../internal/http/types.ts\";\nimport type { UpdateDeveloperProductNameDescriptionParameters } from \"./types.ts\";\n\n/**\n * Builds a `PATCH` request for the localized \"update developer-product\n * name/description\" endpoint. Either `name`, `description`, or both may be\n * supplied; omitted fields are not included in the JSON body so the server\n * leaves the existing value for that locale untouched.\n *\n * @param parameters - Product and language identifiers plus the optional\n * replacement values.\n * @returns A pure {@link HttpRequest} describing the update call.\n */\nexport function buildUpdateRequest(\n\tparameters: UpdateDeveloperProductNameDescriptionParameters,\n): HttpRequest {\n\tconst body: Record<string, 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\treturn {\n\t\tbody,\n\t\tmethod: \"PATCH\",\n\t\turl: `/legacy-game-internationalization/v1/developer-products/${parameters.productId}/name-description/language-codes/${parameters.languageCode}`,\n\t};\n}\n","import type { OperationLimit } from \"../../../internal/http/rate-limit-queue.ts\";\n\n/**\n * Per-second request ceiling for every developer-product localization\n * Operation. The legacy `gameinternationalization` service caps each API key\n * at 100 requests per minute *shared across the entire service* (see the\n * `x-roblox-rate-limits` extension on every operation in the vendored Open\n * Cloud spec), so all developer-product localization methods queue against\n * the same operation key.\n */\nexport const LOCALIZATION_OPERATION_LIMIT: OperationLimit = Object.freeze({\n\tmaxPerSecond: 100 / 60,\n\toperationKey: \"developer-product-localization\",\n});\n\n/**\n * Scopes required for every developer-product localization operation, sourced\n * from `x-roblox-scopes` on the legacy `gameinternationalization`\n * developer-product endpoints in the vendored OpenAPI schema.\n */\nexport const LOCALIZATION_REQUIRED_SCOPES: ReadonlyArray<string> = Object.freeze([\n\t\"legacy-developer-product:manage\",\n]);\n","import type { OpenCloudClientOptions, RequestOptions } from \"../../client/types.ts\";\nimport {\n\tbuildCreateRequest,\n\tbuildGetRequest,\n\tbuildUpdateRequest,\n} from \"../../domains/developer-products/products/builders.ts\";\nimport {\n\tCREATE_OPERATION_LIMIT,\n\tGET_OPERATION_LIMIT,\n\tGET_REQUIRED_SCOPES,\n\tUPDATE_OPERATION_LIMIT,\n\tWRITE_REQUIRED_SCOPES,\n} from \"../../domains/developer-products/products/operations.ts\";\nimport { parseDeveloperProductResponse } from \"../../domains/developer-products/products/parsers.ts\";\nimport type {\n\tCreateDeveloperProductParameters,\n\tDeveloperProduct,\n\tGetDeveloperProductParameters,\n\tUpdateDeveloperProductParameters,\n} from \"../../domains/developer-products/products/types.ts\";\nimport { buildUploadIconRequest } from \"../../domains/game-internationalization/developer-product-icon/builders.ts\";\nimport type { UploadDeveloperProductIconParameters } from \"../../domains/game-internationalization/developer-product-icon/types.ts\";\nimport { buildUpdateRequest as buildLocaleNameDescRequest } from \"../../domains/game-internationalization/developer-product-name-description/builders.ts\";\nimport {\n\tLOCALIZATION_OPERATION_LIMIT,\n\tLOCALIZATION_REQUIRED_SCOPES,\n} from \"../../domains/game-internationalization/developer-product-name-description/operations.ts\";\nimport type { UpdateDeveloperProductNameDescriptionParameters } from \"../../domains/game-internationalization/developer-product-name-description/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 = DeveloperProduct>(\n\tspec: ResourceMethodSpec<P, R>,\n): ResourceMethodSpec<P, R> {\n\treturn Object.freeze(spec);\n}\n\nconst CREATE_SPEC = makeSpec<CreateDeveloperProductParameters>({\n\tbuildRequest: (parameters) => okRequest(buildCreateRequest(parameters)),\n\tmethodDefaults: CREATE_METHOD_DEFAULTS,\n\tmethodKind: \"create\",\n\toperationLimit: CREATE_OPERATION_LIMIT,\n\tparse: parseDeveloperProductResponse,\n\trequiredScopes: WRITE_REQUIRED_SCOPES,\n});\n\nconst GET_SPEC = makeSpec<GetDeveloperProductParameters>({\n\tbuildRequest: (parameters) => okRequest(buildGetRequest(parameters)),\n\tmethodDefaults: IDEMPOTENT_METHOD_DEFAULTS,\n\tmethodKind: \"idempotent\",\n\toperationLimit: GET_OPERATION_LIMIT,\n\tparse: parseDeveloperProductResponse,\n\trequiredScopes: GET_REQUIRED_SCOPES,\n});\n\nconst UPDATE_SPEC = makeSpec<UpdateDeveloperProductParameters, 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: WRITE_REQUIRED_SCOPES,\n});\n\nconst UPDATE_NAME_DESCRIPTION_SPEC = makeSpec<\n\tUpdateDeveloperProductNameDescriptionParameters,\n\tundefined\n>({\n\tbuildRequest: (parameters) => okRequest(buildLocaleNameDescRequest(parameters)),\n\tmethodDefaults: IDEMPOTENT_METHOD_DEFAULTS,\n\tmethodKind: \"idempotent\",\n\toperationLimit: LOCALIZATION_OPERATION_LIMIT,\n\tparse: parseEmptyResponse,\n\trequiredScopes: LOCALIZATION_REQUIRED_SCOPES,\n});\n\nconst UPLOAD_ICON_SPEC = makeSpec<UploadDeveloperProductIconParameters, undefined>({\n\tbuildRequest: (parameters) => okRequest(buildUploadIconRequest(parameters)),\n\tmethodDefaults: CREATE_METHOD_DEFAULTS,\n\tmethodKind: \"create\",\n\toperationLimit: LOCALIZATION_OPERATION_LIMIT,\n\tparse: parseEmptyResponse,\n\trequiredScopes: LOCALIZATION_REQUIRED_SCOPES,\n});\n\ninterface DeveloperProductLocalizationHandle {\n\t/**\n\t * Updates the per-locale display name and/or description registered against\n\t * a developer product. Either `name`, `description`, or both may be\n\t * supplied; omitted fields are not forwarded so the server leaves the\n\t * existing value for that locale untouched. Mirrors the upstream `200 OK`\n\t * echo body as `undefined` data.\n\t *\n\t * @param parameters - Product and language identifiers plus the optional\n\t * replacement values.\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\tupdateNameDescription: (\n\t\tparameters: UpdateDeveloperProductNameDescriptionParameters,\n\t\toptions?: RequestOptions,\n\t) => Promise<Result<undefined, OpenCloudError>>;\n\t/**\n\t * Uploads or replaces the per-locale icon for a developer product. A\n\t * subsequent upload for the same `(productId, languageCode)` pair replaces\n\t * the existing icon for that locale. Does not retry on 5xx so a duplicate\n\t * upload cannot be created if the server fails mid-write.\n\t *\n\t * @param parameters - Product and language identifiers plus the image\n\t * 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\tuploadIcon: (\n\t\tparameters: UploadDeveloperProductIconParameters,\n\t\toptions?: RequestOptions,\n\t) => Promise<Result<undefined, OpenCloudError>>;\n}\n\n/**\n * Public client for the Roblox Open Cloud Developer Products API.\n *\n * Wires request builders, the injected {@link OpenCloudClientOptions.httpClient}, and response\n * parsers into a single ergonomic surface. Every method returns a\n * {@link Result} so callers handle failure explicitly; no thrown\n * `OpenCloudError` ever escapes the client.\n *\n * ```ts\n * import { DeveloperProductsClient } from \"@bedrock-rbx/ocale/developer-products\";\n *\n * const client = new DeveloperProductsClient({ apiKey: process.env.ROBLOX_API_KEY! });\n *\n * const result = await client.get({\n * universeId: \"1234567890\",\n * productId: \"9876543210\",\n * });\n *\n * if (result.success) {\n * console.log(`${result.data.name} (${result.data.id})`);\n * } else {\n * console.error(result.err.message);\n * }\n * ```\n */\nexport class DeveloperProductsClient {\n\treadonly #inner: ResourceClient;\n\n\t/**\n\t * Operation Group exposing per-locale localization Operations\n\t * (`updateNameDescription`, `uploadIcon`) backed by the\n\t * `legacy-game-internationalization` domain. Source-language values\n\t * remain on {@link DeveloperProductsClient.update}; methods on this\n\t * group set per-locale overlays on top. Shares the parent client's\n\t * HTTP, rate-limit, and retry plumbing.\n\t */\n\tpublic readonly localization: DeveloperProductLocalizationHandle;\n\n\t/**\n\t * Creates a new {@link DeveloperProductsClient}. Configuration is frozen\n\t * on 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\tthis.localization = createLocalizationHandle(this.#inner);\n\t}\n\n\t/**\n\t * Creates a new developer product under the supplied universe.\n\t *\n\t * @param parameters - Creation fields including the universe and product name.\n\t * @param options - Optional per-request overrides.\n\t * @returns A {@link Result} wrapping the parsed {@link DeveloperProduct} or\n\t * the {@link OpenCloudError} that caused the request to fail.\n\t */\n\tpublic async create(\n\t\tparameters: CreateDeveloperProductParameters,\n\t\toptions?: RequestOptions,\n\t): Promise<Result<DeveloperProduct, OpenCloudError>> {\n\t\treturn this.#inner.execute({ options, parameters, spec: CREATE_SPEC });\n\t}\n\n\t/**\n\t * Reads a single developer product by ID.\n\t *\n\t * @param parameters - Universe and product identifiers.\n\t * @param options - Optional per-request overrides (e.g. A different\n\t * {@link OpenCloudClientOptions.apiKey} for this call only).\n\t * @returns A {@link Result} wrapping the parsed {@link DeveloperProduct} or\n\t * the {@link OpenCloudError} that caused the request to fail.\n\t */\n\tpublic async get(\n\t\tparameters: GetDeveloperProductParameters,\n\t\toptions?: RequestOptions,\n\t): Promise<Result<DeveloperProduct, OpenCloudError>> {\n\t\treturn this.#inner.execute({ options, parameters, spec: GET_SPEC });\n\t}\n\n\t/**\n\t * Partially updates an existing developer product. Mirrors the upstream\n\t * `204 No Content` response: a successful update yields `undefined` data.\n\t * Callers that need the post-update state (for example to observe a\n\t * server-derived `updatedTimestamp`) chain {@link DeveloperProductsClient.get}\n\t * themselves so the GET only fires when actually needed.\n\t *\n\t * @param parameters - The universe and product identifiers and the\n\t * fields to update. Only fields explicitly provided are forwarded.\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: UpdateDeveloperProductParameters,\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\nfunction createLocalizationHandle(inner: ResourceClient): DeveloperProductLocalizationHandle {\n\treturn {\n\t\tasync updateNameDescription(parameters, options) {\n\t\t\treturn inner.execute({ options, parameters, spec: UPDATE_NAME_DESCRIPTION_SPEC });\n\t\t},\n\t\tasync uploadIcon(parameters, options) {\n\t\t\treturn inner.execute({ options, parameters, spec: UPLOAD_ICON_SPEC });\n\t\t},\n\t};\n}\n"],"mappings":"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"}
|
package/dist/game-passes.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { c as Page, d as OpenCloudError, i as OpenCloudClientOptions, l as Result, s as RequestOptions } from "./types-
|
|
1
|
+
import { c as Page, d as OpenCloudError, i as OpenCloudClientOptions, l as Result, s as RequestOptions } from "./types-rzs1NB-j.mjs";
|
|
2
2
|
import { i as RobloxLocale, r as RobloxLanguageCode } from "./data.generated-Cb6g6asv.mjs";
|
|
3
3
|
|
|
4
4
|
//#region src/domains/game-internationalization/game-pass-icon/types.d.ts
|
package/dist/game-passes.mjs
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { r as ApiError } from "./rate-limit-
|
|
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";
|
|
2
3
|
import { t as toBlob } from "./to-blob-B27VhoRp.mjs";
|
|
3
|
-
import { a as
|
|
4
|
-
import { n as isPriceInformationLike, t as copyPriceInformation } from "./price-information-
|
|
4
|
+
import { a as isDateTimeString, i as isRecord, n as okRequest, r as parseEmptyResponse, t as ResourceClient } from "./resource-client-CG9-BG81.mjs";
|
|
5
|
+
import { n as isPriceInformationLike, t as copyPriceInformation } from "./price-information-DT7_QJN-.mjs";
|
|
5
6
|
//#region src/domains/game-internationalization/game-pass-icon/builders.ts
|
|
6
7
|
/**
|
|
7
8
|
* Builds a `POST` request for the localized "upload game-pass icon"
|