@logto/connector-feishu-web 1.2.1 → 1.3.1

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
@@ -1,22 +1,21 @@
1
- # 飞书网页
1
+ # Set up social sign-in with Feishu
2
2
 
3
- 先进企业协作与管理平台,一站式无缝办公协作,团队上下对齐目标,全面激活组织和个人。
3
+ Feishu Web: An advanced enterprise collaboration and management platform, offering seamless office collaboration and aligning team goals to fully activate organizations and individuals.
4
4
 
5
- ## 开始上手
5
+ ## Getting started with Feishu social sign-in
6
6
 
7
- 飞书网页连接器是为桌面网页应用设计的。它采用了 OAuth 2.0 认证流程。
7
+ The Feishu web connector is designed for desktop web applications and uses the OAuth 2.0 authentication process.
8
8
 
9
- ## 注册飞书开发者账号
9
+ ## Register a Feishu developer account
10
10
 
11
- 如果你还没有飞书开发者账号,请在 [开放平台](https://open.feishu.cn/?lang=zh-CN) 注册。
11
+ If you do not have a Feishu developer account, please register on the [Feishu Open Platform](https://open.feishu.cn/app).
12
12
 
13
- ## 创建应用
13
+ ## Create an application
14
14
 
15
- 1. [开发者首页](https://open.feishu.cn/app?lang=zh-CN) 下方有 「创建企业自建应用」
16
- 2. 填写应用名称,选择「企业自建应用」,点击「创建应用」
17
- 3. 在左侧「安全设置中」添加相应的「重定向 URL `${your_logto_origin}/callback/${connector_id}`。其中 `connector_id` 在管理控制台添加了相应的连接器之后,可以在连接器的详情页中找到
18
- 4. 左侧「凭证与基本信息」中可以获取「AppID」、「AppSecret」
19
- 5. 左侧「应用发布」中需要发布第一个版本「AppID」、「AppSecret」才会生效
15
+ 1. On the [Developer Console](https://open.feishu.cn/app), click "Create Custom App".
16
+ 2. Fill in the application name, description, select an icon, and click "Create" button.
17
+ 3. In the left sidebar, click "Security Settings", fill in the "Redirect URL" as `${logto_endpoint}/callback/${connector_id}`. The corresponding value can be found in the `Callback URI` field on the Feishu connector details page in the Logto Console.
18
+ 4. In "Credentials & Basic Info", you can obtain the "App ID" and "App Secret".
20
19
 
21
20
  > ℹ️ **Note**
22
- > 应用不发布版本,所获取的「AppID」、「AppSecret」 均无法使用,或请求错误。
21
+ > For non-enterprise internal use, you also need to click "Create a version" button in "Version Management and Release" page. The "App ID" and "App Secret" will only take effect after the application status changes to "Enabled."
package/lib/index.js CHANGED
@@ -1,213 +1,231 @@
1
- import { got, HTTPError } from 'got';
2
- import { ConnectorPlatform, ConnectorConfigFormItemType, validateConfig, ConnectorError, ConnectorErrorCodes, jsonGuard, ConnectorType } from '@logto/connector-kit';
3
- import { z } from 'zod';
1
+ // src/index.ts
2
+ import { assert, conditional } from "@silverhand/essentials";
3
+ import { got, HTTPError } from "got";
4
+ import {
5
+ ConnectorError,
6
+ ConnectorErrorCodes,
7
+ ConnectorPlatform as ConnectorPlatform2,
8
+ ConnectorType,
9
+ jsonGuard,
10
+ validateConfig
11
+ } from "@logto/connector-kit";
4
12
 
5
- // https://github.com/facebook/jest/issues/7547
6
- const assert = (value, error) => {
7
- if (!value) {
8
- // https://github.com/typescript-eslint/typescript-eslint/issues/3814
9
- // eslint-disable-next-line @typescript-eslint/no-throw-literal
10
- throw error;
11
- }
12
- };
13
-
14
- // eslint-disable-next-line id-length
15
- // eslint-disable-next-line unicorn/prefer-native-coercion-functions
16
- const notFalsy = (value) => Boolean(value);
17
-
18
- /**
19
- * Conditional return the expression result when it's not {@link Falsy};
20
- * otherwise return `undefined`.
21
- *
22
- * @example
23
- * ```ts
24
- * conditional(1 && '2') // '2'
25
- * conditional(false && '1') // undefined
26
- * ```
27
- */
28
- const conditional = (exp) => (notFalsy(exp) ? exp : undefined);
29
-
30
- const codeEndpoint = 'https://passport.feishu.cn/suite/passport/oauth/authorize';
31
- const accessTokenEndpoint = 'https://passport.feishu.cn/suite/passport/oauth/token';
32
- const userInfoEndpoint = 'https://passport.feishu.cn/suite/passport/oauth/userinfo';
33
- const defaultMetadata = {
34
- id: 'feishu-web',
35
- target: 'feishu',
36
- platform: ConnectorPlatform.Web,
37
- name: {
38
- en: 'Feishu',
39
- 'zh-CN': '飞书',
13
+ // src/constant.ts
14
+ import { ConnectorConfigFormItemType, ConnectorPlatform } from "@logto/connector-kit";
15
+ var codeEndpoint = "https://passport.feishu.cn/suite/passport/oauth/authorize";
16
+ var accessTokenEndpoint = "https://passport.feishu.cn/suite/passport/oauth/token";
17
+ var userInfoEndpoint = "https://passport.feishu.cn/suite/passport/oauth/userinfo";
18
+ var defaultMetadata = {
19
+ id: "feishu-web",
20
+ target: "feishu",
21
+ platform: ConnectorPlatform.Web,
22
+ name: {
23
+ en: "Feishu",
24
+ "zh-CN": "\u98DE\u4E66"
25
+ },
26
+ logo: "./logo.svg",
27
+ logoDark: null,
28
+ description: {
29
+ "zh-CN": "\u98DE\u4E66\u662F\u4E00\u4E2A\u7531\u5B57\u8282\u8DF3\u52A8\u5F00\u53D1\u7684\u4F01\u4E1A\u534F\u4F5C\u4E0E\u7BA1\u7406\u5E73\u53F0",
30
+ en: "Feishu is an enterprise collaboration platform developed by ByteDance"
31
+ },
32
+ readme: "./README.md",
33
+ formItems: [
34
+ {
35
+ key: "appId",
36
+ label: "App ID",
37
+ type: ConnectorConfigFormItemType.Text,
38
+ required: true,
39
+ placeholder: "<app-id>"
40
40
  },
41
- logo: './logo.svg',
42
- logoDark: null,
43
- description: {
44
- 'zh-CN': '飞书是一个由字节跳动开发的企业协作与管理平台',
45
- en: 'Feishu is an enterprise collaboration platform developed by ByteDance',
46
- },
47
- readme: './README.md',
48
- formItems: [
49
- {
50
- key: 'appId',
51
- label: 'App ID',
52
- type: ConnectorConfigFormItemType.Text,
53
- required: true,
54
- placeholder: '<app-id>',
55
- },
56
- {
57
- key: 'appSecret',
58
- label: 'App Secret',
59
- type: ConnectorConfigFormItemType.Text,
60
- required: true,
61
- placeholder: '<app-secret>',
62
- },
63
- ],
41
+ {
42
+ key: "appSecret",
43
+ label: "App Secret",
44
+ type: ConnectorConfigFormItemType.Text,
45
+ required: true,
46
+ placeholder: "<app-secret>"
47
+ }
48
+ ]
64
49
  };
65
50
 
66
- const feishuConfigGuard = z.object({
67
- appId: z.string(),
68
- appSecret: z.string(),
51
+ // src/types.ts
52
+ import { z } from "zod";
53
+ var feishuConfigGuard = z.object({
54
+ appId: z.string(),
55
+ appSecret: z.string()
69
56
  });
70
- const feishuAuthCodeGuard = z.object({
71
- code: z.string(),
72
- redirectUri: z.string(),
57
+ var feishuAuthCodeGuard = z.object({
58
+ code: z.string(),
59
+ redirectUri: z.string()
73
60
  });
74
- const feishuErrorResponse = z.object({
75
- error: z.string(),
76
- error_description: z.string().optional(),
61
+ var feishuErrorResponse = z.object({
62
+ error: z.string(),
63
+ error_description: z.string().optional()
77
64
  });
78
- const feishuAccessTokenResponse = z.object({
79
- access_token: z.string(),
80
- token_type: z.string(),
81
- expires_in: z.number(),
82
- refresh_token: z.string().optional(),
83
- refresh_expires_in: z.number().optional(),
65
+ var feishuAccessTokenResponse = z.object({
66
+ access_token: z.string(),
67
+ token_type: z.string(),
68
+ expires_in: z.number(),
69
+ refresh_token: z.string().optional(),
70
+ refresh_expires_in: z.number().optional()
84
71
  });
85
- const feishuUserInfoResponse = z.object({
86
- sub: z.string(),
87
- name: z.string(),
88
- picture: z.string(),
89
- open_id: z.string(),
90
- union_id: z.string(),
91
- en_name: z.string(),
92
- tenant_key: z.string(),
93
- avatar_url: z.string(),
94
- avatar_thumb: z.string(),
95
- avatar_middle: z.string(),
96
- avatar_big: z.string(),
97
- email: z.string().nullish(),
98
- user_id: z.string().nullish(),
99
- employee_no: z.string().nullish(),
100
- mobile: z.string().nullish(),
72
+ var feishuUserInfoResponse = z.object({
73
+ sub: z.string(),
74
+ name: z.string(),
75
+ picture: z.string(),
76
+ open_id: z.string(),
77
+ union_id: z.string(),
78
+ en_name: z.string(),
79
+ tenant_key: z.string(),
80
+ avatar_url: z.string(),
81
+ avatar_thumb: z.string(),
82
+ avatar_middle: z.string(),
83
+ avatar_big: z.string(),
84
+ email: z.string().nullish(),
85
+ user_id: z.string().nullish(),
86
+ employee_no: z.string().nullish(),
87
+ mobile: z.string().nullish()
101
88
  });
102
89
 
90
+ // src/index.ts
103
91
  function buildAuthorizationUri(clientId, redirectUri, state) {
104
- const queryParameters = new URLSearchParams({
105
- client_id: clientId,
106
- redirect_uri: encodeURI(redirectUri),
107
- response_type: 'code',
108
- state,
109
- });
110
- return `${codeEndpoint}?${queryParameters.toString()}`;
92
+ const queryParameters = new URLSearchParams({
93
+ client_id: clientId,
94
+ redirect_uri: encodeURI(redirectUri),
95
+ response_type: "code",
96
+ state
97
+ });
98
+ return `${codeEndpoint}?${queryParameters.toString()}`;
111
99
  }
112
100
  function getAuthorizationUri(getConfig) {
113
- return async function ({ state, redirectUri }) {
114
- const config = await getConfig(defaultMetadata.id);
115
- validateConfig(config, feishuConfigGuard);
116
- const { appId } = config;
117
- return buildAuthorizationUri(appId, redirectUri, state);
118
- };
101
+ return async function({ state, redirectUri }) {
102
+ const config = await getConfig(defaultMetadata.id);
103
+ validateConfig(config, feishuConfigGuard);
104
+ const { appId } = config;
105
+ return buildAuthorizationUri(appId, redirectUri, state);
106
+ };
119
107
  }
120
108
  async function authorizationCallbackHandler(data) {
121
- const result = feishuAuthCodeGuard.safeParse(data);
122
- assert(result.success, new ConnectorError(ConnectorErrorCodes.InvalidResponse, JSON.stringify(data)));
123
- return result.data;
109
+ const result = feishuAuthCodeGuard.safeParse(data);
110
+ assert(
111
+ result.success,
112
+ new ConnectorError(ConnectorErrorCodes.InvalidResponse, JSON.stringify(data))
113
+ );
114
+ return result.data;
124
115
  }
125
116
  async function getAccessToken(code, appId, appSecret, redirectUri) {
126
- try {
127
- const response = await got.post(accessTokenEndpoint, {
128
- headers: {
129
- contentType: 'application/www-form-urlencoded',
130
- },
131
- form: {
132
- grant_type: 'authorization_code',
133
- code,
134
- client_id: appId,
135
- client_secret: appSecret,
136
- redirect_uri: redirectUri,
137
- },
138
- responseType: 'json',
139
- });
140
- const result = feishuAccessTokenResponse.safeParse(response.body);
141
- assert(result.success, new ConnectorError(ConnectorErrorCodes.InvalidResponse, JSON.stringify(response.body)));
142
- if (result.data.access_token.length === 0) {
143
- throw new ConnectorError(ConnectorErrorCodes.SocialAuthCodeInvalid, 'access_token is empty');
144
- }
145
- return { accessToken: result.data.access_token };
117
+ try {
118
+ const response = await got.post(accessTokenEndpoint, {
119
+ headers: {
120
+ contentType: "application/www-form-urlencoded"
121
+ },
122
+ form: {
123
+ grant_type: "authorization_code",
124
+ code,
125
+ client_id: appId,
126
+ client_secret: appSecret,
127
+ redirect_uri: redirectUri
128
+ },
129
+ responseType: "json"
130
+ });
131
+ const result = feishuAccessTokenResponse.safeParse(response.body);
132
+ assert(
133
+ result.success,
134
+ new ConnectorError(ConnectorErrorCodes.InvalidResponse, JSON.stringify(response.body))
135
+ );
136
+ if (result.data.access_token.length === 0) {
137
+ throw new ConnectorError(ConnectorErrorCodes.SocialAuthCodeInvalid, "access_token is empty");
138
+ }
139
+ return { accessToken: result.data.access_token };
140
+ } catch (error) {
141
+ if (error instanceof ConnectorError) {
142
+ throw error;
146
143
  }
147
- catch (error) {
148
- if (error instanceof ConnectorError) {
149
- throw error;
150
- }
151
- if (error instanceof HTTPError) {
152
- const result = feishuErrorResponse.safeParse(error.response.body);
153
- assert(result.success, new ConnectorError(ConnectorErrorCodes.InvalidResponse, JSON.stringify(error.response.body)));
154
- throw new ConnectorError(ConnectorErrorCodes.SocialAuthCodeInvalid, result.data.error_description);
155
- }
156
- throw new ConnectorError(ConnectorErrorCodes.General, {
157
- errorDescription: 'Failed to get access token',
158
- });
144
+ if (error instanceof HTTPError) {
145
+ const result = feishuErrorResponse.safeParse(error.response.body);
146
+ assert(
147
+ result.success,
148
+ new ConnectorError(ConnectorErrorCodes.InvalidResponse, JSON.stringify(error.response.body))
149
+ );
150
+ throw new ConnectorError(
151
+ ConnectorErrorCodes.SocialAuthCodeInvalid,
152
+ result.data.error_description
153
+ );
159
154
  }
155
+ throw new ConnectorError(ConnectorErrorCodes.General, {
156
+ errorDescription: "Failed to get access token"
157
+ });
158
+ }
160
159
  }
161
160
  function getUserInfo(getConfig) {
162
- return async function (data) {
163
- const { code, redirectUri } = await authorizationCallbackHandler(data);
164
- const config = await getConfig(defaultMetadata.id);
165
- validateConfig(config, feishuConfigGuard);
166
- const { accessToken } = await getAccessToken(code, config.appId, config.appSecret, redirectUri);
167
- try {
168
- const response = await got.get(userInfoEndpoint, {
169
- headers: {
170
- Authorization: `Bearer ${accessToken}`,
171
- },
172
- responseType: 'json',
173
- });
174
- const result = feishuUserInfoResponse.safeParse(response.body);
175
- assert(result.success, new ConnectorError(ConnectorErrorCodes.InvalidResponse, `invalid user response`));
176
- const { sub, user_id, name, email, avatar_url: avatar, mobile } = result.data;
177
- return {
178
- id: sub,
179
- name,
180
- avatar,
181
- email: conditional(email),
182
- userId: conditional(user_id),
183
- phone: conditional(mobile?.replace('+', '')),
184
- rawData: jsonGuard.parse(response.body),
185
- };
186
- }
187
- catch (error) {
188
- if (error instanceof ConnectorError) {
189
- throw error;
190
- }
191
- if (error instanceof HTTPError) {
192
- const result = feishuErrorResponse.safeParse(error.response.body);
193
- assert(result.success, new ConnectorError(ConnectorErrorCodes.InvalidResponse, JSON.stringify(error.response.body)));
194
- throw new ConnectorError(ConnectorErrorCodes.SocialAccessTokenInvalid, result.data.error_description);
195
- }
196
- throw new ConnectorError(ConnectorErrorCodes.General, {
197
- errorDescription: 'Failed to get user info',
198
- });
199
- }
200
- };
161
+ return async function(data) {
162
+ const { code, redirectUri } = await authorizationCallbackHandler(data);
163
+ const config = await getConfig(defaultMetadata.id);
164
+ validateConfig(config, feishuConfigGuard);
165
+ const { accessToken } = await getAccessToken(code, config.appId, config.appSecret, redirectUri);
166
+ try {
167
+ const response = await got.get(userInfoEndpoint, {
168
+ headers: {
169
+ Authorization: `Bearer ${accessToken}`
170
+ },
171
+ responseType: "json"
172
+ });
173
+ const result = feishuUserInfoResponse.safeParse(response.body);
174
+ assert(
175
+ result.success,
176
+ new ConnectorError(ConnectorErrorCodes.InvalidResponse, `invalid user response`)
177
+ );
178
+ const { sub, user_id, name, email, avatar_url: avatar, mobile } = result.data;
179
+ return {
180
+ id: sub,
181
+ name,
182
+ avatar,
183
+ email: conditional(email),
184
+ userId: conditional(user_id),
185
+ phone: conditional(mobile?.replace("+", "")),
186
+ rawData: jsonGuard.parse(response.body)
187
+ };
188
+ } catch (error) {
189
+ if (error instanceof ConnectorError) {
190
+ throw error;
191
+ }
192
+ if (error instanceof HTTPError) {
193
+ const result = feishuErrorResponse.safeParse(error.response.body);
194
+ assert(
195
+ result.success,
196
+ new ConnectorError(
197
+ ConnectorErrorCodes.InvalidResponse,
198
+ JSON.stringify(error.response.body)
199
+ )
200
+ );
201
+ throw new ConnectorError(
202
+ ConnectorErrorCodes.SocialAccessTokenInvalid,
203
+ result.data.error_description
204
+ );
205
+ }
206
+ throw new ConnectorError(ConnectorErrorCodes.General, {
207
+ errorDescription: "Failed to get user info"
208
+ });
209
+ }
210
+ };
201
211
  }
202
- const createFeishuConnector = async ({ getConfig }) => {
203
- return {
204
- metadata: defaultMetadata,
205
- type: ConnectorType.Social,
206
- platform: ConnectorPlatform.Web,
207
- configGuard: feishuConfigGuard,
208
- getAuthorizationUri: getAuthorizationUri(getConfig),
209
- getUserInfo: getUserInfo(getConfig),
210
- };
212
+ var createFeishuConnector = async ({ getConfig }) => {
213
+ return {
214
+ metadata: defaultMetadata,
215
+ type: ConnectorType.Social,
216
+ platform: ConnectorPlatform2.Web,
217
+ configGuard: feishuConfigGuard,
218
+ getAuthorizationUri: getAuthorizationUri(getConfig),
219
+ getUserInfo: getUserInfo(getConfig)
220
+ };
211
221
  };
212
-
213
- export { authorizationCallbackHandler, buildAuthorizationUri, createFeishuConnector as default, getAccessToken, getAuthorizationUri, getUserInfo };
222
+ var index_default = createFeishuConnector;
223
+ export {
224
+ authorizationCallbackHandler,
225
+ buildAuthorizationUri,
226
+ index_default as default,
227
+ getAccessToken,
228
+ getAuthorizationUri,
229
+ getUserInfo
230
+ };
231
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/constant.ts","../src/types.ts"],"sourcesContent":["import { assert, conditional } from '@silverhand/essentials';\nimport { got, HTTPError } from 'got';\n\nimport type {\n CreateConnector,\n GetAuthorizationUri,\n GetConnectorConfig,\n GetUserInfo,\n SocialConnector,\n} from '@logto/connector-kit';\nimport {\n ConnectorError,\n ConnectorErrorCodes,\n ConnectorPlatform,\n ConnectorType,\n jsonGuard,\n validateConfig,\n} from '@logto/connector-kit';\n\nimport {\n accessTokenEndpoint,\n codeEndpoint,\n defaultMetadata,\n userInfoEndpoint,\n} from './constant.js';\nimport {\n feishuAccessTokenResponse,\n feishuAuthCodeGuard,\n feishuConfigGuard,\n feishuErrorResponse,\n feishuUserInfoResponse,\n} from './types.js';\n\nexport function buildAuthorizationUri(\n clientId: string,\n redirectUri: string,\n state: string\n): string {\n const queryParameters = new URLSearchParams({\n client_id: clientId,\n redirect_uri: encodeURI(redirectUri),\n response_type: 'code',\n state,\n });\n\n return `${codeEndpoint}?${queryParameters.toString()}`;\n}\n\nexport function getAuthorizationUri(getConfig: GetConnectorConfig): GetAuthorizationUri {\n return async function ({ state, redirectUri }) {\n const config = await getConfig(defaultMetadata.id);\n validateConfig(config, feishuConfigGuard);\n\n const { appId } = config;\n\n return buildAuthorizationUri(appId, redirectUri, state);\n };\n}\n\nexport async function authorizationCallbackHandler(data: unknown) {\n const result = feishuAuthCodeGuard.safeParse(data);\n assert(\n result.success,\n new ConnectorError(ConnectorErrorCodes.InvalidResponse, JSON.stringify(data))\n );\n\n return result.data;\n}\n\nexport async function getAccessToken(\n code: string,\n appId: string,\n appSecret: string,\n redirectUri: string\n) {\n try {\n const response = await got.post(accessTokenEndpoint, {\n headers: {\n contentType: 'application/www-form-urlencoded',\n },\n form: {\n grant_type: 'authorization_code',\n code,\n client_id: appId,\n client_secret: appSecret,\n redirect_uri: redirectUri,\n },\n responseType: 'json',\n });\n\n const result = feishuAccessTokenResponse.safeParse(response.body);\n assert(\n result.success,\n new ConnectorError(ConnectorErrorCodes.InvalidResponse, JSON.stringify(response.body))\n );\n\n if (result.data.access_token.length === 0) {\n throw new ConnectorError(ConnectorErrorCodes.SocialAuthCodeInvalid, 'access_token is empty');\n }\n\n return { accessToken: result.data.access_token };\n } catch (error: unknown) {\n if (error instanceof ConnectorError) {\n throw error;\n }\n\n if (error instanceof HTTPError) {\n const result = feishuErrorResponse.safeParse(error.response.body);\n assert(\n result.success,\n new ConnectorError(ConnectorErrorCodes.InvalidResponse, JSON.stringify(error.response.body))\n );\n\n throw new ConnectorError(\n ConnectorErrorCodes.SocialAuthCodeInvalid,\n result.data.error_description\n );\n }\n\n throw new ConnectorError(ConnectorErrorCodes.General, {\n errorDescription: 'Failed to get access token',\n });\n }\n}\n\nexport function getUserInfo(getConfig: GetConnectorConfig): GetUserInfo {\n return async function (data) {\n const { code, redirectUri } = await authorizationCallbackHandler(data);\n const config = await getConfig(defaultMetadata.id);\n validateConfig(config, feishuConfigGuard);\n\n const { accessToken } = await getAccessToken(code, config.appId, config.appSecret, redirectUri);\n\n try {\n const response = await got.get(userInfoEndpoint, {\n headers: {\n Authorization: `Bearer ${accessToken}`,\n },\n responseType: 'json',\n });\n\n const result = feishuUserInfoResponse.safeParse(response.body);\n assert(\n result.success,\n new ConnectorError(ConnectorErrorCodes.InvalidResponse, `invalid user response`)\n );\n\n const { sub, user_id, name, email, avatar_url: avatar, mobile } = result.data;\n\n return {\n id: sub,\n name,\n avatar,\n email: conditional(email),\n userId: conditional(user_id),\n phone: conditional(mobile?.replace('+', '')),\n rawData: jsonGuard.parse(response.body),\n };\n } catch (error: unknown) {\n if (error instanceof ConnectorError) {\n throw error;\n }\n\n if (error instanceof HTTPError) {\n const result = feishuErrorResponse.safeParse(error.response.body);\n\n assert(\n result.success,\n new ConnectorError(\n ConnectorErrorCodes.InvalidResponse,\n JSON.stringify(error.response.body)\n )\n );\n\n throw new ConnectorError(\n ConnectorErrorCodes.SocialAccessTokenInvalid,\n result.data.error_description\n );\n }\n\n throw new ConnectorError(ConnectorErrorCodes.General, {\n errorDescription: 'Failed to get user info',\n });\n }\n };\n}\n\nconst createFeishuConnector: CreateConnector<SocialConnector> = async ({ getConfig }) => {\n return {\n metadata: defaultMetadata,\n type: ConnectorType.Social,\n platform: ConnectorPlatform.Web,\n configGuard: feishuConfigGuard,\n getAuthorizationUri: getAuthorizationUri(getConfig),\n getUserInfo: getUserInfo(getConfig),\n };\n};\n\nexport default createFeishuConnector;\n","import type { ConnectorMetadata } from '@logto/connector-kit';\nimport { ConnectorConfigFormItemType, ConnectorPlatform } from '@logto/connector-kit';\n\nexport const codeEndpoint = 'https://passport.feishu.cn/suite/passport/oauth/authorize';\n\nexport const accessTokenEndpoint = 'https://passport.feishu.cn/suite/passport/oauth/token';\n\nexport const userInfoEndpoint = 'https://passport.feishu.cn/suite/passport/oauth/userinfo';\n\nexport const defaultMetadata: ConnectorMetadata = {\n id: 'feishu-web',\n target: 'feishu',\n platform: ConnectorPlatform.Web,\n name: {\n en: 'Feishu',\n 'zh-CN': '飞书',\n },\n logo: './logo.svg',\n logoDark: null,\n description: {\n 'zh-CN': '飞书是一个由字节跳动开发的企业协作与管理平台',\n en: 'Feishu is an enterprise collaboration platform developed by ByteDance',\n },\n readme: './README.md',\n formItems: [\n {\n key: 'appId',\n label: 'App ID',\n type: ConnectorConfigFormItemType.Text,\n required: true,\n placeholder: '<app-id>',\n },\n {\n key: 'appSecret',\n label: 'App Secret',\n type: ConnectorConfigFormItemType.Text,\n required: true,\n placeholder: '<app-secret>',\n },\n ],\n};\n","import { z } from 'zod';\n\nexport const feishuConfigGuard = z.object({\n appId: z.string(),\n appSecret: z.string(),\n});\n\nexport type FeishuConfig = z.infer<typeof feishuConfigGuard>;\n\nexport const feishuAuthCodeGuard = z.object({\n code: z.string(),\n redirectUri: z.string(),\n});\n\nexport const feishuErrorResponse = z.object({\n error: z.string(),\n error_description: z.string().optional(),\n});\n\nexport const feishuAccessTokenResponse = z.object({\n access_token: z.string(),\n token_type: z.string(),\n expires_in: z.number(),\n refresh_token: z.string().optional(),\n refresh_expires_in: z.number().optional(),\n});\n\nexport const feishuUserInfoResponse = z.object({\n sub: z.string(),\n name: z.string(),\n picture: z.string(),\n open_id: z.string(),\n union_id: z.string(),\n en_name: z.string(),\n tenant_key: z.string(),\n avatar_url: z.string(),\n avatar_thumb: z.string(),\n avatar_middle: z.string(),\n avatar_big: z.string(),\n email: z.string().nullish(),\n user_id: z.string().nullish(),\n employee_no: z.string().nullish(),\n mobile: z.string().nullish(),\n});\n"],"mappings":"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","names":["ConnectorPlatform","ConnectorPlatform"]}
package/package.json CHANGED
@@ -1,14 +1,14 @@
1
1
  {
2
2
  "name": "@logto/connector-feishu-web",
3
- "version": "1.2.1",
3
+ "version": "1.3.1",
4
4
  "description": "Feishu web connector.",
5
5
  "author": "Silverhand Inc. <contact@silverhand.io>",
6
6
  "dependencies": {
7
- "@logto/connector-kit": "^4.0.0",
7
+ "@logto/connector-kit": "^4.1.1",
8
8
  "@silverhand/essentials": "^2.9.1",
9
9
  "got": "^14.0.0",
10
- "snakecase-keys": "^8.0.0",
11
- "zod": "^3.22.4"
10
+ "snakecase-keys": "^8.0.1",
11
+ "zod": "^3.23.8"
12
12
  },
13
13
  "main": "./lib/index.js",
14
14
  "module": "./lib/index.js",
@@ -41,30 +41,25 @@
41
41
  "access": "public"
42
42
  },
43
43
  "devDependencies": {
44
- "@rollup/plugin-commonjs": "^26.0.0",
45
- "@rollup/plugin-json": "^6.1.0",
46
- "@rollup/plugin-node-resolve": "^15.2.3",
47
- "@rollup/plugin-typescript": "^11.1.6",
48
44
  "@silverhand/eslint-config": "6.0.1",
49
45
  "@silverhand/ts-config": "6.0.0",
50
46
  "@types/node": "^20.11.20",
51
47
  "@types/supertest": "^6.0.2",
52
- "@vitest/coverage-v8": "^1.4.0",
48
+ "@vitest/coverage-v8": "^2.1.9",
53
49
  "eslint": "^8.56.0",
54
50
  "lint-staged": "^15.0.2",
55
51
  "nock": "^13.3.1",
56
52
  "prettier": "^3.0.0",
57
- "rollup": "^4.12.0",
58
- "rollup-plugin-output-size": "^1.3.0",
59
53
  "supertest": "^7.0.0",
60
- "typescript": "^5.3.3",
61
- "vitest": "^1.4.0"
54
+ "tsup": "^8.3.0",
55
+ "typescript": "^5.5.3",
56
+ "vitest": "^2.1.9"
62
57
  },
63
58
  "scripts": {
64
59
  "precommit": "lint-staged",
65
- "build:test": "rm -rf lib/ && tsc -p tsconfig.test.json --sourcemap",
66
- "build": "rm -rf lib/ && tsc -p tsconfig.build.json --noEmit && rollup -c",
67
- "dev": "tsc -p tsconfig.build.json --watch --preserveWatchOutput --incremental",
60
+ "check": "tsc --noEmit",
61
+ "build": "tsup",
62
+ "dev": "tsup --watch",
68
63
  "lint": "eslint --ext .ts src",
69
64
  "lint:report": "pnpm lint --format json --output-file report.json",
70
65
  "test": "vitest src",
package/lib/constant.d.ts DELETED
@@ -1,5 +0,0 @@
1
- import type { ConnectorMetadata } from '@logto/connector-kit';
2
- export declare const codeEndpoint = "https://passport.feishu.cn/suite/passport/oauth/authorize";
3
- export declare const accessTokenEndpoint = "https://passport.feishu.cn/suite/passport/oauth/token";
4
- export declare const userInfoEndpoint = "https://passport.feishu.cn/suite/passport/oauth/userinfo";
5
- export declare const defaultMetadata: ConnectorMetadata;
package/lib/index.d.ts DELETED
@@ -1,13 +0,0 @@
1
- import type { CreateConnector, GetAuthorizationUri, GetConnectorConfig, GetUserInfo, SocialConnector } from '@logto/connector-kit';
2
- export declare function buildAuthorizationUri(clientId: string, redirectUri: string, state: string): string;
3
- export declare function getAuthorizationUri(getConfig: GetConnectorConfig): GetAuthorizationUri;
4
- export declare function authorizationCallbackHandler(data: unknown): Promise<{
5
- code: string;
6
- redirectUri: string;
7
- }>;
8
- export declare function getAccessToken(code: string, appId: string, appSecret: string, redirectUri: string): Promise<{
9
- accessToken: string;
10
- }>;
11
- export declare function getUserInfo(getConfig: GetConnectorConfig): GetUserInfo;
12
- declare const createFeishuConnector: CreateConnector<SocialConnector>;
13
- export default createFeishuConnector;
package/lib/mock.d.ts DELETED
@@ -1,10 +0,0 @@
1
- import type { FeishuConfig } from './types.js';
2
- export declare const mockedTimestamp = "2022-02-22 22:22:22";
3
- export declare const mockedFeishuConfig: FeishuConfig;
4
- export declare const mockedFeishuPublicParameters: {
5
- format: string;
6
- grantType: string;
7
- timestamp: string;
8
- version: string;
9
- method: string;
10
- };
package/lib/types.d.ts DELETED
@@ -1,100 +0,0 @@
1
- import { z } from 'zod';
2
- export declare const feishuConfigGuard: z.ZodObject<{
3
- appId: z.ZodString;
4
- appSecret: z.ZodString;
5
- }, "strip", z.ZodTypeAny, {
6
- appId: string;
7
- appSecret: string;
8
- }, {
9
- appId: string;
10
- appSecret: string;
11
- }>;
12
- export type FeishuConfig = z.infer<typeof feishuConfigGuard>;
13
- export declare const feishuAuthCodeGuard: z.ZodObject<{
14
- code: z.ZodString;
15
- redirectUri: z.ZodString;
16
- }, "strip", z.ZodTypeAny, {
17
- code: string;
18
- redirectUri: string;
19
- }, {
20
- code: string;
21
- redirectUri: string;
22
- }>;
23
- export declare const feishuErrorResponse: z.ZodObject<{
24
- error: z.ZodString;
25
- error_description: z.ZodOptional<z.ZodString>;
26
- }, "strip", z.ZodTypeAny, {
27
- error: string;
28
- error_description?: string | undefined;
29
- }, {
30
- error: string;
31
- error_description?: string | undefined;
32
- }>;
33
- export declare const feishuAccessTokenResponse: z.ZodObject<{
34
- access_token: z.ZodString;
35
- token_type: z.ZodString;
36
- expires_in: z.ZodNumber;
37
- refresh_token: z.ZodOptional<z.ZodString>;
38
- refresh_expires_in: z.ZodOptional<z.ZodNumber>;
39
- }, "strip", z.ZodTypeAny, {
40
- access_token: string;
41
- token_type: string;
42
- expires_in: number;
43
- refresh_token?: string | undefined;
44
- refresh_expires_in?: number | undefined;
45
- }, {
46
- access_token: string;
47
- token_type: string;
48
- expires_in: number;
49
- refresh_token?: string | undefined;
50
- refresh_expires_in?: number | undefined;
51
- }>;
52
- export declare const feishuUserInfoResponse: z.ZodObject<{
53
- sub: z.ZodString;
54
- name: z.ZodString;
55
- picture: z.ZodString;
56
- open_id: z.ZodString;
57
- union_id: z.ZodString;
58
- en_name: z.ZodString;
59
- tenant_key: z.ZodString;
60
- avatar_url: z.ZodString;
61
- avatar_thumb: z.ZodString;
62
- avatar_middle: z.ZodString;
63
- avatar_big: z.ZodString;
64
- email: z.ZodOptional<z.ZodNullable<z.ZodString>>;
65
- user_id: z.ZodOptional<z.ZodNullable<z.ZodString>>;
66
- employee_no: z.ZodOptional<z.ZodNullable<z.ZodString>>;
67
- mobile: z.ZodOptional<z.ZodNullable<z.ZodString>>;
68
- }, "strip", z.ZodTypeAny, {
69
- sub: string;
70
- name: string;
71
- picture: string;
72
- open_id: string;
73
- union_id: string;
74
- en_name: string;
75
- tenant_key: string;
76
- avatar_url: string;
77
- avatar_thumb: string;
78
- avatar_middle: string;
79
- avatar_big: string;
80
- email?: string | null | undefined;
81
- user_id?: string | null | undefined;
82
- employee_no?: string | null | undefined;
83
- mobile?: string | null | undefined;
84
- }, {
85
- sub: string;
86
- name: string;
87
- picture: string;
88
- open_id: string;
89
- union_id: string;
90
- en_name: string;
91
- tenant_key: string;
92
- avatar_url: string;
93
- avatar_thumb: string;
94
- avatar_middle: string;
95
- avatar_big: string;
96
- email?: string | null | undefined;
97
- user_id?: string | null | undefined;
98
- employee_no?: string | null | undefined;
99
- mobile?: string | null | undefined;
100
- }>;