@ascnd-gg/client 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +314 -0
- package/dist/index.cjs +253 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +635 -0
- package/dist/index.d.ts +635 -0
- package/dist/index.js +212 -0
- package/dist/index.js.map +1 -0
- package/package.json +67 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 Ascnd
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,314 @@
|
|
|
1
|
+
# @ascnd-gg/client
|
|
2
|
+
|
|
3
|
+
TypeScript/JavaScript client library for the [Ascnd](https://ascnd.gg) leaderboard API. Uses gRPC-Web for efficient, type-safe communication.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @ascnd-gg/client
|
|
9
|
+
# or
|
|
10
|
+
yarn add @ascnd-gg/client
|
|
11
|
+
# or
|
|
12
|
+
pnpm add @ascnd-gg/client
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Quick Start
|
|
16
|
+
|
|
17
|
+
```typescript
|
|
18
|
+
import { AscndClient, create, SubmitScoreRequestSchema } from "@ascnd-gg/client";
|
|
19
|
+
|
|
20
|
+
const client = new AscndClient({
|
|
21
|
+
baseUrl: "https://api.ascnd.gg",
|
|
22
|
+
apiKey: "your-api-key",
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
// Submit a score
|
|
26
|
+
const result = await client.submitScore(
|
|
27
|
+
create(SubmitScoreRequestSchema, {
|
|
28
|
+
leaderboardId: "high-scores",
|
|
29
|
+
playerId: "player-123",
|
|
30
|
+
score: 1000n,
|
|
31
|
+
})
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
console.log(`Rank: #${result.rank}`);
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Examples
|
|
38
|
+
|
|
39
|
+
Stand-alone example projects are available in the [`examples/`](./examples) directory:
|
|
40
|
+
|
|
41
|
+
| Example | Description |
|
|
42
|
+
|---------|-------------|
|
|
43
|
+
| [`submit-score`](./examples/submit-score) | Submit a score and display the resulting rank |
|
|
44
|
+
| [`leaderboard`](./examples/leaderboard) | Fetch and display the top 10 leaderboard entries |
|
|
45
|
+
| [`metadata-periods`](./examples/metadata-periods) | Submit scores with metadata and query by time period |
|
|
46
|
+
|
|
47
|
+
Each example is a self-contained project. To run:
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
cd examples/submit-score
|
|
51
|
+
npm install
|
|
52
|
+
export ASCND_API_KEY=your_api_key
|
|
53
|
+
export LEADERBOARD_ID=your_leaderboard_id
|
|
54
|
+
npm start
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Usage
|
|
58
|
+
|
|
59
|
+
### Creating a Client
|
|
60
|
+
|
|
61
|
+
```typescript
|
|
62
|
+
import { AscndClient } from "@ascnd-gg/client";
|
|
63
|
+
|
|
64
|
+
const client = new AscndClient({
|
|
65
|
+
baseUrl: "https://api.ascnd.gg", // API base URL
|
|
66
|
+
apiKey: "your-api-key", // Your API key
|
|
67
|
+
timeout: 30000, // Optional: request timeout in ms (default: 30000)
|
|
68
|
+
});
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### Submit a Score
|
|
72
|
+
|
|
73
|
+
Submit a player's score to a leaderboard:
|
|
74
|
+
|
|
75
|
+
```typescript
|
|
76
|
+
import { create, SubmitScoreRequestSchema } from "@ascnd-gg/client";
|
|
77
|
+
|
|
78
|
+
const result = await client.submitScore(
|
|
79
|
+
create(SubmitScoreRequestSchema, {
|
|
80
|
+
leaderboardId: "high-scores",
|
|
81
|
+
playerId: "player-123",
|
|
82
|
+
score: 1000n,
|
|
83
|
+
metadata: new TextEncoder().encode(JSON.stringify({
|
|
84
|
+
level: 5,
|
|
85
|
+
character: "warrior",
|
|
86
|
+
})),
|
|
87
|
+
idempotencyKey: "unique-key", // Optional: prevent duplicate submissions
|
|
88
|
+
})
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
console.log(`Score ID: ${result.scoreId}`);
|
|
92
|
+
console.log(`Rank: #${result.rank}`);
|
|
93
|
+
console.log(`New personal best: ${result.isNewBest}`);
|
|
94
|
+
console.log(`Was deduplicated: ${result.wasDeduplicated}`);
|
|
95
|
+
|
|
96
|
+
// Check anticheat results (if enabled)
|
|
97
|
+
if (result.anticheat) {
|
|
98
|
+
console.log(`Anticheat passed: ${result.anticheat.passed}`);
|
|
99
|
+
if (!result.anticheat.passed) {
|
|
100
|
+
console.log(`Action taken: ${result.anticheat.action}`);
|
|
101
|
+
for (const violation of result.anticheat.violations) {
|
|
102
|
+
console.log(` - ${violation.flagType}: ${violation.reason}`);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### Get Leaderboard
|
|
109
|
+
|
|
110
|
+
Retrieve the top scores for a leaderboard:
|
|
111
|
+
|
|
112
|
+
```typescript
|
|
113
|
+
import { create, GetLeaderboardRequestSchema } from "@ascnd-gg/client";
|
|
114
|
+
|
|
115
|
+
const leaderboard = await client.getLeaderboard(
|
|
116
|
+
create(GetLeaderboardRequestSchema, {
|
|
117
|
+
leaderboardId: "high-scores",
|
|
118
|
+
limit: 10, // Optional: max entries (default: 10, max: 100)
|
|
119
|
+
offset: 0, // Optional: pagination offset
|
|
120
|
+
period: "current", // Optional: "current", "previous", or timestamp
|
|
121
|
+
viewSlug: "platform-pc", // Optional: filter by metadata view
|
|
122
|
+
})
|
|
123
|
+
);
|
|
124
|
+
|
|
125
|
+
console.log(`Total players: ${leaderboard.totalEntries}`);
|
|
126
|
+
console.log(`Period: ${leaderboard.periodStart} - ${leaderboard.periodEnd}`);
|
|
127
|
+
|
|
128
|
+
for (const entry of leaderboard.entries) {
|
|
129
|
+
console.log(`#${entry.rank}: ${entry.playerId} - ${entry.score}`);
|
|
130
|
+
if (entry.bracket) {
|
|
131
|
+
console.log(` Bracket: ${entry.bracket.name} (${entry.bracket.color})`);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
if (leaderboard.hasMore) {
|
|
136
|
+
// Fetch next page with offset: 10
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// View info (if filtering by viewSlug)
|
|
140
|
+
if (leaderboard.view) {
|
|
141
|
+
console.log(`Viewing: ${leaderboard.view.name}`);
|
|
142
|
+
}
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
### Get Player Rank
|
|
146
|
+
|
|
147
|
+
Get a specific player's rank and score:
|
|
148
|
+
|
|
149
|
+
```typescript
|
|
150
|
+
import { create, GetPlayerRankRequestSchema } from "@ascnd-gg/client";
|
|
151
|
+
|
|
152
|
+
const playerRank = await client.getPlayerRank(
|
|
153
|
+
create(GetPlayerRankRequestSchema, {
|
|
154
|
+
leaderboardId: "high-scores",
|
|
155
|
+
playerId: "player-123",
|
|
156
|
+
period: "current", // Optional
|
|
157
|
+
viewSlug: "platform-pc", // Optional: filter by metadata view
|
|
158
|
+
})
|
|
159
|
+
);
|
|
160
|
+
|
|
161
|
+
if (playerRank.rank !== undefined) {
|
|
162
|
+
console.log(`Rank: #${playerRank.rank}`);
|
|
163
|
+
console.log(`Score: ${playerRank.score}`);
|
|
164
|
+
console.log(`Best score: ${playerRank.bestScore}`);
|
|
165
|
+
console.log(`Percentile: ${playerRank.percentile}`);
|
|
166
|
+
console.log(`Total players: ${playerRank.totalEntries}`);
|
|
167
|
+
|
|
168
|
+
// Bracket info (if brackets are enabled)
|
|
169
|
+
if (playerRank.bracket) {
|
|
170
|
+
console.log(`Bracket: ${playerRank.bracket.name}`);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// Global rank when filtering by view
|
|
174
|
+
if (playerRank.globalRank !== undefined) {
|
|
175
|
+
console.log(`Global Rank: #${playerRank.globalRank}`);
|
|
176
|
+
}
|
|
177
|
+
} else {
|
|
178
|
+
console.log("Player has no score on this leaderboard");
|
|
179
|
+
}
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
## Features
|
|
183
|
+
|
|
184
|
+
### Anticheat
|
|
185
|
+
|
|
186
|
+
When anticheat is enabled for a leaderboard, the `submitScore` response includes validation results:
|
|
187
|
+
|
|
188
|
+
```typescript
|
|
189
|
+
const result = await client.submitScore(request);
|
|
190
|
+
|
|
191
|
+
if (result.anticheat && !result.anticheat.passed) {
|
|
192
|
+
console.log(`Score flagged: ${result.anticheat.action}`);
|
|
193
|
+
// Possible actions: "none", "flag", "shadow_ban", "reject"
|
|
194
|
+
|
|
195
|
+
for (const violation of result.anticheat.violations) {
|
|
196
|
+
console.log(` ${violation.flagType}: ${violation.reason}`);
|
|
197
|
+
// Flag types: "bounds_exceeded", "velocity_exceeded",
|
|
198
|
+
// "duplicate_idempotency", "missing_idempotency_key"
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
### Brackets
|
|
204
|
+
|
|
205
|
+
Leaderboards with brackets enabled include tier information for each entry:
|
|
206
|
+
|
|
207
|
+
```typescript
|
|
208
|
+
const leaderboard = await client.getLeaderboard(request);
|
|
209
|
+
|
|
210
|
+
for (const entry of leaderboard.entries) {
|
|
211
|
+
if (entry.bracket) {
|
|
212
|
+
console.log(`${entry.playerId} is in ${entry.bracket.name}`);
|
|
213
|
+
// entry.bracket.color contains hex color code like "#FFD700"
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
### Metadata Views
|
|
219
|
+
|
|
220
|
+
Filter leaderboards by metadata views to create segmented rankings:
|
|
221
|
+
|
|
222
|
+
```typescript
|
|
223
|
+
import { create, GetLeaderboardRequestSchema } from "@ascnd-gg/client";
|
|
224
|
+
|
|
225
|
+
// Get PC-only leaderboard
|
|
226
|
+
const pcLeaderboard = await client.getLeaderboard(
|
|
227
|
+
create(GetLeaderboardRequestSchema, {
|
|
228
|
+
leaderboardId: "high-scores",
|
|
229
|
+
viewSlug: "platform-pc",
|
|
230
|
+
})
|
|
231
|
+
);
|
|
232
|
+
|
|
233
|
+
// Rankings are within the view
|
|
234
|
+
// Use globalRank in GetPlayerRank to see overall position
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
## Error Handling
|
|
238
|
+
|
|
239
|
+
The client throws `AscndError` for API errors:
|
|
240
|
+
|
|
241
|
+
```typescript
|
|
242
|
+
import { AscndClient, AscndError } from "@ascnd-gg/client";
|
|
243
|
+
|
|
244
|
+
try {
|
|
245
|
+
await client.submitScore(request);
|
|
246
|
+
} catch (error) {
|
|
247
|
+
if (error instanceof AscndError) {
|
|
248
|
+
console.error(`API Error: ${error.message}`);
|
|
249
|
+
console.error(`Code: ${error.code}`);
|
|
250
|
+
console.error(`Details:`, error.details);
|
|
251
|
+
} else {
|
|
252
|
+
throw error;
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
## API Reference
|
|
258
|
+
|
|
259
|
+
### AscndClient
|
|
260
|
+
|
|
261
|
+
#### Constructor
|
|
262
|
+
|
|
263
|
+
```typescript
|
|
264
|
+
new AscndClient(config: AscndClientConfig)
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
| Option | Type | Required | Description |
|
|
268
|
+
|--------|------|----------|-------------|
|
|
269
|
+
| `baseUrl` | `string` | Yes | The base URL of the Ascnd API |
|
|
270
|
+
| `apiKey` | `string` | Yes | Your API key for authentication |
|
|
271
|
+
| `timeout` | `number` | No | Request timeout in milliseconds (default: 30000) |
|
|
272
|
+
|
|
273
|
+
#### Methods
|
|
274
|
+
|
|
275
|
+
##### `submitScore(request: SubmitScoreRequest): Promise<SubmitScoreResponse>`
|
|
276
|
+
|
|
277
|
+
Submit a player's score to a leaderboard.
|
|
278
|
+
|
|
279
|
+
##### `getLeaderboard(request: GetLeaderboardRequest): Promise<GetLeaderboardResponse>`
|
|
280
|
+
|
|
281
|
+
Retrieve the top scores for a leaderboard.
|
|
282
|
+
|
|
283
|
+
##### `getPlayerRank(request: GetPlayerRankRequest): Promise<GetPlayerRankResponse>`
|
|
284
|
+
|
|
285
|
+
Get a specific player's rank and score.
|
|
286
|
+
|
|
287
|
+
### Message Creation
|
|
288
|
+
|
|
289
|
+
Use `create()` with schemas to create properly typed request messages:
|
|
290
|
+
|
|
291
|
+
```typescript
|
|
292
|
+
import { create, SubmitScoreRequestSchema } from "@ascnd-gg/client";
|
|
293
|
+
|
|
294
|
+
const request = create(SubmitScoreRequestSchema, {
|
|
295
|
+
leaderboardId: "high-scores",
|
|
296
|
+
playerId: "player-123",
|
|
297
|
+
score: 1000n, // Note: scores are bigint
|
|
298
|
+
});
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
## Requirements
|
|
302
|
+
|
|
303
|
+
- Node.js 18+ (for native fetch support)
|
|
304
|
+
- Or any runtime with `fetch` available (browsers, Deno, Bun, Cloudflare Workers, etc.)
|
|
305
|
+
|
|
306
|
+
## Links
|
|
307
|
+
|
|
308
|
+
- [Documentation](https://ascnd.gg/docs/sdks/typescript)
|
|
309
|
+
- [GitHub](https://github.com/ascnd-gg/ascnd-client-js)
|
|
310
|
+
- [npm](https://www.npmjs.com/package/@ascnd-gg/client)
|
|
311
|
+
|
|
312
|
+
## License
|
|
313
|
+
|
|
314
|
+
MIT
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
AnticheatResultSchema: () => AnticheatResultSchema,
|
|
24
|
+
AnticheatViolationSchema: () => AnticheatViolationSchema,
|
|
25
|
+
AscndClient: () => AscndClient,
|
|
26
|
+
AscndError: () => AscndError,
|
|
27
|
+
AscndService: () => AscndService,
|
|
28
|
+
BracketInfoSchema: () => BracketInfoSchema,
|
|
29
|
+
GetLeaderboardRequestSchema: () => GetLeaderboardRequestSchema,
|
|
30
|
+
GetLeaderboardResponseSchema: () => GetLeaderboardResponseSchema,
|
|
31
|
+
GetPlayerRankRequestSchema: () => GetPlayerRankRequestSchema,
|
|
32
|
+
GetPlayerRankResponseSchema: () => GetPlayerRankResponseSchema,
|
|
33
|
+
LeaderboardEntrySchema: () => LeaderboardEntrySchema,
|
|
34
|
+
SubmitScoreRequestSchema: () => SubmitScoreRequestSchema,
|
|
35
|
+
SubmitScoreResponseSchema: () => SubmitScoreResponseSchema,
|
|
36
|
+
ViewInfoSchema: () => ViewInfoSchema,
|
|
37
|
+
create: () => import_protobuf.create
|
|
38
|
+
});
|
|
39
|
+
module.exports = __toCommonJS(index_exports);
|
|
40
|
+
|
|
41
|
+
// src/client.ts
|
|
42
|
+
var import_connect = require("@connectrpc/connect");
|
|
43
|
+
var import_connect_web = require("@connectrpc/connect-web");
|
|
44
|
+
|
|
45
|
+
// src/gen/ascnd_pb.ts
|
|
46
|
+
var import_codegenv2 = require("@bufbuild/protobuf/codegenv2");
|
|
47
|
+
var file_ascnd = /* @__PURE__ */ (0, import_codegenv2.fileDesc)("Cgthc2NuZC5wcm90bxIIYXNjbmQudjEipAEKElN1Ym1pdFNjb3JlUmVxdWVzdBIWCg5sZWFkZXJib2FyZF9pZBgBIAEoCRIRCglwbGF5ZXJfaWQYAiABKAkSDQoFc2NvcmUYAyABKAMSFQoIbWV0YWRhdGEYBCABKAxIAIgBARIcCg9pZGVtcG90ZW5jeV9rZXkYBSABKAlIAYgBAUILCglfbWV0YWRhdGFCEgoQX2lkZW1wb3RlbmN5X2tleSKlAQoTU3VibWl0U2NvcmVSZXNwb25zZRIQCghzY29yZV9pZBgBIAEoCRIMCgRyYW5rGAIgASgFEhMKC2lzX25ld19iZXN0GAMgASgIEhgKEHdhc19kZWR1cGxpY2F0ZWQYBCABKAgSMQoJYW50aWNoZWF0GAUgASgLMhkuYXNjbmQudjEuQW50aWNoZWF0UmVzdWx0SACIAQFCDAoKX2FudGljaGVhdCJjCg9BbnRpY2hlYXRSZXN1bHQSDgoGcGFzc2VkGAEgASgIEjAKCnZpb2xhdGlvbnMYAiADKAsyHC5hc2NuZC52MS5BbnRpY2hlYXRWaW9sYXRpb24SDgoGYWN0aW9uGAMgASgJIjcKEkFudGljaGVhdFZpb2xhdGlvbhIRCglmbGFnX3R5cGUYASABKAkSDgoGcmVhc29uGAIgASgJIrMBChVHZXRMZWFkZXJib2FyZFJlcXVlc3QSFgoObGVhZGVyYm9hcmRfaWQYASABKAkSEgoFbGltaXQYAiABKAVIAIgBARITCgZvZmZzZXQYAyABKAVIAYgBARITCgZwZXJpb2QYBCABKAlIAogBARIWCgl2aWV3X3NsdWcYBSABKAlIA4gBAUIICgZfbGltaXRCCQoHX29mZnNldEIJCgdfcGVyaW9kQgwKCl92aWV3X3NsdWci3AEKFkdldExlYWRlcmJvYXJkUmVzcG9uc2USKwoHZW50cmllcxgBIAMoCzIaLmFzY25kLnYxLkxlYWRlcmJvYXJkRW50cnkSFQoNdG90YWxfZW50cmllcxgCIAEoBRIQCghoYXNfbW9yZRgDIAEoCBIUCgxwZXJpb2Rfc3RhcnQYBCABKAkSFwoKcGVyaW9kX2VuZBgFIAEoCUgAiAEBEiUKBHZpZXcYBiABKAsyEi5hc2NuZC52MS5WaWV3SW5mb0gBiAEBQg0KC19wZXJpb2RfZW5kQgcKBV92aWV3IrUBChBMZWFkZXJib2FyZEVudHJ5EgwKBHJhbmsYASABKAUSEQoJcGxheWVyX2lkGAIgASgJEg0KBXNjb3JlGAMgASgDEhQKDHN1Ym1pdHRlZF9hdBgEIAEoCRIVCghtZXRhZGF0YRgFIAEoDEgAiAEBEisKB2JyYWNrZXQYBiABKAsyFS5hc2NuZC52MS5CcmFja2V0SW5mb0gBiAEBQgsKCV9tZXRhZGF0YUIKCghfYnJhY2tldCJFCgtCcmFja2V0SW5mbxIKCgJpZBgBIAEoCRIMCgRuYW1lGAIgASgJEhIKBWNvbG9yGAMgASgJSACIAQFCCAoGX2NvbG9yIiYKCFZpZXdJbmZvEgwKBHNsdWcYASABKAkSDAoEbmFtZRgCIAEoCSKHAQoUR2V0UGxheWVyUmFua1JlcXVlc3QSFgoObGVhZGVyYm9hcmRfaWQYASABKAkSEQoJcGxheWVyX2lkGAIgASgJEhMKBnBlcmlvZBgDIAEoCUgAiAEBEhYKCXZpZXdfc2x1ZxgEIAEoCUgBiAEBQgkKB19wZXJpb2RCDAoKX3ZpZXdfc2x1ZyLLAgoVR2V0UGxheWVyUmFua1Jlc3BvbnNlEhEKBHJhbmsYASABKAVIAIgBARISCgVzY29yZRgCIAEoA0gBiAEBEhcKCmJlc3Rfc2NvcmUYAyABKANIAogBARIVCg10b3RhbF9lbnRyaWVzGAQgASgFEhcKCnBlcmNlbnRpbGUYBSABKAlIA4gBARIrCgdicmFja2V0GAYgASgLMhUuYXNjbmQudjEuQnJhY2tldEluZm9IBIgBARIlCgR2aWV3GAcgASgLMhIuYXNjbmQudjEuVmlld0luZm9IBYgBARIYCgtnbG9iYWxfcmFuaxgIIAEoBUgGiAEBQgcKBV9yYW5rQggKBl9zY29yZUINCgtfYmVzdF9zY29yZUINCgtfcGVyY2VudGlsZUIKCghfYnJhY2tldEIHCgVfdmlld0IOCgxfZ2xvYmFsX3JhbmsygQIKDEFzY25kU2VydmljZRJKCgtTdWJtaXRTY29yZRIcLmFzY25kLnYxLlN1Ym1pdFNjb3JlUmVxdWVzdBodLmFzY25kLnYxLlN1Ym1pdFNjb3JlUmVzcG9uc2USUwoOR2V0TGVhZGVyYm9hcmQSHy5hc2NuZC52MS5HZXRMZWFkZXJib2FyZFJlcXVlc3QaIC5hc2NuZC52MS5HZXRMZWFkZXJib2FyZFJlc3BvbnNlElAKDUdldFBsYXllclJhbmsSHi5hc2NuZC52MS5HZXRQbGF5ZXJSYW5rUmVxdWVzdBofLmFzY25kLnYxLkdldFBsYXllclJhbmtSZXNwb25zZWIGcHJvdG8z");
|
|
48
|
+
var SubmitScoreRequestSchema = /* @__PURE__ */ (0, import_codegenv2.messageDesc)(file_ascnd, 0);
|
|
49
|
+
var SubmitScoreResponseSchema = /* @__PURE__ */ (0, import_codegenv2.messageDesc)(file_ascnd, 1);
|
|
50
|
+
var AnticheatResultSchema = /* @__PURE__ */ (0, import_codegenv2.messageDesc)(file_ascnd, 2);
|
|
51
|
+
var AnticheatViolationSchema = /* @__PURE__ */ (0, import_codegenv2.messageDesc)(file_ascnd, 3);
|
|
52
|
+
var GetLeaderboardRequestSchema = /* @__PURE__ */ (0, import_codegenv2.messageDesc)(file_ascnd, 4);
|
|
53
|
+
var GetLeaderboardResponseSchema = /* @__PURE__ */ (0, import_codegenv2.messageDesc)(file_ascnd, 5);
|
|
54
|
+
var LeaderboardEntrySchema = /* @__PURE__ */ (0, import_codegenv2.messageDesc)(file_ascnd, 6);
|
|
55
|
+
var BracketInfoSchema = /* @__PURE__ */ (0, import_codegenv2.messageDesc)(file_ascnd, 7);
|
|
56
|
+
var ViewInfoSchema = /* @__PURE__ */ (0, import_codegenv2.messageDesc)(file_ascnd, 8);
|
|
57
|
+
var GetPlayerRankRequestSchema = /* @__PURE__ */ (0, import_codegenv2.messageDesc)(file_ascnd, 9);
|
|
58
|
+
var GetPlayerRankResponseSchema = /* @__PURE__ */ (0, import_codegenv2.messageDesc)(file_ascnd, 10);
|
|
59
|
+
var AscndService = /* @__PURE__ */ (0, import_codegenv2.serviceDesc)(file_ascnd, 0);
|
|
60
|
+
|
|
61
|
+
// src/types.ts
|
|
62
|
+
var AscndError = class extends Error {
|
|
63
|
+
/** gRPC/Connect error code. */
|
|
64
|
+
code;
|
|
65
|
+
/** Additional error details. */
|
|
66
|
+
details;
|
|
67
|
+
constructor(message, code, details) {
|
|
68
|
+
super(message);
|
|
69
|
+
this.name = "AscndError";
|
|
70
|
+
this.code = code;
|
|
71
|
+
this.details = details;
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
// src/client.ts
|
|
76
|
+
var AscndClient = class {
|
|
77
|
+
client;
|
|
78
|
+
/**
|
|
79
|
+
* Creates a new Ascnd client.
|
|
80
|
+
*
|
|
81
|
+
* @param config - Client configuration options.
|
|
82
|
+
*/
|
|
83
|
+
constructor(config) {
|
|
84
|
+
if (!config.baseUrl) {
|
|
85
|
+
throw new Error("baseUrl is required");
|
|
86
|
+
}
|
|
87
|
+
if (!config.apiKey) {
|
|
88
|
+
throw new Error("apiKey is required");
|
|
89
|
+
}
|
|
90
|
+
const authInterceptor = (next) => async (req) => {
|
|
91
|
+
req.header.set("x-api-key", config.apiKey);
|
|
92
|
+
return await next(req);
|
|
93
|
+
};
|
|
94
|
+
const transport = (0, import_connect_web.createGrpcWebTransport)({
|
|
95
|
+
baseUrl: config.baseUrl.replace(/\/$/, ""),
|
|
96
|
+
interceptors: [authInterceptor],
|
|
97
|
+
defaultTimeoutMs: config.timeout ?? 3e4
|
|
98
|
+
});
|
|
99
|
+
this.client = (0, import_connect.createClient)(AscndService, transport);
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Submits a player's score to a leaderboard.
|
|
103
|
+
*
|
|
104
|
+
* @param request - The score submission request.
|
|
105
|
+
* @returns The submission result including the player's new rank.
|
|
106
|
+
* @throws {AscndError} If the API returns an error.
|
|
107
|
+
*
|
|
108
|
+
* @example
|
|
109
|
+
* ```typescript
|
|
110
|
+
* import { create } from "@bufbuild/protobuf";
|
|
111
|
+
* import { SubmitScoreRequestSchema } from "@ascnd-gg/client";
|
|
112
|
+
*
|
|
113
|
+
* const result = await client.submitScore(
|
|
114
|
+
* create(SubmitScoreRequestSchema, {
|
|
115
|
+
* leaderboardId: "high-scores",
|
|
116
|
+
* playerId: "player-123",
|
|
117
|
+
* score: 1000n,
|
|
118
|
+
* metadata: new TextEncoder().encode(JSON.stringify({ level: 5 })),
|
|
119
|
+
* })
|
|
120
|
+
* );
|
|
121
|
+
*
|
|
122
|
+
* console.log(`New rank: ${result.rank}`);
|
|
123
|
+
* if (result.isNewBest) {
|
|
124
|
+
* console.log("New personal best!");
|
|
125
|
+
* }
|
|
126
|
+
* if (result.anticheat && !result.anticheat.passed) {
|
|
127
|
+
* console.log("Anticheat flagged:", result.anticheat.violations);
|
|
128
|
+
* }
|
|
129
|
+
* ```
|
|
130
|
+
*/
|
|
131
|
+
async submitScore(request) {
|
|
132
|
+
try {
|
|
133
|
+
return await this.client.submitScore(request);
|
|
134
|
+
} catch (error) {
|
|
135
|
+
throw this.convertError(error);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Retrieves the top scores for a leaderboard.
|
|
140
|
+
*
|
|
141
|
+
* @param request - The leaderboard request parameters.
|
|
142
|
+
* @returns The leaderboard entries and pagination info.
|
|
143
|
+
* @throws {AscndError} If the API returns an error.
|
|
144
|
+
*
|
|
145
|
+
* @example
|
|
146
|
+
* ```typescript
|
|
147
|
+
* import { create } from "@bufbuild/protobuf";
|
|
148
|
+
* import { GetLeaderboardRequestSchema } from "@ascnd-gg/client";
|
|
149
|
+
*
|
|
150
|
+
* const leaderboard = await client.getLeaderboard(
|
|
151
|
+
* create(GetLeaderboardRequestSchema, {
|
|
152
|
+
* leaderboardId: "high-scores",
|
|
153
|
+
* limit: 10,
|
|
154
|
+
* viewSlug: "platform-pc", // Filter by metadata view
|
|
155
|
+
* })
|
|
156
|
+
* );
|
|
157
|
+
*
|
|
158
|
+
* for (const entry of leaderboard.entries) {
|
|
159
|
+
* console.log(`#${entry.rank}: ${entry.playerId} - ${entry.score}`);
|
|
160
|
+
* if (entry.bracket) {
|
|
161
|
+
* console.log(` Bracket: ${entry.bracket.name}`);
|
|
162
|
+
* }
|
|
163
|
+
* }
|
|
164
|
+
* ```
|
|
165
|
+
*/
|
|
166
|
+
async getLeaderboard(request) {
|
|
167
|
+
try {
|
|
168
|
+
return await this.client.getLeaderboard(request);
|
|
169
|
+
} catch (error) {
|
|
170
|
+
throw this.convertError(error);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Retrieves a specific player's rank and score information.
|
|
175
|
+
*
|
|
176
|
+
* @param request - The player rank request parameters.
|
|
177
|
+
* @returns The player's rank, score, and percentile information.
|
|
178
|
+
* @throws {AscndError} If the API returns an error.
|
|
179
|
+
*
|
|
180
|
+
* @example
|
|
181
|
+
* ```typescript
|
|
182
|
+
* import { create } from "@bufbuild/protobuf";
|
|
183
|
+
* import { GetPlayerRankRequestSchema } from "@ascnd-gg/client";
|
|
184
|
+
*
|
|
185
|
+
* const playerRank = await client.getPlayerRank(
|
|
186
|
+
* create(GetPlayerRankRequestSchema, {
|
|
187
|
+
* leaderboardId: "high-scores",
|
|
188
|
+
* playerId: "player-123",
|
|
189
|
+
* viewSlug: "platform-pc",
|
|
190
|
+
* })
|
|
191
|
+
* );
|
|
192
|
+
*
|
|
193
|
+
* if (playerRank.rank !== undefined) {
|
|
194
|
+
* console.log(`Rank: #${playerRank.rank}`);
|
|
195
|
+
* console.log(`Score: ${playerRank.score}`);
|
|
196
|
+
* console.log(`Percentile: ${playerRank.percentile}`);
|
|
197
|
+
* if (playerRank.bracket) {
|
|
198
|
+
* console.log(`Bracket: ${playerRank.bracket.name}`);
|
|
199
|
+
* }
|
|
200
|
+
* if (playerRank.globalRank !== undefined) {
|
|
201
|
+
* console.log(`Global Rank: #${playerRank.globalRank}`);
|
|
202
|
+
* }
|
|
203
|
+
* } else {
|
|
204
|
+
* console.log("Player not on leaderboard");
|
|
205
|
+
* }
|
|
206
|
+
* ```
|
|
207
|
+
*/
|
|
208
|
+
async getPlayerRank(request) {
|
|
209
|
+
try {
|
|
210
|
+
return await this.client.getPlayerRank(request);
|
|
211
|
+
} catch (error) {
|
|
212
|
+
throw this.convertError(error);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
/**
|
|
216
|
+
* Converts a Connect error to an AscndError.
|
|
217
|
+
*/
|
|
218
|
+
convertError(error) {
|
|
219
|
+
if (error instanceof import_connect.ConnectError) {
|
|
220
|
+
return new AscndError(
|
|
221
|
+
error.message,
|
|
222
|
+
error.code.toString(),
|
|
223
|
+
error.details?.length ? { details: error.details } : void 0
|
|
224
|
+
);
|
|
225
|
+
}
|
|
226
|
+
if (error instanceof Error) {
|
|
227
|
+
return new AscndError(error.message, "UNKNOWN");
|
|
228
|
+
}
|
|
229
|
+
return new AscndError("Unknown error occurred", "UNKNOWN");
|
|
230
|
+
}
|
|
231
|
+
};
|
|
232
|
+
|
|
233
|
+
// src/index.ts
|
|
234
|
+
var import_protobuf = require("@bufbuild/protobuf");
|
|
235
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
236
|
+
0 && (module.exports = {
|
|
237
|
+
AnticheatResultSchema,
|
|
238
|
+
AnticheatViolationSchema,
|
|
239
|
+
AscndClient,
|
|
240
|
+
AscndError,
|
|
241
|
+
AscndService,
|
|
242
|
+
BracketInfoSchema,
|
|
243
|
+
GetLeaderboardRequestSchema,
|
|
244
|
+
GetLeaderboardResponseSchema,
|
|
245
|
+
GetPlayerRankRequestSchema,
|
|
246
|
+
GetPlayerRankResponseSchema,
|
|
247
|
+
LeaderboardEntrySchema,
|
|
248
|
+
SubmitScoreRequestSchema,
|
|
249
|
+
SubmitScoreResponseSchema,
|
|
250
|
+
ViewInfoSchema,
|
|
251
|
+
create
|
|
252
|
+
});
|
|
253
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/client.ts","../src/gen/ascnd_pb.ts","../src/types.ts"],"sourcesContent":["/**\n * @ascnd-gg/client - TypeScript/JavaScript client for the Ascnd leaderboard API.\n *\n * @example\n * ```typescript\n * import { AscndClient, create, SubmitScoreRequestSchema } from \"@ascnd-gg/client\";\n *\n * const client = new AscndClient({\n * baseUrl: \"https://api.ascnd.gg\",\n * apiKey: \"your-api-key\",\n * });\n *\n * // Submit a score\n * const result = await client.submitScore(\n * create(SubmitScoreRequestSchema, {\n * leaderboardId: \"high-scores\",\n * playerId: \"player-123\",\n * score: 1000n,\n * })\n * );\n *\n * console.log(`New rank: ${result.rank}`);\n * ```\n *\n * @packageDocumentation\n */\n\n// Export the main client class\nexport { AscndClient } from \"./client.js\";\n\n// Re-export create helper from protobuf for convenience\nexport { create } from \"@bufbuild/protobuf\";\n\n// Export all types\nexport type {\n // Client configuration\n AscndClientConfig,\n\n // Submit Score\n SubmitScoreRequest,\n SubmitScoreResponse,\n\n // Get Leaderboard\n GetLeaderboardRequest,\n GetLeaderboardResponse,\n LeaderboardEntry,\n\n // Get Player Rank\n GetPlayerRankRequest,\n GetPlayerRankResponse,\n\n // Anticheat\n AnticheatResult,\n AnticheatViolation,\n\n // Bracket\n BracketInfo,\n\n // View\n ViewInfo,\n} from \"./types.js\";\n\n// Export error class\nexport { AscndError } from \"./types.js\";\n\n// Export the service definition\nexport { AscndService } from \"./types.js\";\n\n// Export schemas for creating messages\nexport {\n SubmitScoreRequestSchema,\n SubmitScoreResponseSchema,\n GetLeaderboardRequestSchema,\n GetLeaderboardResponseSchema,\n GetPlayerRankRequestSchema,\n GetPlayerRankResponseSchema,\n LeaderboardEntrySchema,\n AnticheatResultSchema,\n AnticheatViolationSchema,\n BracketInfoSchema,\n ViewInfoSchema,\n} from \"./types.js\";\n","import { createClient, type Client, type Interceptor, ConnectError } from \"@connectrpc/connect\";\nimport { createGrpcWebTransport } from \"@connectrpc/connect-web\";\nimport {\n AscndService,\n type SubmitScoreRequest,\n type SubmitScoreResponse,\n type GetLeaderboardRequest,\n type GetLeaderboardResponse,\n type GetPlayerRankRequest,\n type GetPlayerRankResponse,\n AscndClientConfig,\n AscndError,\n} from \"./types.js\";\n\n/**\n * Client for the Ascnd leaderboard API using gRPC-Web.\n *\n * @example\n * ```typescript\n * import { AscndClient, create } from \"@ascnd-gg/client\";\n * import { SubmitScoreRequestSchema } from \"@ascnd-gg/client\";\n *\n * const client = new AscndClient({\n * baseUrl: \"https://api.ascnd.gg\",\n * apiKey: \"your-api-key\",\n * });\n *\n * // Submit a score\n * const result = await client.submitScore(\n * create(SubmitScoreRequestSchema, {\n * leaderboardId: \"high-scores\",\n * playerId: \"player-123\",\n * score: 1000n,\n * })\n * );\n *\n * console.log(`Rank: #${result.rank}`);\n * ```\n */\nexport class AscndClient {\n private readonly client: Client<typeof AscndService>;\n\n /**\n * Creates a new Ascnd client.\n *\n * @param config - Client configuration options.\n */\n constructor(config: AscndClientConfig) {\n if (!config.baseUrl) {\n throw new Error(\"baseUrl is required\");\n }\n if (!config.apiKey) {\n throw new Error(\"apiKey is required\");\n }\n\n // Create an interceptor to add the API key header\n const authInterceptor: Interceptor = (next) => async (req) => {\n req.header.set(\"x-api-key\", config.apiKey);\n return await next(req);\n };\n\n // Create the gRPC-Web transport\n const transport = createGrpcWebTransport({\n baseUrl: config.baseUrl.replace(/\\/$/, \"\"),\n interceptors: [authInterceptor],\n defaultTimeoutMs: config.timeout ?? 30000,\n });\n\n this.client = createClient(AscndService, transport);\n }\n\n /**\n * Submits a player's score to a leaderboard.\n *\n * @param request - The score submission request.\n * @returns The submission result including the player's new rank.\n * @throws {AscndError} If the API returns an error.\n *\n * @example\n * ```typescript\n * import { create } from \"@bufbuild/protobuf\";\n * import { SubmitScoreRequestSchema } from \"@ascnd-gg/client\";\n *\n * const result = await client.submitScore(\n * create(SubmitScoreRequestSchema, {\n * leaderboardId: \"high-scores\",\n * playerId: \"player-123\",\n * score: 1000n,\n * metadata: new TextEncoder().encode(JSON.stringify({ level: 5 })),\n * })\n * );\n *\n * console.log(`New rank: ${result.rank}`);\n * if (result.isNewBest) {\n * console.log(\"New personal best!\");\n * }\n * if (result.anticheat && !result.anticheat.passed) {\n * console.log(\"Anticheat flagged:\", result.anticheat.violations);\n * }\n * ```\n */\n async submitScore(request: SubmitScoreRequest): Promise<SubmitScoreResponse> {\n try {\n return await this.client.submitScore(request);\n } catch (error) {\n throw this.convertError(error);\n }\n }\n\n /**\n * Retrieves the top scores for a leaderboard.\n *\n * @param request - The leaderboard request parameters.\n * @returns The leaderboard entries and pagination info.\n * @throws {AscndError} If the API returns an error.\n *\n * @example\n * ```typescript\n * import { create } from \"@bufbuild/protobuf\";\n * import { GetLeaderboardRequestSchema } from \"@ascnd-gg/client\";\n *\n * const leaderboard = await client.getLeaderboard(\n * create(GetLeaderboardRequestSchema, {\n * leaderboardId: \"high-scores\",\n * limit: 10,\n * viewSlug: \"platform-pc\", // Filter by metadata view\n * })\n * );\n *\n * for (const entry of leaderboard.entries) {\n * console.log(`#${entry.rank}: ${entry.playerId} - ${entry.score}`);\n * if (entry.bracket) {\n * console.log(` Bracket: ${entry.bracket.name}`);\n * }\n * }\n * ```\n */\n async getLeaderboard(\n request: GetLeaderboardRequest\n ): Promise<GetLeaderboardResponse> {\n try {\n return await this.client.getLeaderboard(request);\n } catch (error) {\n throw this.convertError(error);\n }\n }\n\n /**\n * Retrieves a specific player's rank and score information.\n *\n * @param request - The player rank request parameters.\n * @returns The player's rank, score, and percentile information.\n * @throws {AscndError} If the API returns an error.\n *\n * @example\n * ```typescript\n * import { create } from \"@bufbuild/protobuf\";\n * import { GetPlayerRankRequestSchema } from \"@ascnd-gg/client\";\n *\n * const playerRank = await client.getPlayerRank(\n * create(GetPlayerRankRequestSchema, {\n * leaderboardId: \"high-scores\",\n * playerId: \"player-123\",\n * viewSlug: \"platform-pc\",\n * })\n * );\n *\n * if (playerRank.rank !== undefined) {\n * console.log(`Rank: #${playerRank.rank}`);\n * console.log(`Score: ${playerRank.score}`);\n * console.log(`Percentile: ${playerRank.percentile}`);\n * if (playerRank.bracket) {\n * console.log(`Bracket: ${playerRank.bracket.name}`);\n * }\n * if (playerRank.globalRank !== undefined) {\n * console.log(`Global Rank: #${playerRank.globalRank}`);\n * }\n * } else {\n * console.log(\"Player not on leaderboard\");\n * }\n * ```\n */\n async getPlayerRank(\n request: GetPlayerRankRequest\n ): Promise<GetPlayerRankResponse> {\n try {\n return await this.client.getPlayerRank(request);\n } catch (error) {\n throw this.convertError(error);\n }\n }\n\n /**\n * Converts a Connect error to an AscndError.\n */\n private convertError(error: unknown): AscndError {\n if (error instanceof ConnectError) {\n return new AscndError(\n error.message,\n error.code.toString(),\n error.details?.length ? { details: error.details } : undefined\n );\n }\n if (error instanceof Error) {\n return new AscndError(error.message, \"UNKNOWN\");\n }\n return new AscndError(\"Unknown error occurred\", \"UNKNOWN\");\n }\n}\n","// @generated by protoc-gen-es v2.10.2 with parameter \"target=ts\"\n// @generated from file ascnd.proto (package ascnd.v1, syntax proto3)\n/* eslint-disable */\n\nimport type { GenFile, GenMessage, GenService } from \"@bufbuild/protobuf/codegenv2\";\nimport { fileDesc, messageDesc, serviceDesc } from \"@bufbuild/protobuf/codegenv2\";\nimport type { Message } from \"@bufbuild/protobuf\";\n\n/**\n * Describes the file ascnd.proto.\n */\nexport const file_ascnd: GenFile = /*@__PURE__*/\n fileDesc(\"Cgthc2NuZC5wcm90bxIIYXNjbmQudjEipAEKElN1Ym1pdFNjb3JlUmVxdWVzdBIWCg5sZWFkZXJib2FyZF9pZBgBIAEoCRIRCglwbGF5ZXJfaWQYAiABKAkSDQoFc2NvcmUYAyABKAMSFQoIbWV0YWRhdGEYBCABKAxIAIgBARIcCg9pZGVtcG90ZW5jeV9rZXkYBSABKAlIAYgBAUILCglfbWV0YWRhdGFCEgoQX2lkZW1wb3RlbmN5X2tleSKlAQoTU3VibWl0U2NvcmVSZXNwb25zZRIQCghzY29yZV9pZBgBIAEoCRIMCgRyYW5rGAIgASgFEhMKC2lzX25ld19iZXN0GAMgASgIEhgKEHdhc19kZWR1cGxpY2F0ZWQYBCABKAgSMQoJYW50aWNoZWF0GAUgASgLMhkuYXNjbmQudjEuQW50aWNoZWF0UmVzdWx0SACIAQFCDAoKX2FudGljaGVhdCJjCg9BbnRpY2hlYXRSZXN1bHQSDgoGcGFzc2VkGAEgASgIEjAKCnZpb2xhdGlvbnMYAiADKAsyHC5hc2NuZC52MS5BbnRpY2hlYXRWaW9sYXRpb24SDgoGYWN0aW9uGAMgASgJIjcKEkFudGljaGVhdFZpb2xhdGlvbhIRCglmbGFnX3R5cGUYASABKAkSDgoGcmVhc29uGAIgASgJIrMBChVHZXRMZWFkZXJib2FyZFJlcXVlc3QSFgoObGVhZGVyYm9hcmRfaWQYASABKAkSEgoFbGltaXQYAiABKAVIAIgBARITCgZvZmZzZXQYAyABKAVIAYgBARITCgZwZXJpb2QYBCABKAlIAogBARIWCgl2aWV3X3NsdWcYBSABKAlIA4gBAUIICgZfbGltaXRCCQoHX29mZnNldEIJCgdfcGVyaW9kQgwKCl92aWV3X3NsdWci3AEKFkdldExlYWRlcmJvYXJkUmVzcG9uc2USKwoHZW50cmllcxgBIAMoCzIaLmFzY25kLnYxLkxlYWRlcmJvYXJkRW50cnkSFQoNdG90YWxfZW50cmllcxgCIAEoBRIQCghoYXNfbW9yZRgDIAEoCBIUCgxwZXJpb2Rfc3RhcnQYBCABKAkSFwoKcGVyaW9kX2VuZBgFIAEoCUgAiAEBEiUKBHZpZXcYBiABKAsyEi5hc2NuZC52MS5WaWV3SW5mb0gBiAEBQg0KC19wZXJpb2RfZW5kQgcKBV92aWV3IrUBChBMZWFkZXJib2FyZEVudHJ5EgwKBHJhbmsYASABKAUSEQoJcGxheWVyX2lkGAIgASgJEg0KBXNjb3JlGAMgASgDEhQKDHN1Ym1pdHRlZF9hdBgEIAEoCRIVCghtZXRhZGF0YRgFIAEoDEgAiAEBEisKB2JyYWNrZXQYBiABKAsyFS5hc2NuZC52MS5CcmFja2V0SW5mb0gBiAEBQgsKCV9tZXRhZGF0YUIKCghfYnJhY2tldCJFCgtCcmFja2V0SW5mbxIKCgJpZBgBIAEoCRIMCgRuYW1lGAIgASgJEhIKBWNvbG9yGAMgASgJSACIAQFCCAoGX2NvbG9yIiYKCFZpZXdJbmZvEgwKBHNsdWcYASABKAkSDAoEbmFtZRgCIAEoCSKHAQoUR2V0UGxheWVyUmFua1JlcXVlc3QSFgoObGVhZGVyYm9hcmRfaWQYASABKAkSEQoJcGxheWVyX2lkGAIgASgJEhMKBnBlcmlvZBgDIAEoCUgAiAEBEhYKCXZpZXdfc2x1ZxgEIAEoCUgBiAEBQgkKB19wZXJpb2RCDAoKX3ZpZXdfc2x1ZyLLAgoVR2V0UGxheWVyUmFua1Jlc3BvbnNlEhEKBHJhbmsYASABKAVIAIgBARISCgVzY29yZRgCIAEoA0gBiAEBEhcKCmJlc3Rfc2NvcmUYAyABKANIAogBARIVCg10b3RhbF9lbnRyaWVzGAQgASgFEhcKCnBlcmNlbnRpbGUYBSABKAlIA4gBARIrCgdicmFja2V0GAYgASgLMhUuYXNjbmQudjEuQnJhY2tldEluZm9IBIgBARIlCgR2aWV3GAcgASgLMhIuYXNjbmQudjEuVmlld0luZm9IBYgBARIYCgtnbG9iYWxfcmFuaxgIIAEoBUgGiAEBQgcKBV9yYW5rQggKBl9zY29yZUINCgtfYmVzdF9zY29yZUINCgtfcGVyY2VudGlsZUIKCghfYnJhY2tldEIHCgVfdmlld0IOCgxfZ2xvYmFsX3JhbmsygQIKDEFzY25kU2VydmljZRJKCgtTdWJtaXRTY29yZRIcLmFzY25kLnYxLlN1Ym1pdFNjb3JlUmVxdWVzdBodLmFzY25kLnYxLlN1Ym1pdFNjb3JlUmVzcG9uc2USUwoOR2V0TGVhZGVyYm9hcmQSHy5hc2NuZC52MS5HZXRMZWFkZXJib2FyZFJlcXVlc3QaIC5hc2NuZC52MS5HZXRMZWFkZXJib2FyZFJlc3BvbnNlElAKDUdldFBsYXllclJhbmsSHi5hc2NuZC52MS5HZXRQbGF5ZXJSYW5rUmVxdWVzdBofLmFzY25kLnYxLkdldFBsYXllclJhbmtSZXNwb25zZWIGcHJvdG8z\");\n\n/**\n * SubmitScoreRequest contains the score submission details.\n *\n * @generated from message ascnd.v1.SubmitScoreRequest\n */\nexport type SubmitScoreRequest = Message<\"ascnd.v1.SubmitScoreRequest\"> & {\n /**\n * The leaderboard to submit the score to.\n *\n * @generated from field: string leaderboard_id = 1;\n */\n leaderboardId: string;\n\n /**\n * The player's unique identifier (provided by the game).\n *\n * @generated from field: string player_id = 2;\n */\n playerId: string;\n\n /**\n * The score value.\n *\n * @generated from field: int64 score = 3;\n */\n score: bigint;\n\n /**\n * Optional metadata (JSON-encoded game-specific data).\n *\n * @generated from field: optional bytes metadata = 4;\n */\n metadata?: Uint8Array;\n\n /**\n * Optional idempotency key to prevent duplicate submissions.\n *\n * @generated from field: optional string idempotency_key = 5;\n */\n idempotencyKey?: string;\n};\n\n/**\n * Describes the message ascnd.v1.SubmitScoreRequest.\n * Use `create(SubmitScoreRequestSchema)` to create a new message.\n */\nexport const SubmitScoreRequestSchema: GenMessage<SubmitScoreRequest> = /*@__PURE__*/\n messageDesc(file_ascnd, 0);\n\n/**\n * SubmitScoreResponse contains the result of the score submission.\n *\n * @generated from message ascnd.v1.SubmitScoreResponse\n */\nexport type SubmitScoreResponse = Message<\"ascnd.v1.SubmitScoreResponse\"> & {\n /**\n * The unique identifier of the submitted score.\n *\n * @generated from field: string score_id = 1;\n */\n scoreId: string;\n\n /**\n * The player's rank after this submission.\n *\n * @generated from field: int32 rank = 2;\n */\n rank: number;\n\n /**\n * Whether this is the player's new best score for this period.\n *\n * @generated from field: bool is_new_best = 3;\n */\n isNewBest: boolean;\n\n /**\n * Whether the score was deduplicated (already submitted recently).\n *\n * @generated from field: bool was_deduplicated = 4;\n */\n wasDeduplicated: boolean;\n\n /**\n * Anticheat validation result (if anticheat is enabled for this leaderboard).\n *\n * @generated from field: optional ascnd.v1.AnticheatResult anticheat = 5;\n */\n anticheat?: AnticheatResult;\n};\n\n/**\n * Describes the message ascnd.v1.SubmitScoreResponse.\n * Use `create(SubmitScoreResponseSchema)` to create a new message.\n */\nexport const SubmitScoreResponseSchema: GenMessage<SubmitScoreResponse> = /*@__PURE__*/\n messageDesc(file_ascnd, 1);\n\n/**\n * AnticheatResult contains the result of anticheat validation.\n *\n * @generated from message ascnd.v1.AnticheatResult\n */\nexport type AnticheatResult = Message<\"ascnd.v1.AnticheatResult\"> & {\n /**\n * Whether the score passed all anticheat checks.\n *\n * @generated from field: bool passed = 1;\n */\n passed: boolean;\n\n /**\n * List of violation flags that were triggered.\n *\n * @generated from field: repeated ascnd.v1.AnticheatViolation violations = 2;\n */\n violations: AnticheatViolation[];\n\n /**\n * The enforcement action taken: \"none\", \"flag\", \"shadow_ban\", or \"reject\".\n *\n * @generated from field: string action = 3;\n */\n action: string;\n};\n\n/**\n * Describes the message ascnd.v1.AnticheatResult.\n * Use `create(AnticheatResultSchema)` to create a new message.\n */\nexport const AnticheatResultSchema: GenMessage<AnticheatResult> = /*@__PURE__*/\n messageDesc(file_ascnd, 2);\n\n/**\n * AnticheatViolation describes a single anticheat rule violation.\n *\n * @generated from message ascnd.v1.AnticheatViolation\n */\nexport type AnticheatViolation = Message<\"ascnd.v1.AnticheatViolation\"> & {\n /**\n * The type of violation: \"bounds_exceeded\", \"velocity_exceeded\",\n * \"duplicate_idempotency\", \"missing_idempotency_key\".\n *\n * @generated from field: string flag_type = 1;\n */\n flagType: string;\n\n /**\n * Human-readable description of the violation.\n *\n * @generated from field: string reason = 2;\n */\n reason: string;\n};\n\n/**\n * Describes the message ascnd.v1.AnticheatViolation.\n * Use `create(AnticheatViolationSchema)` to create a new message.\n */\nexport const AnticheatViolationSchema: GenMessage<AnticheatViolation> = /*@__PURE__*/\n messageDesc(file_ascnd, 3);\n\n/**\n * GetLeaderboardRequest specifies which leaderboard and page to retrieve.\n *\n * @generated from message ascnd.v1.GetLeaderboardRequest\n */\nexport type GetLeaderboardRequest = Message<\"ascnd.v1.GetLeaderboardRequest\"> & {\n /**\n * The leaderboard to retrieve.\n *\n * @generated from field: string leaderboard_id = 1;\n */\n leaderboardId: string;\n\n /**\n * Maximum number of entries to return (default: 10, max: 100).\n *\n * @generated from field: optional int32 limit = 2;\n */\n limit?: number;\n\n /**\n * Number of entries to skip for pagination.\n *\n * @generated from field: optional int32 offset = 3;\n */\n offset?: number;\n\n /**\n * Which period to retrieve: \"current\", \"previous\", or a timestamp.\n *\n * @generated from field: optional string period = 4;\n */\n period?: string;\n\n /**\n * Optional view slug to filter by metadata criteria.\n * If provided, returns rankings within the view (not global rank).\n *\n * @generated from field: optional string view_slug = 5;\n */\n viewSlug?: string;\n};\n\n/**\n * Describes the message ascnd.v1.GetLeaderboardRequest.\n * Use `create(GetLeaderboardRequestSchema)` to create a new message.\n */\nexport const GetLeaderboardRequestSchema: GenMessage<GetLeaderboardRequest> = /*@__PURE__*/\n messageDesc(file_ascnd, 4);\n\n/**\n * GetLeaderboardResponse contains the leaderboard entries.\n *\n * @generated from message ascnd.v1.GetLeaderboardResponse\n */\nexport type GetLeaderboardResponse = Message<\"ascnd.v1.GetLeaderboardResponse\"> & {\n /**\n * The leaderboard entries.\n *\n * @generated from field: repeated ascnd.v1.LeaderboardEntry entries = 1;\n */\n entries: LeaderboardEntry[];\n\n /**\n * Approximate total number of entries.\n *\n * @generated from field: int32 total_entries = 2;\n */\n totalEntries: number;\n\n /**\n * Whether there are more entries after this page.\n *\n * @generated from field: bool has_more = 3;\n */\n hasMore: boolean;\n\n /**\n * The start of the current period (ISO 8601 timestamp).\n *\n * @generated from field: string period_start = 4;\n */\n periodStart: string;\n\n /**\n * The end of the current period, if applicable (ISO 8601 timestamp).\n *\n * @generated from field: optional string period_end = 5;\n */\n periodEnd?: string;\n\n /**\n * Active view info if filtering by view_slug.\n *\n * @generated from field: optional ascnd.v1.ViewInfo view = 6;\n */\n view?: ViewInfo;\n};\n\n/**\n * Describes the message ascnd.v1.GetLeaderboardResponse.\n * Use `create(GetLeaderboardResponseSchema)` to create a new message.\n */\nexport const GetLeaderboardResponseSchema: GenMessage<GetLeaderboardResponse> = /*@__PURE__*/\n messageDesc(file_ascnd, 5);\n\n/**\n * LeaderboardEntry represents a single entry on the leaderboard.\n *\n * @generated from message ascnd.v1.LeaderboardEntry\n */\nexport type LeaderboardEntry = Message<\"ascnd.v1.LeaderboardEntry\"> & {\n /**\n * The player's rank (1-indexed).\n *\n * @generated from field: int32 rank = 1;\n */\n rank: number;\n\n /**\n * The player's unique identifier.\n *\n * @generated from field: string player_id = 2;\n */\n playerId: string;\n\n /**\n * The player's score.\n *\n * @generated from field: int64 score = 3;\n */\n score: bigint;\n\n /**\n * When the score was submitted (ISO 8601 timestamp).\n *\n * @generated from field: string submitted_at = 4;\n */\n submittedAt: string;\n\n /**\n * Optional metadata associated with the score.\n *\n * @generated from field: optional bytes metadata = 5;\n */\n metadata?: Uint8Array;\n\n /**\n * Optional bracket assignment for this player.\n *\n * @generated from field: optional ascnd.v1.BracketInfo bracket = 6;\n */\n bracket?: BracketInfo;\n};\n\n/**\n * Describes the message ascnd.v1.LeaderboardEntry.\n * Use `create(LeaderboardEntrySchema)` to create a new message.\n */\nexport const LeaderboardEntrySchema: GenMessage<LeaderboardEntry> = /*@__PURE__*/\n messageDesc(file_ascnd, 6);\n\n/**\n * BracketInfo contains minimal bracket information.\n *\n * @generated from message ascnd.v1.BracketInfo\n */\nexport type BracketInfo = Message<\"ascnd.v1.BracketInfo\"> & {\n /**\n * The bracket's unique identifier.\n *\n * @generated from field: string id = 1;\n */\n id: string;\n\n /**\n * The bracket's name (e.g., \"Gold\", \"Silver\", \"Bronze\").\n *\n * @generated from field: string name = 2;\n */\n name: string;\n\n /**\n * Optional hex color code (e.g., \"#FF5500\") for the bracket badge.\n *\n * @generated from field: optional string color = 3;\n */\n color?: string;\n};\n\n/**\n * Describes the message ascnd.v1.BracketInfo.\n * Use `create(BracketInfoSchema)` to create a new message.\n */\nexport const BracketInfoSchema: GenMessage<BracketInfo> = /*@__PURE__*/\n messageDesc(file_ascnd, 7);\n\n/**\n * ViewInfo contains minimal metadata view information.\n *\n * @generated from message ascnd.v1.ViewInfo\n */\nexport type ViewInfo = Message<\"ascnd.v1.ViewInfo\"> & {\n /**\n * The view's slug identifier.\n *\n * @generated from field: string slug = 1;\n */\n slug: string;\n\n /**\n * The view's display name.\n *\n * @generated from field: string name = 2;\n */\n name: string;\n};\n\n/**\n * Describes the message ascnd.v1.ViewInfo.\n * Use `create(ViewInfoSchema)` to create a new message.\n */\nexport const ViewInfoSchema: GenMessage<ViewInfo> = /*@__PURE__*/\n messageDesc(file_ascnd, 8);\n\n/**\n * GetPlayerRankRequest specifies which player and leaderboard to query.\n *\n * @generated from message ascnd.v1.GetPlayerRankRequest\n */\nexport type GetPlayerRankRequest = Message<\"ascnd.v1.GetPlayerRankRequest\"> & {\n /**\n * The leaderboard to query.\n *\n * @generated from field: string leaderboard_id = 1;\n */\n leaderboardId: string;\n\n /**\n * The player's unique identifier.\n *\n * @generated from field: string player_id = 2;\n */\n playerId: string;\n\n /**\n * Which period to query: \"current\", \"previous\", or a timestamp.\n *\n * @generated from field: optional string period = 3;\n */\n period?: string;\n\n /**\n * Optional view slug to get rank within a filtered view.\n *\n * @generated from field: optional string view_slug = 4;\n */\n viewSlug?: string;\n};\n\n/**\n * Describes the message ascnd.v1.GetPlayerRankRequest.\n * Use `create(GetPlayerRankRequestSchema)` to create a new message.\n */\nexport const GetPlayerRankRequestSchema: GenMessage<GetPlayerRankRequest> = /*@__PURE__*/\n messageDesc(file_ascnd, 9);\n\n/**\n * GetPlayerRankResponse contains the player's rank information.\n *\n * @generated from message ascnd.v1.GetPlayerRankResponse\n */\nexport type GetPlayerRankResponse = Message<\"ascnd.v1.GetPlayerRankResponse\"> & {\n /**\n * The player's rank (null if not on leaderboard).\n * When querying a view, this is the rank within the view.\n *\n * @generated from field: optional int32 rank = 1;\n */\n rank?: number;\n\n /**\n * The player's current score (null if not on leaderboard).\n *\n * @generated from field: optional int64 score = 2;\n */\n score?: bigint;\n\n /**\n * The player's best score this period.\n *\n * @generated from field: optional int64 best_score = 3;\n */\n bestScore?: bigint;\n\n /**\n * Total number of entries on this leaderboard (or view if filtered).\n *\n * @generated from field: int32 total_entries = 4;\n */\n totalEntries: number;\n\n /**\n * The player's percentile (e.g., \"top 5%\").\n *\n * @generated from field: optional string percentile = 5;\n */\n percentile?: string;\n\n /**\n * Optional bracket assignment for this player.\n *\n * @generated from field: optional ascnd.v1.BracketInfo bracket = 6;\n */\n bracket?: BracketInfo;\n\n /**\n * Active view info if querying with view_slug.\n *\n * @generated from field: optional ascnd.v1.ViewInfo view = 7;\n */\n view?: ViewInfo;\n\n /**\n * Global rank when querying a view (shows overall position).\n *\n * @generated from field: optional int32 global_rank = 8;\n */\n globalRank?: number;\n};\n\n/**\n * Describes the message ascnd.v1.GetPlayerRankResponse.\n * Use `create(GetPlayerRankResponseSchema)` to create a new message.\n */\nexport const GetPlayerRankResponseSchema: GenMessage<GetPlayerRankResponse> = /*@__PURE__*/\n messageDesc(file_ascnd, 10);\n\n/**\n * AscndService provides leaderboard management for games.\n *\n * @generated from service ascnd.v1.AscndService\n */\nexport const AscndService: GenService<{\n /**\n * SubmitScore records a player's score on a leaderboard.\n *\n * @generated from rpc ascnd.v1.AscndService.SubmitScore\n */\n submitScore: {\n methodKind: \"unary\";\n input: typeof SubmitScoreRequestSchema;\n output: typeof SubmitScoreResponseSchema;\n },\n /**\n * GetLeaderboard retrieves the top scores for a leaderboard.\n *\n * @generated from rpc ascnd.v1.AscndService.GetLeaderboard\n */\n getLeaderboard: {\n methodKind: \"unary\";\n input: typeof GetLeaderboardRequestSchema;\n output: typeof GetLeaderboardResponseSchema;\n },\n /**\n * GetPlayerRank retrieves a specific player's rank and score.\n *\n * @generated from rpc ascnd.v1.AscndService.GetPlayerRank\n */\n getPlayerRank: {\n methodKind: \"unary\";\n input: typeof GetPlayerRankRequestSchema;\n output: typeof GetPlayerRankResponseSchema;\n },\n}> = /*@__PURE__*/\n serviceDesc(file_ascnd, 0);\n\n","/**\n * TypeScript types for the Ascnd gRPC API.\n *\n * Types are generated from the proto file and re-exported here.\n */\n\n// Re-export all generated types\nexport type {\n SubmitScoreRequest,\n SubmitScoreResponse,\n GetLeaderboardRequest,\n GetLeaderboardResponse,\n GetPlayerRankRequest,\n GetPlayerRankResponse,\n LeaderboardEntry,\n AnticheatResult,\n AnticheatViolation,\n BracketInfo,\n ViewInfo,\n} from \"./gen/ascnd_pb.js\";\n\n// Re-export the service definition\nexport { AscndService } from \"./gen/ascnd_pb.js\";\n\n// Re-export schemas for creating messages\nexport {\n SubmitScoreRequestSchema,\n SubmitScoreResponseSchema,\n GetLeaderboardRequestSchema,\n GetLeaderboardResponseSchema,\n GetPlayerRankRequestSchema,\n GetPlayerRankResponseSchema,\n LeaderboardEntrySchema,\n AnticheatResultSchema,\n AnticheatViolationSchema,\n BracketInfoSchema,\n ViewInfoSchema,\n} from \"./gen/ascnd_pb.js\";\n\n// ============================================================================\n// Client Configuration\n// ============================================================================\n\n/**\n * Configuration options for the Ascnd client.\n */\nexport interface AscndClientConfig {\n /** The base URL of the Ascnd API (e.g., \"https://api.ascnd.gg\"). */\n baseUrl: string;\n\n /** Your API key for authentication. */\n apiKey: string;\n\n /** Optional request timeout in milliseconds (default: 30000). */\n timeout?: number;\n}\n\n// ============================================================================\n// Error Types\n// ============================================================================\n\n/**\n * Custom error class for Ascnd API errors.\n */\nexport class AscndError extends Error {\n /** gRPC/Connect error code. */\n readonly code: string;\n\n /** Additional error details. */\n readonly details?: Record<string, unknown>;\n\n constructor(\n message: string,\n code: string,\n details?: Record<string, unknown>\n ) {\n super(message);\n this.name = \"AscndError\";\n this.code = code;\n this.details = details;\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,qBAA0E;AAC1E,yBAAuC;;;ACIvC,uBAAmD;AAM5C,IAAM,aACX,+CAAS,kjFAAkjF;AAgDtjF,IAAM,2BACX,kDAAY,YAAY,CAAC;AAgDpB,IAAM,4BACX,kDAAY,YAAY,CAAC;AAkCpB,IAAM,wBACX,kDAAY,YAAY,CAAC;AA4BpB,IAAM,2BACX,kDAAY,YAAY,CAAC;AAiDpB,IAAM,8BACX,kDAAY,YAAY,CAAC;AAuDpB,IAAM,+BACX,kDAAY,YAAY,CAAC;AAuDpB,IAAM,yBACX,kDAAY,YAAY,CAAC;AAkCpB,IAAM,oBACX,kDAAY,YAAY,CAAC;AA2BpB,IAAM,iBACX,kDAAY,YAAY,CAAC;AAyCpB,IAAM,6BACX,kDAAY,YAAY,CAAC;AAsEpB,IAAM,8BACX,kDAAY,YAAY,EAAE;AAOrB,IAAM,eAgCX,kDAAY,YAAY,CAAC;;;ACvepB,IAAM,aAAN,cAAyB,MAAM;AAAA;AAAA,EAE3B;AAAA;AAAA,EAGA;AAAA,EAET,YACE,SACA,MACA,SACA;AACA,UAAM,OAAO;AACb,SAAK,OAAO;AACZ,SAAK,OAAO;AACZ,SAAK,UAAU;AAAA,EACjB;AACF;;;AF1CO,IAAM,cAAN,MAAkB;AAAA,EACN;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOjB,YAAY,QAA2B;AACrC,QAAI,CAAC,OAAO,SAAS;AACnB,YAAM,IAAI,MAAM,qBAAqB;AAAA,IACvC;AACA,QAAI,CAAC,OAAO,QAAQ;AAClB,YAAM,IAAI,MAAM,oBAAoB;AAAA,IACtC;AAGA,UAAM,kBAA+B,CAAC,SAAS,OAAO,QAAQ;AAC5D,UAAI,OAAO,IAAI,aAAa,OAAO,MAAM;AACzC,aAAO,MAAM,KAAK,GAAG;AAAA,IACvB;AAGA,UAAM,gBAAY,2CAAuB;AAAA,MACvC,SAAS,OAAO,QAAQ,QAAQ,OAAO,EAAE;AAAA,MACzC,cAAc,CAAC,eAAe;AAAA,MAC9B,kBAAkB,OAAO,WAAW;AAAA,IACtC,CAAC;AAED,SAAK,aAAS,6BAAa,cAAc,SAAS;AAAA,EACpD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAgCA,MAAM,YAAY,SAA2D;AAC3E,QAAI;AACF,aAAO,MAAM,KAAK,OAAO,YAAY,OAAO;AAAA,IAC9C,SAAS,OAAO;AACd,YAAM,KAAK,aAAa,KAAK;AAAA,IAC/B;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EA8BA,MAAM,eACJ,SACiC;AACjC,QAAI;AACF,aAAO,MAAM,KAAK,OAAO,eAAe,OAAO;AAAA,IACjD,SAAS,OAAO;AACd,YAAM,KAAK,aAAa,KAAK;AAAA,IAC/B;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAqCA,MAAM,cACJ,SACgC;AAChC,QAAI;AACF,aAAO,MAAM,KAAK,OAAO,cAAc,OAAO;AAAA,IAChD,SAAS,OAAO;AACd,YAAM,KAAK,aAAa,KAAK;AAAA,IAC/B;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKQ,aAAa,OAA4B;AAC/C,QAAI,iBAAiB,6BAAc;AACjC,aAAO,IAAI;AAAA,QACT,MAAM;AAAA,QACN,MAAM,KAAK,SAAS;AAAA,QACpB,MAAM,SAAS,SAAS,EAAE,SAAS,MAAM,QAAQ,IAAI;AAAA,MACvD;AAAA,IACF;AACA,QAAI,iBAAiB,OAAO;AAC1B,aAAO,IAAI,WAAW,MAAM,SAAS,SAAS;AAAA,IAChD;AACA,WAAO,IAAI,WAAW,0BAA0B,SAAS;AAAA,EAC3D;AACF;;;ADjLA,sBAAuB;","names":[]}
|