@logto/cli 1.0.0-rc.1 → 1.0.0-rc.3

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.
Files changed (58) hide show
  1. package/LICENSE +6 -0
  2. package/lib/commands/connector/add.d.ts +1 -0
  3. package/lib/commands/connector/add.js +13 -3
  4. package/lib/commands/connector/remove.js +1 -1
  5. package/lib/commands/connector/utils.d.ts +2 -2
  6. package/lib/commands/connector/utils.js +6 -5
  7. package/lib/commands/database/alteration/index.js +1 -1
  8. package/lib/commands/database/alteration/index.test.js +1 -1
  9. package/lib/commands/database/alteration/utils.js +1 -1
  10. package/lib/commands/database/alteration/version.js +1 -1
  11. package/lib/commands/database/config.js +39 -17
  12. package/lib/commands/database/index.js +2 -1
  13. package/lib/commands/database/seed/cloud.d.ts +10 -0
  14. package/lib/commands/database/seed/cloud.js +34 -0
  15. package/lib/commands/database/seed/index.d.ts +2 -4
  16. package/lib/commands/database/seed/index.js +24 -129
  17. package/lib/commands/database/seed/oidc-config.d.ts +2 -0
  18. package/lib/commands/database/seed/oidc-config.js +45 -2
  19. package/lib/commands/database/seed/tables.d.ts +4 -0
  20. package/lib/commands/database/seed/tables.js +109 -0
  21. package/lib/commands/database/seed/tenant.d.ts +5 -0
  22. package/lib/commands/database/seed/tenant.js +53 -0
  23. package/lib/commands/database/system.d.ts +3 -0
  24. package/lib/commands/database/system.js +63 -0
  25. package/lib/commands/database/{utilities.js → utils.js} +2 -2
  26. package/lib/commands/install/index.d.ts +2 -2
  27. package/lib/commands/install/index.js +9 -8
  28. package/lib/commands/install/utils.d.ts +1 -1
  29. package/lib/commands/install/utils.js +3 -3
  30. package/lib/connector/consts.d.ts +7 -0
  31. package/lib/connector/consts.js +10 -0
  32. package/lib/connector/factories.d.ts +2 -0
  33. package/lib/connector/factories.js +34 -0
  34. package/lib/connector/index.d.ts +4 -0
  35. package/lib/connector/index.js +4 -0
  36. package/lib/connector/loader.d.ts +2 -0
  37. package/lib/connector/loader.js +48 -0
  38. package/lib/connector/types.d.ts +12 -0
  39. package/lib/connector/types.js +1 -0
  40. package/lib/connector/utils.d.ts +303 -0
  41. package/lib/connector/utils.js +51 -0
  42. package/lib/database.js +3 -4
  43. package/lib/index.js +1 -1
  44. package/lib/package-json.d.ts +2 -3
  45. package/lib/package-json.js +10 -14
  46. package/lib/queries/database.d.ts +2 -0
  47. package/lib/queries/database.js +7 -0
  48. package/lib/queries/logto-config.d.ts +2 -2
  49. package/lib/queries/logto-config.js +6 -5
  50. package/lib/queries/system.d.ts +5 -0
  51. package/lib/queries/system.js +13 -4
  52. package/lib/queries/system.test.js +7 -3
  53. package/package.json +12 -16
  54. /package/lib/commands/database/{utilities.d.ts → utils.d.ts} +0 -0
  55. /package/lib/{test-utilities.d.ts → test-utils.d.ts} +0 -0
  56. /package/lib/{test-utilities.js → test-utils.js} +0 -0
  57. /package/lib/{utilities.d.ts → utils.d.ts} +0 -0
  58. /package/lib/{utilities.js → utils.js} +0 -0
@@ -0,0 +1,303 @@
1
+ import type { AllConnector, BaseConnector, GetConnectorConfig } from '@logto/connector-kit';
2
+ import { ConnectorType } from '@logto/connector-kit';
3
+ import type { ConnectorFactory } from './types.js';
4
+ export declare function validateConnectorModule(connector: Partial<BaseConnector<ConnectorType>>): asserts connector is BaseConnector<ConnectorType>;
5
+ export declare const readUrl: (url: string, baseUrl: string, type: 'text' | 'svg') => Promise<string>;
6
+ export declare const parseMetadata: (metadata: AllConnector['metadata'], packagePath: string) => Promise<AllConnector['metadata']>;
7
+ export declare const buildRawConnector: (connectorFactory: ConnectorFactory, getConnectorConfig?: GetConnectorConfig) => Promise<{
8
+ rawConnector: AllConnector;
9
+ rawMetadata: {
10
+ isStandard?: boolean | undefined;
11
+ configTemplate?: string | undefined;
12
+ formItems?: ({
13
+ placeholder?: string | undefined;
14
+ required?: boolean | undefined;
15
+ defaultValue?: unknown;
16
+ showConditions?: {
17
+ expectValue?: unknown;
18
+ targetKey: string;
19
+ }[] | undefined;
20
+ description?: string | undefined;
21
+ tooltip?: string | undefined;
22
+ type: import("@logto/connector-kit").ConnectorConfigFormItemType.Select;
23
+ selectItems: {
24
+ value: string;
25
+ title: string;
26
+ }[];
27
+ key: string;
28
+ label: string;
29
+ } | {
30
+ placeholder?: string | undefined;
31
+ required?: boolean | undefined;
32
+ defaultValue?: unknown;
33
+ showConditions?: {
34
+ expectValue?: unknown;
35
+ targetKey: string;
36
+ }[] | undefined;
37
+ description?: string | undefined;
38
+ tooltip?: string | undefined;
39
+ type: import("@logto/connector-kit").ConnectorConfigFormItemType.Text | import("@logto/connector-kit").ConnectorConfigFormItemType.Number | import("@logto/connector-kit").ConnectorConfigFormItemType.MultilineText | import("@logto/connector-kit").ConnectorConfigFormItemType.Switch | import("@logto/connector-kit").ConnectorConfigFormItemType.Json;
40
+ key: string;
41
+ label: string;
42
+ })[] | undefined;
43
+ description: {
44
+ en: string;
45
+ } & {
46
+ "af-ZA"?: string | undefined;
47
+ "am-ET"?: string | undefined;
48
+ "ar-AR"?: string | undefined;
49
+ "as-IN"?: string | undefined;
50
+ "az-AZ"?: string | undefined;
51
+ "be-BY"?: string | undefined;
52
+ "bg-BG"?: string | undefined;
53
+ "bn-IN"?: string | undefined;
54
+ "br-FR"?: string | undefined;
55
+ "bs-BA"?: string | undefined;
56
+ "ca-ES"?: string | undefined;
57
+ "cb-IQ"?: string | undefined;
58
+ "co-FR"?: string | undefined;
59
+ "cs-CZ"?: string | undefined;
60
+ "cx-PH"?: string | undefined;
61
+ "cy-GB"?: string | undefined;
62
+ "da-DK"?: string | undefined;
63
+ de?: string | undefined;
64
+ "de-DE"?: string | undefined;
65
+ "el-GR"?: string | undefined;
66
+ "en-GB"?: string | undefined;
67
+ "en-US"?: string | undefined;
68
+ "eo-EO"?: string | undefined;
69
+ es?: string | undefined;
70
+ "es-ES"?: string | undefined;
71
+ "es-419"?: string | undefined;
72
+ "et-EE"?: string | undefined;
73
+ "eu-ES"?: string | undefined;
74
+ "fa-IR"?: string | undefined;
75
+ "ff-NG"?: string | undefined;
76
+ "fi-FI"?: string | undefined;
77
+ "fo-FO"?: string | undefined;
78
+ fr?: string | undefined;
79
+ "fr-CA"?: string | undefined;
80
+ "fr-FR"?: string | undefined;
81
+ "fy-NL"?: string | undefined;
82
+ "ga-IE"?: string | undefined;
83
+ "gl-ES"?: string | undefined;
84
+ "gn-PY"?: string | undefined;
85
+ "gu-IN"?: string | undefined;
86
+ "ha-NG"?: string | undefined;
87
+ "he-IL"?: string | undefined;
88
+ "hi-IN"?: string | undefined;
89
+ "hr-HR"?: string | undefined;
90
+ "ht-HT"?: string | undefined;
91
+ "hu-HU"?: string | undefined;
92
+ "hy-AM"?: string | undefined;
93
+ "id-ID"?: string | undefined;
94
+ "ik-US"?: string | undefined;
95
+ "is-IS"?: string | undefined;
96
+ it?: string | undefined;
97
+ "it-IT"?: string | undefined;
98
+ "iu-CA"?: string | undefined;
99
+ ja?: string | undefined;
100
+ "ja-JP"?: string | undefined;
101
+ "ja-KS"?: string | undefined;
102
+ "jv-ID"?: string | undefined;
103
+ "ka-GE"?: string | undefined;
104
+ "kk-KZ"?: string | undefined;
105
+ "km-KH"?: string | undefined;
106
+ "kn-IN"?: string | undefined;
107
+ ko?: string | undefined;
108
+ "ko-KR"?: string | undefined;
109
+ "ku-TR"?: string | undefined;
110
+ "ky-KG"?: string | undefined;
111
+ "lo-LA"?: string | undefined;
112
+ "lt-LT"?: string | undefined;
113
+ "lv-LV"?: string | undefined;
114
+ "mg-MG"?: string | undefined;
115
+ "mk-MK"?: string | undefined;
116
+ "ml-IN"?: string | undefined;
117
+ "mn-MN"?: string | undefined;
118
+ "mr-IN"?: string | undefined;
119
+ "ms-MY"?: string | undefined;
120
+ "mt-MT"?: string | undefined;
121
+ "my-MM"?: string | undefined;
122
+ "nb-NO"?: string | undefined;
123
+ "ne-NP"?: string | undefined;
124
+ "nl-BE"?: string | undefined;
125
+ "nl-NL"?: string | undefined;
126
+ "nn-NO"?: string | undefined;
127
+ "or-IN"?: string | undefined;
128
+ "pa-IN"?: string | undefined;
129
+ "pl-PL"?: string | undefined;
130
+ "ps-AF"?: string | undefined;
131
+ pt?: string | undefined;
132
+ "pt-BR"?: string | undefined;
133
+ "pt-PT"?: string | undefined;
134
+ "ro-RO"?: string | undefined;
135
+ ru?: string | undefined;
136
+ "ru-RU"?: string | undefined;
137
+ "rw-RW"?: string | undefined;
138
+ "sc-IT"?: string | undefined;
139
+ "si-LK"?: string | undefined;
140
+ "sk-SK"?: string | undefined;
141
+ "sl-SI"?: string | undefined;
142
+ "sn-ZW"?: string | undefined;
143
+ "sq-AL"?: string | undefined;
144
+ "sr-RS"?: string | undefined;
145
+ "sv-SE"?: string | undefined;
146
+ "sw-KE"?: string | undefined;
147
+ "sy-SY"?: string | undefined;
148
+ "sz-PL"?: string | undefined;
149
+ "ta-IN"?: string | undefined;
150
+ "te-IN"?: string | undefined;
151
+ "tg-TJ"?: string | undefined;
152
+ "th-TH"?: string | undefined;
153
+ "tl-PH"?: string | undefined;
154
+ tr?: string | undefined;
155
+ "tr-TR"?: string | undefined;
156
+ "tt-RU"?: string | undefined;
157
+ "tz-MA"?: string | undefined;
158
+ "uk-UA"?: string | undefined;
159
+ "ur-PK"?: string | undefined;
160
+ "uz-UZ"?: string | undefined;
161
+ "vi-VN"?: string | undefined;
162
+ zh?: string | undefined;
163
+ "zh-CN"?: string | undefined;
164
+ "zh-HK"?: string | undefined;
165
+ "zh-MO"?: string | undefined;
166
+ "zh-TW"?: string | undefined;
167
+ "zz-TR"?: string | undefined;
168
+ };
169
+ id: string;
170
+ target: string;
171
+ platform: import("@logto/connector-kit").ConnectorPlatform | null;
172
+ name: {
173
+ en: string;
174
+ } & {
175
+ "af-ZA"?: string | undefined;
176
+ "am-ET"?: string | undefined;
177
+ "ar-AR"?: string | undefined;
178
+ "as-IN"?: string | undefined;
179
+ "az-AZ"?: string | undefined;
180
+ "be-BY"?: string | undefined;
181
+ "bg-BG"?: string | undefined;
182
+ "bn-IN"?: string | undefined;
183
+ "br-FR"?: string | undefined;
184
+ "bs-BA"?: string | undefined;
185
+ "ca-ES"?: string | undefined;
186
+ "cb-IQ"?: string | undefined;
187
+ "co-FR"?: string | undefined;
188
+ "cs-CZ"?: string | undefined;
189
+ "cx-PH"?: string | undefined;
190
+ "cy-GB"?: string | undefined;
191
+ "da-DK"?: string | undefined;
192
+ de?: string | undefined;
193
+ "de-DE"?: string | undefined;
194
+ "el-GR"?: string | undefined;
195
+ "en-GB"?: string | undefined;
196
+ "en-US"?: string | undefined;
197
+ "eo-EO"?: string | undefined;
198
+ es?: string | undefined;
199
+ "es-ES"?: string | undefined;
200
+ "es-419"?: string | undefined;
201
+ "et-EE"?: string | undefined;
202
+ "eu-ES"?: string | undefined;
203
+ "fa-IR"?: string | undefined;
204
+ "ff-NG"?: string | undefined;
205
+ "fi-FI"?: string | undefined;
206
+ "fo-FO"?: string | undefined;
207
+ fr?: string | undefined;
208
+ "fr-CA"?: string | undefined;
209
+ "fr-FR"?: string | undefined;
210
+ "fy-NL"?: string | undefined;
211
+ "ga-IE"?: string | undefined;
212
+ "gl-ES"?: string | undefined;
213
+ "gn-PY"?: string | undefined;
214
+ "gu-IN"?: string | undefined;
215
+ "ha-NG"?: string | undefined;
216
+ "he-IL"?: string | undefined;
217
+ "hi-IN"?: string | undefined;
218
+ "hr-HR"?: string | undefined;
219
+ "ht-HT"?: string | undefined;
220
+ "hu-HU"?: string | undefined;
221
+ "hy-AM"?: string | undefined;
222
+ "id-ID"?: string | undefined;
223
+ "ik-US"?: string | undefined;
224
+ "is-IS"?: string | undefined;
225
+ it?: string | undefined;
226
+ "it-IT"?: string | undefined;
227
+ "iu-CA"?: string | undefined;
228
+ ja?: string | undefined;
229
+ "ja-JP"?: string | undefined;
230
+ "ja-KS"?: string | undefined;
231
+ "jv-ID"?: string | undefined;
232
+ "ka-GE"?: string | undefined;
233
+ "kk-KZ"?: string | undefined;
234
+ "km-KH"?: string | undefined;
235
+ "kn-IN"?: string | undefined;
236
+ ko?: string | undefined;
237
+ "ko-KR"?: string | undefined;
238
+ "ku-TR"?: string | undefined;
239
+ "ky-KG"?: string | undefined;
240
+ "lo-LA"?: string | undefined;
241
+ "lt-LT"?: string | undefined;
242
+ "lv-LV"?: string | undefined;
243
+ "mg-MG"?: string | undefined;
244
+ "mk-MK"?: string | undefined;
245
+ "ml-IN"?: string | undefined;
246
+ "mn-MN"?: string | undefined;
247
+ "mr-IN"?: string | undefined;
248
+ "ms-MY"?: string | undefined;
249
+ "mt-MT"?: string | undefined;
250
+ "my-MM"?: string | undefined;
251
+ "nb-NO"?: string | undefined;
252
+ "ne-NP"?: string | undefined;
253
+ "nl-BE"?: string | undefined;
254
+ "nl-NL"?: string | undefined;
255
+ "nn-NO"?: string | undefined;
256
+ "or-IN"?: string | undefined;
257
+ "pa-IN"?: string | undefined;
258
+ "pl-PL"?: string | undefined;
259
+ "ps-AF"?: string | undefined;
260
+ pt?: string | undefined;
261
+ "pt-BR"?: string | undefined;
262
+ "pt-PT"?: string | undefined;
263
+ "ro-RO"?: string | undefined;
264
+ ru?: string | undefined;
265
+ "ru-RU"?: string | undefined;
266
+ "rw-RW"?: string | undefined;
267
+ "sc-IT"?: string | undefined;
268
+ "si-LK"?: string | undefined;
269
+ "sk-SK"?: string | undefined;
270
+ "sl-SI"?: string | undefined;
271
+ "sn-ZW"?: string | undefined;
272
+ "sq-AL"?: string | undefined;
273
+ "sr-RS"?: string | undefined;
274
+ "sv-SE"?: string | undefined;
275
+ "sw-KE"?: string | undefined;
276
+ "sy-SY"?: string | undefined;
277
+ "sz-PL"?: string | undefined;
278
+ "ta-IN"?: string | undefined;
279
+ "te-IN"?: string | undefined;
280
+ "tg-TJ"?: string | undefined;
281
+ "th-TH"?: string | undefined;
282
+ "tl-PH"?: string | undefined;
283
+ tr?: string | undefined;
284
+ "tr-TR"?: string | undefined;
285
+ "tt-RU"?: string | undefined;
286
+ "tz-MA"?: string | undefined;
287
+ "uk-UA"?: string | undefined;
288
+ "ur-PK"?: string | undefined;
289
+ "uz-UZ"?: string | undefined;
290
+ "vi-VN"?: string | undefined;
291
+ zh?: string | undefined;
292
+ "zh-CN"?: string | undefined;
293
+ "zh-HK"?: string | undefined;
294
+ "zh-MO"?: string | undefined;
295
+ "zh-TW"?: string | undefined;
296
+ "zz-TR"?: string | undefined;
297
+ };
298
+ logo: string;
299
+ logoDark: string | null;
300
+ readme: string;
301
+ };
302
+ }>;
303
+ export declare const isKeyInObject: <Key extends string>(object: unknown, key: Key) => object is Record<string, unknown> & Record<Key, unknown>;
@@ -0,0 +1,51 @@
1
+ import { existsSync } from 'fs';
2
+ import { readFile } from 'fs/promises';
3
+ import path from 'path';
4
+ import { ConnectorError, ConnectorErrorCodes, ConnectorType } from '@logto/connector-kit';
5
+ import { notImplemented } from './consts.js';
6
+ export function validateConnectorModule(connector) {
7
+ if (!connector.metadata) {
8
+ throw new ConnectorError(ConnectorErrorCodes.InvalidMetadata);
9
+ }
10
+ if (!connector.configGuard) {
11
+ throw new ConnectorError(ConnectorErrorCodes.InvalidConfigGuard);
12
+ }
13
+ if (!connector.type || !Object.values(ConnectorType).includes(connector.type)) {
14
+ throw new ConnectorError(ConnectorErrorCodes.UnexpectedType);
15
+ }
16
+ }
17
+ export const readUrl = async (url, baseUrl, type) => {
18
+ if (!url) {
19
+ return url;
20
+ }
21
+ if (type !== 'text' && url.startsWith('http')) {
22
+ return url;
23
+ }
24
+ if (!existsSync(path.join(baseUrl, url))) {
25
+ return url;
26
+ }
27
+ if (type === 'svg') {
28
+ const data = await readFile(path.join(baseUrl, url));
29
+ return `data:image/svg+xml;base64,${data.toString('base64')}`;
30
+ }
31
+ return readFile(path.join(baseUrl, url), 'utf8');
32
+ };
33
+ export const parseMetadata = async (metadata, packagePath) => {
34
+ return {
35
+ ...metadata,
36
+ logo: await readUrl(metadata.logo, packagePath, 'svg'),
37
+ logoDark: metadata.logoDark && (await readUrl(metadata.logoDark, packagePath, 'svg')),
38
+ readme: await readUrl(metadata.readme, packagePath, 'text'),
39
+ configTemplate: metadata.configTemplate && (await readUrl(metadata.configTemplate, packagePath, 'text')),
40
+ };
41
+ };
42
+ export const buildRawConnector = async (connectorFactory, getConnectorConfig) => {
43
+ const { createConnector, path: packagePath } = connectorFactory;
44
+ const rawConnector = await createConnector({
45
+ getConfig: getConnectorConfig ?? notImplemented,
46
+ });
47
+ validateConnectorModule(rawConnector);
48
+ const rawMetadata = await parseMetadata(rawConnector.metadata, packagePath);
49
+ return { rawConnector, rawMetadata };
50
+ };
51
+ export const isKeyInObject = (object, key) => object !== null && typeof object === 'object' && key in object;
package/lib/database.js CHANGED
@@ -1,10 +1,10 @@
1
1
  import { convertToPrimitiveOrSql } from '@logto/shared';
2
2
  import { assert } from '@silverhand/essentials';
3
3
  import decamelize from 'decamelize';
4
+ import { DatabaseError } from 'pg-protocol';
4
5
  import { createPool, parseDsn, sql, stringifyDsn } from 'slonik';
5
6
  import { createInterceptors } from 'slonik-interceptor-preset';
6
- import { z } from 'zod';
7
- import { ConfigKey, getCliConfigWithPrompt, log } from './utilities.js';
7
+ import { ConfigKey, getCliConfigWithPrompt, log } from './utils.js';
8
8
  export const defaultDatabaseUrl = 'postgresql://localhost:5432/logto';
9
9
  export const getDatabaseUrlFromConfig = async () => (await getCliConfigWithPrompt({
10
10
  key: ConfigKey.DatabaseUrl,
@@ -29,10 +29,9 @@ export const createPoolAndDatabaseIfNeeded = async () => {
29
29
  return await createPoolFromConfig();
30
30
  }
31
31
  catch (error) {
32
- const result = z.object({ code: z.string() }).safeParse(error);
33
32
  // Database does not exist, try to create one
34
33
  // https://www.postgresql.org/docs/14/errcodes-appendix.html
35
- if (!(result.success && result.data.code === '3D000')) {
34
+ if (!(error instanceof DatabaseError && error.code === '3D000')) {
36
35
  log.error(error);
37
36
  }
38
37
  const databaseUrl = await getDatabaseUrlFromConfig();
package/lib/index.js CHANGED
@@ -6,7 +6,7 @@ import connector from './commands/connector/index.js';
6
6
  import database from './commands/database/index.js';
7
7
  import install from './commands/install/index.js';
8
8
  import { packageJson } from './package-json.js';
9
- import { cliConfig, ConfigKey } from './utilities.js';
9
+ import { cliConfig, ConfigKey } from './utils.js';
10
10
  void yargs(hideBin(process.argv))
11
11
  .version(false)
12
12
  .option('env', {
@@ -40,6 +40,7 @@ export declare const packageJson: {
40
40
  url: string;
41
41
  };
42
42
  dependencies: {
43
+ "@logto/connector-kit": string;
43
44
  "@logto/core-kit": string;
44
45
  "@logto/schemas": string;
45
46
  "@logto/shared": string;
@@ -54,6 +55,7 @@ export declare const packageJson: {
54
55
  ora: string;
55
56
  "p-limit": string;
56
57
  "p-retry": string;
58
+ "pg-protocol": string;
57
59
  roarr: string;
58
60
  semver: string;
59
61
  slonik: string;
@@ -82,9 +84,6 @@ export declare const packageJson: {
82
84
  };
83
85
  eslintConfig: {
84
86
  extends: string;
85
- rules: {
86
- complexity: (string | number)[];
87
- };
88
87
  ignorePatterns: string[];
89
88
  };
90
89
  prettier: string;
@@ -1,6 +1,6 @@
1
1
  export const packageJson = {
2
2
  "name": "@logto/cli",
3
- "version": "1.0.0-rc.1",
3
+ "version": "1.0.0-rc.3",
4
4
  "description": "Logto CLI.",
5
5
  "author": "Silverhand Inc. <contact@silverhand.io>",
6
6
  "homepage": "https://github.com/logto-io/logto#readme",
@@ -43,10 +43,11 @@ export const packageJson = {
43
43
  "url": "https://github.com/logto-io/logto/issues"
44
44
  },
45
45
  "dependencies": {
46
+ "@logto/connector-kit": "workspace:*",
46
47
  "@logto/core-kit": "workspace:*",
47
48
  "@logto/schemas": "workspace:*",
48
49
  "@logto/shared": "workspace:*",
49
- "@silverhand/essentials": "2.1.0",
50
+ "@silverhand/essentials": "^2.5.0",
50
51
  "chalk": "^5.0.0",
51
52
  "decamelize": "^6.0.0",
52
53
  "dotenv": "^16.0.0",
@@ -57,6 +58,7 @@ export const packageJson = {
57
58
  "ora": "^6.1.2",
58
59
  "p-limit": "^4.0.0",
59
60
  "p-retry": "^5.1.2",
61
+ "pg-protocol": "^1.6.0",
60
62
  "roarr": "^7.11.0",
61
63
  "semver": "^7.3.8",
62
64
  "slonik": "^30.0.0",
@@ -67,30 +69,24 @@ export const packageJson = {
67
69
  "zod": "^3.20.2"
68
70
  },
69
71
  "devDependencies": {
70
- "@silverhand/eslint-config": "1.3.0",
71
- "@silverhand/ts-config": "1.2.1",
72
+ "@silverhand/eslint-config": "2.0.1",
73
+ "@silverhand/ts-config": "2.0.3",
72
74
  "@types/inquirer": "^9.0.0",
73
- "@types/jest": "^29.1.2",
75
+ "@types/jest": "^29.4.0",
74
76
  "@types/node": "^18.11.18",
75
77
  "@types/semver": "^7.3.12",
76
78
  "@types/sinon": "^10.0.13",
77
79
  "@types/tar": "^6.1.2",
78
80
  "@types/yargs": "^17.0.13",
79
- "eslint": "^8.21.0",
80
- "jest": "^29.3.1",
81
+ "eslint": "^8.34.0",
82
+ "jest": "^29.5.0",
81
83
  "lint-staged": "^13.0.0",
82
- "prettier": "^2.8.1",
84
+ "prettier": "^2.8.2",
83
85
  "sinon": "^15.0.0",
84
86
  "typescript": "^4.9.4"
85
87
  },
86
88
  "eslintConfig": {
87
89
  "extends": "@silverhand",
88
- "rules": {
89
- "complexity": [
90
- "error",
91
- 11
92
- ]
93
- },
94
90
  "ignorePatterns": [
95
91
  "src/package-json.ts"
96
92
  ]
@@ -0,0 +1,2 @@
1
+ import type { CommonQueryMethods } from 'slonik';
2
+ export declare const getDatabaseName: (pool: CommonQueryMethods, normalized?: boolean) => Promise<string>;
@@ -0,0 +1,7 @@
1
+ import { sql } from 'slonik';
2
+ export const getDatabaseName = async (pool, normalized = false) => {
3
+ const { currentDatabase } = await pool.one(sql `
4
+ select current_database();
5
+ `);
6
+ return normalized ? currentDatabase.replaceAll('-', '_') : currentDatabase;
7
+ };
@@ -2,5 +2,5 @@ import type { LogtoConfig, LogtoConfigKey, logtoConfigGuards } from '@logto/sche
2
2
  import type { CommonQueryMethods } from 'slonik';
3
3
  import type { z } from 'zod';
4
4
  export declare const doesConfigsTableExist: (pool: CommonQueryMethods) => Promise<boolean>;
5
- export declare const getRowsByKeys: (pool: CommonQueryMethods, keys: LogtoConfigKey[]) => Promise<import("slonik").QueryResult<LogtoConfig>>;
6
- export declare const updateValueByKey: <T extends LogtoConfigKey>(pool: CommonQueryMethods, key: T, value: z.TypeOf<import("@logto/schemas").LogtoConfigGuard[T]>) => Promise<import("slonik").QueryResult<import("slonik").QueryResultRow>>;
5
+ export declare const getRowsByKeys: (pool: CommonQueryMethods, tenantId: string, keys: LogtoConfigKey[]) => Promise<import("slonik").QueryResult<LogtoConfig>>;
6
+ export declare const updateValueByKey: <T extends LogtoConfigKey>(pool: CommonQueryMethods, tenantId: string, key: T, value: z.TypeOf<import("@logto/schemas").LogtoConfigGuard[T]>) => Promise<import("slonik").QueryResult<import("slonik").QueryResultRow>>;
@@ -6,13 +6,14 @@ export const doesConfigsTableExist = async (pool) => {
6
6
  const { rows } = await pool.query(sql `select to_regclass(${LogtoConfigs.table}) as regclass`);
7
7
  return Boolean(rows[0]?.regclass);
8
8
  };
9
- export const getRowsByKeys = async (pool, keys) => pool.query(sql `
9
+ export const getRowsByKeys = async (pool, tenantId, keys) => pool.query(sql `
10
10
  select ${sql.join([fields.key, fields.value], sql `,`)} from ${table}
11
- where ${fields.key} in (${sql.join(keys, sql `,`)})
11
+ where ${fields.tenantId} = ${tenantId}
12
+ and ${fields.key} in (${sql.join(keys, sql `,`)})
12
13
  `);
13
- export const updateValueByKey = async (pool, key, value) => pool.query(sql `
14
- insert into ${table} (${fields.key}, ${fields.value})
15
- values (${key}, ${sql.jsonb(value)})
14
+ export const updateValueByKey = async (pool, tenantId, key, value) => pool.query(sql `
15
+ insert into ${table} (${fields.tenantId}, ${fields.key}, ${fields.value})
16
+ values (${tenantId}, ${key}, ${sql.jsonb(value)})
16
17
  on conflict (${fields.tenantId}, ${fields.key})
17
18
  do update set ${fields.value}=excluded.${fields.value}
18
19
  `);
@@ -1,4 +1,9 @@
1
+ import type { System, SystemKey } from '@logto/schemas';
2
+ import { systemGuards } from '@logto/schemas';
1
3
  import type { CommonQueryMethods, DatabaseTransactionConnection } from 'slonik';
4
+ import type { z } from 'zod';
2
5
  export declare const doesSystemsTableExist: (pool: CommonQueryMethods) => Promise<boolean>;
3
6
  export declare const getCurrentDatabaseAlterationTimestamp: (pool: CommonQueryMethods) => Promise<number>;
4
7
  export declare const updateDatabaseTimestamp: (connection: DatabaseTransactionConnection, timestamp: number) => Promise<void>;
8
+ export declare const getRowByKey: (pool: CommonQueryMethods, key: SystemKey) => Promise<System | null>;
9
+ export declare const updateValueByKey: <T extends SystemKey>(pool: CommonQueryMethods, key: T, value: z.TypeOf<import("@logto/schemas").SystemGuard[T]>) => Promise<import("slonik").QueryResult<import("slonik").QueryResultRow>>;
@@ -1,8 +1,8 @@
1
1
  import { systemGuards, Systems, AlterationStateKey } from '@logto/schemas';
2
2
  import { convertToIdentifiers } from '@logto/shared';
3
+ import { DatabaseError } from 'pg-protocol';
3
4
  import { sql } from 'slonik';
4
- import { z } from 'zod';
5
- const { fields } = convertToIdentifiers(Systems);
5
+ const { fields, table } = convertToIdentifiers(Systems);
6
6
  const doesTableExist = async (pool, table) => {
7
7
  const { rows } = await pool.query(sql `select to_regclass(${table}) as regclass`);
8
8
  return Boolean(rows[0]?.regclass);
@@ -20,10 +20,9 @@ export const getCurrentDatabaseAlterationTimestamp = async (pool) => {
20
20
  return (parsed.success && parsed.data.timestamp) || 0;
21
21
  }
22
22
  catch (error) {
23
- const result = z.object({ code: z.string() }).safeParse(error);
24
23
  // Relation does not exist, treat as 0
25
24
  // https://www.postgresql.org/docs/14/errcodes-appendix.html
26
- if (result.success && result.data.code === '42P01') {
25
+ if (error instanceof DatabaseError && error.code === '42P01') {
27
26
  return 0;
28
27
  }
29
28
  throw error;
@@ -41,3 +40,13 @@ export const updateDatabaseTimestamp = async (connection, timestamp) => {
41
40
  on conflict (${fields.key}) do update set ${fields.value}=excluded.${fields.value}
42
41
  `);
43
42
  };
43
+ export const getRowByKey = async (pool, key) => pool.maybeOne(sql `
44
+ select ${sql.join([fields.key, fields.value], sql `,`)} from ${table}
45
+ where ${fields.key} = ${key}
46
+ `);
47
+ export const updateValueByKey = async (pool, key, value) => pool.query(sql `
48
+ insert into ${table} (${fields.key}, ${fields.value})
49
+ values (${key}, ${sql.jsonb(value)})
50
+ on conflict (${fields.key})
51
+ do update set ${fields.value}=excluded.${fields.value}
52
+ `);
@@ -1,7 +1,8 @@
1
1
  import { AlterationStateKey, Systems } from '@logto/schemas';
2
2
  import { convertToIdentifiers } from '@logto/shared';
3
+ import { DatabaseError } from 'pg-protocol';
3
4
  import { createMockPool, createMockQueryResult, sql } from 'slonik';
4
- import { expectSqlAssert } from '../test-utilities.js';
5
+ import { expectSqlAssert } from '../test-utils.js';
5
6
  import { updateDatabaseTimestamp, getCurrentDatabaseAlterationTimestamp } from './system.js';
6
7
  const { jest } = import.meta;
7
8
  const mockQuery = jest.fn();
@@ -11,11 +12,14 @@ const pool = createMockPool({
11
12
  },
12
13
  });
13
14
  const { table, fields } = convertToIdentifiers(Systems);
14
- const timestamp = 1663923776;
15
+ const timestamp = 1_663_923_776;
15
16
  const systemsTableExists = async () => createMockQueryResult([{ regclass: true }]);
16
17
  describe('getCurrentDatabaseAlterationTimestamp()', () => {
17
18
  it('returns 0 if query failed (table not found)', async () => {
18
- mockQuery.mockImplementationOnce(systemsTableExists).mockRejectedValueOnce({ code: '42P01' });
19
+ const error = new DatabaseError('test', 0, 'noData');
20
+ // eslint-disable-next-line @silverhand/fp/no-mutation
21
+ error.code = '42P01';
22
+ mockQuery.mockImplementationOnce(systemsTableExists).mockRejectedValueOnce(error);
19
23
  await expect(getCurrentDatabaseAlterationTimestamp(pool)).resolves.toBe(0);
20
24
  });
21
25
  it('returns 0 if the row is not found', async () => {