@yuuko1410/feishu-bitable 0.0.4 → 0.0.5

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
@@ -17,6 +17,7 @@
17
17
  - 支持小文件直接上传
18
18
  - 支持大文件自动切换分片上传
19
19
  - 支持文件下载
20
+ - 支持 H5 OAuth 登录(生成登录链接、code 换 token、获取用户信息)
20
21
  - 支持字段值归一化
21
22
  - 支持批量请求自动分片
22
23
  - 支持并发控制
@@ -33,11 +34,13 @@ packages/feishu-bitable/
33
34
  ├── tsconfig.build.json
34
35
  ├── src/
35
36
  │ ├── client.ts
37
+ │ ├── auth.ts
36
38
  │ ├── errors.ts
37
39
  │ ├── index.ts
38
40
  │ ├── types.ts
39
41
  │ └── utils.ts
40
42
  ├── test/
43
+ │ ├── auth.test.ts
41
44
  │ └── bitable.test.ts
42
45
  └── lib/ # bun run build 后生成
43
46
  ```
@@ -81,6 +84,7 @@ npm install @yuuko1410/feishu-bitable
81
84
  主导出:
82
85
 
83
86
  - `Bitable`
87
+ - `FeishuOAuthClient`
84
88
  - `FeishuBitableError`
85
89
  - `AppType`
86
90
  - `Domain`
@@ -89,6 +93,11 @@ npm install @yuuko1410/feishu-bitable
89
93
  类型导出:
90
94
 
91
95
  - `BitableConstructorOptions`
96
+ - `FeishuOAuthConstructorOptions`
97
+ - `BuildOAuthLoginUrlOptions`
98
+ - `OAuthTokenInfo`
99
+ - `OAuthUserInfo`
100
+ - `OAuthCallbackResult`
92
101
  - `FetchAllRecordsOptions`
93
102
  - `BatchOperationOptions`
94
103
  - `UpdateRecordsOptions`
@@ -141,6 +150,7 @@ const bitable = Bitable.fromEnv();
141
150
  - `FEISHU_APP_ID`
142
151
  - `FEISHU_APP_SECRET`
143
152
  - `FEISHU_APP_TOKEN`
153
+ - `FEISHU_OAUTH_REDIRECT_URI`(OAuth 登录回调地址)
144
154
 
145
155
  ## 构造参数说明
146
156
 
@@ -156,6 +166,15 @@ const bitable = Bitable.fromEnv();
156
166
  - `defaultConcurrency`:默认并发数,默认 `1`
157
167
  - `sdkClient`:可注入已创建的官方 SDK client,便于测试或复用
158
168
 
169
+ `FeishuOAuthConstructorOptions` 支持:
170
+
171
+ - `appId`:飞书应用 `app_id`
172
+ - `appSecret`:飞书应用 `app_secret`
173
+ - `redirectUri`:OAuth 回调地址
174
+ - `domain`:飞书 SDK `Domain`
175
+ - `sdkClient`:可注入官方 SDK client
176
+ - `logger`:可注入日志器;传 `null` 可禁用日志
177
+
159
178
  ## 公开 API
160
179
 
161
180
  ### 1. `fetchAllRecords`
@@ -352,6 +371,84 @@ const buffer = await bitable.downloadFile("file_token");
352
371
  const buffer = await bitable.downLoadFile("file_token");
353
372
  ```
354
373
 
374
+ ### 9. `FeishuOAuthClient.buildLoginUrl`
375
+
376
+ 生成 H5 飞书登录链接。
377
+
378
+ ```ts
379
+ import { FeishuOAuthClient } from "@yuuko1410/feishu-bitable";
380
+
381
+ const oauth = FeishuOAuthClient.fromEnv();
382
+ const loginUrl = oauth.buildLoginUrl({
383
+ state: "csrf_state_123",
384
+ scope: ["contact:user.base:readonly"],
385
+ });
386
+ ```
387
+
388
+ ### 10. `FeishuOAuthClient.exchangeCodeForUserToken`
389
+
390
+ 后端使用回调 `code` 换取 `user_access_token`。
391
+
392
+ ```ts
393
+ const token = await oauth.exchangeCodeForUserToken(code);
394
+ ```
395
+
396
+ ### 11. `FeishuOAuthClient.getUserInfo`
397
+
398
+ 使用 `user_access_token` 拉取飞书用户信息。
399
+
400
+ ```ts
401
+ const user = await oauth.getUserInfo(token.access_token);
402
+ ```
403
+
404
+ ### 12. `FeishuOAuthClient.handleCallback`
405
+
406
+ 最简回调处理:一键完成 `code -> token -> user`。
407
+
408
+ ```ts
409
+ const result = await oauth.handleCallback(code);
410
+ // result.token
411
+ // result.user
412
+ ```
413
+
414
+ ## H5 登录最简接入
415
+
416
+ 前端按钮跳转:
417
+
418
+ ```ts
419
+ window.location.href = "/api/auth/feishu/login";
420
+ ```
421
+
422
+ 后端示例(Express/SvelteKit 思路一致):
423
+
424
+ ```ts
425
+ import { FeishuOAuthClient } from "@yuuko1410/feishu-bitable";
426
+
427
+ const oauth = FeishuOAuthClient.fromEnv();
428
+
429
+ export async function loginHandler() {
430
+ const state = crypto.randomUUID();
431
+ const loginUrl = oauth.buildLoginUrl({
432
+ state,
433
+ scope: ["contact:user.base:readonly"],
434
+ });
435
+
436
+ return Response.redirect(loginUrl, 302);
437
+ }
438
+
439
+ export async function callbackHandler(code: string) {
440
+ const { token, user } = await oauth.handleCallback(code);
441
+
442
+ // 这里创建你自己的业务登录态(session/JWT)
443
+ // 不建议把 user_access_token 暴露给前端
444
+ return {
445
+ uid: user.open_id ?? user.user_id,
446
+ name: user.name,
447
+ tokenType: token.token_type,
448
+ };
449
+ }
450
+ ```
451
+
355
452
  ## 自动行为说明
356
453
 
357
454
  ### 自动分页
@@ -449,6 +546,9 @@ await bitable.deleteList("table_id", ["recyyy"]);
449
546
  - 数组式批量更新到官方 payload 的转换
450
547
  - 官方批量更新 payload 透传
451
548
  - 文件下载
549
+ - OAuth 登录链接生成
550
+ - OAuth code 换 token
551
+ - OAuth 回调获取用户信息
452
552
  - 凭证缺失报错
453
553
 
454
554
  ## 发布
package/lib/auth.d.ts ADDED
@@ -0,0 +1,20 @@
1
+ import * as lark from "@larksuiteoapi/node-sdk";
2
+ import type { BitableLogger, BuildOAuthLoginUrlOptions, FeishuOAuthConstructorOptions, OAuthCallbackResult, OAuthTokenInfo, OAuthUserInfo } from "./types";
3
+ type OAuthSdkClient = Pick<lark.Client, "authen">;
4
+ export declare class FeishuOAuthClient {
5
+ readonly appId: string;
6
+ readonly appSecret: string;
7
+ readonly redirectUri?: string;
8
+ readonly domain: lark.Domain | string;
9
+ readonly logger: BitableLogger | null;
10
+ readonly client: OAuthSdkClient;
11
+ constructor(options?: FeishuOAuthConstructorOptions);
12
+ static fromEnv(env?: NodeJS.ProcessEnv): FeishuOAuthClient;
13
+ buildLoginUrl(options: BuildOAuthLoginUrlOptions): string;
14
+ exchangeCodeForUserToken(code: string): Promise<OAuthTokenInfo>;
15
+ refreshUserAccessToken(refreshToken: string): Promise<OAuthTokenInfo>;
16
+ getUserInfo(userAccessToken: string): Promise<OAuthUserInfo>;
17
+ handleCallback(code: string): Promise<OAuthCallbackResult>;
18
+ private logInfo;
19
+ }
20
+ export {};
package/lib/index.cjs CHANGED
@@ -65,6 +65,7 @@ var exports_src = {};
65
65
  __export(exports_src, {
66
66
  default: () => src_default,
67
67
  LoggerLevel: () => import_node_sdk.LoggerLevel,
68
+ FeishuOAuthClient: () => FeishuOAuthClient,
68
69
  FeishuBitableError: () => FeishuBitableError,
69
70
  Domain: () => import_node_sdk.Domain,
70
71
  Bitable: () => Bitable,
@@ -675,6 +676,183 @@ class Bitable {
675
676
  }
676
677
  }
677
678
 
679
+ // src/auth.ts
680
+ var lark2 = __toESM(require("@larksuiteoapi/node-sdk"));
681
+ var DEFAULT_LOGGER2 = {
682
+ info(message, meta) {
683
+ if (meta) {
684
+ console.info(`[feishu-bitable] ${message}`, meta);
685
+ return;
686
+ }
687
+ console.info(`[feishu-bitable] ${message}`);
688
+ },
689
+ warn(message, meta) {
690
+ if (meta) {
691
+ console.warn(`[feishu-bitable] ${message}`, meta);
692
+ return;
693
+ }
694
+ console.warn(`[feishu-bitable] ${message}`);
695
+ },
696
+ error(message, meta) {
697
+ if (meta) {
698
+ console.error(`[feishu-bitable] ${message}`, meta);
699
+ return;
700
+ }
701
+ console.error(`[feishu-bitable] ${message}`);
702
+ }
703
+ };
704
+
705
+ class FeishuOAuthClient {
706
+ appId;
707
+ appSecret;
708
+ redirectUri;
709
+ domain;
710
+ logger;
711
+ client;
712
+ constructor(options = {}) {
713
+ const appId = options.appId ?? process.env.FEISHU_APP_ID;
714
+ const appSecret = options.appSecret ?? process.env.FEISHU_APP_SECRET;
715
+ if (!appId || !appSecret) {
716
+ throw new FeishuBitableError("appId and appSecret are required. Pass them in constructor or provide FEISHU_APP_ID and FEISHU_APP_SECRET.");
717
+ }
718
+ this.appId = appId;
719
+ this.appSecret = appSecret;
720
+ this.redirectUri = options.redirectUri;
721
+ this.domain = options.domain ?? lark2.Domain.Feishu;
722
+ this.logger = options.logger === null ? null : options.logger ?? DEFAULT_LOGGER2;
723
+ this.client = options.sdkClient ?? new lark2.Client({
724
+ appId: this.appId,
725
+ appSecret: this.appSecret,
726
+ appType: lark2.AppType.SelfBuild,
727
+ domain: this.domain
728
+ });
729
+ }
730
+ static fromEnv(env = process.env) {
731
+ return new FeishuOAuthClient({
732
+ appId: env.FEISHU_APP_ID,
733
+ appSecret: env.FEISHU_APP_SECRET,
734
+ redirectUri: env.FEISHU_OAUTH_REDIRECT_URI
735
+ });
736
+ }
737
+ buildLoginUrl(options) {
738
+ const redirectUri = options.redirectUri ?? this.redirectUri;
739
+ if (!redirectUri) {
740
+ throw new FeishuBitableError("redirectUri is required. Pass it into constructor or buildLoginUrl options.");
741
+ }
742
+ const scope = Array.isArray(options.scope) ? options.scope.join(" ") : options.scope;
743
+ const url = new URL("/open-apis/authen/v1/authorize", resolveOpenDomain(this.domain));
744
+ url.searchParams.set("app_id", this.appId);
745
+ url.searchParams.set("redirect_uri", redirectUri);
746
+ url.searchParams.set("state", options.state);
747
+ if (scope?.trim()) {
748
+ url.searchParams.set("scope", scope.trim());
749
+ }
750
+ this.logInfo("oauth buildLoginUrl", {
751
+ redirectUri,
752
+ hasScope: Boolean(scope),
753
+ state: options.state
754
+ });
755
+ return url.toString();
756
+ }
757
+ async exchangeCodeForUserToken(code) {
758
+ if (!code.trim()) {
759
+ throw new FeishuBitableError("code is required to exchange user token.");
760
+ }
761
+ this.logInfo("oauth exchangeCodeForUserToken started");
762
+ const response = assertFeishuResponse(await this.client.authen.v1.oidcAccessToken.create({
763
+ data: {
764
+ grant_type: "authorization_code",
765
+ code
766
+ }
767
+ }), "oauth exchange code");
768
+ const data = response.data;
769
+ if (!data?.access_token || !data.token_type) {
770
+ throw new FeishuBitableError("oauth exchange code failed: missing token fields.", {
771
+ details: response
772
+ });
773
+ }
774
+ const token = {
775
+ access_token: data.access_token,
776
+ refresh_token: data.refresh_token,
777
+ token_type: data.token_type,
778
+ expires_in: data.expires_in,
779
+ refresh_expires_in: data.refresh_expires_in,
780
+ scope: data.scope
781
+ };
782
+ this.logInfo("oauth exchangeCodeForUserToken succeeded", {
783
+ hasRefreshToken: Boolean(token.refresh_token),
784
+ expiresIn: token.expires_in
785
+ });
786
+ return token;
787
+ }
788
+ async refreshUserAccessToken(refreshToken) {
789
+ if (!refreshToken.trim()) {
790
+ throw new FeishuBitableError("refreshToken is required to refresh user token.");
791
+ }
792
+ this.logInfo("oauth refreshUserAccessToken started");
793
+ const response = assertFeishuResponse(await this.client.authen.v1.oidcRefreshAccessToken.create({
794
+ data: {
795
+ grant_type: "refresh_token",
796
+ refresh_token: refreshToken
797
+ }
798
+ }), "oauth refresh token");
799
+ const data = response.data;
800
+ if (!data?.access_token || !data.token_type) {
801
+ throw new FeishuBitableError("oauth refresh token failed: missing token fields.", {
802
+ details: response
803
+ });
804
+ }
805
+ const token = {
806
+ access_token: data.access_token,
807
+ refresh_token: data.refresh_token,
808
+ token_type: data.token_type,
809
+ expires_in: data.expires_in,
810
+ refresh_expires_in: data.refresh_expires_in,
811
+ scope: data.scope
812
+ };
813
+ this.logInfo("oauth refreshUserAccessToken succeeded", {
814
+ hasRefreshToken: Boolean(token.refresh_token),
815
+ expiresIn: token.expires_in
816
+ });
817
+ return token;
818
+ }
819
+ async getUserInfo(userAccessToken) {
820
+ if (!userAccessToken.trim()) {
821
+ throw new FeishuBitableError("userAccessToken is required to get user info.");
822
+ }
823
+ this.logInfo("oauth getUserInfo started");
824
+ const response = assertFeishuResponse(await this.client.authen.v1.userInfo.get({}, lark2.withUserAccessToken(userAccessToken)), "oauth get user info");
825
+ this.logInfo("oauth getUserInfo succeeded", {
826
+ openId: response.data?.open_id,
827
+ userId: response.data?.user_id
828
+ });
829
+ return response.data ?? {};
830
+ }
831
+ async handleCallback(code) {
832
+ const token = await this.exchangeCodeForUserToken(code);
833
+ const user = await this.getUserInfo(token.access_token);
834
+ return { token, user };
835
+ }
836
+ logInfo(message, meta) {
837
+ this.logger?.info(message, meta);
838
+ }
839
+ }
840
+ function resolveOpenDomain(domain) {
841
+ if (domain === lark2.Domain.Lark) {
842
+ return "https://open.larksuite.com";
843
+ }
844
+ if (domain === lark2.Domain.Feishu) {
845
+ return "https://open.feishu.cn";
846
+ }
847
+ if (typeof domain === "string") {
848
+ if (domain.includes("larksuite")) {
849
+ return "https://open.larksuite.com";
850
+ }
851
+ return "https://open.feishu.cn";
852
+ }
853
+ return "https://open.feishu.cn";
854
+ }
855
+
678
856
  // src/index.ts
679
857
  var import_node_sdk = require("@larksuiteoapi/node-sdk");
680
858
  var src_default = Bitable;
package/lib/index.d.ts CHANGED
@@ -1,6 +1,8 @@
1
1
  import { Bitable } from "./client";
2
+ import { FeishuOAuthClient } from "./auth";
2
3
  export { Bitable };
4
+ export { FeishuOAuthClient };
3
5
  export { FeishuBitableError } from "./errors";
4
6
  export { AppType, Domain, LoggerLevel } from "@larksuiteoapi/node-sdk";
5
- export type { BatchOperationOptions, BitableBatchUpdatePayload, BitableBatchUpdateResponse, BitableConstructorOptions, BitableFieldValue, BitableFilterCondition, BitableFilterGroup, BitableInsertRecord, BitableLocationValue, BitableLogger, BitableMemberValue, BitableRecord, BitableRecordFields, BitableSort, BitableTextValue, BitableUpdateRecord, FetchAllRecordsOptions, MediaParentType, UpdateRecordsOptions, UploadFileOptions, UploadableFile, } from "./types";
7
+ export type { BatchOperationOptions, BitableBatchUpdatePayload, BitableBatchUpdateResponse, BitableConstructorOptions, BitableFieldValue, BitableFilterCondition, BitableFilterGroup, BitableInsertRecord, BitableLocationValue, BitableLogger, BitableMemberValue, BitableRecord, BitableRecordFields, BitableSort, BitableTextValue, BitableUpdateRecord, BuildOAuthLoginUrlOptions, FetchAllRecordsOptions, FeishuOAuthConstructorOptions, MediaParentType, OAuthCallbackResult, OAuthTokenInfo, OAuthUserInfo, UpdateRecordsOptions, UploadFileOptions, UploadableFile, } from "./types";
6
8
  export default Bitable;
package/lib/index.js CHANGED
@@ -601,14 +601,192 @@ class Bitable {
601
601
  }
602
602
  }
603
603
 
604
+ // src/auth.ts
605
+ import * as lark2 from "@larksuiteoapi/node-sdk";
606
+ var DEFAULT_LOGGER2 = {
607
+ info(message, meta) {
608
+ if (meta) {
609
+ console.info(`[feishu-bitable] ${message}`, meta);
610
+ return;
611
+ }
612
+ console.info(`[feishu-bitable] ${message}`);
613
+ },
614
+ warn(message, meta) {
615
+ if (meta) {
616
+ console.warn(`[feishu-bitable] ${message}`, meta);
617
+ return;
618
+ }
619
+ console.warn(`[feishu-bitable] ${message}`);
620
+ },
621
+ error(message, meta) {
622
+ if (meta) {
623
+ console.error(`[feishu-bitable] ${message}`, meta);
624
+ return;
625
+ }
626
+ console.error(`[feishu-bitable] ${message}`);
627
+ }
628
+ };
629
+
630
+ class FeishuOAuthClient {
631
+ appId;
632
+ appSecret;
633
+ redirectUri;
634
+ domain;
635
+ logger;
636
+ client;
637
+ constructor(options = {}) {
638
+ const appId = options.appId ?? process.env.FEISHU_APP_ID;
639
+ const appSecret = options.appSecret ?? process.env.FEISHU_APP_SECRET;
640
+ if (!appId || !appSecret) {
641
+ throw new FeishuBitableError("appId and appSecret are required. Pass them in constructor or provide FEISHU_APP_ID and FEISHU_APP_SECRET.");
642
+ }
643
+ this.appId = appId;
644
+ this.appSecret = appSecret;
645
+ this.redirectUri = options.redirectUri;
646
+ this.domain = options.domain ?? lark2.Domain.Feishu;
647
+ this.logger = options.logger === null ? null : options.logger ?? DEFAULT_LOGGER2;
648
+ this.client = options.sdkClient ?? new lark2.Client({
649
+ appId: this.appId,
650
+ appSecret: this.appSecret,
651
+ appType: lark2.AppType.SelfBuild,
652
+ domain: this.domain
653
+ });
654
+ }
655
+ static fromEnv(env = process.env) {
656
+ return new FeishuOAuthClient({
657
+ appId: env.FEISHU_APP_ID,
658
+ appSecret: env.FEISHU_APP_SECRET,
659
+ redirectUri: env.FEISHU_OAUTH_REDIRECT_URI
660
+ });
661
+ }
662
+ buildLoginUrl(options) {
663
+ const redirectUri = options.redirectUri ?? this.redirectUri;
664
+ if (!redirectUri) {
665
+ throw new FeishuBitableError("redirectUri is required. Pass it into constructor or buildLoginUrl options.");
666
+ }
667
+ const scope = Array.isArray(options.scope) ? options.scope.join(" ") : options.scope;
668
+ const url = new URL("/open-apis/authen/v1/authorize", resolveOpenDomain(this.domain));
669
+ url.searchParams.set("app_id", this.appId);
670
+ url.searchParams.set("redirect_uri", redirectUri);
671
+ url.searchParams.set("state", options.state);
672
+ if (scope?.trim()) {
673
+ url.searchParams.set("scope", scope.trim());
674
+ }
675
+ this.logInfo("oauth buildLoginUrl", {
676
+ redirectUri,
677
+ hasScope: Boolean(scope),
678
+ state: options.state
679
+ });
680
+ return url.toString();
681
+ }
682
+ async exchangeCodeForUserToken(code) {
683
+ if (!code.trim()) {
684
+ throw new FeishuBitableError("code is required to exchange user token.");
685
+ }
686
+ this.logInfo("oauth exchangeCodeForUserToken started");
687
+ const response = assertFeishuResponse(await this.client.authen.v1.oidcAccessToken.create({
688
+ data: {
689
+ grant_type: "authorization_code",
690
+ code
691
+ }
692
+ }), "oauth exchange code");
693
+ const data = response.data;
694
+ if (!data?.access_token || !data.token_type) {
695
+ throw new FeishuBitableError("oauth exchange code failed: missing token fields.", {
696
+ details: response
697
+ });
698
+ }
699
+ const token = {
700
+ access_token: data.access_token,
701
+ refresh_token: data.refresh_token,
702
+ token_type: data.token_type,
703
+ expires_in: data.expires_in,
704
+ refresh_expires_in: data.refresh_expires_in,
705
+ scope: data.scope
706
+ };
707
+ this.logInfo("oauth exchangeCodeForUserToken succeeded", {
708
+ hasRefreshToken: Boolean(token.refresh_token),
709
+ expiresIn: token.expires_in
710
+ });
711
+ return token;
712
+ }
713
+ async refreshUserAccessToken(refreshToken) {
714
+ if (!refreshToken.trim()) {
715
+ throw new FeishuBitableError("refreshToken is required to refresh user token.");
716
+ }
717
+ this.logInfo("oauth refreshUserAccessToken started");
718
+ const response = assertFeishuResponse(await this.client.authen.v1.oidcRefreshAccessToken.create({
719
+ data: {
720
+ grant_type: "refresh_token",
721
+ refresh_token: refreshToken
722
+ }
723
+ }), "oauth refresh token");
724
+ const data = response.data;
725
+ if (!data?.access_token || !data.token_type) {
726
+ throw new FeishuBitableError("oauth refresh token failed: missing token fields.", {
727
+ details: response
728
+ });
729
+ }
730
+ const token = {
731
+ access_token: data.access_token,
732
+ refresh_token: data.refresh_token,
733
+ token_type: data.token_type,
734
+ expires_in: data.expires_in,
735
+ refresh_expires_in: data.refresh_expires_in,
736
+ scope: data.scope
737
+ };
738
+ this.logInfo("oauth refreshUserAccessToken succeeded", {
739
+ hasRefreshToken: Boolean(token.refresh_token),
740
+ expiresIn: token.expires_in
741
+ });
742
+ return token;
743
+ }
744
+ async getUserInfo(userAccessToken) {
745
+ if (!userAccessToken.trim()) {
746
+ throw new FeishuBitableError("userAccessToken is required to get user info.");
747
+ }
748
+ this.logInfo("oauth getUserInfo started");
749
+ const response = assertFeishuResponse(await this.client.authen.v1.userInfo.get({}, lark2.withUserAccessToken(userAccessToken)), "oauth get user info");
750
+ this.logInfo("oauth getUserInfo succeeded", {
751
+ openId: response.data?.open_id,
752
+ userId: response.data?.user_id
753
+ });
754
+ return response.data ?? {};
755
+ }
756
+ async handleCallback(code) {
757
+ const token = await this.exchangeCodeForUserToken(code);
758
+ const user = await this.getUserInfo(token.access_token);
759
+ return { token, user };
760
+ }
761
+ logInfo(message, meta) {
762
+ this.logger?.info(message, meta);
763
+ }
764
+ }
765
+ function resolveOpenDomain(domain) {
766
+ if (domain === lark2.Domain.Lark) {
767
+ return "https://open.larksuite.com";
768
+ }
769
+ if (domain === lark2.Domain.Feishu) {
770
+ return "https://open.feishu.cn";
771
+ }
772
+ if (typeof domain === "string") {
773
+ if (domain.includes("larksuite")) {
774
+ return "https://open.larksuite.com";
775
+ }
776
+ return "https://open.feishu.cn";
777
+ }
778
+ return "https://open.feishu.cn";
779
+ }
780
+
604
781
  // src/index.ts
605
- import { AppType as AppType2, Domain as Domain2, LoggerLevel } from "@larksuiteoapi/node-sdk";
782
+ import { AppType as AppType3, Domain as Domain3, LoggerLevel } from "@larksuiteoapi/node-sdk";
606
783
  var src_default = Bitable;
607
784
  export {
608
785
  src_default as default,
609
786
  LoggerLevel,
787
+ FeishuOAuthClient,
610
788
  FeishuBitableError,
611
- Domain2 as Domain,
789
+ Domain3 as Domain,
612
790
  Bitable,
613
- AppType2 as AppType
791
+ AppType3 as AppType
614
792
  };
package/lib/types.d.ts CHANGED
@@ -83,6 +83,47 @@ export interface BitableConstructorOptions {
83
83
  sdkClient?: lark.Client;
84
84
  logger?: BitableLogger | null;
85
85
  }
86
+ export interface FeishuOAuthConstructorOptions {
87
+ appId?: string;
88
+ appSecret?: string;
89
+ redirectUri?: string;
90
+ domain?: lark.Domain | string;
91
+ sdkClient?: lark.Client;
92
+ logger?: BitableLogger | null;
93
+ }
94
+ export interface BuildOAuthLoginUrlOptions {
95
+ state: string;
96
+ redirectUri?: string;
97
+ scope?: string | string[];
98
+ }
99
+ export interface OAuthTokenInfo {
100
+ access_token: string;
101
+ refresh_token?: string;
102
+ token_type: string;
103
+ expires_in?: number;
104
+ refresh_expires_in?: number;
105
+ scope?: string;
106
+ }
107
+ export interface OAuthUserInfo {
108
+ name?: string;
109
+ en_name?: string;
110
+ avatar_url?: string;
111
+ avatar_thumb?: string;
112
+ avatar_middle?: string;
113
+ avatar_big?: string;
114
+ open_id?: string;
115
+ union_id?: string;
116
+ email?: string;
117
+ enterprise_email?: string;
118
+ user_id?: string;
119
+ mobile?: string;
120
+ tenant_key?: string;
121
+ employee_no?: string;
122
+ }
123
+ export interface OAuthCallbackResult {
124
+ token: OAuthTokenInfo;
125
+ user: OAuthUserInfo;
126
+ }
86
127
  export interface FetchAllRecordsOptions {
87
128
  viewId?: string;
88
129
  fieldNames?: string[];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yuuko1410/feishu-bitable",
3
- "version": "0.0.4",
3
+ "version": "0.0.5",
4
4
  "description": "基于 Bun + TypeScript + 飞书官方 SDK 的多维表格操作库",
5
5
  "type": "module",
6
6
  "main": "./lib/index.cjs",