lxns-rhythm-api 0.1.9 → 0.1.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -72,6 +72,45 @@ const jacket = await client.maimai.getAsset("jacket", 114);
72
72
  const icon = await client.chunithm.getAsset("icon", 1);
73
73
  ```
74
74
 
75
+ ## OAuth 流程
76
+
77
+ ```ts
78
+ import { LxnsOAuthClient } from "lxns-rhythm-api";
79
+
80
+ const client = new LxnsOAuthClient({
81
+ clientId: "<your-client-id>",
82
+ redirectURI: "https://example.com/callback",
83
+ clientSecret: "<your-client-secret>", // PKCE 可不传
84
+ });
85
+
86
+ // 1) 生成授权链接,跳转给用户
87
+ const authorizeURL = client.createAuthorizeURL({
88
+ scope: ["read_user_profile", "read_player"],
89
+ state: "nonce", // 可选
90
+ codeChallenge: "<code-challenge>", // PKCE 可选
91
+ codeChallengeMethod: "S256",
92
+ });
93
+
94
+ // 2) 回调拿到 code 后换 token
95
+ const token = await client.exchangeCodeForToken({
96
+ code: "<auth-code>",
97
+ codeVerifier: "<code-verifier>", // PKCE 模式传这个
98
+ });
99
+
100
+ // 3) 用 access_token 创建授权态客户端
101
+ const authorized = client.createAuthorizedClient(token.access_token);
102
+
103
+ // 4) 调用 OAuth 用户接口和 personal 接口
104
+ const profile = await authorized.user.getProfile();
105
+ const maimaiPlayer = await authorized.maimai.getPlayer();
106
+ const chunithmPlayer = await authorized.chunithm.getPlayer();
107
+
108
+ // 5) 刷新 token
109
+ const nextToken = await client.refreshAccessToken({
110
+ refreshToken: token.refresh_token,
111
+ });
112
+ ```
113
+
75
114
  ## 错误处理
76
115
 
77
116
  SDK 会将 API 错误统一抛为 `LxnsApiError`。
@@ -112,6 +151,7 @@ try {
112
151
  ```ts
113
152
  import {
114
153
  LxnsApiClient,
154
+ LxnsOAuthClient,
115
155
  LxnsApiError,
116
156
  isLxnsApiError,
117
157
  MaimaiModels,
@@ -123,6 +163,8 @@ import {
123
163
 
124
164
  - [Lxns maimai API](https://maimai.lxns.net/docs/api/maimai)
125
165
  - [Lxns chunithm API](https://maimai.lxns.net/docs/api/chunithm)
166
+ - [Lxns OAuth API](https://maimai.lxns.net/docs/api/oauth)
167
+ - [Lxns OAuth 接入指南](https://maimai.lxns.net/docs/oauth-guide)
126
168
 
127
169
  ## 构建与测试
128
170
 
package/dist/index.cjs CHANGED
@@ -23,7 +23,10 @@ __export(index_exports, {
23
23
  ChunithmModels: () => models_exports,
24
24
  LxnsApiClient: () => LxnsApiClient,
25
25
  LxnsApiError: () => LxnsApiError,
26
+ LxnsOAuthClient: () => LxnsOAuthClient,
26
27
  MaimaiModels: () => models_exports2,
28
+ OAuthAuthorizedApi: () => OAuthAuthorizedApi,
29
+ OAuthUserApi: () => OAuthUserApi,
27
30
  isAuthError: () => isAuthError,
28
31
  isLxnsApiError: () => isLxnsApiError,
29
32
  isNotFoundError: () => isNotFoundError,
@@ -61,6 +64,106 @@ var LevelIndex2 = /* @__PURE__ */ ((LevelIndex3) => {
61
64
  return LevelIndex3;
62
65
  })(LevelIndex2 || {});
63
66
 
67
+ // src/api/chunithm/personal-api.ts
68
+ var ChunithmPersonalApi = class {
69
+ constructor(http) {
70
+ this.http = http;
71
+ }
72
+ /**
73
+ * 获取玩家信息
74
+ * GET /api/v0/user/chunithm/player
75
+ */
76
+ async getPlayer() {
77
+ return this.http.get("player").json();
78
+ }
79
+ /**
80
+ * 获取玩家所有成绩
81
+ * GET /api/v0/user/chunithm/player/scores
82
+ */
83
+ async getScores() {
84
+ return this.http.get("scores").json();
85
+ }
86
+ /**
87
+ * 上传玩家成绩
88
+ * POST /api/v0/user/chunithm/player/scores
89
+ */
90
+ async postScores(scores) {
91
+ const body = { scores };
92
+ return this.http.post("scores", { json: body }).json();
93
+ }
94
+ };
95
+
96
+ // src/api/maimai/personal-api.ts
97
+ var MaimaiPersonalApi = class {
98
+ constructor(http) {
99
+ this.http = http;
100
+ }
101
+ /**
102
+ * 获取玩家信息
103
+ * GET /api/v0/user/maimai/player
104
+ * @returns PlayerInfo
105
+ */
106
+ async getPlayer() {
107
+ return this.http.get("player").json();
108
+ }
109
+ /**
110
+ * 获取玩家所有成绩
111
+ * GET /api/v0/user/maimai/player/scores
112
+ * @returns PlayerScores
113
+ */
114
+ async getScores() {
115
+ return this.http.get("scores").json();
116
+ }
117
+ /**
118
+ * 上传玩家成绩
119
+ * POST /api/v0/user/maimai/player/scores
120
+ * @param scores 成绩列表
121
+ * @returns 上传结果
122
+ */
123
+ async postScores(scores) {
124
+ const body = { scores };
125
+ return this.http.post("scores", { json: body }).json();
126
+ }
127
+ };
128
+
129
+ // src/api/oauth/user.ts
130
+ var OAuthUserApi = class {
131
+ constructor(http) {
132
+ this.http = http;
133
+ }
134
+ /**
135
+ * 获取用户基本信息
136
+ * GET /api/v0/user/profile
137
+ * @returns 用户基本信息
138
+ */
139
+ async getProfile() {
140
+ return this.http.get("profile").json();
141
+ }
142
+ /**
143
+ * 获取用户个人 API 密钥
144
+ * GET /api/v0/user/token
145
+ * @returns 用户个人 API 密钥
146
+ */
147
+ async getToken() {
148
+ return this.http.get("token").json();
149
+ }
150
+ };
151
+
152
+ // src/api/oauth/oauth.ts
153
+ var OAuthAuthorizedApi = class {
154
+ /** GET /api/v0/user/profile 与 GET /api/v0/user/token */
155
+ user;
156
+ /** OAuth 授权后可访问的 maimai personal API */
157
+ maimai;
158
+ /** OAuth 授权后可访问的 chunithm personal API */
159
+ chunithm;
160
+ constructor(userHttp, maimaiHttp, chunithmHttp) {
161
+ this.user = new OAuthUserApi(userHttp);
162
+ this.maimai = new MaimaiPersonalApi(maimaiHttp);
163
+ this.chunithm = new ChunithmPersonalApi(chunithmHttp);
164
+ }
165
+ };
166
+
64
167
  // node_modules/.pnpm/ky@1.10.0/node_modules/ky/distribution/errors/HTTPError.js
65
168
  var HTTPError = class extends Error {
66
169
  response;
@@ -683,7 +786,7 @@ var createInstance = (defaults) => {
683
786
  var ky = createInstance();
684
787
  var distribution_default = ky;
685
788
 
686
- // src/api/chunithm/dev.ts
789
+ // src/api/chunithm/dev-api.ts
687
790
  var ChunithmDevApi = class {
688
791
  constructor(http) {
689
792
  this.http = http;
@@ -783,36 +886,7 @@ var ChunithmDevApi = class {
783
886
  }
784
887
  };
785
888
 
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
889
+ // src/api/chunithm/public-api.ts
816
890
  var ChunithmPublicApi = class {
817
891
  constructor(http) {
818
892
  this.http = http;
@@ -855,7 +929,7 @@ var ChunithmPublicApi = class {
855
929
  }
856
930
  };
857
931
 
858
- // src/api/maimai/dev.ts
932
+ // src/api/maimai/dev-api.ts
859
933
  var MaimaiDevApi = class {
860
934
  constructor(http) {
861
935
  this.http = http;
@@ -974,39 +1048,6 @@ var MaimaiDevApi = class {
974
1048
  }
975
1049
  };
976
1050
 
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
1051
  // src/api/maimai/entities/song.ts
1011
1052
  var LevelArray = [
1012
1053
  "basic",
@@ -1067,7 +1108,7 @@ var Song = class {
1067
1108
  }
1068
1109
  };
1069
1110
 
1070
- // src/api/maimai/public.ts
1111
+ // src/api/maimai/public-api.ts
1071
1112
  var MaimaiPublicApi = class {
1072
1113
  constructor(http) {
1073
1114
  this.http = http;
@@ -1175,7 +1216,7 @@ function isServerError(error) {
1175
1216
  return typeof code === "number" && code >= 500;
1176
1217
  }
1177
1218
 
1178
- // src/client/lxns-api-client.ts
1219
+ // src/client/http-options.ts
1179
1220
  function parseLxnsJson(text) {
1180
1221
  const payload = JSON.parse(text);
1181
1222
  if (payload.success === false) {
@@ -1212,7 +1253,16 @@ async function wrapKyError(error) {
1212
1253
  status
1213
1254
  );
1214
1255
  }
1215
- var LxnsApiClient = class _LxnsApiClient {
1256
+ var LXNS_HTTP_OPTIONS = {
1257
+ parseJson: parseLxnsJson,
1258
+ throwHttpErrors: true,
1259
+ hooks: {
1260
+ beforeError: [wrapKyError]
1261
+ }
1262
+ };
1263
+
1264
+ // src/client/lxns-api-client.ts
1265
+ var LxnsApiClient = class {
1216
1266
  config;
1217
1267
  /**
1218
1268
  * maimai API
@@ -1222,13 +1272,6 @@ var LxnsApiClient = class _LxnsApiClient {
1222
1272
  * chunithm API
1223
1273
  */
1224
1274
  chunithm;
1225
- static BASE_OPTIONS = {
1226
- parseJson: parseLxnsJson,
1227
- throwHttpErrors: true,
1228
- hooks: {
1229
- beforeError: [wrapKyError]
1230
- }
1231
- };
1232
1275
  mountGameApi({
1233
1276
  public: publicApi,
1234
1277
  dev,
@@ -1255,21 +1298,21 @@ var LxnsApiClient = class _LxnsApiClient {
1255
1298
  const { baseURL, devAccessToken, personalAccessToken } = this.config;
1256
1299
  const maimaiHttpPublic = distribution_default.create({
1257
1300
  prefixUrl: new URL("maimai/", baseURL),
1258
- ..._LxnsApiClient.BASE_OPTIONS
1301
+ ...LXNS_HTTP_OPTIONS
1259
1302
  });
1260
1303
  const maimaiHttpDev = devAccessToken ? distribution_default.create({
1261
1304
  prefixUrl: new URL("maimai/", baseURL),
1262
1305
  headers: {
1263
1306
  Authorization: devAccessToken
1264
1307
  },
1265
- ..._LxnsApiClient.BASE_OPTIONS
1308
+ ...LXNS_HTTP_OPTIONS
1266
1309
  }) : void 0;
1267
1310
  const maimaiHttpPersonal = personalAccessToken ? distribution_default.create({
1268
1311
  prefixUrl: new URL("user/maimai/", baseURL),
1269
1312
  headers: {
1270
1313
  "X-User-Token": personalAccessToken
1271
1314
  },
1272
- ..._LxnsApiClient.BASE_OPTIONS
1315
+ ...LXNS_HTTP_OPTIONS
1273
1316
  }) : void 0;
1274
1317
  this.maimai = this.mountGameApi({
1275
1318
  public: new MaimaiPublicApi(maimaiHttpPublic),
@@ -1287,21 +1330,21 @@ var LxnsApiClient = class _LxnsApiClient {
1287
1330
  });
1288
1331
  const chunithmHttpPublic = distribution_default.create({
1289
1332
  prefixUrl: new URL("chunithm/", baseURL),
1290
- ..._LxnsApiClient.BASE_OPTIONS
1333
+ ...LXNS_HTTP_OPTIONS
1291
1334
  });
1292
1335
  const chunithmHttpDev = devAccessToken ? distribution_default.create({
1293
1336
  prefixUrl: new URL("chunithm/", baseURL),
1294
1337
  headers: {
1295
1338
  Authorization: devAccessToken
1296
1339
  },
1297
- ..._LxnsApiClient.BASE_OPTIONS
1340
+ ...LXNS_HTTP_OPTIONS
1298
1341
  }) : void 0;
1299
1342
  const chunithmHttpPersonal = personalAccessToken ? distribution_default.create({
1300
1343
  prefixUrl: new URL("user/chunithm/", baseURL),
1301
1344
  headers: {
1302
1345
  "X-User-Token": personalAccessToken
1303
1346
  },
1304
- ..._LxnsApiClient.BASE_OPTIONS
1347
+ ...LXNS_HTTP_OPTIONS
1305
1348
  }) : void 0;
1306
1349
  this.chunithm = this.mountGameApi({
1307
1350
  public: new ChunithmPublicApi(chunithmHttpPublic),
@@ -1317,12 +1360,141 @@ var LxnsApiClient = class _LxnsApiClient {
1317
1360
  });
1318
1361
  }
1319
1362
  };
1363
+
1364
+ // src/client/lxns-oauth-client.ts
1365
+ var LxnsOAuthClient = class {
1366
+ /** 当前 OAuth 客户端配置 */
1367
+ config;
1368
+ http;
1369
+ /**
1370
+ * @param config OAuth 配置
1371
+ */
1372
+ constructor(config) {
1373
+ this.config = {
1374
+ ...config,
1375
+ baseURL: config.baseURL ?? "https://maimai.lxns.net/api/v0/"
1376
+ };
1377
+ this.http = distribution_default.create({
1378
+ prefixUrl: new URL("oauth/", this.config.baseURL),
1379
+ ...LXNS_HTTP_OPTIONS
1380
+ });
1381
+ }
1382
+ /**
1383
+ * 生成 OAuth 授权链接(/oauth/authorize)。
1384
+ * @returns 可直接跳转的授权 URL
1385
+ */
1386
+ createAuthorizeURL({
1387
+ scope,
1388
+ state,
1389
+ codeChallenge,
1390
+ codeChallengeMethod
1391
+ }) {
1392
+ const url = new URL("/oauth/authorize", this.config.baseURL);
1393
+ const search = new URLSearchParams({
1394
+ response_type: "code",
1395
+ client_id: this.config.clientId,
1396
+ redirect_uri: this.config.redirectURI,
1397
+ scope: Array.isArray(scope) ? scope.join(" ") : scope
1398
+ });
1399
+ if (state) {
1400
+ search.set("state", state);
1401
+ }
1402
+ if (codeChallenge) {
1403
+ search.set("code_challenge", codeChallenge);
1404
+ }
1405
+ if (codeChallengeMethod) {
1406
+ search.set("code_challenge_method", codeChallengeMethod);
1407
+ }
1408
+ url.search = search.toString();
1409
+ return url.toString();
1410
+ }
1411
+ /**
1412
+ * 使用授权码换取访问令牌(grant_type=authorization_code)。
1413
+ * - 机密客户端:依赖 constructor 传入的 clientSecret
1414
+ * - PKCE 客户端:通过 codeVerifier 交换
1415
+ */
1416
+ async exchangeCodeForToken({ code, codeVerifier }) {
1417
+ const payload = codeVerifier ? {
1418
+ client_id: this.config.clientId,
1419
+ code,
1420
+ redirect_uri: this.config.redirectURI,
1421
+ code_verifier: codeVerifier
1422
+ } : {
1423
+ client_id: this.config.clientId,
1424
+ code,
1425
+ redirect_uri: this.config.redirectURI,
1426
+ client_secret: this.config.clientSecret ?? ""
1427
+ };
1428
+ if (!codeVerifier && !this.config.clientSecret) {
1429
+ throw new Error(
1430
+ "clientSecret is required when codeVerifier is not provided."
1431
+ );
1432
+ }
1433
+ return this.http.post("token", {
1434
+ json: {
1435
+ ...payload,
1436
+ grant_type: "authorization_code"
1437
+ }
1438
+ }).json();
1439
+ }
1440
+ /**
1441
+ * 使用 refresh_token 刷新访问令牌(grant_type=refresh_token)。
1442
+ */
1443
+ async refreshAccessToken({ refreshToken }) {
1444
+ const payload = {
1445
+ client_id: this.config.clientId,
1446
+ refresh_token: refreshToken,
1447
+ client_secret: this.config.clientSecret
1448
+ };
1449
+ return this.http.post("token", {
1450
+ json: {
1451
+ ...payload,
1452
+ grant_type: "refresh_token"
1453
+ }
1454
+ }).json();
1455
+ }
1456
+ /**
1457
+ * 基于 access_token 创建授权态 API 客户端。
1458
+ */
1459
+ createAuthorizedClient(accessToken) {
1460
+ const authorization = `Bearer ${accessToken}`;
1461
+ const userHttp = distribution_default.create({
1462
+ prefixUrl: new URL("user/", this.config.baseURL),
1463
+ headers: {
1464
+ Authorization: authorization
1465
+ },
1466
+ ...LXNS_HTTP_OPTIONS
1467
+ });
1468
+ const maimaiPersonalHttp = distribution_default.create({
1469
+ prefixUrl: new URL("user/maimai/", this.config.baseURL),
1470
+ headers: {
1471
+ Authorization: authorization
1472
+ },
1473
+ ...LXNS_HTTP_OPTIONS
1474
+ });
1475
+ const chunithmPersonalHttp = distribution_default.create({
1476
+ prefixUrl: new URL("user/chunithm/", this.config.baseURL),
1477
+ headers: {
1478
+ Authorization: authorization
1479
+ },
1480
+ ...LXNS_HTTP_OPTIONS
1481
+ });
1482
+ return new OAuthAuthorizedApi(
1483
+ userHttp,
1484
+ maimaiPersonalHttp,
1485
+ chunithmPersonalHttp
1486
+ );
1487
+ }
1488
+ };
1320
1489
  // Annotate the CommonJS export names for ESM import in node:
1321
1490
  0 && (module.exports = {
1322
1491
  ChunithmModels,
1323
1492
  LxnsApiClient,
1324
1493
  LxnsApiError,
1494
+ LxnsOAuthClient,
1325
1495
  MaimaiModels,
1496
+ OAuthAuthorizedApi,
1497
+ OAuthUserApi,
1326
1498
  isAuthError,
1327
1499
  isLxnsApiError,
1328
1500
  isNotFoundError,
package/dist/index.d.ts CHANGED
@@ -456,6 +456,164 @@ declare namespace models {
456
456
  export { type models_Alias as Alias, type models_AssetType as AssetType, type models_BuddyNotes as BuddyNotes, type models_Collection as Collection, type models_CollectionGenre as CollectionGenre, type models_CollectionRequired as CollectionRequired, type models_CollectionRequiredSong as CollectionRequiredSong, type models_FCType as FCType, type models_FSType as FSType, type models_Frame as Frame, type models_Genre as Genre, type models_Icon as Icon, models_LevelIndex as LevelIndex, type models_NamePlate as NamePlate, type models_Notes as Notes, type models_Player as Player, type models_RateType as RateType, type models_RatingTrend as RatingTrend, type models_Score as Score, type models_SimpleScore as SimpleScore, type Song$1 as Song, type models_SongDifficulties as SongDifficulties, type models_SongDifficulty as SongDifficulty, type models_SongDifficultyUtage as SongDifficultyUtage, type models_SongType as SongType, type models_Trophy as Trophy, type models_Version as Version };
457
457
  }
458
458
 
459
+ type PlayerScores$1 = Score$1[];
460
+
461
+ /**
462
+ * chunithm 个人 API(需用户身份)
463
+ */
464
+ declare class ChunithmPersonalApi {
465
+ readonly http: KyInstance;
466
+ constructor(http: KyInstance);
467
+ /**
468
+ * 获取玩家信息
469
+ * GET /api/v0/user/chunithm/player
470
+ */
471
+ getPlayer(): Promise<Player$1>;
472
+ /**
473
+ * 获取玩家所有成绩
474
+ * GET /api/v0/user/chunithm/player/scores
475
+ */
476
+ getScores(): Promise<PlayerScores$1>;
477
+ /**
478
+ * 上传玩家成绩
479
+ * POST /api/v0/user/chunithm/player/scores
480
+ */
481
+ postScores(scores: Score$1[]): Promise<unknown>;
482
+ }
483
+
484
+ type PlayerScores = Score[];
485
+
486
+ /**
487
+ * maimai 个人 API(需用户身份,路径遵循文档)
488
+ */
489
+ declare class MaimaiPersonalApi {
490
+ readonly http: KyInstance;
491
+ constructor(http: KyInstance);
492
+ /**
493
+ * 获取玩家信息
494
+ * GET /api/v0/user/maimai/player
495
+ * @returns PlayerInfo
496
+ */
497
+ getPlayer(): Promise<Player>;
498
+ /**
499
+ * 获取玩家所有成绩
500
+ * GET /api/v0/user/maimai/player/scores
501
+ * @returns PlayerScores
502
+ */
503
+ getScores(): Promise<PlayerScores>;
504
+ /**
505
+ * 上传玩家成绩
506
+ * POST /api/v0/user/maimai/player/scores
507
+ * @param scores 成绩列表
508
+ * @returns 上传结果
509
+ */
510
+ postScores(scores: Score[]): Promise<unknown>;
511
+ }
512
+
513
+ interface OAuthUserProfile {
514
+ /** 用户 ID */
515
+ id: number;
516
+ /** 用户名 */
517
+ username: string;
518
+ /** 昵称 */
519
+ nickname: string;
520
+ /** 头像 URL */
521
+ avatar: string;
522
+ /** 账号角色 */
523
+ role: string;
524
+ /** 地区 */
525
+ region: string;
526
+ }
527
+ interface OAuthUserToken {
528
+ /** 用户的个人 API 密钥 */
529
+ token: string;
530
+ }
531
+ /**
532
+ * OAuth 用户 API
533
+ */
534
+ declare class OAuthUserApi {
535
+ readonly http: KyInstance;
536
+ constructor(http: KyInstance);
537
+ /**
538
+ * 获取用户基本信息
539
+ * GET /api/v0/user/profile
540
+ * @returns 用户基本信息
541
+ */
542
+ getProfile(): Promise<OAuthUserProfile>;
543
+ /**
544
+ * 获取用户个人 API 密钥
545
+ * GET /api/v0/user/token
546
+ * @returns 用户个人 API 密钥
547
+ */
548
+ getToken(): Promise<OAuthUserToken>;
549
+ }
550
+
551
+ type OAuthAuthorizeCodeTokenRequest = {
552
+ client_id: string;
553
+ code: string;
554
+ redirect_uri: string;
555
+ client_secret: string;
556
+ code_verifier?: never;
557
+ } | {
558
+ client_id: string;
559
+ code: string;
560
+ redirect_uri: string;
561
+ code_verifier: string;
562
+ client_secret?: never;
563
+ };
564
+ interface OAuthRefreshTokenRequest {
565
+ /** OAuth 应用 ID */
566
+ client_id: string;
567
+ /** refresh_token */
568
+ refresh_token: string;
569
+ /** 机密客户端可传,PKCE 可省略 */
570
+ client_secret?: string;
571
+ }
572
+ interface OAuthTokenResponse {
573
+ /** 访问令牌 */
574
+ access_token: string;
575
+ /** 令牌类型(通常为 Bearer) */
576
+ token_type: string;
577
+ /** access_token 过期时间(秒) */
578
+ expires_in: number;
579
+ /** 刷新令牌 */
580
+ refresh_token: string;
581
+ /** 授权范围(空格分隔) */
582
+ scope: string;
583
+ }
584
+ interface OAuthAuthorizeURLParams {
585
+ /** OAuth 应用 ID */
586
+ client_id: string;
587
+ /** 回调地址,必须与应用配置一致 */
588
+ redirect_uri: string;
589
+ /** 权限范围,数组会自动以空格拼接 */
590
+ scope: OAuthScope | OAuthScope[];
591
+ /** 可选状态参数,用于防 CSRF 或携带上下文 */
592
+ state?: string;
593
+ /** PKCE 的 code_challenge(公共客户端推荐) */
594
+ code_challenge?: string;
595
+ /** PKCE 挑战方式,目前固定为 S256 */
596
+ code_challenge_method?: "S256";
597
+ }
598
+ /**
599
+ * OAuth scope。
600
+ * 提供常用 scope 的字面量补全,同时保留 string 扩展以兼容未来新增 scope。
601
+ */
602
+ type OAuthScope = "read_user_profile" | "read_user_token" | "read_player" | "write_player" | (string & {});
603
+ /**
604
+ * OAuth access_token 绑定后的 API 客户端。
605
+ * 使用 Bearer Token 访问用户信息和各游戏 personal 接口。
606
+ */
607
+ declare class OAuthAuthorizedApi {
608
+ /** GET /api/v0/user/profile 与 GET /api/v0/user/token */
609
+ readonly user: OAuthUserApi;
610
+ /** OAuth 授权后可访问的 maimai personal API */
611
+ readonly maimai: MaimaiPersonalApi;
612
+ /** OAuth 授权后可访问的 chunithm personal API */
613
+ readonly chunithm: ChunithmPersonalApi;
614
+ constructor(userHttp: KyInstance, maimaiHttp: KyInstance, chunithmHttp: KyInstance);
615
+ }
616
+
459
617
  interface Bests$1 {
460
618
  bests: Score$1[];
461
619
  selections: Score$1[];
@@ -534,31 +692,6 @@ declare class ChunithmDevApi {
534
692
  postHtml(friendCode: number, htmlSource: string): Promise<unknown>;
535
693
  }
536
694
 
537
- type PlayerScores$1 = Score$1[];
538
-
539
- /**
540
- * chunithm 个人 API(需用户身份)
541
- */
542
- declare class ChunithmPersonalApi {
543
- readonly http: KyInstance;
544
- constructor(http: KyInstance);
545
- /**
546
- * 获取玩家信息
547
- * GET /api/v0/user/chunithm/player
548
- */
549
- getPlayer(): Promise<Player$1>;
550
- /**
551
- * 获取玩家所有成绩
552
- * GET /api/v0/user/chunithm/player/scores
553
- */
554
- getScores(): Promise<PlayerScores$1>;
555
- /**
556
- * 上传玩家成绩
557
- * POST /api/v0/user/chunithm/player/scores
558
- */
559
- postScores(scores: Score$1[]): Promise<unknown>;
560
- }
561
-
562
695
  interface SongList$1 {
563
696
  songs: Song$2[];
564
697
  genres: Genre$1[];
@@ -698,35 +831,6 @@ declare class MaimaiDevApi {
698
831
  postHtml(friendCode: number, htmlSource: string): Promise<unknown>;
699
832
  }
700
833
 
701
- type PlayerScores = Score[];
702
-
703
- /**
704
- * maimai 个人 API(需用户身份,路径遵循文档)
705
- */
706
- declare class MaimaiPersonalApi {
707
- readonly http: KyInstance;
708
- constructor(http: KyInstance);
709
- /**
710
- * 获取玩家信息
711
- * GET /api/v0/user/maimai/player
712
- * @returns PlayerInfo
713
- */
714
- getPlayer(): Promise<Player>;
715
- /**
716
- * 获取玩家所有成绩
717
- * GET /api/v0/user/maimai/player/scores
718
- * @returns PlayerScores
719
- */
720
- getScores(): Promise<PlayerScores>;
721
- /**
722
- * 上传玩家成绩
723
- * POST /api/v0/user/maimai/player/scores
724
- * @param scores 成绩列表
725
- * @returns 上传结果
726
- */
727
- postScores(scores: Score[]): Promise<unknown>;
728
- }
729
-
730
834
  type DifficultyMap<T> = {
731
835
  0: T;
732
836
  1: T;
@@ -878,11 +982,75 @@ declare class LxnsApiClient<O extends LxnsApiClientOptions> {
878
982
  * chunithm API
879
983
  */
880
984
  readonly chunithm: ChunithmApiOf<O>;
881
- private static readonly BASE_OPTIONS;
882
985
  private mountGameApi;
883
986
  constructor(config?: Readonly<O>);
884
987
  }
885
988
 
989
+ interface LxnsOAuthClientOptions {
990
+ /** OAuth 应用的 client_id */
991
+ clientId: string;
992
+ /** OAuth 回调地址,需与应用配置一致 */
993
+ redirectURI: string;
994
+ /** 机密客户端使用的 client_secret;PKCE 场景可不传 */
995
+ clientSecret?: string;
996
+ /** API 基础地址,默认 https://maimai.lxns.net/api/v0/ */
997
+ baseURL?: string;
998
+ }
999
+ interface OAuthAuthorizeURLOptions {
1000
+ /** 申请的权限范围 */
1001
+ scope: OAuthAuthorizeURLParams["scope"];
1002
+ /** 可选状态参数,用于防 CSRF 或携带上下文 */
1003
+ state?: string;
1004
+ /** PKCE code_challenge */
1005
+ codeChallenge?: string;
1006
+ /** PKCE 挑战方式,当前仅支持 S256 */
1007
+ codeChallengeMethod?: OAuthAuthorizeURLParams["code_challenge_method"];
1008
+ }
1009
+ interface OAuthExchangeCodeOptions {
1010
+ /** OAuth 回调返回的 code */
1011
+ code: string;
1012
+ /** PKCE 场景对应的 code_verifier */
1013
+ codeVerifier?: string;
1014
+ }
1015
+ interface OAuthRefreshTokenOptions {
1016
+ /** 刷新令牌 */
1017
+ refreshToken: string;
1018
+ }
1019
+ /**
1020
+ * Lxns OAuth 客户端。
1021
+ * 负责授权链接生成、code 换 token、refresh token,以及基于 access_token 创建授权态 API。
1022
+ */
1023
+ declare class LxnsOAuthClient {
1024
+ /** 当前 OAuth 客户端配置 */
1025
+ readonly config: LxnsOAuthClientOptions & {
1026
+ baseURL: string;
1027
+ };
1028
+ private readonly http;
1029
+ /**
1030
+ * @param config OAuth 配置
1031
+ */
1032
+ constructor(config: Readonly<LxnsOAuthClientOptions>);
1033
+ /**
1034
+ * 生成 OAuth 授权链接(/oauth/authorize)。
1035
+ * @returns 可直接跳转的授权 URL
1036
+ */
1037
+ createAuthorizeURL({ scope, state, codeChallenge, codeChallengeMethod, }: OAuthAuthorizeURLOptions): string;
1038
+ /**
1039
+ * 使用授权码换取访问令牌(grant_type=authorization_code)。
1040
+ * - 机密客户端:依赖 constructor 传入的 clientSecret
1041
+ * - PKCE 客户端:通过 codeVerifier 交换
1042
+ */
1043
+ exchangeCodeForToken({ code, codeVerifier }: OAuthExchangeCodeOptions): Promise<OAuthTokenResponse>;
1044
+ /**
1045
+ * 使用 refresh_token 刷新访问令牌(grant_type=refresh_token)。
1046
+ */
1047
+ refreshAccessToken({ refreshToken }: OAuthRefreshTokenOptions): Promise<OAuthTokenResponse>;
1048
+ /**
1049
+ * 基于 access_token 创建授权态 API 客户端。
1050
+ */
1051
+ createAuthorizedClient(accessToken: string): OAuthAuthorizedApi;
1052
+ }
1053
+
886
1054
  declare class LxnsApiError extends Error {
887
1055
  readonly code: number;
888
1056
  readonly status?: number;
@@ -896,4 +1064,4 @@ declare function isAuthError(error: unknown): error is LxnsApiError;
896
1064
  declare function isRateLimitError(error: unknown): error is LxnsApiError;
897
1065
  declare function isServerError(error: unknown): error is LxnsApiError;
898
1066
 
899
- export { models$1 as ChunithmModels, LxnsApiClient, LxnsApiError, models as MaimaiModels, isAuthError, isLxnsApiError, isNotFoundError, isRateLimitError, isServerError };
1067
+ export { models$1 as ChunithmModels, LxnsApiClient, type LxnsApiClientOptions, LxnsApiError, LxnsOAuthClient, type LxnsOAuthClientOptions, models as MaimaiModels, type OAuthAuthorizeCodeTokenRequest, type OAuthAuthorizeURLOptions, type OAuthAuthorizeURLParams, OAuthAuthorizedApi, type OAuthExchangeCodeOptions, type OAuthRefreshTokenOptions, type OAuthRefreshTokenRequest, type OAuthScope, type OAuthTokenResponse, OAuthUserApi, isAuthError, isLxnsApiError, isNotFoundError, isRateLimitError, isServerError };
package/dist/index.js CHANGED
@@ -33,10 +33,110 @@ var LevelIndex2 = /* @__PURE__ */ ((LevelIndex3) => {
33
33
  return LevelIndex3;
34
34
  })(LevelIndex2 || {});
35
35
 
36
+ // src/api/chunithm/personal-api.ts
37
+ var ChunithmPersonalApi = class {
38
+ constructor(http) {
39
+ this.http = http;
40
+ }
41
+ /**
42
+ * 获取玩家信息
43
+ * GET /api/v0/user/chunithm/player
44
+ */
45
+ async getPlayer() {
46
+ return this.http.get("player").json();
47
+ }
48
+ /**
49
+ * 获取玩家所有成绩
50
+ * GET /api/v0/user/chunithm/player/scores
51
+ */
52
+ async getScores() {
53
+ return this.http.get("scores").json();
54
+ }
55
+ /**
56
+ * 上传玩家成绩
57
+ * POST /api/v0/user/chunithm/player/scores
58
+ */
59
+ async postScores(scores) {
60
+ const body = { scores };
61
+ return this.http.post("scores", { json: body }).json();
62
+ }
63
+ };
64
+
65
+ // src/api/maimai/personal-api.ts
66
+ var MaimaiPersonalApi = class {
67
+ constructor(http) {
68
+ this.http = http;
69
+ }
70
+ /**
71
+ * 获取玩家信息
72
+ * GET /api/v0/user/maimai/player
73
+ * @returns PlayerInfo
74
+ */
75
+ async getPlayer() {
76
+ return this.http.get("player").json();
77
+ }
78
+ /**
79
+ * 获取玩家所有成绩
80
+ * GET /api/v0/user/maimai/player/scores
81
+ * @returns PlayerScores
82
+ */
83
+ async getScores() {
84
+ return this.http.get("scores").json();
85
+ }
86
+ /**
87
+ * 上传玩家成绩
88
+ * POST /api/v0/user/maimai/player/scores
89
+ * @param scores 成绩列表
90
+ * @returns 上传结果
91
+ */
92
+ async postScores(scores) {
93
+ const body = { scores };
94
+ return this.http.post("scores", { json: body }).json();
95
+ }
96
+ };
97
+
98
+ // src/api/oauth/user.ts
99
+ var OAuthUserApi = class {
100
+ constructor(http) {
101
+ this.http = http;
102
+ }
103
+ /**
104
+ * 获取用户基本信息
105
+ * GET /api/v0/user/profile
106
+ * @returns 用户基本信息
107
+ */
108
+ async getProfile() {
109
+ return this.http.get("profile").json();
110
+ }
111
+ /**
112
+ * 获取用户个人 API 密钥
113
+ * GET /api/v0/user/token
114
+ * @returns 用户个人 API 密钥
115
+ */
116
+ async getToken() {
117
+ return this.http.get("token").json();
118
+ }
119
+ };
120
+
121
+ // src/api/oauth/oauth.ts
122
+ var OAuthAuthorizedApi = class {
123
+ /** GET /api/v0/user/profile 与 GET /api/v0/user/token */
124
+ user;
125
+ /** OAuth 授权后可访问的 maimai personal API */
126
+ maimai;
127
+ /** OAuth 授权后可访问的 chunithm personal API */
128
+ chunithm;
129
+ constructor(userHttp, maimaiHttp, chunithmHttp) {
130
+ this.user = new OAuthUserApi(userHttp);
131
+ this.maimai = new MaimaiPersonalApi(maimaiHttp);
132
+ this.chunithm = new ChunithmPersonalApi(chunithmHttp);
133
+ }
134
+ };
135
+
36
136
  // src/client/lxns-api-client.ts
37
137
  import ky from "ky";
38
138
 
39
- // src/api/chunithm/dev.ts
139
+ // src/api/chunithm/dev-api.ts
40
140
  var ChunithmDevApi = class {
41
141
  constructor(http) {
42
142
  this.http = http;
@@ -136,36 +236,7 @@ var ChunithmDevApi = class {
136
236
  }
137
237
  };
138
238
 
139
- // src/api/chunithm/personal.ts
140
- var ChunithmPersonalApi = class {
141
- constructor(http) {
142
- this.http = http;
143
- }
144
- /**
145
- * 获取玩家信息
146
- * GET /api/v0/user/chunithm/player
147
- */
148
- async getPlayer() {
149
- return this.http.get("player").json();
150
- }
151
- /**
152
- * 获取玩家所有成绩
153
- * GET /api/v0/user/chunithm/player/scores
154
- */
155
- async getScores() {
156
- return this.http.get("scores").json();
157
- }
158
- /**
159
- * 上传玩家成绩
160
- * POST /api/v0/user/chunithm/player/scores
161
- */
162
- async postScores(scores) {
163
- const body = { scores };
164
- return this.http.post("scores", { json: body }).json();
165
- }
166
- };
167
-
168
- // src/api/chunithm/public.ts
239
+ // src/api/chunithm/public-api.ts
169
240
  var ChunithmPublicApi = class {
170
241
  constructor(http) {
171
242
  this.http = http;
@@ -208,7 +279,7 @@ var ChunithmPublicApi = class {
208
279
  }
209
280
  };
210
281
 
211
- // src/api/maimai/dev.ts
282
+ // src/api/maimai/dev-api.ts
212
283
  var MaimaiDevApi = class {
213
284
  constructor(http) {
214
285
  this.http = http;
@@ -327,39 +398,6 @@ var MaimaiDevApi = class {
327
398
  }
328
399
  };
329
400
 
330
- // src/api/maimai/personal.ts
331
- var MaimaiPersonalApi = class {
332
- constructor(http) {
333
- this.http = http;
334
- }
335
- /**
336
- * 获取玩家信息
337
- * GET /api/v0/user/maimai/player
338
- * @returns PlayerInfo
339
- */
340
- async getPlayer() {
341
- return this.http.get("player").json();
342
- }
343
- /**
344
- * 获取玩家所有成绩
345
- * GET /api/v0/user/maimai/player/scores
346
- * @returns PlayerScores
347
- */
348
- async getScores() {
349
- return this.http.get("scores").json();
350
- }
351
- /**
352
- * 上传玩家成绩
353
- * POST /api/v0/user/maimai/player/scores
354
- * @param scores 成绩列表
355
- * @returns 上传结果
356
- */
357
- async postScores(scores) {
358
- const body = { scores };
359
- return this.http.post("scores", { json: body }).json();
360
- }
361
- };
362
-
363
401
  // src/api/maimai/entities/song.ts
364
402
  var LevelArray = [
365
403
  "basic",
@@ -420,7 +458,7 @@ var Song = class {
420
458
  }
421
459
  };
422
460
 
423
- // src/api/maimai/public.ts
461
+ // src/api/maimai/public-api.ts
424
462
  var MaimaiPublicApi = class {
425
463
  constructor(http) {
426
464
  this.http = http;
@@ -528,7 +566,7 @@ function isServerError(error) {
528
566
  return typeof code === "number" && code >= 500;
529
567
  }
530
568
 
531
- // src/client/lxns-api-client.ts
569
+ // src/client/http-options.ts
532
570
  function parseLxnsJson(text) {
533
571
  const payload = JSON.parse(text);
534
572
  if (payload.success === false) {
@@ -565,7 +603,16 @@ async function wrapKyError(error) {
565
603
  status
566
604
  );
567
605
  }
568
- var LxnsApiClient = class _LxnsApiClient {
606
+ var LXNS_HTTP_OPTIONS = {
607
+ parseJson: parseLxnsJson,
608
+ throwHttpErrors: true,
609
+ hooks: {
610
+ beforeError: [wrapKyError]
611
+ }
612
+ };
613
+
614
+ // src/client/lxns-api-client.ts
615
+ var LxnsApiClient = class {
569
616
  config;
570
617
  /**
571
618
  * maimai API
@@ -575,13 +622,6 @@ var LxnsApiClient = class _LxnsApiClient {
575
622
  * chunithm API
576
623
  */
577
624
  chunithm;
578
- static BASE_OPTIONS = {
579
- parseJson: parseLxnsJson,
580
- throwHttpErrors: true,
581
- hooks: {
582
- beforeError: [wrapKyError]
583
- }
584
- };
585
625
  mountGameApi({
586
626
  public: publicApi,
587
627
  dev,
@@ -608,21 +648,21 @@ var LxnsApiClient = class _LxnsApiClient {
608
648
  const { baseURL, devAccessToken, personalAccessToken } = this.config;
609
649
  const maimaiHttpPublic = ky.create({
610
650
  prefixUrl: new URL("maimai/", baseURL),
611
- ..._LxnsApiClient.BASE_OPTIONS
651
+ ...LXNS_HTTP_OPTIONS
612
652
  });
613
653
  const maimaiHttpDev = devAccessToken ? ky.create({
614
654
  prefixUrl: new URL("maimai/", baseURL),
615
655
  headers: {
616
656
  Authorization: devAccessToken
617
657
  },
618
- ..._LxnsApiClient.BASE_OPTIONS
658
+ ...LXNS_HTTP_OPTIONS
619
659
  }) : void 0;
620
660
  const maimaiHttpPersonal = personalAccessToken ? ky.create({
621
661
  prefixUrl: new URL("user/maimai/", baseURL),
622
662
  headers: {
623
663
  "X-User-Token": personalAccessToken
624
664
  },
625
- ..._LxnsApiClient.BASE_OPTIONS
665
+ ...LXNS_HTTP_OPTIONS
626
666
  }) : void 0;
627
667
  this.maimai = this.mountGameApi({
628
668
  public: new MaimaiPublicApi(maimaiHttpPublic),
@@ -640,21 +680,21 @@ var LxnsApiClient = class _LxnsApiClient {
640
680
  });
641
681
  const chunithmHttpPublic = ky.create({
642
682
  prefixUrl: new URL("chunithm/", baseURL),
643
- ..._LxnsApiClient.BASE_OPTIONS
683
+ ...LXNS_HTTP_OPTIONS
644
684
  });
645
685
  const chunithmHttpDev = devAccessToken ? ky.create({
646
686
  prefixUrl: new URL("chunithm/", baseURL),
647
687
  headers: {
648
688
  Authorization: devAccessToken
649
689
  },
650
- ..._LxnsApiClient.BASE_OPTIONS
690
+ ...LXNS_HTTP_OPTIONS
651
691
  }) : void 0;
652
692
  const chunithmHttpPersonal = personalAccessToken ? ky.create({
653
693
  prefixUrl: new URL("user/chunithm/", baseURL),
654
694
  headers: {
655
695
  "X-User-Token": personalAccessToken
656
696
  },
657
- ..._LxnsApiClient.BASE_OPTIONS
697
+ ...LXNS_HTTP_OPTIONS
658
698
  }) : void 0;
659
699
  this.chunithm = this.mountGameApi({
660
700
  public: new ChunithmPublicApi(chunithmHttpPublic),
@@ -670,11 +710,141 @@ var LxnsApiClient = class _LxnsApiClient {
670
710
  });
671
711
  }
672
712
  };
713
+
714
+ // src/client/lxns-oauth-client.ts
715
+ import ky2 from "ky";
716
+ var LxnsOAuthClient = class {
717
+ /** 当前 OAuth 客户端配置 */
718
+ config;
719
+ http;
720
+ /**
721
+ * @param config OAuth 配置
722
+ */
723
+ constructor(config) {
724
+ this.config = {
725
+ ...config,
726
+ baseURL: config.baseURL ?? "https://maimai.lxns.net/api/v0/"
727
+ };
728
+ this.http = ky2.create({
729
+ prefixUrl: new URL("oauth/", this.config.baseURL),
730
+ ...LXNS_HTTP_OPTIONS
731
+ });
732
+ }
733
+ /**
734
+ * 生成 OAuth 授权链接(/oauth/authorize)。
735
+ * @returns 可直接跳转的授权 URL
736
+ */
737
+ createAuthorizeURL({
738
+ scope,
739
+ state,
740
+ codeChallenge,
741
+ codeChallengeMethod
742
+ }) {
743
+ const url = new URL("/oauth/authorize", this.config.baseURL);
744
+ const search = new URLSearchParams({
745
+ response_type: "code",
746
+ client_id: this.config.clientId,
747
+ redirect_uri: this.config.redirectURI,
748
+ scope: Array.isArray(scope) ? scope.join(" ") : scope
749
+ });
750
+ if (state) {
751
+ search.set("state", state);
752
+ }
753
+ if (codeChallenge) {
754
+ search.set("code_challenge", codeChallenge);
755
+ }
756
+ if (codeChallengeMethod) {
757
+ search.set("code_challenge_method", codeChallengeMethod);
758
+ }
759
+ url.search = search.toString();
760
+ return url.toString();
761
+ }
762
+ /**
763
+ * 使用授权码换取访问令牌(grant_type=authorization_code)。
764
+ * - 机密客户端:依赖 constructor 传入的 clientSecret
765
+ * - PKCE 客户端:通过 codeVerifier 交换
766
+ */
767
+ async exchangeCodeForToken({ code, codeVerifier }) {
768
+ const payload = codeVerifier ? {
769
+ client_id: this.config.clientId,
770
+ code,
771
+ redirect_uri: this.config.redirectURI,
772
+ code_verifier: codeVerifier
773
+ } : {
774
+ client_id: this.config.clientId,
775
+ code,
776
+ redirect_uri: this.config.redirectURI,
777
+ client_secret: this.config.clientSecret ?? ""
778
+ };
779
+ if (!codeVerifier && !this.config.clientSecret) {
780
+ throw new Error(
781
+ "clientSecret is required when codeVerifier is not provided."
782
+ );
783
+ }
784
+ return this.http.post("token", {
785
+ json: {
786
+ ...payload,
787
+ grant_type: "authorization_code"
788
+ }
789
+ }).json();
790
+ }
791
+ /**
792
+ * 使用 refresh_token 刷新访问令牌(grant_type=refresh_token)。
793
+ */
794
+ async refreshAccessToken({ refreshToken }) {
795
+ const payload = {
796
+ client_id: this.config.clientId,
797
+ refresh_token: refreshToken,
798
+ client_secret: this.config.clientSecret
799
+ };
800
+ return this.http.post("token", {
801
+ json: {
802
+ ...payload,
803
+ grant_type: "refresh_token"
804
+ }
805
+ }).json();
806
+ }
807
+ /**
808
+ * 基于 access_token 创建授权态 API 客户端。
809
+ */
810
+ createAuthorizedClient(accessToken) {
811
+ const authorization = `Bearer ${accessToken}`;
812
+ const userHttp = ky2.create({
813
+ prefixUrl: new URL("user/", this.config.baseURL),
814
+ headers: {
815
+ Authorization: authorization
816
+ },
817
+ ...LXNS_HTTP_OPTIONS
818
+ });
819
+ const maimaiPersonalHttp = ky2.create({
820
+ prefixUrl: new URL("user/maimai/", this.config.baseURL),
821
+ headers: {
822
+ Authorization: authorization
823
+ },
824
+ ...LXNS_HTTP_OPTIONS
825
+ });
826
+ const chunithmPersonalHttp = ky2.create({
827
+ prefixUrl: new URL("user/chunithm/", this.config.baseURL),
828
+ headers: {
829
+ Authorization: authorization
830
+ },
831
+ ...LXNS_HTTP_OPTIONS
832
+ });
833
+ return new OAuthAuthorizedApi(
834
+ userHttp,
835
+ maimaiPersonalHttp,
836
+ chunithmPersonalHttp
837
+ );
838
+ }
839
+ };
673
840
  export {
674
841
  models_exports as ChunithmModels,
675
842
  LxnsApiClient,
676
843
  LxnsApiError,
844
+ LxnsOAuthClient,
677
845
  models_exports2 as MaimaiModels,
846
+ OAuthAuthorizedApi,
847
+ OAuthUserApi,
678
848
  isAuthError,
679
849
  isLxnsApiError,
680
850
  isNotFoundError,
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.9",
15
+ "version": "0.1.10",
16
16
  "license": "MIT",
17
17
  "repository": {
18
18
  "type": "git",