lxns-rhythm-api 0.1.8 → 0.1.9

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/dist/index.cjs ADDED
@@ -0,0 +1,1336 @@
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
+ ChunithmModels: () => models_exports,
24
+ LxnsApiClient: () => LxnsApiClient,
25
+ LxnsApiError: () => LxnsApiError,
26
+ MaimaiModels: () => models_exports2,
27
+ isAuthError: () => isAuthError,
28
+ isLxnsApiError: () => isLxnsApiError,
29
+ isNotFoundError: () => isNotFoundError,
30
+ isRateLimitError: () => isRateLimitError,
31
+ isServerError: () => isServerError
32
+ });
33
+ module.exports = __toCommonJS(index_exports);
34
+
35
+ // src/api/chunithm/models.ts
36
+ var models_exports = {};
37
+ __export(models_exports, {
38
+ LevelIndex: () => LevelIndex
39
+ });
40
+ var LevelIndex = /* @__PURE__ */ ((LevelIndex3) => {
41
+ LevelIndex3[LevelIndex3["BASIC"] = 0] = "BASIC";
42
+ LevelIndex3[LevelIndex3["ADVANCED"] = 1] = "ADVANCED";
43
+ LevelIndex3[LevelIndex3["EXPERT"] = 2] = "EXPERT";
44
+ LevelIndex3[LevelIndex3["MASTER"] = 3] = "MASTER";
45
+ LevelIndex3[LevelIndex3["ULTIMA"] = 4] = "ULTIMA";
46
+ LevelIndex3[LevelIndex3["WORLDS_END"] = 5] = "WORLDS_END";
47
+ return LevelIndex3;
48
+ })(LevelIndex || {});
49
+
50
+ // src/api/maimai/models.ts
51
+ var models_exports2 = {};
52
+ __export(models_exports2, {
53
+ LevelIndex: () => LevelIndex2
54
+ });
55
+ var LevelIndex2 = /* @__PURE__ */ ((LevelIndex3) => {
56
+ LevelIndex3[LevelIndex3["BASIC"] = 0] = "BASIC";
57
+ LevelIndex3[LevelIndex3["ADVANCED"] = 1] = "ADVANCED";
58
+ LevelIndex3[LevelIndex3["EXPERT"] = 2] = "EXPERT";
59
+ LevelIndex3[LevelIndex3["MASTER"] = 3] = "MASTER";
60
+ LevelIndex3[LevelIndex3["RE_MASTER"] = 4] = "RE_MASTER";
61
+ return LevelIndex3;
62
+ })(LevelIndex2 || {});
63
+
64
+ // node_modules/.pnpm/ky@1.10.0/node_modules/ky/distribution/errors/HTTPError.js
65
+ var HTTPError = class extends Error {
66
+ response;
67
+ request;
68
+ options;
69
+ constructor(response, request, options) {
70
+ const code = response.status || response.status === 0 ? response.status : "";
71
+ const title = response.statusText || "";
72
+ const status = `${code} ${title}`.trim();
73
+ const reason = status ? `status code ${status}` : "an unknown error";
74
+ super(`Request failed with ${reason}: ${request.method} ${request.url}`);
75
+ this.name = "HTTPError";
76
+ this.response = response;
77
+ this.request = request;
78
+ this.options = options;
79
+ }
80
+ };
81
+
82
+ // node_modules/.pnpm/ky@1.10.0/node_modules/ky/distribution/errors/TimeoutError.js
83
+ var TimeoutError = class extends Error {
84
+ request;
85
+ constructor(request) {
86
+ super(`Request timed out: ${request.method} ${request.url}`);
87
+ this.name = "TimeoutError";
88
+ this.request = request;
89
+ }
90
+ };
91
+
92
+ // node_modules/.pnpm/ky@1.10.0/node_modules/ky/distribution/core/constants.js
93
+ var supportsRequestStreams = (() => {
94
+ let duplexAccessed = false;
95
+ let hasContentType = false;
96
+ const supportsReadableStream = typeof globalThis.ReadableStream === "function";
97
+ const supportsRequest = typeof globalThis.Request === "function";
98
+ if (supportsReadableStream && supportsRequest) {
99
+ try {
100
+ hasContentType = new globalThis.Request("https://empty.invalid", {
101
+ body: new globalThis.ReadableStream(),
102
+ method: "POST",
103
+ // @ts-expect-error - Types are outdated.
104
+ get duplex() {
105
+ duplexAccessed = true;
106
+ return "half";
107
+ }
108
+ }).headers.has("Content-Type");
109
+ } catch (error) {
110
+ if (error instanceof Error && error.message === "unsupported BodyInit type") {
111
+ return false;
112
+ }
113
+ throw error;
114
+ }
115
+ }
116
+ return duplexAccessed && !hasContentType;
117
+ })();
118
+ var supportsAbortController = typeof globalThis.AbortController === "function";
119
+ var supportsAbortSignal = typeof globalThis.AbortSignal === "function" && typeof globalThis.AbortSignal.any === "function";
120
+ var supportsResponseStreams = typeof globalThis.ReadableStream === "function";
121
+ var supportsFormData = typeof globalThis.FormData === "function";
122
+ var requestMethods = ["get", "post", "put", "patch", "head", "delete"];
123
+ var validate = () => void 0;
124
+ validate();
125
+ var responseTypes = {
126
+ json: "application/json",
127
+ text: "text/*",
128
+ formData: "multipart/form-data",
129
+ arrayBuffer: "*/*",
130
+ blob: "*/*",
131
+ // Supported in modern Fetch implementations (for example, browsers and recent Node.js/undici).
132
+ // We still feature-check at runtime before exposing the shortcut.
133
+ bytes: "*/*"
134
+ };
135
+ var maxSafeTimeout = 2147483647;
136
+ var usualFormBoundarySize = new TextEncoder().encode("------WebKitFormBoundaryaxpyiPgbbPti10Rw").length;
137
+ var stop = Symbol("stop");
138
+ var kyOptionKeys = {
139
+ json: true,
140
+ parseJson: true,
141
+ stringifyJson: true,
142
+ searchParams: true,
143
+ prefixUrl: true,
144
+ retry: true,
145
+ timeout: true,
146
+ hooks: true,
147
+ throwHttpErrors: true,
148
+ onDownloadProgress: true,
149
+ onUploadProgress: true,
150
+ fetch: true
151
+ };
152
+ var requestOptionsRegistry = {
153
+ method: true,
154
+ headers: true,
155
+ body: true,
156
+ mode: true,
157
+ credentials: true,
158
+ cache: true,
159
+ redirect: true,
160
+ referrer: true,
161
+ referrerPolicy: true,
162
+ integrity: true,
163
+ keepalive: true,
164
+ signal: true,
165
+ window: true,
166
+ dispatcher: true,
167
+ duplex: true,
168
+ priority: true
169
+ };
170
+
171
+ // node_modules/.pnpm/ky@1.10.0/node_modules/ky/distribution/utils/body.js
172
+ var getBodySize = (body) => {
173
+ if (!body) {
174
+ return 0;
175
+ }
176
+ if (body instanceof FormData) {
177
+ let size = 0;
178
+ for (const [key, value] of body) {
179
+ size += usualFormBoundarySize;
180
+ size += new TextEncoder().encode(`Content-Disposition: form-data; name="${key}"`).length;
181
+ size += typeof value === "string" ? new TextEncoder().encode(value).length : value.size;
182
+ }
183
+ return size;
184
+ }
185
+ if (body instanceof Blob) {
186
+ return body.size;
187
+ }
188
+ if (body instanceof ArrayBuffer) {
189
+ return body.byteLength;
190
+ }
191
+ if (typeof body === "string") {
192
+ return new TextEncoder().encode(body).length;
193
+ }
194
+ if (body instanceof URLSearchParams) {
195
+ return new TextEncoder().encode(body.toString()).length;
196
+ }
197
+ if ("byteLength" in body) {
198
+ return body.byteLength;
199
+ }
200
+ if (typeof body === "object" && body !== null) {
201
+ try {
202
+ const jsonString = JSON.stringify(body);
203
+ return new TextEncoder().encode(jsonString).length;
204
+ } catch {
205
+ return 0;
206
+ }
207
+ }
208
+ return 0;
209
+ };
210
+ var streamResponse = (response, onDownloadProgress) => {
211
+ const totalBytes = Number(response.headers.get("content-length")) || 0;
212
+ let transferredBytes = 0;
213
+ if (response.status === 204) {
214
+ if (onDownloadProgress) {
215
+ onDownloadProgress({ percent: 1, totalBytes, transferredBytes }, new Uint8Array());
216
+ }
217
+ return new Response(null, {
218
+ status: response.status,
219
+ statusText: response.statusText,
220
+ headers: response.headers
221
+ });
222
+ }
223
+ return new Response(new ReadableStream({
224
+ async start(controller) {
225
+ const reader = response.body.getReader();
226
+ if (onDownloadProgress) {
227
+ onDownloadProgress({ percent: 0, transferredBytes: 0, totalBytes }, new Uint8Array());
228
+ }
229
+ async function read() {
230
+ const { done, value } = await reader.read();
231
+ if (done) {
232
+ controller.close();
233
+ return;
234
+ }
235
+ if (onDownloadProgress) {
236
+ transferredBytes += value.byteLength;
237
+ const percent = totalBytes === 0 ? 0 : transferredBytes / totalBytes;
238
+ onDownloadProgress({ percent, transferredBytes, totalBytes }, value);
239
+ }
240
+ controller.enqueue(value);
241
+ await read();
242
+ }
243
+ await read();
244
+ }
245
+ }), {
246
+ status: response.status,
247
+ statusText: response.statusText,
248
+ headers: response.headers
249
+ });
250
+ };
251
+ var streamRequest = (request, onUploadProgress, originalBody) => {
252
+ const totalBytes = getBodySize(originalBody ?? request.body);
253
+ let transferredBytes = 0;
254
+ return new Request(request, {
255
+ // @ts-expect-error - Types are outdated.
256
+ duplex: "half",
257
+ body: new ReadableStream({
258
+ async start(controller) {
259
+ const reader = request.body instanceof ReadableStream ? request.body.getReader() : new Response("").body.getReader();
260
+ async function read() {
261
+ const { done, value } = await reader.read();
262
+ if (done) {
263
+ if (onUploadProgress) {
264
+ onUploadProgress({ percent: 1, transferredBytes, totalBytes: Math.max(totalBytes, transferredBytes) }, new Uint8Array());
265
+ }
266
+ controller.close();
267
+ return;
268
+ }
269
+ transferredBytes += value.byteLength;
270
+ let percent = totalBytes === 0 ? 0 : transferredBytes / totalBytes;
271
+ if (totalBytes < transferredBytes || percent === 1) {
272
+ percent = 0.99;
273
+ }
274
+ if (onUploadProgress) {
275
+ onUploadProgress({ percent: Number(percent.toFixed(2)), transferredBytes, totalBytes }, value);
276
+ }
277
+ controller.enqueue(value);
278
+ await read();
279
+ }
280
+ await read();
281
+ }
282
+ })
283
+ });
284
+ };
285
+
286
+ // node_modules/.pnpm/ky@1.10.0/node_modules/ky/distribution/utils/is.js
287
+ var isObject = (value) => value !== null && typeof value === "object";
288
+
289
+ // node_modules/.pnpm/ky@1.10.0/node_modules/ky/distribution/utils/merge.js
290
+ var validateAndMerge = (...sources) => {
291
+ for (const source of sources) {
292
+ if ((!isObject(source) || Array.isArray(source)) && source !== void 0) {
293
+ throw new TypeError("The `options` argument must be an object");
294
+ }
295
+ }
296
+ return deepMerge({}, ...sources);
297
+ };
298
+ var mergeHeaders = (source1 = {}, source2 = {}) => {
299
+ const result = new globalThis.Headers(source1);
300
+ const isHeadersInstance = source2 instanceof globalThis.Headers;
301
+ const source = new globalThis.Headers(source2);
302
+ for (const [key, value] of source.entries()) {
303
+ if (isHeadersInstance && value === "undefined" || value === void 0) {
304
+ result.delete(key);
305
+ } else {
306
+ result.set(key, value);
307
+ }
308
+ }
309
+ return result;
310
+ };
311
+ function newHookValue(original, incoming, property) {
312
+ return Object.hasOwn(incoming, property) && incoming[property] === void 0 ? [] : deepMerge(original[property] ?? [], incoming[property] ?? []);
313
+ }
314
+ var mergeHooks = (original = {}, incoming = {}) => ({
315
+ beforeRequest: newHookValue(original, incoming, "beforeRequest"),
316
+ beforeRetry: newHookValue(original, incoming, "beforeRetry"),
317
+ afterResponse: newHookValue(original, incoming, "afterResponse"),
318
+ beforeError: newHookValue(original, incoming, "beforeError")
319
+ });
320
+ var deepMerge = (...sources) => {
321
+ let returnValue = {};
322
+ let headers = {};
323
+ let hooks = {};
324
+ for (const source of sources) {
325
+ if (Array.isArray(source)) {
326
+ if (!Array.isArray(returnValue)) {
327
+ returnValue = [];
328
+ }
329
+ returnValue = [...returnValue, ...source];
330
+ } else if (isObject(source)) {
331
+ for (let [key, value] of Object.entries(source)) {
332
+ if (isObject(value) && key in returnValue) {
333
+ value = deepMerge(returnValue[key], value);
334
+ }
335
+ returnValue = { ...returnValue, [key]: value };
336
+ }
337
+ if (isObject(source.hooks)) {
338
+ hooks = mergeHooks(hooks, source.hooks);
339
+ returnValue.hooks = hooks;
340
+ }
341
+ if (isObject(source.headers)) {
342
+ headers = mergeHeaders(headers, source.headers);
343
+ returnValue.headers = headers;
344
+ }
345
+ }
346
+ }
347
+ return returnValue;
348
+ };
349
+
350
+ // node_modules/.pnpm/ky@1.10.0/node_modules/ky/distribution/utils/normalize.js
351
+ var normalizeRequestMethod = (input) => requestMethods.includes(input) ? input.toUpperCase() : input;
352
+ var retryMethods = ["get", "put", "head", "delete", "options", "trace"];
353
+ var retryStatusCodes = [408, 413, 429, 500, 502, 503, 504];
354
+ var retryAfterStatusCodes = [413, 429, 503];
355
+ var defaultRetryOptions = {
356
+ limit: 2,
357
+ methods: retryMethods,
358
+ statusCodes: retryStatusCodes,
359
+ afterStatusCodes: retryAfterStatusCodes,
360
+ maxRetryAfter: Number.POSITIVE_INFINITY,
361
+ backoffLimit: Number.POSITIVE_INFINITY,
362
+ delay: (attemptCount) => 0.3 * 2 ** (attemptCount - 1) * 1e3
363
+ };
364
+ var normalizeRetryOptions = (retry = {}) => {
365
+ if (typeof retry === "number") {
366
+ return {
367
+ ...defaultRetryOptions,
368
+ limit: retry
369
+ };
370
+ }
371
+ if (retry.methods && !Array.isArray(retry.methods)) {
372
+ throw new Error("retry.methods must be an array");
373
+ }
374
+ if (retry.statusCodes && !Array.isArray(retry.statusCodes)) {
375
+ throw new Error("retry.statusCodes must be an array");
376
+ }
377
+ return {
378
+ ...defaultRetryOptions,
379
+ ...retry
380
+ };
381
+ };
382
+
383
+ // node_modules/.pnpm/ky@1.10.0/node_modules/ky/distribution/utils/timeout.js
384
+ async function timeout(request, init, abortController, options) {
385
+ return new Promise((resolve, reject) => {
386
+ const timeoutId = setTimeout(() => {
387
+ if (abortController) {
388
+ abortController.abort();
389
+ }
390
+ reject(new TimeoutError(request));
391
+ }, options.timeout);
392
+ void options.fetch(request, init).then(resolve).catch(reject).then(() => {
393
+ clearTimeout(timeoutId);
394
+ });
395
+ });
396
+ }
397
+
398
+ // node_modules/.pnpm/ky@1.10.0/node_modules/ky/distribution/utils/delay.js
399
+ async function delay(ms, { signal }) {
400
+ return new Promise((resolve, reject) => {
401
+ if (signal) {
402
+ signal.throwIfAborted();
403
+ signal.addEventListener("abort", abortHandler, { once: true });
404
+ }
405
+ function abortHandler() {
406
+ clearTimeout(timeoutId);
407
+ reject(signal.reason);
408
+ }
409
+ const timeoutId = setTimeout(() => {
410
+ signal?.removeEventListener("abort", abortHandler);
411
+ resolve();
412
+ }, ms);
413
+ });
414
+ }
415
+
416
+ // node_modules/.pnpm/ky@1.10.0/node_modules/ky/distribution/utils/options.js
417
+ var findUnknownOptions = (request, options) => {
418
+ const unknownOptions = {};
419
+ for (const key in options) {
420
+ if (!(key in requestOptionsRegistry) && !(key in kyOptionKeys) && !(key in request)) {
421
+ unknownOptions[key] = options[key];
422
+ }
423
+ }
424
+ return unknownOptions;
425
+ };
426
+ var hasSearchParameters = (search) => {
427
+ if (search === void 0) {
428
+ return false;
429
+ }
430
+ if (Array.isArray(search)) {
431
+ return search.length > 0;
432
+ }
433
+ if (search instanceof URLSearchParams) {
434
+ return search.size > 0;
435
+ }
436
+ if (typeof search === "object") {
437
+ return Object.keys(search).length > 0;
438
+ }
439
+ if (typeof search === "string") {
440
+ return search.trim().length > 0;
441
+ }
442
+ return Boolean(search);
443
+ };
444
+
445
+ // node_modules/.pnpm/ky@1.10.0/node_modules/ky/distribution/core/Ky.js
446
+ var Ky = class _Ky {
447
+ static create(input, options) {
448
+ const ky2 = new _Ky(input, options);
449
+ const function_ = async () => {
450
+ if (typeof ky2._options.timeout === "number" && ky2._options.timeout > maxSafeTimeout) {
451
+ throw new RangeError(`The \`timeout\` option cannot be greater than ${maxSafeTimeout}`);
452
+ }
453
+ await Promise.resolve();
454
+ let response = await ky2._fetch();
455
+ for (const hook of ky2._options.hooks.afterResponse) {
456
+ const modifiedResponse = await hook(ky2.request, ky2._options, ky2._decorateResponse(response.clone()));
457
+ if (modifiedResponse instanceof globalThis.Response) {
458
+ response = modifiedResponse;
459
+ }
460
+ }
461
+ ky2._decorateResponse(response);
462
+ if (!response.ok && ky2._options.throwHttpErrors) {
463
+ let error = new HTTPError(response, ky2.request, ky2._options);
464
+ for (const hook of ky2._options.hooks.beforeError) {
465
+ error = await hook(error);
466
+ }
467
+ throw error;
468
+ }
469
+ if (ky2._options.onDownloadProgress) {
470
+ if (typeof ky2._options.onDownloadProgress !== "function") {
471
+ throw new TypeError("The `onDownloadProgress` option must be a function");
472
+ }
473
+ if (!supportsResponseStreams) {
474
+ throw new Error("Streams are not supported in your environment. `ReadableStream` is missing.");
475
+ }
476
+ return streamResponse(response.clone(), ky2._options.onDownloadProgress);
477
+ }
478
+ return response;
479
+ };
480
+ const isRetriableMethod = ky2._options.retry.methods.includes(ky2.request.method.toLowerCase());
481
+ const result = (isRetriableMethod ? ky2._retry(function_) : function_()).finally(async () => {
482
+ if (!ky2.request.bodyUsed) {
483
+ await ky2.request.body?.cancel();
484
+ }
485
+ });
486
+ for (const [type, mimeType] of Object.entries(responseTypes)) {
487
+ if (type === "bytes" && typeof globalThis.Response?.prototype?.bytes !== "function") {
488
+ continue;
489
+ }
490
+ result[type] = async () => {
491
+ ky2.request.headers.set("accept", ky2.request.headers.get("accept") || mimeType);
492
+ const response = await result;
493
+ if (type === "json") {
494
+ if (response.status === 204) {
495
+ return "";
496
+ }
497
+ const text = await response.text();
498
+ if (text === "") {
499
+ return "";
500
+ }
501
+ if (options.parseJson) {
502
+ return options.parseJson(text);
503
+ }
504
+ return JSON.parse(text);
505
+ }
506
+ return response[type]();
507
+ };
508
+ }
509
+ return result;
510
+ }
511
+ // eslint-disable-next-line unicorn/prevent-abbreviations
512
+ static #normalizeSearchParams(searchParams) {
513
+ if (searchParams && typeof searchParams === "object" && !Array.isArray(searchParams) && !(searchParams instanceof URLSearchParams)) {
514
+ return Object.fromEntries(Object.entries(searchParams).filter(([, value]) => value !== void 0));
515
+ }
516
+ return searchParams;
517
+ }
518
+ request;
519
+ abortController;
520
+ _retryCount = 0;
521
+ _input;
522
+ _options;
523
+ // eslint-disable-next-line complexity
524
+ constructor(input, options = {}) {
525
+ this._input = input;
526
+ this._options = {
527
+ ...options,
528
+ headers: mergeHeaders(this._input.headers, options.headers),
529
+ hooks: mergeHooks({
530
+ beforeRequest: [],
531
+ beforeRetry: [],
532
+ beforeError: [],
533
+ afterResponse: []
534
+ }, options.hooks),
535
+ method: normalizeRequestMethod(options.method ?? this._input.method ?? "GET"),
536
+ // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
537
+ prefixUrl: String(options.prefixUrl || ""),
538
+ retry: normalizeRetryOptions(options.retry),
539
+ throwHttpErrors: options.throwHttpErrors !== false,
540
+ timeout: options.timeout ?? 1e4,
541
+ fetch: options.fetch ?? globalThis.fetch.bind(globalThis)
542
+ };
543
+ if (typeof this._input !== "string" && !(this._input instanceof URL || this._input instanceof globalThis.Request)) {
544
+ throw new TypeError("`input` must be a string, URL, or Request");
545
+ }
546
+ if (this._options.prefixUrl && typeof this._input === "string") {
547
+ if (this._input.startsWith("/")) {
548
+ throw new Error("`input` must not begin with a slash when using `prefixUrl`");
549
+ }
550
+ if (!this._options.prefixUrl.endsWith("/")) {
551
+ this._options.prefixUrl += "/";
552
+ }
553
+ this._input = this._options.prefixUrl + this._input;
554
+ }
555
+ if (supportsAbortController && supportsAbortSignal) {
556
+ const originalSignal = this._options.signal ?? this._input.signal;
557
+ this.abortController = new globalThis.AbortController();
558
+ this._options.signal = originalSignal ? AbortSignal.any([originalSignal, this.abortController.signal]) : this.abortController.signal;
559
+ }
560
+ if (supportsRequestStreams) {
561
+ this._options.duplex = "half";
562
+ }
563
+ if (this._options.json !== void 0) {
564
+ this._options.body = this._options.stringifyJson?.(this._options.json) ?? JSON.stringify(this._options.json);
565
+ this._options.headers.set("content-type", this._options.headers.get("content-type") ?? "application/json");
566
+ }
567
+ this.request = new globalThis.Request(this._input, this._options);
568
+ if (hasSearchParameters(this._options.searchParams)) {
569
+ const textSearchParams = typeof this._options.searchParams === "string" ? this._options.searchParams.replace(/^\?/, "") : new URLSearchParams(_Ky.#normalizeSearchParams(this._options.searchParams)).toString();
570
+ const searchParams = "?" + textSearchParams;
571
+ const url = this.request.url.replace(/(?:\?.*?)?(?=#|$)/, searchParams);
572
+ if ((supportsFormData && this._options.body instanceof globalThis.FormData || this._options.body instanceof URLSearchParams) && !(this._options.headers && this._options.headers["content-type"])) {
573
+ this.request.headers.delete("content-type");
574
+ }
575
+ this.request = new globalThis.Request(new globalThis.Request(url, { ...this.request }), this._options);
576
+ }
577
+ if (this._options.onUploadProgress) {
578
+ if (typeof this._options.onUploadProgress !== "function") {
579
+ throw new TypeError("The `onUploadProgress` option must be a function");
580
+ }
581
+ if (!supportsRequestStreams) {
582
+ throw new Error("Request streams are not supported in your environment. The `duplex` option for `Request` is not available.");
583
+ }
584
+ const originalBody = this.request.body;
585
+ if (originalBody) {
586
+ this.request = streamRequest(this.request, this._options.onUploadProgress, this._options.body);
587
+ }
588
+ }
589
+ }
590
+ _calculateRetryDelay(error) {
591
+ this._retryCount++;
592
+ if (this._retryCount > this._options.retry.limit || error instanceof TimeoutError) {
593
+ throw error;
594
+ }
595
+ if (error instanceof HTTPError) {
596
+ if (!this._options.retry.statusCodes.includes(error.response.status)) {
597
+ throw error;
598
+ }
599
+ const retryAfter = error.response.headers.get("Retry-After") ?? error.response.headers.get("RateLimit-Reset") ?? error.response.headers.get("X-RateLimit-Reset") ?? error.response.headers.get("X-Rate-Limit-Reset");
600
+ if (retryAfter && this._options.retry.afterStatusCodes.includes(error.response.status)) {
601
+ let after = Number(retryAfter) * 1e3;
602
+ if (Number.isNaN(after)) {
603
+ after = Date.parse(retryAfter) - Date.now();
604
+ } else if (after >= Date.parse("2024-01-01")) {
605
+ after -= Date.now();
606
+ }
607
+ const max = this._options.retry.maxRetryAfter ?? after;
608
+ return after < max ? after : max;
609
+ }
610
+ if (error.response.status === 413) {
611
+ throw error;
612
+ }
613
+ }
614
+ const retryDelay = this._options.retry.delay(this._retryCount);
615
+ return Math.min(this._options.retry.backoffLimit, retryDelay);
616
+ }
617
+ _decorateResponse(response) {
618
+ if (this._options.parseJson) {
619
+ response.json = async () => this._options.parseJson(await response.text());
620
+ }
621
+ return response;
622
+ }
623
+ async _retry(function_) {
624
+ try {
625
+ return await function_();
626
+ } catch (error) {
627
+ const ms = Math.min(this._calculateRetryDelay(error), maxSafeTimeout);
628
+ if (this._retryCount < 1) {
629
+ throw error;
630
+ }
631
+ await delay(ms, { signal: this._options.signal });
632
+ for (const hook of this._options.hooks.beforeRetry) {
633
+ const hookResult = await hook({
634
+ request: this.request,
635
+ options: this._options,
636
+ error,
637
+ retryCount: this._retryCount
638
+ });
639
+ if (hookResult === stop) {
640
+ return;
641
+ }
642
+ }
643
+ return this._retry(function_);
644
+ }
645
+ }
646
+ async _fetch() {
647
+ for (const hook of this._options.hooks.beforeRequest) {
648
+ const result = await hook(this.request, this._options);
649
+ if (result instanceof Request) {
650
+ this.request = result;
651
+ break;
652
+ }
653
+ if (result instanceof Response) {
654
+ return result;
655
+ }
656
+ }
657
+ const nonRequestOptions = findUnknownOptions(this.request, this._options);
658
+ const mainRequest = this.request;
659
+ this.request = mainRequest.clone();
660
+ if (this._options.timeout === false) {
661
+ return this._options.fetch(mainRequest, nonRequestOptions);
662
+ }
663
+ return timeout(mainRequest, nonRequestOptions, this.abortController, this._options);
664
+ }
665
+ };
666
+
667
+ // node_modules/.pnpm/ky@1.10.0/node_modules/ky/distribution/index.js
668
+ var createInstance = (defaults) => {
669
+ const ky2 = (input, options) => Ky.create(input, validateAndMerge(defaults, options));
670
+ for (const method of requestMethods) {
671
+ ky2[method] = (input, options) => Ky.create(input, validateAndMerge(defaults, options, { method }));
672
+ }
673
+ ky2.create = (newDefaults) => createInstance(validateAndMerge(newDefaults));
674
+ ky2.extend = (newDefaults) => {
675
+ if (typeof newDefaults === "function") {
676
+ newDefaults = newDefaults(defaults ?? {});
677
+ }
678
+ return createInstance(validateAndMerge(defaults, newDefaults));
679
+ };
680
+ ky2.stop = stop;
681
+ return ky2;
682
+ };
683
+ var ky = createInstance();
684
+ var distribution_default = ky;
685
+
686
+ // src/api/chunithm/dev.ts
687
+ var ChunithmDevApi = class {
688
+ constructor(http) {
689
+ this.http = http;
690
+ }
691
+ /**
692
+ * 创建或修改玩家信息
693
+ */
694
+ async postPlayer(body) {
695
+ return this.http.post("player", { json: body }).json();
696
+ }
697
+ /**
698
+ * 获取玩家信息(通过好友码)
699
+ */
700
+ async getPlayer(friendCode) {
701
+ return this.http.get(`player/${friendCode}`).json();
702
+ }
703
+ /**
704
+ * 获取玩家信息(通过 QQ 号)
705
+ */
706
+ async getPlayerByQQ(qq) {
707
+ return this.http.get(`player/qq/${qq}`).json();
708
+ }
709
+ /**
710
+ * 获取 Best 30(old 15 + new 15)
711
+ */
712
+ async getBests(friendCode) {
713
+ return this.http.get(`player/${friendCode}/bests`).json();
714
+ }
715
+ /**
716
+ * 获取玩家缓存谱面的最佳成绩
717
+ */
718
+ async getBest(friendCode, options) {
719
+ return this.http.get(`player/${friendCode}/best`, {
720
+ searchParams: {
721
+ song_id: options.songId,
722
+ song_name: options.songName,
723
+ level_index: options.levelIndex
724
+ }
725
+ }).json();
726
+ }
727
+ /**
728
+ * 获取玩家缓存的所有最佳成绩(简化)
729
+ */
730
+ async getAllBestScores(friendCode) {
731
+ return this.http.get(`player/${friendCode}/scores`).json();
732
+ }
733
+ /**
734
+ * 获取 Recent 50
735
+ */
736
+ async getRecents(friendCode) {
737
+ return this.http.get(`player/${friendCode}/recents`).json();
738
+ }
739
+ /**
740
+ * 获取成绩游玩历史记录
741
+ */
742
+ async getScoreHistory(friendCode, options) {
743
+ return this.http.get(`player/${friendCode}/score/history`, {
744
+ searchParams: {
745
+ song_id: options?.songId,
746
+ level_index: options?.levelIndex
747
+ }
748
+ }).json();
749
+ }
750
+ /**
751
+ * 成绩上传热力图
752
+ */
753
+ async getHeatmap(friendCode) {
754
+ return this.http.get(`player/${friendCode}/heatmap`).json();
755
+ }
756
+ /**
757
+ * Rating 趋势
758
+ */
759
+ async getTrend(friendCode, version) {
760
+ return this.http.get(`player/${friendCode}/trend`, { searchParams: { version } }).json();
761
+ }
762
+ /**
763
+ * 获取玩家收藏品进度
764
+ */
765
+ async getCollectionProgress(friendCode, collectionType, collectionId) {
766
+ return this.http.get(`player/${friendCode}/${collectionType}/${collectionId}`).json();
767
+ }
768
+ /**
769
+ * 上传玩家成绩
770
+ */
771
+ async postScores(friendCode, scores) {
772
+ const body = { scores };
773
+ return this.http.post(`player/${friendCode}/scores`, { json: body }).json();
774
+ }
775
+ /**
776
+ * 通过 NET 的 HTML 源代码上传玩家数据
777
+ */
778
+ async postHtml(friendCode, htmlSource) {
779
+ return this.http.post(`player/${friendCode}/html`, {
780
+ body: htmlSource,
781
+ headers: { "content-type": "text/plain" }
782
+ }).json();
783
+ }
784
+ };
785
+
786
+ // src/api/chunithm/personal.ts
787
+ var ChunithmPersonalApi = class {
788
+ constructor(http) {
789
+ this.http = http;
790
+ }
791
+ /**
792
+ * 获取玩家信息
793
+ * GET /api/v0/user/chunithm/player
794
+ */
795
+ async getPlayer() {
796
+ return this.http.get("player").json();
797
+ }
798
+ /**
799
+ * 获取玩家所有成绩
800
+ * GET /api/v0/user/chunithm/player/scores
801
+ */
802
+ async getScores() {
803
+ return this.http.get("scores").json();
804
+ }
805
+ /**
806
+ * 上传玩家成绩
807
+ * POST /api/v0/user/chunithm/player/scores
808
+ */
809
+ async postScores(scores) {
810
+ const body = { scores };
811
+ return this.http.post("scores", { json: body }).json();
812
+ }
813
+ };
814
+
815
+ // src/api/chunithm/public.ts
816
+ var ChunithmPublicApi = class {
817
+ constructor(http) {
818
+ this.http = http;
819
+ }
820
+ /**
821
+ * 获取歌曲列表
822
+ */
823
+ async getSongList(version, notes) {
824
+ return this.http.get("song/list", { searchParams: { version, notes } }).json();
825
+ }
826
+ /**
827
+ * 获取歌曲信息
828
+ */
829
+ async getSong(id, version) {
830
+ return this.http.get(`song/${id}`, { searchParams: { version } }).json();
831
+ }
832
+ /**
833
+ * 获取歌曲别名列表
834
+ */
835
+ async getAliasList() {
836
+ return this.http.get("alias/list").json();
837
+ }
838
+ /**
839
+ * 获取收藏品列表
840
+ */
841
+ async getCollectionList(collectionType, version) {
842
+ const collectionMap = {
843
+ trophy: "trophies",
844
+ character: "characters",
845
+ plate: "plates",
846
+ icon: "icons"
847
+ };
848
+ return this.http.get(`${collectionType}/list`, { searchParams: { version } }).json().then((res) => res[collectionMap[collectionType]]);
849
+ }
850
+ /**
851
+ * 获取收藏品信息
852
+ */
853
+ async getCollectionInfo(collectionType, id, version) {
854
+ return this.http.get(`${collectionType}/${id}`, { searchParams: { version } }).json();
855
+ }
856
+ };
857
+
858
+ // src/api/maimai/dev.ts
859
+ var MaimaiDevApi = class {
860
+ constructor(http) {
861
+ this.http = http;
862
+ }
863
+ /**
864
+ * 创建或修改玩家信息
865
+ * @param body 玩家信息
866
+ * @returns 玩家信息
867
+ */
868
+ async postPlayer(body) {
869
+ return this.http.post("player", { json: body }).json();
870
+ }
871
+ /**
872
+ * 获取玩家信息(通过好友码)
873
+ * @param friendCode 好友码
874
+ * @returns 玩家信息
875
+ */
876
+ async getPlayer(friendCode) {
877
+ return this.http.get(`player/${friendCode}`).json();
878
+ }
879
+ /**
880
+ * 获取玩家信息(通过 QQ 号)
881
+ * @param qq QQ 号
882
+ * @returns 玩家信息
883
+ */
884
+ async getPlayerByQQ(qq) {
885
+ return this.http.get(`player/qq/${qq}`).json();
886
+ }
887
+ /**
888
+ * 获取 Best 50(standard 35 + dx 15)
889
+ * @param friendCode 好友码
890
+ * @returns Best 50
891
+ */
892
+ async getBests(friendCode) {
893
+ return this.http.get(`player/${friendCode}/bests`).json();
894
+ }
895
+ /**
896
+ * 获取 AP 50
897
+ * @param friendCode 好友码
898
+ * @returns AP 50
899
+ */
900
+ async getApBests(friendCode) {
901
+ return this.http.get(`player/${friendCode}/bests/ap`).json();
902
+ }
903
+ /**
904
+ * 获取 Recent 50(仅增量爬取可用)
905
+ * @param friendCode 好友码
906
+ * @returns Recent 50
907
+ */
908
+ async getRecents(friendCode) {
909
+ return this.http.get(`player/${friendCode}/recents`).json();
910
+ }
911
+ /**
912
+ * 获取玩家缓存的所有最佳成绩(简化)
913
+ * @param friendCode 好友码
914
+ * @returns BestScoreList
915
+ */
916
+ async getAllBestScores(friendCode) {
917
+ return this.http.get(`player/${friendCode}/scores`).json();
918
+ }
919
+ /**
920
+ * 成绩上传热力图(YYYY-MM-DD -> 数量)
921
+ * @param friendCode 好友码
922
+ * @returns Heatmap
923
+ */
924
+ async getHeatmap(friendCode) {
925
+ return this.http.get(`player/${friendCode}/heatmap`).json();
926
+ }
927
+ /**
928
+ * DX Rating 趋势
929
+ * @param friendCode 好友码
930
+ * @returns TrendList
931
+ */
932
+ async getTrend(friendCode) {
933
+ return this.http.get(`player/${friendCode}/trend`).json();
934
+ }
935
+ /**
936
+ * 成绩游玩历史记录(仅返回带有 play_time 的成绩)
937
+ * @param friendCode 好友码
938
+ * @returns 游玩历史记录
939
+ */
940
+ async getScoreHistory(friendCode) {
941
+ return this.http.get(`player/${friendCode}/score/history`).json();
942
+ }
943
+ /**
944
+ * 获取玩家收藏品进度
945
+ * @param friendCode 好友码
946
+ * @param collectionType 收藏品类型
947
+ * @param collectionId 收藏品 ID
948
+ * @returns 收藏品进度
949
+ */
950
+ async getCollectionProgress(friendCode, collectionType, collectionId) {
951
+ return this.http.get(`player/${friendCode}/${collectionType}/${collectionId}`).json();
952
+ }
953
+ /**
954
+ * 上传玩家成绩
955
+ * @param friendCode 好友码
956
+ * @param scores 成绩列表
957
+ * @returns 上传结果
958
+ */
959
+ async postScores(friendCode, scores) {
960
+ const body = { scores };
961
+ return this.http.post(`player/${friendCode}/scores`, { json: body }).json();
962
+ }
963
+ /**
964
+ * 通过 NET 的 HTML 源代码上传玩家数据
965
+ * @param friendCode 好友码
966
+ * @param htmlSource HTML 源代码
967
+ * @returns 上传结果
968
+ */
969
+ async postHtml(friendCode, htmlSource) {
970
+ return this.http.post(`player/${friendCode}/html`, {
971
+ body: htmlSource,
972
+ headers: { "content-type": "text/plain" }
973
+ }).json();
974
+ }
975
+ };
976
+
977
+ // src/api/maimai/personal.ts
978
+ var MaimaiPersonalApi = class {
979
+ constructor(http) {
980
+ this.http = http;
981
+ }
982
+ /**
983
+ * 获取玩家信息
984
+ * GET /api/v0/user/maimai/player
985
+ * @returns PlayerInfo
986
+ */
987
+ async getPlayer() {
988
+ return this.http.get("player").json();
989
+ }
990
+ /**
991
+ * 获取玩家所有成绩
992
+ * GET /api/v0/user/maimai/player/scores
993
+ * @returns PlayerScores
994
+ */
995
+ async getScores() {
996
+ return this.http.get("scores").json();
997
+ }
998
+ /**
999
+ * 上传玩家成绩
1000
+ * POST /api/v0/user/maimai/player/scores
1001
+ * @param scores 成绩列表
1002
+ * @returns 上传结果
1003
+ */
1004
+ async postScores(scores) {
1005
+ const body = { scores };
1006
+ return this.http.post("scores", { json: body }).json();
1007
+ }
1008
+ };
1009
+
1010
+ // src/api/maimai/entities/song.ts
1011
+ var LevelArray = [
1012
+ "basic",
1013
+ "advanced",
1014
+ "expert",
1015
+ "master",
1016
+ "remaster"
1017
+ ];
1018
+ function convertDifficulty(difficulty) {
1019
+ const sortedDifficulty = [...difficulty].sort(
1020
+ (a, b) => a.difficulty - b.difficulty
1021
+ );
1022
+ return sortedDifficulty.reduce(
1023
+ (acc, cur) => {
1024
+ acc[cur.difficulty] = cur;
1025
+ acc[LevelArray[cur.difficulty]] = cur;
1026
+ return acc;
1027
+ },
1028
+ {}
1029
+ );
1030
+ }
1031
+ var Song = class {
1032
+ id;
1033
+ title;
1034
+ artist;
1035
+ genre;
1036
+ version;
1037
+ bpm;
1038
+ difficulties;
1039
+ locked;
1040
+ disabled;
1041
+ rights;
1042
+ constructor(song) {
1043
+ this.id = song.id;
1044
+ this.title = song.title;
1045
+ this.artist = song.artist;
1046
+ this.genre = song.genre;
1047
+ this.version = song.version;
1048
+ this.bpm = song.bpm;
1049
+ this.difficulties = song.difficulties;
1050
+ this.locked = song.locked ?? false;
1051
+ this.disabled = song.disabled ?? false;
1052
+ this.rights = song.rights;
1053
+ }
1054
+ get standard() {
1055
+ if (this.difficulties.standard.length === 0) return null;
1056
+ return convertDifficulty(this.difficulties.standard);
1057
+ }
1058
+ get dx() {
1059
+ if (this.difficulties.dx.length === 0) return null;
1060
+ return convertDifficulty(this.difficulties.dx);
1061
+ }
1062
+ get utage() {
1063
+ if (!this.difficulties.utage || this.difficulties.utage.length === 0) {
1064
+ return null;
1065
+ }
1066
+ return this.difficulties.utage;
1067
+ }
1068
+ };
1069
+
1070
+ // src/api/maimai/public.ts
1071
+ var MaimaiPublicApi = class {
1072
+ constructor(http) {
1073
+ this.http = http;
1074
+ }
1075
+ /**
1076
+ * 获取歌曲列表
1077
+ * @param version 版本,不填写遵循api默认行为
1078
+ * @param notes 是否包含谱面信息,不填写遵循api默认行为
1079
+ * @returns 歌曲列表
1080
+ */
1081
+ async getSongList(version, notes) {
1082
+ return this.http.get("song/list?", { searchParams: { version, notes } }).json();
1083
+ }
1084
+ /**
1085
+ * 获取歌曲信息
1086
+ * @param id 歌曲 ID
1087
+ * @returns 歌曲信息
1088
+ */
1089
+ async getSong(id) {
1090
+ const songInfo = await this.http.get(`song/${id}`).json();
1091
+ return new Song(songInfo);
1092
+ }
1093
+ /**
1094
+ * 获取歌曲别名列表
1095
+ * @returns 歌曲别名列表
1096
+ */
1097
+ async getAliasList() {
1098
+ return this.http.get("alias/list").json();
1099
+ }
1100
+ /**
1101
+ * 获取收藏品列表
1102
+ * @param collectionType trophy | icon | plate | frame
1103
+ * @param options.version 版本,不填写遵循api默认行为
1104
+ * @param options.required 是否包含曲目需求,默认值为 false
1105
+ * @returns 收藏品列表
1106
+ */
1107
+ async getCollectionList(collectionType, options) {
1108
+ const collectionMap = {
1109
+ trophy: "trophies",
1110
+ icon: "icons",
1111
+ plate: "plates",
1112
+ frame: "frames"
1113
+ };
1114
+ return this.http.get(`${collectionType}/list`, { searchParams: { ...options } }).json().then((res) => res[collectionMap[collectionType]]);
1115
+ }
1116
+ /**
1117
+ * 获取收藏品信息
1118
+ * @param collectionType trophy | icon | plate | frame
1119
+ * @param id 收藏品 ID
1120
+ * @param options.version 版本,不填写遵循api默认行为
1121
+ * @returns 收藏品信息
1122
+ */
1123
+ async getCollectionInfo(collectionType, id, options) {
1124
+ return this.http.get(`${collectionType}/${id}`, { searchParams: { ...options } }).json();
1125
+ }
1126
+ /**
1127
+ * 获取收藏品分类列表
1128
+ * @param options.version 版本,不填写遵循api默认行为
1129
+ * @returns 收藏品分类列表
1130
+ */
1131
+ async getCollectionGenreList(options) {
1132
+ return this.http.get("collection-genre/list", { searchParams: { ...options } }).json();
1133
+ }
1134
+ /**
1135
+ * 获取收藏品分类信息
1136
+ * @param id 收藏品分类 ID
1137
+ * @param options.version 版本,不填写遵循api默认行为
1138
+ * @returns 收藏品分类信息
1139
+ */
1140
+ async getCollectionGenreInfo(id, options) {
1141
+ return this.http.get(`collection-genre/${id}`, { searchParams: { ...options } }).json();
1142
+ }
1143
+ };
1144
+
1145
+ // src/lxns-api-error.ts
1146
+ var LxnsApiError = class extends Error {
1147
+ code;
1148
+ status;
1149
+ success;
1150
+ data;
1151
+ constructor(payload, status) {
1152
+ super(payload.message ?? `Lxns API unknown error (code: ${payload.code})`);
1153
+ this.name = "LxnsApiError";
1154
+ this.code = payload.code;
1155
+ this.status = status;
1156
+ this.success = payload.success;
1157
+ this.data = payload.data;
1158
+ }
1159
+ };
1160
+ function isLxnsApiError(error) {
1161
+ return error instanceof LxnsApiError;
1162
+ }
1163
+ function isNotFoundError(error) {
1164
+ return isLxnsApiError(error) && (error.status ?? error.code) === 404;
1165
+ }
1166
+ function isAuthError(error) {
1167
+ const code = isLxnsApiError(error) ? error.status ?? error.code : void 0;
1168
+ return code === 401 || code === 403;
1169
+ }
1170
+ function isRateLimitError(error) {
1171
+ return isLxnsApiError(error) && (error.status ?? error.code) === 429;
1172
+ }
1173
+ function isServerError(error) {
1174
+ const code = isLxnsApiError(error) ? error.status ?? error.code : void 0;
1175
+ return typeof code === "number" && code >= 500;
1176
+ }
1177
+
1178
+ // src/client/lxns-api-client.ts
1179
+ function parseLxnsJson(text) {
1180
+ const payload = JSON.parse(text);
1181
+ if (payload.success === false) {
1182
+ throw new LxnsApiError(payload);
1183
+ }
1184
+ if (payload.success === true) {
1185
+ return payload.data;
1186
+ }
1187
+ return payload;
1188
+ }
1189
+ async function wrapKyError(error) {
1190
+ const status = error.response.status;
1191
+ try {
1192
+ const payload = await error.response.clone().json();
1193
+ if (typeof payload.success === "boolean") {
1194
+ throw new LxnsApiError(
1195
+ {
1196
+ success: payload.success,
1197
+ code: payload.code ?? status,
1198
+ message: payload.message ?? error.message,
1199
+ data: payload.data
1200
+ },
1201
+ status
1202
+ );
1203
+ }
1204
+ } catch {
1205
+ }
1206
+ throw new LxnsApiError(
1207
+ {
1208
+ success: false,
1209
+ code: status,
1210
+ message: error.message
1211
+ },
1212
+ status
1213
+ );
1214
+ }
1215
+ var LxnsApiClient = class _LxnsApiClient {
1216
+ config;
1217
+ /**
1218
+ * maimai API
1219
+ */
1220
+ maimai;
1221
+ /**
1222
+ * chunithm API
1223
+ */
1224
+ chunithm;
1225
+ static BASE_OPTIONS = {
1226
+ parseJson: parseLxnsJson,
1227
+ throwHttpErrors: true,
1228
+ hooks: {
1229
+ beforeError: [wrapKyError]
1230
+ }
1231
+ };
1232
+ mountGameApi({
1233
+ public: publicApi,
1234
+ dev,
1235
+ personal,
1236
+ extra
1237
+ }) {
1238
+ const namespace = {
1239
+ public: publicApi,
1240
+ ...extra ?? {}
1241
+ };
1242
+ if (dev) {
1243
+ namespace.dev = dev;
1244
+ }
1245
+ if (personal) {
1246
+ namespace.personal = personal;
1247
+ }
1248
+ return namespace;
1249
+ }
1250
+ constructor(config) {
1251
+ this.config = {
1252
+ ...config,
1253
+ baseURL: config?.baseURL ?? "https://maimai.lxns.net/api/v0/"
1254
+ };
1255
+ const { baseURL, devAccessToken, personalAccessToken } = this.config;
1256
+ const maimaiHttpPublic = distribution_default.create({
1257
+ prefixUrl: new URL("maimai/", baseURL),
1258
+ ..._LxnsApiClient.BASE_OPTIONS
1259
+ });
1260
+ const maimaiHttpDev = devAccessToken ? distribution_default.create({
1261
+ prefixUrl: new URL("maimai/", baseURL),
1262
+ headers: {
1263
+ Authorization: devAccessToken
1264
+ },
1265
+ ..._LxnsApiClient.BASE_OPTIONS
1266
+ }) : void 0;
1267
+ const maimaiHttpPersonal = personalAccessToken ? distribution_default.create({
1268
+ prefixUrl: new URL("user/maimai/", baseURL),
1269
+ headers: {
1270
+ "X-User-Token": personalAccessToken
1271
+ },
1272
+ ..._LxnsApiClient.BASE_OPTIONS
1273
+ }) : void 0;
1274
+ this.maimai = this.mountGameApi({
1275
+ public: new MaimaiPublicApi(maimaiHttpPublic),
1276
+ dev: maimaiHttpDev ? new MaimaiDevApi(maimaiHttpDev) : void 0,
1277
+ personal: maimaiHttpPersonal ? new MaimaiPersonalApi(maimaiHttpPersonal) : void 0,
1278
+ extra: {
1279
+ getAsset: async (type, id) => {
1280
+ return new Uint8Array(
1281
+ await distribution_default.get(
1282
+ `https://assets2.lxns.net/maimai/${type}/${id + (type === "music" ? ".mp3" : ".png")}`
1283
+ ).arrayBuffer()
1284
+ );
1285
+ }
1286
+ }
1287
+ });
1288
+ const chunithmHttpPublic = distribution_default.create({
1289
+ prefixUrl: new URL("chunithm/", baseURL),
1290
+ ..._LxnsApiClient.BASE_OPTIONS
1291
+ });
1292
+ const chunithmHttpDev = devAccessToken ? distribution_default.create({
1293
+ prefixUrl: new URL("chunithm/", baseURL),
1294
+ headers: {
1295
+ Authorization: devAccessToken
1296
+ },
1297
+ ..._LxnsApiClient.BASE_OPTIONS
1298
+ }) : void 0;
1299
+ const chunithmHttpPersonal = personalAccessToken ? distribution_default.create({
1300
+ prefixUrl: new URL("user/chunithm/", baseURL),
1301
+ headers: {
1302
+ "X-User-Token": personalAccessToken
1303
+ },
1304
+ ..._LxnsApiClient.BASE_OPTIONS
1305
+ }) : void 0;
1306
+ this.chunithm = this.mountGameApi({
1307
+ public: new ChunithmPublicApi(chunithmHttpPublic),
1308
+ dev: chunithmHttpDev ? new ChunithmDevApi(chunithmHttpDev) : void 0,
1309
+ personal: chunithmHttpPersonal ? new ChunithmPersonalApi(chunithmHttpPersonal) : void 0,
1310
+ extra: {
1311
+ getAsset: async (type, id) => {
1312
+ return new Uint8Array(
1313
+ await distribution_default.get(`https://assets2.lxns.net/chunithm/${type}/${id}.png`).arrayBuffer()
1314
+ );
1315
+ }
1316
+ }
1317
+ });
1318
+ }
1319
+ };
1320
+ // Annotate the CommonJS export names for ESM import in node:
1321
+ 0 && (module.exports = {
1322
+ ChunithmModels,
1323
+ LxnsApiClient,
1324
+ LxnsApiError,
1325
+ MaimaiModels,
1326
+ isAuthError,
1327
+ isLxnsApiError,
1328
+ isNotFoundError,
1329
+ isRateLimitError,
1330
+ isServerError
1331
+ });
1332
+ /*! Bundled license information:
1333
+
1334
+ ky/distribution/index.js:
1335
+ (*! MIT License © Sindre Sorhus *)
1336
+ */
package/dist/index.d.ts CHANGED
@@ -727,6 +727,18 @@ declare class MaimaiPersonalApi {
727
727
  postScores(scores: Score[]): Promise<unknown>;
728
728
  }
729
729
 
730
+ type DifficultyMap<T> = {
731
+ 0: T;
732
+ 1: T;
733
+ 2: T;
734
+ 3: T;
735
+ 4?: T;
736
+ basic: T;
737
+ advanced: T;
738
+ expert: T;
739
+ master: T;
740
+ remaster?: T;
741
+ };
730
742
  declare class Song implements Song$1 {
731
743
  readonly id: Song$1["id"];
732
744
  readonly title: Song$1["title"];
@@ -739,9 +751,9 @@ declare class Song implements Song$1 {
739
751
  readonly disabled: boolean;
740
752
  readonly rights: Song$1["rights"];
741
753
  constructor(song: Song$1);
742
- get standard(): Record<"basic" | "advanced" | "expert" | "master" | "remaster", SongDifficulty> | null;
743
- get dx(): Record<"basic" | "advanced" | "expert" | "master" | "remaster", SongDifficulty> | null;
744
- get utage(): Record<"basic" | "advanced" | "expert" | "master" | "remaster", SongDifficultyUtage> | null;
754
+ get standard(): DifficultyMap<SongDifficulty> | null;
755
+ get dx(): DifficultyMap<SongDifficulty> | null;
756
+ get utage(): SongDifficultyUtage[] | null;
745
757
  }
746
758
 
747
759
  interface SongList {
package/dist/index.js CHANGED
@@ -369,8 +369,12 @@ var LevelArray = [
369
369
  "remaster"
370
370
  ];
371
371
  function convertDifficulty(difficulty) {
372
- return difficulty.reduce(
372
+ const sortedDifficulty = [...difficulty].sort(
373
+ (a, b) => a.difficulty - b.difficulty
374
+ );
375
+ return sortedDifficulty.reduce(
373
376
  (acc, cur) => {
377
+ acc[cur.difficulty] = cur;
374
378
  acc[LevelArray[cur.difficulty]] = cur;
375
379
  return acc;
376
380
  },
@@ -409,8 +413,10 @@ var Song = class {
409
413
  return convertDifficulty(this.difficulties.dx);
410
414
  }
411
415
  get utage() {
412
- if (!this.difficulties.utage) return null;
413
- return convertDifficulty(this.difficulties.utage);
416
+ if (!this.difficulties.utage || this.difficulties.utage.length === 0) {
417
+ return null;
418
+ }
419
+ return this.difficulties.utage;
414
420
  }
415
421
  };
416
422
 
package/package.json CHANGED
@@ -12,7 +12,7 @@
12
12
  "api-client"
13
13
  ],
14
14
  "author": "amatsuka <amatsukamao@qq.com>",
15
- "version": "0.1.8",
15
+ "version": "0.1.9",
16
16
  "license": "MIT",
17
17
  "repository": {
18
18
  "type": "git",
@@ -20,7 +20,9 @@
20
20
  },
21
21
  "exports": {
22
22
  ".": {
23
- "import": "./dist/index.js"
23
+ "types": "./dist/index.d.ts",
24
+ "import": "./dist/index.js",
25
+ "require": "./dist/index.cjs"
24
26
  },
25
27
  "./package.json": "./package.json"
26
28
  },
@@ -32,7 +34,7 @@
32
34
  ],
33
35
  "packageManager": "pnpm@10.16.1",
34
36
  "type": "module",
35
- "main": "./dist/index.js",
37
+ "main": "./dist/index.cjs",
36
38
  "module": "./dist/index.js",
37
39
  "types": "./dist/index.d.ts",
38
40
  "scripts": {