@logto/cli 1.7.0 → 1.8.0

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.
@@ -3,7 +3,7 @@ import chalk from 'chalk';
3
3
  import { createPoolFromConfig } from '../../../database.js';
4
4
  import { getCurrentDatabaseAlterationTimestamp, updateDatabaseTimestamp, } from '../../../queries/system.js';
5
5
  import { consoleLog } from '../../../utils.js';
6
- import { getAlterationFiles, getTimestampFromFilename } from './utils.js';
6
+ import { getAlterationFiles, getTimestampFromFilename, chooseRevertAlterationsByTimestamp, } from './utils.js';
7
7
  import { chooseAlterationsByVersion, chooseRevertAlterationsByVersion } from './version.js';
8
8
  const importAlterationScript = async (filePath) => {
9
9
  // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
@@ -52,6 +52,15 @@ const deployAlteration = async (pool, { path: filePath, filename }, action = 'up
52
52
  }
53
53
  consoleLog.info(`Run alteration ${filename} \`${action}()\` function succeeded`);
54
54
  };
55
+ const revertAlterations = async (alterations, pool) => {
56
+ consoleLog.info(`Found ${alterations.length} alteration${conditionalString(alterations.length > 1 && 's')} to revert`);
57
+ // The await inside the loop is intended, alterations should run in order
58
+ // eslint-disable-next-line @silverhand/fp/no-mutating-methods
59
+ for (const alteration of alterations.slice().reverse()) {
60
+ // eslint-disable-next-line no-await-in-loop
61
+ await deployAlteration(pool, alteration, 'down');
62
+ }
63
+ };
55
64
  const alteration = {
56
65
  command: ['alteration <action> [target]', 'alt', 'alter'],
57
66
  describe: 'Perform database alteration',
@@ -66,37 +75,44 @@ const alteration = {
66
75
  type: 'string',
67
76
  }),
68
77
  handler: async ({ action, target }) => {
69
- if (action === 'list') {
70
- const files = await getAlterationFiles();
71
- for (const file of files) {
72
- consoleLog.plain(file.filename);
78
+ switch (action) {
79
+ case 'list': {
80
+ const files = await getAlterationFiles();
81
+ for (const file of files) {
82
+ consoleLog.plain(file.filename);
83
+ }
84
+ break;
73
85
  }
74
- }
75
- else if (action === 'deploy') {
76
- const pool = await createPoolFromConfig();
77
- const alterations = await chooseAlterationsByVersion(await getAvailableAlterations(pool), target);
78
- consoleLog.info(`Found ${alterations.length} alteration${conditionalString(alterations.length > 1 && 's')} to deploy`);
79
- // The await inside the loop is intended, alterations should run in order
80
- for (const alteration of alterations) {
81
- // eslint-disable-next-line no-await-in-loop
82
- await deployAlteration(pool, alteration);
86
+ case 'deploy': {
87
+ const pool = await createPoolFromConfig();
88
+ const alterations = await chooseAlterationsByVersion(await getAvailableAlterations(pool), target);
89
+ consoleLog.info(`Found ${alterations.length} alteration${conditionalString(alterations.length > 1 && 's')} to deploy`);
90
+ // The await inside the loop is intended, alterations should run in order
91
+ for (const alteration of alterations) {
92
+ // eslint-disable-next-line no-await-in-loop
93
+ await deployAlteration(pool, alteration);
94
+ }
95
+ await pool.end();
96
+ break;
83
97
  }
84
- await pool.end();
85
- }
86
- else if (['rollback', 'r'].includes(action)) {
87
- const pool = await createPoolFromConfig();
88
- const alterations = await chooseRevertAlterationsByVersion(await getAvailableAlterations(pool, 'lte'), target ?? '');
89
- consoleLog.info(`Found ${alterations.length} alteration${conditionalString(alterations.length > 1 && 's')} to revert`);
90
- // The await inside the loop is intended, alterations should run in order
91
- // eslint-disable-next-line @silverhand/fp/no-mutating-methods
92
- for (const alteration of alterations.slice().reverse()) {
93
- // eslint-disable-next-line no-await-in-loop
94
- await deployAlteration(pool, alteration, 'down');
98
+ case 'rollback':
99
+ case 'r': {
100
+ const pool = await createPoolFromConfig();
101
+ const alterations = await chooseRevertAlterationsByVersion(await getAvailableAlterations(pool, 'lte'), target ?? '');
102
+ await revertAlterations(alterations, pool);
103
+ await pool.end();
104
+ break;
105
+ }
106
+ case 'rollback-to-timestamp': {
107
+ const pool = await createPoolFromConfig();
108
+ const alterations = await chooseRevertAlterationsByTimestamp(target ?? '');
109
+ await revertAlterations(alterations, pool);
110
+ await pool.end();
111
+ break;
112
+ }
113
+ default: {
114
+ consoleLog.fatal('Unsupported action');
95
115
  }
96
- await pool.end();
97
- }
98
- else {
99
- consoleLog.fatal('Unsupported action');
100
116
  }
101
117
  },
102
118
  };
@@ -2,3 +2,4 @@ import type { AlterationFile } from './type.js';
2
2
  export declare const getTimestampFromFilename: (filename: string) => number;
3
3
  export declare const getAlterationDirectory: () => string;
4
4
  export declare const getAlterationFiles: () => Promise<AlterationFile[]>;
5
+ export declare const chooseRevertAlterationsByTimestamp: (target: string) => Promise<AlterationFile[]>;
@@ -36,3 +36,11 @@ export const getAlterationFiles = async () => {
36
36
  .sort((file1, file2) => getTimestampFromFilename(file1) - getTimestampFromFilename(file2))
37
37
  .map((filename) => ({ path: path.join(localAlterationDirectory, filename), filename }));
38
38
  };
39
+ export const chooseRevertAlterationsByTimestamp = async (target) => {
40
+ const files = await getAlterationFiles();
41
+ const targetTimestamp = Number(target);
42
+ if (Number.isNaN(targetTimestamp)) {
43
+ return [];
44
+ }
45
+ return files.filter(({ filename }) => getTimestampFromFilename(filename) > targetTimestamp);
46
+ };
@@ -4,7 +4,7 @@ import chalk from 'chalk';
4
4
  import { createPoolFromConfig } from '../../database.js';
5
5
  import { getRowsByKeys, updateValueByKey } from '../../queries/logto-config.js';
6
6
  import { consoleLog } from '../../utils.js';
7
- import { generateOidcCookieKey, generateOidcPrivateKey } from './utils.js';
7
+ import { PrivateKeyType, generateOidcCookieKey, generateOidcPrivateKey } from './utils.js';
8
8
  const validKeysDisplay = chalk.green(logtoConfigKeys.join(', '));
9
9
  const validateKeys = (keys) => {
10
10
  const invalidKey = (Array.isArray(keys) ? keys : [keys]).find(
@@ -19,6 +19,7 @@ const validRotateKeys = Object.freeze([
19
19
  LogtoOidcConfigKey.PrivateKeys,
20
20
  LogtoOidcConfigKey.CookieKeys,
21
21
  ]);
22
+ const validPrivateKeyTypes = Object.freeze([PrivateKeyType.RSA, PrivateKeyType.EC]);
22
23
  const validateRotateKey = (key) => {
23
24
  // Using `.includes()` will result a type error
24
25
  // eslint-disable-next-line unicorn/prefer-includes
@@ -26,6 +27,13 @@ const validateRotateKey = (key) => {
26
27
  consoleLog.fatal(`Invalid config key ${chalk.red(key)} found, expected one of ${validKeysDisplay}`);
27
28
  }
28
29
  };
30
+ const validatePrivateKeyType = (key) => {
31
+ // Using `.includes()` will result a type error
32
+ // eslint-disable-next-line unicorn/prefer-includes
33
+ if (!validPrivateKeyTypes.some((element) => element === key)) {
34
+ consoleLog.fatal(`Invalid private key type ${chalk.red(key)} found, expected one of ${validPrivateKeyTypes.join(', ')}`);
35
+ }
36
+ };
29
37
  const getConfig = {
30
38
  command: 'get <key> [keys...]',
31
39
  describe: 'Get config value(s) of the given key(s) in Logto database',
@@ -103,9 +111,15 @@ const rotateConfig = {
103
111
  describe: 'The tenant to operate',
104
112
  type: 'string',
105
113
  default: defaultTenantId,
114
+ })
115
+ .option('type', {
116
+ describe: `The key type for ${LogtoOidcConfigKey.PrivateKeys}, one of ${validPrivateKeyTypes.join(', ')}`,
117
+ type: 'string',
118
+ default: 'ec',
106
119
  }),
107
- handler: async ({ key, tenantId }) => {
120
+ handler: async ({ key, tenantId, type }) => {
108
121
  validateRotateKey(key);
122
+ validatePrivateKeyType(type);
109
123
  const pool = await createPoolFromConfig();
110
124
  const { rows } = await getRowsByKeys(pool, tenantId, [key]);
111
125
  if (!rows[0]) {
@@ -117,7 +131,7 @@ const rotateConfig = {
117
131
  // No need for default. It's already exhaustive
118
132
  switch (key) {
119
133
  case LogtoOidcConfigKey.PrivateKeys: {
120
- return [await generateOidcPrivateKey(), ...original];
134
+ return [await generateOidcPrivateKey(type), ...original];
121
135
  }
122
136
  case LogtoOidcConfigKey.CookieKeys: {
123
137
  return [generateOidcCookieKey(), ...original];
@@ -8,3 +8,11 @@ import type { CommonQueryMethods } from 'slonik';
8
8
  * For why it is necessary, see the redirect lifecycle of cloud Admin Console.
9
9
  */
10
10
  export declare const appendAdminConsoleRedirectUris: (pool: CommonQueryMethods) => Promise<void>;
11
+ /**
12
+ * Create Cloud Service M2M application for the tenant.
13
+ *
14
+ * This is a built-in M2M application for the tenant to communicate with the cloud service.
15
+ * - default
16
+ * - admin
17
+ */
18
+ export declare const seedTenantCloudServiceApplication: (pool: CommonQueryMethods, tenantId: string) => Promise<void>;
@@ -1,7 +1,8 @@
1
- import { adminConsoleApplicationId, adminTenantId, defaultTenantId } from '@logto/schemas';
1
+ import { adminConsoleApplicationId, adminTenantId, defaultTenantId, createTenantMachineToMachineApplication, createAdminTenantApplicationRole, createCloudConnectionConfig, AdminTenantRole, } from '@logto/schemas';
2
2
  import { GlobalValues } from '@logto/shared';
3
3
  import { appendPath } from '@silverhand/essentials';
4
4
  import { sql } from 'slonik';
5
+ import { insertInto } from '../../../database.js';
5
6
  import { consoleLog } from '../../../utils.js';
6
7
  /**
7
8
  * Append Redirect URIs for the default tenant callback in cloud Admin Console.
@@ -32,3 +33,25 @@ export const appendAdminConsoleRedirectUris = async (pool) => {
32
33
  `);
33
34
  consoleLog.succeed('Appended initial Redirect URIs to Admin Console:', redirectUris.map(String));
34
35
  };
36
+ /**
37
+ * Create Cloud Service M2M application for the tenant.
38
+ *
39
+ * This is a built-in M2M application for the tenant to communicate with the cloud service.
40
+ * - default
41
+ * - admin
42
+ */
43
+ export const seedTenantCloudServiceApplication = async (pool, tenantId) => {
44
+ // Create Cloud Service M2M application
45
+ const cloudServiceApplication = createTenantMachineToMachineApplication(tenantId);
46
+ await pool.query(insertInto(cloudServiceApplication, 'applications'));
47
+ // Assign tenantApplicationRole to Cloud Service M2M application
48
+ const { id: tenantApplicationRoleId } = await pool.one(sql `
49
+ select id from roles
50
+ where name = ${AdminTenantRole.TenantApplication}
51
+ and tenant_id = ${adminTenantId}
52
+ `);
53
+ await pool.query(insertInto(createAdminTenantApplicationRole(cloudServiceApplication.id, tenantApplicationRoleId), 'applications_roles'));
54
+ // Create Cloud Service M2M application logto_config
55
+ await pool.query(insertInto(createCloudConnectionConfig(tenantId, cloudServiceApplication.id, cloudServiceApplication.secret), 'logto_configs'));
56
+ consoleLog.succeed('Cloud Service Application successfully created for:', tenantId);
57
+ };
@@ -1,6 +1,6 @@
1
1
  import { readdir, readFile } from 'node:fs/promises';
2
2
  import path from 'node:path';
3
- import { createDefaultAdminConsoleConfig, createCloudConnectionConfig, defaultTenantId, adminTenantId, defaultManagementApi, createAdminDataInAdminTenant, createMeApiInAdminTenant, createDefaultSignInExperience, createAdminTenantSignInExperience, createDefaultAdminConsoleApplication, createCloudApi, createTenantApplicationRole, createTenantMachineToMachineApplication, createAdminTenantApplicationRole, CloudScope, } from '@logto/schemas';
3
+ import { createDefaultAdminConsoleConfig, defaultTenantId, adminTenantId, defaultManagementApi, createAdminDataInAdminTenant, createMeApiInAdminTenant, createDefaultSignInExperience, createAdminTenantSignInExperience, createDefaultAdminConsoleApplication, createCloudApi, createTenantApplicationRole, CloudScope, } from '@logto/schemas';
4
4
  import { Tenants } from '@logto/schemas/models';
5
5
  import { sql } from 'slonik';
6
6
  import { raw } from 'slonik-sql-tag-raw';
@@ -8,7 +8,7 @@ import { insertInto } from '../../../database.js';
8
8
  import { getDatabaseName } from '../../../queries/database.js';
9
9
  import { updateDatabaseTimestamp } from '../../../queries/system.js';
10
10
  import { consoleLog, getPathInModule } from '../../../utils.js';
11
- import { appendAdminConsoleRedirectUris } from './cloud.js';
11
+ import { appendAdminConsoleRedirectUris, seedTenantCloudServiceApplication } from './cloud.js';
12
12
  import { seedOidcConfigs } from './oidc-config.js';
13
13
  import { assignScopesToRole, createTenant, seedAdminData } from './tenant.js';
14
14
  const getExplicitOrder = (query) => {
@@ -82,23 +82,21 @@ export const seedTables = async (connection, latestTimestamp, isCloud) => {
82
82
  await seedAdminData(connection, adminAdminData);
83
83
  await seedAdminData(connection, createMeApiInAdminTenant());
84
84
  const [cloudData, ...cloudAdditionalScopes] = createCloudApi();
85
- const applicationRole = createTenantApplicationRole();
86
85
  await seedAdminData(connection, cloudData, ...cloudAdditionalScopes);
86
+ // Create tenant application role
87
+ const applicationRole = createTenantApplicationRole();
87
88
  await connection.query(insertInto(applicationRole, 'roles'));
88
89
  await assignScopesToRole(connection, adminTenantId, applicationRole.id, ...cloudAdditionalScopes
89
90
  .filter(({ name }) => name === CloudScope.SendSms || name === CloudScope.SendEmail)
90
91
  .map(({ id }) => id));
91
- // Add M2M app for default tenant
92
- const defaultTenantApplication = createTenantMachineToMachineApplication(defaultTenantId);
93
- await connection.query(insertInto(defaultTenantApplication, 'applications'));
94
- await connection.query(insertInto(createAdminTenantApplicationRole(defaultTenantApplication.id, applicationRole.id), 'applications_roles'));
95
92
  // Assign all cloud API scopes to role `admin:admin`
96
93
  await assignScopesToRole(connection, adminTenantId, adminAdminData.role.id, ...cloudAdditionalScopes.map(({ id }) => id));
94
+ // FIXME: @wangsijie should not create tenant Cloud Service application in the OSS DB.
95
+ await seedTenantCloudServiceApplication(connection, defaultTenantId);
97
96
  await Promise.all([
98
97
  connection.query(insertInto(createDefaultAdminConsoleConfig(defaultTenantId), 'logto_configs')),
99
- connection.query(insertInto(createCloudConnectionConfig(defaultTenantId, defaultTenantApplication.id, defaultTenantApplication.secret), 'logto_configs')),
100
- connection.query(insertInto(createDefaultSignInExperience(defaultTenantId, isCloud), 'sign_in_experiences')),
101
98
  connection.query(insertInto(createDefaultAdminConsoleConfig(adminTenantId), 'logto_configs')),
99
+ connection.query(insertInto(createDefaultSignInExperience(defaultTenantId, isCloud), 'sign_in_experiences')),
102
100
  connection.query(insertInto(createAdminTenantSignInExperience(), 'sign_in_experiences')),
103
101
  connection.query(insertInto(createDefaultAdminConsoleApplication(), 'applications')),
104
102
  updateDatabaseTimestamp(connection, latestTimestamp),
@@ -106,5 +104,8 @@ export const seedTables = async (connection, latestTimestamp, isCloud) => {
106
104
  consoleLog.succeed('Seed data');
107
105
  };
108
106
  export const seedCloud = async (connection) => {
109
- await appendAdminConsoleRedirectUris(connection);
107
+ await Promise.all([
108
+ appendAdminConsoleRedirectUris(connection),
109
+ seedTenantCloudServiceApplication(connection, adminTenantId),
110
+ ]);
110
111
  };
@@ -1,2 +1,6 @@
1
- export declare const generateOidcPrivateKey: (type?: 'rsa' | 'ec') => Promise<string>;
1
+ export declare enum PrivateKeyType {
2
+ RSA = "rsa",
3
+ EC = "ec"
4
+ }
5
+ export declare const generateOidcPrivateKey: (type?: PrivateKeyType) => Promise<string>;
2
6
  export declare const generateOidcCookieKey: () => string;
@@ -1,8 +1,13 @@
1
1
  import { generateKeyPair } from 'node:crypto';
2
2
  import { promisify } from 'node:util';
3
3
  import { generateStandardId } from '@logto/shared';
4
- export const generateOidcPrivateKey = async (type = 'ec') => {
5
- if (type === 'rsa') {
4
+ export var PrivateKeyType;
5
+ (function (PrivateKeyType) {
6
+ PrivateKeyType["RSA"] = "rsa";
7
+ PrivateKeyType["EC"] = "ec";
8
+ })(PrivateKeyType || (PrivateKeyType = {}));
9
+ export const generateOidcPrivateKey = async (type = PrivateKeyType.EC) => {
10
+ if (type === PrivateKeyType.RSA) {
6
11
  const { privateKey } = await promisify(generateKeyPair)('rsa', {
7
12
  modulusLength: 4096,
8
13
  publicKeyEncoding: {
@@ -17,7 +22,7 @@ export const generateOidcPrivateKey = async (type = 'ec') => {
17
22
  return privateKey;
18
23
  }
19
24
  // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
20
- if (type === 'ec') {
25
+ if (type === PrivateKeyType.EC) {
21
26
  const { privateKey } = await promisify(generateKeyPair)('ec', {
22
27
  // https://security.stackexchange.com/questions/78621/which-elliptic-curve-should-i-use
23
28
  namedCurve: 'secp384r1',
@@ -1,10 +1,10 @@
1
- import type { AllConnector, BaseConnector, ConnectorMetadata, GetConnectorConfig } from '@logto/connector-kit';
1
+ import type { AllConnector, BaseConnector, ConnectorMetadata, GetConnectorConfig, GetCloudServiceClient } from '@logto/connector-kit';
2
2
  import { ConnectorType } from '@logto/connector-kit';
3
3
  import type { ConnectorFactory } from './types.js';
4
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>;
5
+ export declare const readUrl: (url: string, baseUrl: string, type: 'text' | 'image') => Promise<string>;
6
6
  export declare const parseMetadata: (metadata: AllConnector['metadata'], packagePath: string) => Promise<AllConnector['metadata']>;
7
- export declare const buildRawConnector: <T extends AllConnector = AllConnector>(connectorFactory: ConnectorFactory<T>, getConnectorConfig?: GetConnectorConfig) => Promise<{
7
+ export declare const buildRawConnector: <T extends AllConnector = AllConnector>(connectorFactory: ConnectorFactory<T>, getConnectorConfig?: GetConnectorConfig, getCloudServiceClient?: GetCloudServiceClient) => Promise<{
8
8
  rawConnector: T;
9
9
  rawMetadata: ConnectorMetadata;
10
10
  }>;
@@ -2,6 +2,7 @@ import { existsSync } from 'node:fs';
2
2
  import { readFile } from 'node:fs/promises';
3
3
  import path from 'node:path';
4
4
  import { ConnectorError, ConnectorErrorCodes, ConnectorType } from '@logto/connector-kit';
5
+ import { consoleLog } from '../utils.js';
5
6
  import { notImplemented } from './consts.js';
6
7
  export function validateConnectorModule(connector) {
7
8
  if (!connector.metadata) {
@@ -14,6 +15,11 @@ export function validateConnectorModule(connector) {
14
15
  throw new ConnectorError(ConnectorErrorCodes.UnexpectedType);
15
16
  }
16
17
  }
18
+ const supportedImageTypes = Object.freeze({
19
+ '.svg': 'image/svg+xml',
20
+ '.png': 'image/png',
21
+ });
22
+ const isSupportedImageType = (extension) => Object.keys(supportedImageTypes).includes(extension);
17
23
  export const readUrl = async (url, baseUrl, type) => {
18
24
  if (!url) {
19
25
  return url;
@@ -24,25 +30,32 @@ export const readUrl = async (url, baseUrl, type) => {
24
30
  if (!existsSync(path.join(baseUrl, url))) {
25
31
  return url;
26
32
  }
27
- if (type === 'svg') {
28
- const data = await readFile(path.join(baseUrl, url));
29
- return `data:image/svg+xml;base64,${data.toString('base64')}`;
33
+ if (type === 'image') {
34
+ const filePath = path.join(baseUrl, url);
35
+ const extension = path.extname(filePath);
36
+ if (!isSupportedImageType(extension)) {
37
+ consoleLog.warn(`[readUrl] unexpected image type: ${filePath}, only support ".svg" and ".png". Falling back to empty string.`);
38
+ return '';
39
+ }
40
+ const data = await readFile(filePath);
41
+ return `data:${supportedImageTypes[extension]};base64,${data.toString('base64')}`;
30
42
  }
31
43
  return readFile(path.join(baseUrl, url), 'utf8');
32
44
  };
33
45
  export const parseMetadata = async (metadata, packagePath) => {
34
46
  return {
35
47
  ...metadata,
36
- logo: await readUrl(metadata.logo, packagePath, 'svg'),
37
- logoDark: metadata.logoDark && (await readUrl(metadata.logoDark, packagePath, 'svg')),
48
+ logo: await readUrl(metadata.logo, packagePath, 'image'),
49
+ logoDark: metadata.logoDark && (await readUrl(metadata.logoDark, packagePath, 'image')),
38
50
  readme: await readUrl(metadata.readme, packagePath, 'text'),
39
51
  configTemplate: metadata.configTemplate && (await readUrl(metadata.configTemplate, packagePath, 'text')),
40
52
  };
41
53
  };
42
- export const buildRawConnector = async (connectorFactory, getConnectorConfig) => {
54
+ export const buildRawConnector = async (connectorFactory, getConnectorConfig, getCloudServiceClient) => {
43
55
  const { createConnector, path: packagePath } = connectorFactory;
44
56
  const rawConnector = await createConnector({
45
57
  getConfig: getConnectorConfig ?? notImplemented,
58
+ getCloudServiceClient,
46
59
  });
47
60
  validateConnectorModule(rawConnector);
48
61
  const rawMetadata = await parseMetadata(rawConnector.metadata, packagePath);
@@ -1,6 +1,6 @@
1
1
  export const packageJson = {
2
2
  "name": "@logto/cli",
3
- "version": "1.7.0",
3
+ "version": "1.8.0",
4
4
  "description": "Logto CLI.",
5
5
  "author": "Silverhand Inc. <contact@silverhand.io>",
6
6
  "homepage": "https://github.com/logto-io/logto#readme",
@@ -48,7 +48,7 @@ export const packageJson = {
48
48
  "@logto/language-kit": "workspace:^1.0.0",
49
49
  "@logto/phrases": "workspace:^1.4.1",
50
50
  "@logto/phrases-ui": "workspace:^1.2.0",
51
- "@logto/schemas": "workspace:1.7.0",
51
+ "@logto/schemas": "workspace:1.8.0",
52
52
  "@logto/shared": "workspace:^2.0.0",
53
53
  "@silverhand/essentials": "^2.5.0",
54
54
  "chalk": "^5.0.0",
@@ -61,7 +61,7 @@ export const packageJson = {
61
61
  "ora": "^6.1.2",
62
62
  "p-limit": "^4.0.0",
63
63
  "p-queue": "^7.3.4",
64
- "p-retry": "^5.1.2",
64
+ "p-retry": "^6.0.0",
65
65
  "pg-protocol": "^1.6.0",
66
66
  "roarr": "^7.11.0",
67
67
  "semver": "^7.3.8",
@@ -85,7 +85,7 @@ export const packageJson = {
85
85
  "@types/yargs": "^17.0.13",
86
86
  "eslint": "^8.44.0",
87
87
  "jest": "^29.5.0",
88
- "lint-staged": "^13.0.0",
88
+ "lint-staged": "^14.0.0",
89
89
  "prettier": "^3.0.0",
90
90
  "sinon": "^15.0.0"
91
91
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@logto/cli",
3
- "version": "1.7.0",
3
+ "version": "1.8.0",
4
4
  "description": "Logto CLI.",
5
5
  "author": "Silverhand Inc. <contact@silverhand.io>",
6
6
  "homepage": "https://github.com/logto-io/logto#readme",
@@ -33,7 +33,7 @@
33
33
  "@logto/language-kit": "^1.0.0",
34
34
  "@logto/phrases": "^1.4.1",
35
35
  "@logto/phrases-ui": "^1.2.0",
36
- "@logto/schemas": "1.7.0",
36
+ "@logto/schemas": "1.8.0",
37
37
  "@logto/shared": "^2.0.0",
38
38
  "@silverhand/essentials": "^2.5.0",
39
39
  "chalk": "^5.0.0",
@@ -46,7 +46,7 @@
46
46
  "ora": "^6.1.2",
47
47
  "p-limit": "^4.0.0",
48
48
  "p-queue": "^7.3.4",
49
- "p-retry": "^5.1.2",
49
+ "p-retry": "^6.0.0",
50
50
  "pg-protocol": "^1.6.0",
51
51
  "roarr": "^7.11.0",
52
52
  "semver": "^7.3.8",
@@ -70,7 +70,7 @@
70
70
  "@types/yargs": "^17.0.13",
71
71
  "eslint": "^8.44.0",
72
72
  "jest": "^29.5.0",
73
- "lint-staged": "^13.0.0",
73
+ "lint-staged": "^14.0.0",
74
74
  "prettier": "^3.0.0",
75
75
  "sinon": "^15.0.0"
76
76
  },