@logto/connector-discord 1.3.1 → 1.4.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/lib/index.js CHANGED
@@ -1,215 +1,190 @@
1
- import { got, HTTPError } from 'got';
2
- import { ConnectorPlatform, ConnectorConfigFormItemType, parseJson, ConnectorError, ConnectorErrorCodes, ConnectorType, validateConfig, socialUserInfoGuard } 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
+ socialUserInfoGuard,
6
+ validateConfig,
7
+ ConnectorError,
8
+ ConnectorErrorCodes,
9
+ ConnectorType,
10
+ parseJson
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
- /**
31
- * Base authorization URL.
32
- * https://discord.com/developers/docs/topics/oauth2#shared-resources-oauth2-urls
33
- */
34
- const authorizationEndpoint = 'https://discord.com/oauth2/authorize';
35
- /**
36
- * Discord exposes different versions of the API, You should specify which version to use by including it in your requests.
37
- * https://discord.com/developers/docs/reference#api-reference
38
- */
39
- const accessTokenEndpoint = 'https://discord.com/api/v10/oauth2/token';
40
- const userInfoEndpoint = 'https://discord.com/api/v10/users/@me';
41
- /**
42
- * OAuth2 Scopes
43
- * https://discord.com/developers/docs/topics/oauth2#shared-resources-oauth2-scopes
44
- */
45
- const scope = 'identify email';
46
- const defaultMetadata = {
47
- id: 'discord-universal',
48
- target: 'discord',
49
- platform: ConnectorPlatform.Universal,
50
- name: {
51
- en: 'Discord',
13
+ // src/constant.ts
14
+ import { ConnectorConfigFormItemType, ConnectorPlatform } from "@logto/connector-kit";
15
+ var authorizationEndpoint = "https://discord.com/oauth2/authorize";
16
+ var accessTokenEndpoint = "https://discord.com/api/v10/oauth2/token";
17
+ var userInfoEndpoint = "https://discord.com/api/v10/users/@me";
18
+ var scope = "identify email";
19
+ var defaultMetadata = {
20
+ id: "discord-universal",
21
+ target: "discord",
22
+ platform: ConnectorPlatform.Universal,
23
+ name: {
24
+ en: "Discord"
25
+ },
26
+ logo: "./logo.svg",
27
+ logoDark: null,
28
+ description: {
29
+ en: "Discord is the easiest way to talk over voice, video, and text.",
30
+ "pt-PT": "Discord \xE9 a forma mais f\xE1cil de comunicar por voz, v\xEDdeo e texto.",
31
+ "zh-CN": "Discord \u662F\u4E00\u6B3E\u4E13\u4E3A\u793E\u7FA4\u8BBE\u8BA1\u7684\u514D\u8D39\u7F51\u7EDC\u5B9E\u65F6\u901A\u8BDD\u8F6F\u4EF6\u4E0E\u6570\u5B57\u53D1\u884C\u5E73\u53F0\u3002",
32
+ "tr-TR": "Discord, sesli, g\xF6r\xFCnt\xFCl\xFC ve metin \xFCzerinden konu\u015Fman\u0131n en kolay yoludur.",
33
+ ko: "Discord\uB294 \uC74C\uC131, \uBE44\uB514\uC624 \uBC0F \uD14D\uC2A4\uD2B8\uB85C \uB300\uD654\uD558\uB294 \uAC00\uC7A5 \uC26C\uC6B4 \uBC29\uBC95\uC785\uB2C8\uB2E4."
34
+ },
35
+ readme: "./README.md",
36
+ formItems: [
37
+ {
38
+ key: "clientId",
39
+ type: ConnectorConfigFormItemType.Text,
40
+ required: true,
41
+ label: "Client ID",
42
+ placeholder: "<client-id>"
52
43
  },
53
- logo: './logo.svg',
54
- logoDark: null,
55
- description: {
56
- en: 'Discord is the easiest way to talk over voice, video, and text.',
57
- 'pt-PT': 'Discord é a forma mais fácil de comunicar por voz, vídeo e texto.',
58
- 'zh-CN': 'Discord 是一款专为社群设计的免费网络实时通话软件与数字发行平台。',
59
- 'tr-TR': 'Discord, sesli, görüntülü ve metin üzerinden konuşmanın en kolay yoludur.',
60
- ko: 'Discord는 음성, 비디오 및 텍스트로 대화하는 가장 쉬운 방법입니다.',
44
+ {
45
+ key: "clientSecret",
46
+ type: ConnectorConfigFormItemType.Text,
47
+ required: true,
48
+ label: "Client Secret",
49
+ placeholder: "<client-secret>"
61
50
  },
62
- readme: './README.md',
63
- formItems: [
64
- {
65
- key: 'clientId',
66
- type: ConnectorConfigFormItemType.Text,
67
- required: true,
68
- label: 'Client ID',
69
- placeholder: '<client-id>',
70
- },
71
- {
72
- key: 'clientSecret',
73
- type: ConnectorConfigFormItemType.Text,
74
- required: true,
75
- label: 'Client Secret',
76
- placeholder: '<client-secret>',
77
- },
78
- {
79
- key: 'scope',
80
- type: ConnectorConfigFormItemType.Text,
81
- required: false,
82
- label: 'Scope',
83
- placeholder: '<scope>',
84
- description: "The `scope` determines permissions granted by the user's authorization. If you are not sure what to enter, do not worry, just leave it blank.",
85
- },
86
- ],
51
+ {
52
+ key: "scope",
53
+ type: ConnectorConfigFormItemType.Text,
54
+ required: false,
55
+ label: "Scope",
56
+ placeholder: "<scope>",
57
+ description: "The `scope` determines permissions granted by the user's authorization. If you are not sure what to enter, do not worry, just leave it blank."
58
+ }
59
+ ]
87
60
  };
88
- const defaultTimeout = 5000;
61
+ var defaultTimeout = 5e3;
89
62
 
90
- const nullishToUndefined = (input) => {
91
- if (!input) {
92
- return;
93
- }
94
- return input;
63
+ // src/types.ts
64
+ import { z } from "zod";
65
+ var nullishToUndefined = (input) => {
66
+ if (!input) {
67
+ return;
68
+ }
69
+ return input;
95
70
  };
96
- const discordConfigGuard = z.object({
97
- clientId: z.string(),
98
- clientSecret: z.string(),
99
- scope: z.string().optional(),
71
+ var discordConfigGuard = z.object({
72
+ clientId: z.string(),
73
+ clientSecret: z.string(),
74
+ scope: z.string().optional()
100
75
  });
101
- const accessTokenResponseGuard = z.object({
102
- access_token: z.string(),
103
- token_type: z.string(),
104
- expires_in: z.number(),
105
- scope: z.string(),
76
+ var accessTokenResponseGuard = z.object({
77
+ access_token: z.string(),
78
+ token_type: z.string(),
79
+ expires_in: z.number(),
80
+ scope: z.string()
106
81
  });
107
- const userInfoResponseGuard = z.object({
108
- id: z.string(),
109
- username: z.string().nullish().transform(nullishToUndefined),
110
- avatar: z.string().nullish().transform(nullishToUndefined),
111
- email: z.string().nullish().transform(nullishToUndefined),
112
- verified: z.boolean().nullish().transform(nullishToUndefined),
82
+ var userInfoResponseGuard = z.object({
83
+ id: z.string(),
84
+ username: z.string().nullish().transform(nullishToUndefined),
85
+ avatar: z.string().nullish().transform(nullishToUndefined),
86
+ email: z.string().nullish().transform(nullishToUndefined),
87
+ verified: z.boolean().nullish().transform(nullishToUndefined)
113
88
  });
114
- z.object({
115
- error: z.string(),
116
- error_description: z.string(),
89
+ var authorizationCallbackErrorGuard = z.object({
90
+ error: z.string(),
91
+ error_description: z.string()
117
92
  });
118
- const authResponseGuard = z.object({ code: z.string(), redirectUri: z.string() });
93
+ var authResponseGuard = z.object({ code: z.string(), redirectUri: z.string() });
119
94
 
120
- /**
121
- * Discord OAuth2 Connector
122
- * https://discord.com/developers/docs/topics/oauth2
123
- */
124
- const getAuthorizationUri = (getConfig) => async ({ state, redirectUri }) => {
125
- const config = await getConfig(defaultMetadata.id);
126
- validateConfig(config, discordConfigGuard);
127
- const queryParameters = new URLSearchParams({
128
- client_id: config.clientId,
129
- redirect_uri: redirectUri,
130
- response_type: 'code',
131
- scope: config.scope ?? scope,
132
- state,
133
- });
134
- return `${authorizationEndpoint}?${queryParameters.toString()}`;
95
+ // src/index.ts
96
+ var getAuthorizationUri = (getConfig) => async ({ state, redirectUri }) => {
97
+ const config = await getConfig(defaultMetadata.id);
98
+ validateConfig(config, discordConfigGuard);
99
+ const queryParameters = new URLSearchParams({
100
+ client_id: config.clientId,
101
+ redirect_uri: redirectUri,
102
+ response_type: "code",
103
+ scope: config.scope ?? scope,
104
+ state
105
+ });
106
+ return `${authorizationEndpoint}?${queryParameters.toString()}`;
135
107
  };
136
- const getAccessToken = async (config, codeObject) => {
137
- const { code, redirectUri } = codeObject;
138
- const { clientId: client_id, clientSecret: client_secret } = config;
139
- const httpResponse = await got.post(accessTokenEndpoint, {
140
- form: {
141
- client_id,
142
- client_secret,
143
- grant_type: 'authorization_code',
144
- code,
145
- redirect_uri: redirectUri,
146
- },
147
- timeout: { request: defaultTimeout },
108
+ var getAccessToken = async (config, codeObject) => {
109
+ const { code, redirectUri } = codeObject;
110
+ const { clientId: client_id, clientSecret: client_secret } = config;
111
+ const httpResponse = await got.post(accessTokenEndpoint, {
112
+ form: {
113
+ client_id,
114
+ client_secret,
115
+ grant_type: "authorization_code",
116
+ code,
117
+ redirect_uri: redirectUri
118
+ },
119
+ timeout: { request: defaultTimeout }
120
+ });
121
+ const result = accessTokenResponseGuard.safeParse(parseJson(httpResponse.body));
122
+ if (!result.success) {
123
+ throw new ConnectorError(ConnectorErrorCodes.InvalidResponse, result.error);
124
+ }
125
+ const { access_token: accessToken } = result.data;
126
+ assert(accessToken, new ConnectorError(ConnectorErrorCodes.SocialAuthCodeInvalid));
127
+ return { accessToken };
128
+ };
129
+ var getUserInfo = (getConfig) => async (data) => {
130
+ const { code, redirectUri } = await authorizationCallbackHandler(data);
131
+ const config = await getConfig(defaultMetadata.id);
132
+ validateConfig(config, discordConfigGuard);
133
+ const { accessToken } = await getAccessToken(config, { code, redirectUri });
134
+ try {
135
+ const httpResponse = await got.get(userInfoEndpoint, {
136
+ headers: {
137
+ authorization: `Bearer ${accessToken}`
138
+ },
139
+ timeout: { request: defaultTimeout }
148
140
  });
149
- const result = accessTokenResponseGuard.safeParse(parseJson(httpResponse.body));
141
+ const rawData = parseJson(httpResponse.body);
142
+ const result = userInfoResponseGuard.safeParse(rawData);
150
143
  if (!result.success) {
151
- throw new ConnectorError(ConnectorErrorCodes.InvalidResponse, result.error);
144
+ throw new ConnectorError(ConnectorErrorCodes.InvalidResponse, result.error);
152
145
  }
153
- const { access_token: accessToken } = result.data;
154
- assert(accessToken, new ConnectorError(ConnectorErrorCodes.SocialAuthCodeInvalid));
155
- return { accessToken };
156
- };
157
- const getUserInfo = (getConfig) => async (data) => {
158
- const { code, redirectUri } = await authorizationCallbackHandler(data);
159
- const config = await getConfig(defaultMetadata.id);
160
- validateConfig(config, discordConfigGuard);
161
- const { accessToken } = await getAccessToken(config, { code, redirectUri });
162
- try {
163
- const httpResponse = await got.get(userInfoEndpoint, {
164
- headers: {
165
- authorization: `Bearer ${accessToken}`,
166
- },
167
- timeout: { request: defaultTimeout },
168
- });
169
- const rawData = parseJson(httpResponse.body);
170
- const result = userInfoResponseGuard.safeParse(rawData);
171
- if (!result.success) {
172
- throw new ConnectorError(ConnectorErrorCodes.InvalidResponse, result.error);
173
- }
174
- const { id, username: name, avatar, email, verified } = result.data;
175
- const rawUserInfo = {
176
- id,
177
- name,
178
- avatar: conditional(avatar && `https://cdn.discordapp.com/avatars/${id}/${avatar}`),
179
- email: conditional(verified && email),
180
- };
181
- const userInfoResult = socialUserInfoGuard.safeParse(rawUserInfo);
182
- if (!userInfoResult.success) {
183
- throw new ConnectorError(ConnectorErrorCodes.InvalidResponse, userInfoResult.error);
184
- }
185
- return { ...userInfoResult.data, rawData };
146
+ const { id, username: name, avatar, email, verified } = result.data;
147
+ const rawUserInfo = {
148
+ id,
149
+ name,
150
+ avatar: conditional(avatar && `https://cdn.discordapp.com/avatars/${id}/${avatar}`),
151
+ email: conditional(verified && email)
152
+ };
153
+ const userInfoResult = socialUserInfoGuard.safeParse(rawUserInfo);
154
+ if (!userInfoResult.success) {
155
+ throw new ConnectorError(ConnectorErrorCodes.InvalidResponse, userInfoResult.error);
186
156
  }
187
- catch (error) {
188
- if (error instanceof HTTPError) {
189
- const { statusCode, body: rawBody } = error.response;
190
- if (statusCode === 401) {
191
- throw new ConnectorError(ConnectorErrorCodes.SocialAccessTokenInvalid);
192
- }
193
- throw new ConnectorError(ConnectorErrorCodes.General, JSON.stringify(rawBody));
194
- }
195
- throw error;
157
+ return { ...userInfoResult.data, rawData };
158
+ } catch (error) {
159
+ if (error instanceof HTTPError) {
160
+ const { statusCode, body: rawBody } = error.response;
161
+ if (statusCode === 401) {
162
+ throw new ConnectorError(ConnectorErrorCodes.SocialAccessTokenInvalid);
163
+ }
164
+ throw new ConnectorError(ConnectorErrorCodes.General, JSON.stringify(rawBody));
196
165
  }
166
+ throw error;
167
+ }
197
168
  };
198
- const authorizationCallbackHandler = async (parameterObject) => {
199
- const result = authResponseGuard.safeParse(parameterObject);
200
- if (!result.success) {
201
- throw new ConnectorError(ConnectorErrorCodes.General, JSON.stringify(parameterObject));
202
- }
203
- return result.data;
169
+ var authorizationCallbackHandler = async (parameterObject) => {
170
+ const result = authResponseGuard.safeParse(parameterObject);
171
+ if (!result.success) {
172
+ throw new ConnectorError(ConnectorErrorCodes.General, JSON.stringify(parameterObject));
173
+ }
174
+ return result.data;
204
175
  };
205
- const createDiscordConnector = async ({ getConfig }) => {
206
- return {
207
- metadata: defaultMetadata,
208
- type: ConnectorType.Social,
209
- configGuard: discordConfigGuard,
210
- getAuthorizationUri: getAuthorizationUri(getConfig),
211
- getUserInfo: getUserInfo(getConfig),
212
- };
176
+ var createDiscordConnector = async ({ getConfig }) => {
177
+ return {
178
+ metadata: defaultMetadata,
179
+ type: ConnectorType.Social,
180
+ configGuard: discordConfigGuard,
181
+ getAuthorizationUri: getAuthorizationUri(getConfig),
182
+ getUserInfo: getUserInfo(getConfig)
183
+ };
213
184
  };
214
-
215
- export { createDiscordConnector as default, getAccessToken };
185
+ var index_default = createDiscordConnector;
186
+ export {
187
+ index_default as default,
188
+ getAccessToken
189
+ };
190
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/constant.ts","../src/types.ts"],"sourcesContent":["/**\n * Discord OAuth2 Connector\n * https://discord.com/developers/docs/topics/oauth2\n */\n\nimport { assert, conditional } from '@silverhand/essentials';\nimport { got, HTTPError } from 'got';\n\nimport type {\n GetConnectorConfig,\n GetAuthorizationUri,\n GetUserInfo,\n CreateConnector,\n SocialConnector,\n} from '@logto/connector-kit';\nimport {\n socialUserInfoGuard,\n validateConfig,\n ConnectorError,\n ConnectorErrorCodes,\n ConnectorType,\n parseJson,\n} from '@logto/connector-kit';\n\nimport {\n defaultMetadata,\n scope as defaultScope,\n authorizationEndpoint,\n accessTokenEndpoint,\n defaultTimeout,\n userInfoEndpoint,\n} from './constant.js';\nimport type { DiscordConfig } from './types.js';\nimport {\n discordConfigGuard,\n authResponseGuard,\n accessTokenResponseGuard,\n userInfoResponseGuard,\n} from './types.js';\n\nconst getAuthorizationUri =\n (getConfig: GetConnectorConfig): GetAuthorizationUri =>\n async ({ state, redirectUri }) => {\n const config = await getConfig(defaultMetadata.id);\n validateConfig(config, discordConfigGuard);\n\n const queryParameters = new URLSearchParams({\n client_id: config.clientId,\n redirect_uri: redirectUri,\n response_type: 'code',\n scope: config.scope ?? defaultScope,\n state,\n });\n\n return `${authorizationEndpoint}?${queryParameters.toString()}`;\n };\n\nexport const getAccessToken = async (\n config: DiscordConfig,\n codeObject: { code: string; redirectUri: string }\n) => {\n const { code, redirectUri } = codeObject;\n\n const { clientId: client_id, clientSecret: client_secret } = config;\n\n const httpResponse = await got.post(accessTokenEndpoint, {\n form: {\n client_id,\n client_secret,\n grant_type: 'authorization_code',\n code,\n redirect_uri: redirectUri,\n },\n timeout: { request: defaultTimeout },\n });\n\n const result = accessTokenResponseGuard.safeParse(parseJson(httpResponse.body));\n\n if (!result.success) {\n throw new ConnectorError(ConnectorErrorCodes.InvalidResponse, result.error);\n }\n\n const { access_token: accessToken } = result.data;\n\n assert(accessToken, new ConnectorError(ConnectorErrorCodes.SocialAuthCodeInvalid));\n\n return { accessToken };\n};\n\nconst getUserInfo =\n (getConfig: GetConnectorConfig): GetUserInfo =>\n async (data) => {\n const { code, redirectUri } = await authorizationCallbackHandler(data);\n const config = await getConfig(defaultMetadata.id);\n validateConfig(config, discordConfigGuard);\n const { accessToken } = await getAccessToken(config, { code, redirectUri });\n\n try {\n const httpResponse = await got.get(userInfoEndpoint, {\n headers: {\n authorization: `Bearer ${accessToken}`,\n },\n timeout: { request: defaultTimeout },\n });\n const rawData = parseJson(httpResponse.body);\n const result = userInfoResponseGuard.safeParse(rawData);\n\n if (!result.success) {\n throw new ConnectorError(ConnectorErrorCodes.InvalidResponse, result.error);\n }\n\n const { id, username: name, avatar, email, verified } = result.data;\n\n const rawUserInfo = {\n id,\n name,\n avatar: conditional(avatar && `https://cdn.discordapp.com/avatars/${id}/${avatar}`),\n email: conditional(verified && email),\n };\n\n const userInfoResult = socialUserInfoGuard.safeParse(rawUserInfo);\n\n if (!userInfoResult.success) {\n throw new ConnectorError(ConnectorErrorCodes.InvalidResponse, userInfoResult.error);\n }\n\n return { ...userInfoResult.data, rawData };\n } catch (error: unknown) {\n if (error instanceof HTTPError) {\n const { statusCode, body: rawBody } = error.response;\n\n if (statusCode === 401) {\n throw new ConnectorError(ConnectorErrorCodes.SocialAccessTokenInvalid);\n }\n\n throw new ConnectorError(ConnectorErrorCodes.General, JSON.stringify(rawBody));\n }\n\n throw error;\n }\n };\n\nconst authorizationCallbackHandler = async (parameterObject: unknown) => {\n const result = authResponseGuard.safeParse(parameterObject);\n\n if (!result.success) {\n throw new ConnectorError(ConnectorErrorCodes.General, JSON.stringify(parameterObject));\n }\n\n return result.data;\n};\n\nconst createDiscordConnector: CreateConnector<SocialConnector> = async ({ getConfig }) => {\n return {\n metadata: defaultMetadata,\n type: ConnectorType.Social,\n configGuard: discordConfigGuard,\n getAuthorizationUri: getAuthorizationUri(getConfig),\n getUserInfo: getUserInfo(getConfig),\n };\n};\n\nexport default createDiscordConnector;\n","import type { ConnectorMetadata } from '@logto/connector-kit';\nimport { ConnectorConfigFormItemType, ConnectorPlatform } from '@logto/connector-kit';\n\n/**\n * Base authorization URL.\n * https://discord.com/developers/docs/topics/oauth2#shared-resources-oauth2-urls\n */\nexport const authorizationEndpoint = 'https://discord.com/oauth2/authorize';\n\n/**\n * Discord exposes different versions of the API, You should specify which version to use by including it in your requests.\n * https://discord.com/developers/docs/reference#api-reference\n */\nexport const accessTokenEndpoint = 'https://discord.com/api/v10/oauth2/token';\nexport const userInfoEndpoint = 'https://discord.com/api/v10/users/@me';\n\n/**\n * OAuth2 Scopes\n * https://discord.com/developers/docs/topics/oauth2#shared-resources-oauth2-scopes\n */\nexport const scope = 'identify email';\n\nexport const defaultMetadata: ConnectorMetadata = {\n id: 'discord-universal',\n target: 'discord',\n platform: ConnectorPlatform.Universal,\n name: {\n en: 'Discord',\n },\n logo: './logo.svg',\n logoDark: null,\n description: {\n en: 'Discord is the easiest way to talk over voice, video, and text.',\n 'pt-PT': 'Discord é a forma mais fácil de comunicar por voz, vídeo e texto.',\n 'zh-CN': 'Discord 是一款专为社群设计的免费网络实时通话软件与数字发行平台。',\n 'tr-TR': 'Discord, sesli, görüntülü ve metin üzerinden konuşmanın en kolay yoludur.',\n ko: 'Discord는 음성, 비디오 및 텍스트로 대화하는 가장 쉬운 방법입니다.',\n },\n readme: './README.md',\n formItems: [\n {\n key: 'clientId',\n type: ConnectorConfigFormItemType.Text,\n required: true,\n label: 'Client ID',\n placeholder: '<client-id>',\n },\n {\n key: 'clientSecret',\n type: ConnectorConfigFormItemType.Text,\n required: true,\n label: 'Client Secret',\n placeholder: '<client-secret>',\n },\n {\n key: 'scope',\n type: ConnectorConfigFormItemType.Text,\n required: false,\n label: 'Scope',\n placeholder: '<scope>',\n description:\n \"The `scope` determines permissions granted by the user's authorization. If you are not sure what to enter, do not worry, just leave it blank.\",\n },\n ],\n};\n\nexport const defaultTimeout = 5000;\n","import type { Nullable, Optional } from '@silverhand/essentials';\nimport { z } from 'zod';\n\nconst nullishToUndefined = <T = unknown>(input: Nullable<T>): Optional<T> => {\n if (!input) {\n return;\n }\n\n return input;\n};\n\nexport const discordConfigGuard = z.object({\n clientId: z.string(),\n clientSecret: z.string(),\n scope: z.string().optional(),\n});\n\nexport type DiscordConfig = z.infer<typeof discordConfigGuard>;\n\nexport const accessTokenResponseGuard = z.object({\n access_token: z.string(),\n token_type: z.string(),\n expires_in: z.number(),\n scope: z.string(),\n});\n\nexport type AccessTokenResponse = z.infer<typeof accessTokenResponseGuard>;\n\nexport const userInfoResponseGuard = z.object({\n id: z.string(),\n username: z.string().nullish().transform(nullishToUndefined),\n avatar: z.string().nullish().transform(nullishToUndefined),\n email: z.string().nullish().transform(nullishToUndefined),\n verified: z.boolean().nullish().transform(nullishToUndefined),\n});\n\nexport type UserInfoResponse = z.infer<typeof userInfoResponseGuard>;\n\nexport const authorizationCallbackErrorGuard = z.object({\n error: z.string(),\n error_description: z.string(),\n});\n\nexport const authResponseGuard = z.object({ code: z.string(), redirectUri: z.string() });\n"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -1,14 +1,14 @@
1
1
  {
2
2
  "name": "@logto/connector-discord",
3
- "version": "1.3.1",
3
+ "version": "1.4.1",
4
4
  "description": "Discord connector implementation.",
5
5
  "author": "ZR3SYSTEMS. <https://github.com/FlurryNight>",
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,19 +0,0 @@
1
- import type { ConnectorMetadata } from '@logto/connector-kit';
2
- /**
3
- * Base authorization URL.
4
- * https://discord.com/developers/docs/topics/oauth2#shared-resources-oauth2-urls
5
- */
6
- export declare const authorizationEndpoint = "https://discord.com/oauth2/authorize";
7
- /**
8
- * Discord exposes different versions of the API, You should specify which version to use by including it in your requests.
9
- * https://discord.com/developers/docs/reference#api-reference
10
- */
11
- export declare const accessTokenEndpoint = "https://discord.com/api/v10/oauth2/token";
12
- export declare const userInfoEndpoint = "https://discord.com/api/v10/users/@me";
13
- /**
14
- * OAuth2 Scopes
15
- * https://discord.com/developers/docs/topics/oauth2#shared-resources-oauth2-scopes
16
- */
17
- export declare const scope = "identify email";
18
- export declare const defaultMetadata: ConnectorMetadata;
19
- export declare const defaultTimeout = 5000;
package/lib/index.d.ts DELETED
@@ -1,14 +0,0 @@
1
- /**
2
- * Discord OAuth2 Connector
3
- * https://discord.com/developers/docs/topics/oauth2
4
- */
5
- import type { CreateConnector, SocialConnector } from '@logto/connector-kit';
6
- import type { DiscordConfig } from './types.js';
7
- export declare const getAccessToken: (config: DiscordConfig, codeObject: {
8
- code: string;
9
- redirectUri: string;
10
- }) => Promise<{
11
- accessToken: string;
12
- }>;
13
- declare const createDiscordConnector: CreateConnector<SocialConnector>;
14
- export default createDiscordConnector;
package/lib/mock.d.ts DELETED
@@ -1,4 +0,0 @@
1
- export declare const mockedConfig: {
2
- clientId: string;
3
- clientSecret: string;
4
- };
package/lib/types.d.ts DELETED
@@ -1,72 +0,0 @@
1
- import { z } from 'zod';
2
- export declare const discordConfigGuard: z.ZodObject<{
3
- clientId: z.ZodString;
4
- clientSecret: z.ZodString;
5
- scope: z.ZodOptional<z.ZodString>;
6
- }, "strip", z.ZodTypeAny, {
7
- clientId: string;
8
- clientSecret: string;
9
- scope?: string | undefined;
10
- }, {
11
- clientId: string;
12
- clientSecret: string;
13
- scope?: string | undefined;
14
- }>;
15
- export type DiscordConfig = z.infer<typeof discordConfigGuard>;
16
- export declare const accessTokenResponseGuard: z.ZodObject<{
17
- access_token: z.ZodString;
18
- token_type: z.ZodString;
19
- expires_in: z.ZodNumber;
20
- scope: z.ZodString;
21
- }, "strip", z.ZodTypeAny, {
22
- scope: string;
23
- access_token: string;
24
- token_type: string;
25
- expires_in: number;
26
- }, {
27
- scope: string;
28
- access_token: string;
29
- token_type: string;
30
- expires_in: number;
31
- }>;
32
- export type AccessTokenResponse = z.infer<typeof accessTokenResponseGuard>;
33
- export declare const userInfoResponseGuard: z.ZodObject<{
34
- id: z.ZodString;
35
- username: z.ZodEffects<z.ZodOptional<z.ZodNullable<z.ZodString>>, string | undefined, string | null | undefined>;
36
- avatar: z.ZodEffects<z.ZodOptional<z.ZodNullable<z.ZodString>>, string | undefined, string | null | undefined>;
37
- email: z.ZodEffects<z.ZodOptional<z.ZodNullable<z.ZodString>>, string | undefined, string | null | undefined>;
38
- verified: z.ZodEffects<z.ZodOptional<z.ZodNullable<z.ZodBoolean>>, boolean | undefined, boolean | null | undefined>;
39
- }, "strip", z.ZodTypeAny, {
40
- id: string;
41
- username?: string | undefined;
42
- avatar?: string | undefined;
43
- email?: string | undefined;
44
- verified?: boolean | undefined;
45
- }, {
46
- id: string;
47
- username?: string | null | undefined;
48
- avatar?: string | null | undefined;
49
- email?: string | null | undefined;
50
- verified?: boolean | null | undefined;
51
- }>;
52
- export type UserInfoResponse = z.infer<typeof userInfoResponseGuard>;
53
- export declare const authorizationCallbackErrorGuard: z.ZodObject<{
54
- error: z.ZodString;
55
- error_description: z.ZodString;
56
- }, "strip", z.ZodTypeAny, {
57
- error: string;
58
- error_description: string;
59
- }, {
60
- error: string;
61
- error_description: string;
62
- }>;
63
- export declare const authResponseGuard: z.ZodObject<{
64
- code: z.ZodString;
65
- redirectUri: z.ZodString;
66
- }, "strip", z.ZodTypeAny, {
67
- code: string;
68
- redirectUri: string;
69
- }, {
70
- code: string;
71
- redirectUri: string;
72
- }>;