@logto/cli 1.12.0 → 1.13.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.
- package/lib/commands/database/seed/index.d.ts +3 -1
- package/lib/commands/database/seed/index.js +33 -6
- package/lib/commands/database/seed/tables.d.ts +9 -0
- package/lib/commands/database/seed/tables.js +73 -12
- package/lib/commands/database/seed/tenant-organizations.d.ts +8 -0
- package/lib/commands/database/seed/tenant-organizations.js +28 -0
- package/lib/commands/database/seed/tenant.d.ts +12 -2
- package/lib/commands/database/seed/tenant.js +48 -0
- package/lib/commands/install/utils.js +1 -1
- package/lib/commands/translate/index.js +5 -0
- package/lib/commands/translate/sync-keys/index.d.ts +2 -0
- package/lib/commands/translate/sync-keys/index.js +4 -7
- package/lib/commands/translate/sync.d.ts +3 -0
- package/lib/commands/translate/sync.js +21 -20
- package/lib/commands/translate/utils.d.ts +1 -1
- package/lib/commands/translate/utils.js +2 -2
- package/lib/database.d.ts +5 -1
- package/lib/database.js +12 -3
- package/lib/package-json.js +20 -20
- package/lib/utils.d.ts +2 -2
- package/lib/utils.js +10 -8
- package/package.json +20 -20
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import type { DatabasePool } from 'slonik';
|
|
2
2
|
import type { CommandModule } from 'yargs';
|
|
3
|
-
export declare const seedByPool: (pool: DatabasePool, cloud?: boolean) => Promise<void>;
|
|
3
|
+
export declare const seedByPool: (pool: DatabasePool, cloud?: boolean, test?: boolean) => Promise<void>;
|
|
4
4
|
declare const seed: CommandModule<Record<string, unknown>, {
|
|
5
5
|
swe?: boolean;
|
|
6
6
|
cloud?: boolean;
|
|
7
|
+
test?: boolean;
|
|
8
|
+
'legacy-test-data'?: boolean;
|
|
7
9
|
}>;
|
|
8
10
|
export default seed;
|
|
@@ -3,8 +3,8 @@ import { doesConfigsTableExist } from '../../../queries/logto-config.js';
|
|
|
3
3
|
import { consoleLog, oraPromise } from '../../../utils.js';
|
|
4
4
|
import { getLatestAlterationTimestamp } from '../alteration/index.js';
|
|
5
5
|
import { getAlterationDirectory } from '../alteration/utils.js';
|
|
6
|
-
import { createTables, seedCloud, seedTables } from './tables.js';
|
|
7
|
-
export const seedByPool = async (pool, cloud = false) => {
|
|
6
|
+
import { createTables, seedCloud, seedTables, seedTest } from './tables.js';
|
|
7
|
+
export const seedByPool = async (pool, cloud = false, test = false) => {
|
|
8
8
|
await pool.transaction(async (connection) => {
|
|
9
9
|
// Check alteration scripts available in order to insert correct timestamp
|
|
10
10
|
const latestTimestamp = await getLatestAlterationTimestamp();
|
|
@@ -19,6 +19,14 @@ export const seedByPool = async (pool, cloud = false) => {
|
|
|
19
19
|
if (cloud) {
|
|
20
20
|
await seedCloud(connection);
|
|
21
21
|
}
|
|
22
|
+
if (test) {
|
|
23
|
+
await seedTest(connection);
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
};
|
|
27
|
+
const seedLegacyTestData = async (pool) => {
|
|
28
|
+
return pool.transaction(async (connection) => {
|
|
29
|
+
await seedTest(connection, true);
|
|
22
30
|
});
|
|
23
31
|
};
|
|
24
32
|
const seed = {
|
|
@@ -33,25 +41,44 @@ const seed = {
|
|
|
33
41
|
.option('cloud', {
|
|
34
42
|
describe: 'Seed additional cloud data',
|
|
35
43
|
type: 'boolean',
|
|
36
|
-
|
|
44
|
+
})
|
|
45
|
+
.option('test', {
|
|
46
|
+
describe: 'Seed additional test data',
|
|
47
|
+
type: 'boolean',
|
|
48
|
+
})
|
|
49
|
+
.option('legacy-test-data', {
|
|
50
|
+
describe: 'Seed test data only for legacy Logto versions (<=1.12.0), this option conflicts with others',
|
|
51
|
+
type: 'boolean',
|
|
37
52
|
}),
|
|
38
|
-
handler: async ({ swe, cloud }) => {
|
|
53
|
+
handler: async ({ swe, cloud, test, legacyTestData }) => {
|
|
39
54
|
const pool = await createPoolAndDatabaseIfNeeded();
|
|
55
|
+
if (legacyTestData) {
|
|
56
|
+
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
|
|
57
|
+
if (swe || cloud || test) {
|
|
58
|
+
throw new Error('The `legacy-test-data` option conflicts with other options, please use it alone.');
|
|
59
|
+
}
|
|
60
|
+
await seedLegacyTestData(pool);
|
|
61
|
+
await pool.end();
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
40
64
|
if (swe && (await doesConfigsTableExist(pool))) {
|
|
41
65
|
consoleLog.info('Seeding skipped');
|
|
42
66
|
await pool.end();
|
|
43
67
|
return;
|
|
44
68
|
}
|
|
45
69
|
try {
|
|
46
|
-
await seedByPool(pool, cloud);
|
|
70
|
+
await seedByPool(pool, cloud, test);
|
|
47
71
|
}
|
|
48
72
|
catch (error) {
|
|
49
73
|
consoleLog.error(error);
|
|
50
74
|
consoleLog.error('Error ocurred during seeding your database.\n\n' +
|
|
51
75
|
' Nothing has changed since the seeding process was in a transaction.\n' +
|
|
52
76
|
' Try to fix the error and seed again.');
|
|
77
|
+
throw error;
|
|
78
|
+
}
|
|
79
|
+
finally {
|
|
80
|
+
await pool.end();
|
|
53
81
|
}
|
|
54
|
-
await pool.end();
|
|
55
82
|
},
|
|
56
83
|
};
|
|
57
84
|
export default seed;
|
|
@@ -2,3 +2,12 @@ import type { DatabaseTransactionConnection } from 'slonik';
|
|
|
2
2
|
export declare const createTables: (connection: DatabaseTransactionConnection) => Promise<void>;
|
|
3
3
|
export declare const seedTables: (connection: DatabaseTransactionConnection, latestTimestamp: number, isCloud: boolean) => Promise<void>;
|
|
4
4
|
export declare const seedCloud: (connection: DatabaseTransactionConnection) => Promise<void>;
|
|
5
|
+
/**
|
|
6
|
+
* Seed additional test data for integration or alteration tests.
|
|
7
|
+
*
|
|
8
|
+
* It will create two users to the admin tenant (`test-1` and `test-2`), and do the following:
|
|
9
|
+
*
|
|
10
|
+
* - `test-1` will be assigned the management roles for both `default` and `admin` tenant.
|
|
11
|
+
* - `test-2` will be assigned the management role for `default` tenant.
|
|
12
|
+
*/
|
|
13
|
+
export declare const seedTest: (connection: DatabaseTransactionConnection, forLegacy?: boolean) => Promise<void>;
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { readdir, readFile } from 'node:fs/promises';
|
|
2
2
|
import path from 'node:path';
|
|
3
|
-
import { createDefaultAdminConsoleConfig, defaultTenantId, adminTenantId, defaultManagementApi, createAdminDataInAdminTenant, createMeApiInAdminTenant, createDefaultSignInExperience, createAdminTenantSignInExperience, createDefaultAdminConsoleApplication, createCloudApi, createTenantApplicationRole, CloudScope, } from '@logto/schemas';
|
|
3
|
+
import { createDefaultAdminConsoleConfig, defaultTenantId, adminTenantId, defaultManagementApi, createAdminDataInAdminTenant, createMeApiInAdminTenant, createDefaultSignInExperience, createAdminTenantSignInExperience, createDefaultAdminConsoleApplication, createCloudApi, createTenantApplicationRole, CloudScope, Roles, UsersRoles, LogtoConfigs, SignInExperiences, Applications, OrganizationUserRelations, getTenantOrganizationId, Users, OrganizationRoleUserRelations, TenantRole, } from '@logto/schemas';
|
|
4
|
+
import { getTenantRole } from '@logto/schemas';
|
|
4
5
|
import { Tenants } from '@logto/schemas/models';
|
|
6
|
+
import { convertToIdentifiers, generateStandardId } from '@logto/shared';
|
|
5
7
|
import { sql } from 'slonik';
|
|
6
8
|
import { raw } from 'slonik-sql-tag-raw';
|
|
7
9
|
import { insertInto } from '../../../database.js';
|
|
@@ -10,7 +12,8 @@ import { updateDatabaseTimestamp } from '../../../queries/system.js';
|
|
|
10
12
|
import { consoleLog, getPathInModule } from '../../../utils.js';
|
|
11
13
|
import { appendAdminConsoleRedirectUris, seedTenantCloudServiceApplication } from './cloud.js';
|
|
12
14
|
import { seedOidcConfigs } from './oidc-config.js';
|
|
13
|
-
import {
|
|
15
|
+
import { seedTenantOrganizations } from './tenant-organizations.js';
|
|
16
|
+
import { assignScopesToRole, createTenant, seedAdminData, seedLegacyManagementApiUserRole, seedManagementApiProxyApplications, } from './tenant.js';
|
|
14
17
|
const getExplicitOrder = (query) => {
|
|
15
18
|
const matched = /\/\*\s*init_order\s*=\s*([\d.]+)\s*\*\//.exec(query)?.[1];
|
|
16
19
|
return matched ? Number(matched) : undefined;
|
|
@@ -85,21 +88,21 @@ export const seedTables = async (connection, latestTimestamp, isCloud) => {
|
|
|
85
88
|
await seedAdminData(connection, cloudData, ...cloudAdditionalScopes);
|
|
86
89
|
// Create tenant application role
|
|
87
90
|
const applicationRole = createTenantApplicationRole();
|
|
88
|
-
await connection.query(insertInto(applicationRole,
|
|
91
|
+
await connection.query(insertInto(applicationRole, Roles.table));
|
|
89
92
|
await assignScopesToRole(connection, adminTenantId, applicationRole.id, ...cloudAdditionalScopes
|
|
90
93
|
.filter(({ name }) => name === CloudScope.SendSms || name === CloudScope.SendEmail)
|
|
91
94
|
.map(({ id }) => id));
|
|
92
|
-
// Assign all cloud API scopes to role `admin:admin`
|
|
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);
|
|
96
95
|
await Promise.all([
|
|
97
|
-
connection
|
|
98
|
-
connection
|
|
99
|
-
connection.query(insertInto(
|
|
100
|
-
connection.query(insertInto(
|
|
101
|
-
connection.query(insertInto(
|
|
96
|
+
seedLegacyManagementApiUserRole(connection),
|
|
97
|
+
seedTenantCloudServiceApplication(connection, defaultTenantId),
|
|
98
|
+
connection.query(insertInto(createDefaultAdminConsoleConfig(defaultTenantId), LogtoConfigs.table)),
|
|
99
|
+
connection.query(insertInto(createDefaultAdminConsoleConfig(adminTenantId), LogtoConfigs.table)),
|
|
100
|
+
connection.query(insertInto(createDefaultSignInExperience(defaultTenantId, isCloud), SignInExperiences.table)),
|
|
101
|
+
connection.query(insertInto(createAdminTenantSignInExperience(), SignInExperiences.table)),
|
|
102
|
+
connection.query(insertInto(createDefaultAdminConsoleApplication(), Applications.table)),
|
|
102
103
|
updateDatabaseTimestamp(connection, latestTimestamp),
|
|
104
|
+
seedTenantOrganizations(connection),
|
|
105
|
+
seedManagementApiProxyApplications(connection),
|
|
103
106
|
]);
|
|
104
107
|
consoleLog.succeed('Seed data');
|
|
105
108
|
};
|
|
@@ -109,3 +112,61 @@ export const seedCloud = async (connection) => {
|
|
|
109
112
|
seedTenantCloudServiceApplication(connection, adminTenantId),
|
|
110
113
|
]);
|
|
111
114
|
};
|
|
115
|
+
/**
|
|
116
|
+
* Seed additional test data for integration or alteration tests.
|
|
117
|
+
*
|
|
118
|
+
* It will create two users to the admin tenant (`test-1` and `test-2`), and do the following:
|
|
119
|
+
*
|
|
120
|
+
* - `test-1` will be assigned the management roles for both `default` and `admin` tenant.
|
|
121
|
+
* - `test-2` will be assigned the management role for `default` tenant.
|
|
122
|
+
*/
|
|
123
|
+
export const seedTest = async (connection, forLegacy = false) => {
|
|
124
|
+
const roles = convertToIdentifiers(Roles);
|
|
125
|
+
const getManagementRole = async (tenantId) => connection.one(sql `
|
|
126
|
+
select ${roles.fields.id}
|
|
127
|
+
from ${roles.table}
|
|
128
|
+
where ${roles.fields.tenantId} = ${adminTenantId}
|
|
129
|
+
and ${roles.fields.name} = ${`${tenantId}:admin`}
|
|
130
|
+
`);
|
|
131
|
+
const assignRoleToUser = async (userId, roleId) => connection.query(insertInto({ id: generateStandardId(), userId, roleId, tenantId: adminTenantId }, UsersRoles.table));
|
|
132
|
+
const userIds = Object.freeze(['test-1', 'test-2']);
|
|
133
|
+
await Promise.all([
|
|
134
|
+
connection.query(insertInto({ id: userIds[0], username: 'test1', tenantId: adminTenantId }, Users.table)),
|
|
135
|
+
connection.query(insertInto({ id: userIds[1], username: 'test2', tenantId: adminTenantId }, Users.table)),
|
|
136
|
+
]);
|
|
137
|
+
if (forLegacy) {
|
|
138
|
+
const adminTenantRole = await getManagementRole(adminTenantId);
|
|
139
|
+
await assignRoleToUser(userIds[0], adminTenantRole.id);
|
|
140
|
+
}
|
|
141
|
+
consoleLog.succeed('Created test users');
|
|
142
|
+
// The only legacy user role for Management API. Used in OSS only.
|
|
143
|
+
const defaultTenantRole = await getManagementRole(defaultTenantId);
|
|
144
|
+
await Promise.all([
|
|
145
|
+
assignRoleToUser(userIds[0], defaultTenantRole.id),
|
|
146
|
+
assignRoleToUser(userIds[1], defaultTenantRole.id),
|
|
147
|
+
]);
|
|
148
|
+
consoleLog.succeed('Assigned tenant management roles to the test users');
|
|
149
|
+
// This check is for older versions (<=v.12.0) that don't have tenant organization initialized.
|
|
150
|
+
if (forLegacy) {
|
|
151
|
+
consoleLog.warn('Tenant organization is not enabled in legacy Logto versions, skip seeding tenant organization data');
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
const addOrganizationMembership = async (userId, tenantId) => connection.query(insertInto({ userId, organizationId: getTenantOrganizationId(tenantId), tenantId: adminTenantId }, OrganizationUserRelations.table));
|
|
155
|
+
await Promise.all([
|
|
156
|
+
addOrganizationMembership(userIds[0], adminTenantId),
|
|
157
|
+
addOrganizationMembership(userIds[0], defaultTenantId),
|
|
158
|
+
addOrganizationMembership(userIds[1], defaultTenantId),
|
|
159
|
+
]);
|
|
160
|
+
const assignOrganizationRole = async (userId, tenantId, tenantRole) => connection.query(insertInto({
|
|
161
|
+
userId,
|
|
162
|
+
organizationRoleId: getTenantRole(tenantRole).id,
|
|
163
|
+
organizationId: getTenantOrganizationId(tenantId),
|
|
164
|
+
tenantId: adminTenantId,
|
|
165
|
+
}, OrganizationRoleUserRelations.table));
|
|
166
|
+
await Promise.all([
|
|
167
|
+
assignOrganizationRole(userIds[0], adminTenantId, TenantRole.Admin),
|
|
168
|
+
assignOrganizationRole(userIds[0], defaultTenantId, TenantRole.Admin),
|
|
169
|
+
assignOrganizationRole(userIds[1], defaultTenantId, TenantRole.Admin),
|
|
170
|
+
]);
|
|
171
|
+
consoleLog.succeed('Assigned tenant organization membership and roles to the test users');
|
|
172
|
+
};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { DatabaseTransactionConnection } from 'slonik';
|
|
2
|
+
/**
|
|
3
|
+
* Seed initial data in the admin tenant for tenant organizations (`default` and `admin`):
|
|
4
|
+
*
|
|
5
|
+
* - Organization roles and scopes for tenant management.
|
|
6
|
+
* - Create tenant organizations for the initial tenants (`default` and `admin`).
|
|
7
|
+
*/
|
|
8
|
+
export declare const seedTenantOrganizations: (connection: DatabaseTransactionConnection) => Promise<void>;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { defaultTenantId, adminTenantId, OrganizationRoles, TenantRole, getTenantScope, OrganizationScopes, TenantScope, getTenantRole, OrganizationRoleScopeRelations, tenantRoleScopes, getTenantOrganizationCreateData, Organizations, } from '@logto/schemas';
|
|
2
|
+
import { insertInto } from '../../../database.js';
|
|
3
|
+
import { consoleLog } from '../../../utils.js';
|
|
4
|
+
/**
|
|
5
|
+
* Seed initial data in the admin tenant for tenant organizations (`default` and `admin`):
|
|
6
|
+
*
|
|
7
|
+
* - Organization roles and scopes for tenant management.
|
|
8
|
+
* - Create tenant organizations for the initial tenants (`default` and `admin`).
|
|
9
|
+
*/
|
|
10
|
+
export const seedTenantOrganizations = async (connection) => {
|
|
11
|
+
const tenantIds = [defaultTenantId, adminTenantId];
|
|
12
|
+
// Init organization template
|
|
13
|
+
await Promise.all([
|
|
14
|
+
connection.query(insertInto(Object.values(TenantRole).map((role) => getTenantRole(role)), OrganizationRoles.table)),
|
|
15
|
+
connection.query(insertInto(Object.values(TenantScope).map((scope) => getTenantScope(scope)), OrganizationScopes.table)),
|
|
16
|
+
]);
|
|
17
|
+
// Link organization roles and scopes
|
|
18
|
+
await connection.query(insertInto(Object.entries(tenantRoleScopes).flatMap(([role, scopes]) => scopes.map((scope) => ({
|
|
19
|
+
tenantId: adminTenantId,
|
|
20
|
+
// eslint-disable-next-line no-restricted-syntax -- `Object.entries` converts the enum to string
|
|
21
|
+
organizationRoleId: getTenantRole(role).id,
|
|
22
|
+
organizationScopeId: getTenantScope(scope).id,
|
|
23
|
+
}))), OrganizationRoleScopeRelations.table));
|
|
24
|
+
consoleLog.succeed('Initialized tenant organization template');
|
|
25
|
+
// Init tenant organizations
|
|
26
|
+
await connection.query(insertInto(tenantIds.map((id) => getTenantOrganizationCreateData(id)), Organizations.table));
|
|
27
|
+
consoleLog.succeed('Created tenant organizations');
|
|
28
|
+
};
|
|
@@ -1,5 +1,15 @@
|
|
|
1
|
-
import type
|
|
2
|
-
import type { CommonQueryMethods } from 'slonik';
|
|
1
|
+
import { type AdminData, type UpdateAdminData, type CreateScope } from '@logto/schemas';
|
|
2
|
+
import type { CommonQueryMethods, DatabaseTransactionConnection } from 'slonik';
|
|
3
3
|
export declare const createTenant: (pool: CommonQueryMethods, tenantId: string) => Promise<void>;
|
|
4
4
|
export declare const seedAdminData: (pool: CommonQueryMethods, data: AdminData | UpdateAdminData, ...additionalScopes: CreateScope[]) => Promise<void>;
|
|
5
5
|
export declare const assignScopesToRole: (pool: CommonQueryMethods, tenantId: string, roleId: string, ...scopeIds: string[]) => Promise<void>;
|
|
6
|
+
/**
|
|
7
|
+
* For each initial tenant (`default` and `admin`), create a machine-to-machine application for
|
|
8
|
+
* Management API proxy and assign the corresponding proxy role to it.
|
|
9
|
+
*/
|
|
10
|
+
export declare const seedManagementApiProxyApplications: (connection: DatabaseTransactionConnection) => Promise<void>;
|
|
11
|
+
/**
|
|
12
|
+
* Seed the legacy user role for accessing default Management API, and assign the `all` scope to
|
|
13
|
+
* it. Used in OSS only.
|
|
14
|
+
*/
|
|
15
|
+
export declare const seedLegacyManagementApiUserRole: (connection: DatabaseTransactionConnection) => Promise<void>;
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import { createTenantMetadata } from '@logto/core-kit';
|
|
2
|
+
import { defaultTenantId, adminTenantId, Applications, ApplicationsRoles, getMapiProxyM2mApp, getMapiProxyRole, defaultManagementApiAdminName, Roles, PredefinedScope, getManagementApiResourceIndicator, } from '@logto/schemas';
|
|
2
3
|
import { generateStandardId } from '@logto/shared';
|
|
3
4
|
import { assert } from '@silverhand/essentials';
|
|
4
5
|
import { sql } from 'slonik';
|
|
5
6
|
import { raw } from 'slonik-sql-tag-raw';
|
|
6
7
|
import { insertInto } from '../../../database.js';
|
|
7
8
|
import { getDatabaseName } from '../../../queries/database.js';
|
|
9
|
+
import { consoleLog } from '../../../utils.js';
|
|
8
10
|
export const createTenant = async (pool, tenantId) => {
|
|
9
11
|
const database = await getDatabaseName(pool, true);
|
|
10
12
|
const { parentRole, role, password } = createTenantMetadata(database, tenantId);
|
|
@@ -54,3 +56,49 @@ export const assignScopesToRole = async (pool, tenantId, roleId, ...scopeIds) =>
|
|
|
54
56
|
tenantId,
|
|
55
57
|
}, 'roles_scopes'))));
|
|
56
58
|
};
|
|
59
|
+
/**
|
|
60
|
+
* For each initial tenant (`default` and `admin`), create a machine-to-machine application for
|
|
61
|
+
* Management API proxy and assign the corresponding proxy role to it.
|
|
62
|
+
*/
|
|
63
|
+
export const seedManagementApiProxyApplications = async (connection) => {
|
|
64
|
+
const tenantIds = [defaultTenantId, adminTenantId];
|
|
65
|
+
// Create machine-to-machine applications for Management API proxy
|
|
66
|
+
await connection.query(insertInto(tenantIds.map((tenantId) => getMapiProxyM2mApp(tenantId)), Applications.table));
|
|
67
|
+
consoleLog.succeed('Created machine-to-machine applications for Management API proxy');
|
|
68
|
+
// Assign the proxy roles to the applications
|
|
69
|
+
await connection.query(insertInto(tenantIds.map((tenantId) => ({
|
|
70
|
+
tenantId: adminTenantId,
|
|
71
|
+
id: generateStandardId(),
|
|
72
|
+
applicationId: getMapiProxyM2mApp(tenantId).id,
|
|
73
|
+
roleId: getMapiProxyRole(tenantId).id,
|
|
74
|
+
})), ApplicationsRoles.table));
|
|
75
|
+
consoleLog.succeed('Assigned the proxy roles to the applications');
|
|
76
|
+
};
|
|
77
|
+
/**
|
|
78
|
+
* Seed the legacy user role for accessing default Management API, and assign the `all` scope to
|
|
79
|
+
* it. Used in OSS only.
|
|
80
|
+
*/
|
|
81
|
+
export const seedLegacyManagementApiUserRole = async (connection) => {
|
|
82
|
+
const roleId = generateStandardId();
|
|
83
|
+
await connection.query(insertInto({
|
|
84
|
+
tenantId: adminTenantId,
|
|
85
|
+
id: roleId,
|
|
86
|
+
name: defaultManagementApiAdminName,
|
|
87
|
+
description: 'Legacy user role for accessing default Management API. Used in OSS only.',
|
|
88
|
+
}, Roles.table));
|
|
89
|
+
await connection.query(sql `
|
|
90
|
+
insert into roles_scopes (id, role_id, scope_id, tenant_id)
|
|
91
|
+
values (
|
|
92
|
+
${generateStandardId()},
|
|
93
|
+
${roleId},
|
|
94
|
+
(
|
|
95
|
+
select scopes.id from scopes
|
|
96
|
+
join resources on scopes.resource_id = resources.id
|
|
97
|
+
where resources.indicator = ${getManagementApiResourceIndicator(defaultTenantId)}
|
|
98
|
+
and scopes.name = ${PredefinedScope.All}
|
|
99
|
+
and scopes.tenant_id = ${adminTenantId}
|
|
100
|
+
),
|
|
101
|
+
${adminTenantId}
|
|
102
|
+
);
|
|
103
|
+
`);
|
|
104
|
+
};
|
|
@@ -16,7 +16,7 @@ import { cliConfig, ConfigKey, consoleLog, downloadFile, isTty, oraPromise, safe
|
|
|
16
16
|
import { seedByPool } from '../database/seed/index.js';
|
|
17
17
|
const pgRequired = new semver.SemVer('14.0.0');
|
|
18
18
|
export const validateNodeVersion = () => {
|
|
19
|
-
const required = [new semver.SemVer('
|
|
19
|
+
const required = [new semver.SemVer('20.9.0')];
|
|
20
20
|
const requiredVersionString = required.map((version) => '^' + version.version).join(' || ');
|
|
21
21
|
const current = new semver.SemVer(execSync('node -v', { encoding: 'utf8', stdio: 'pipe' }));
|
|
22
22
|
if (required.every((version) => version.major !== current.major)) {
|
|
@@ -11,6 +11,11 @@ const translate = {
|
|
|
11
11
|
alias: 'p',
|
|
12
12
|
type: 'string',
|
|
13
13
|
describe: 'The path to your Logto instance directory',
|
|
14
|
+
})
|
|
15
|
+
.option('skip-core-check', {
|
|
16
|
+
alias: 'sc',
|
|
17
|
+
type: 'boolean',
|
|
18
|
+
describe: 'Skip checking if the core package is existed',
|
|
14
19
|
})
|
|
15
20
|
.command(create)
|
|
16
21
|
.command(listTags)
|
|
@@ -22,7 +22,7 @@ const syncKeys = {
|
|
|
22
22
|
.option('package', {
|
|
23
23
|
alias: 'pkg',
|
|
24
24
|
type: 'string',
|
|
25
|
-
describe: 'The package name of the phrases,
|
|
25
|
+
describe: 'The package name of the phrases, e.g. `phrases` or `phrases-experience`',
|
|
26
26
|
default: 'phrases',
|
|
27
27
|
})
|
|
28
28
|
.option('target', {
|
|
@@ -31,12 +31,12 @@ const syncKeys = {
|
|
|
31
31
|
describe: 'The target language tag, or `all` to sync all languages',
|
|
32
32
|
})
|
|
33
33
|
.option('skip-lint', {
|
|
34
|
-
alias: '
|
|
34
|
+
alias: 'sl',
|
|
35
35
|
type: 'boolean',
|
|
36
36
|
describe: 'Skip running `eslint --fix` for locales after syncing',
|
|
37
37
|
})
|
|
38
38
|
.demandOption(['baseline', 'target']),
|
|
39
|
-
handler: async ({ path: inputPath, baseline: baselineTag, target: targetTag, skipLint, package: packageName, }) => {
|
|
39
|
+
handler: async ({ path: inputPath, skipCoreCheck, baseline: baselineTag, target: targetTag, skipLint, package: packageName, }) => {
|
|
40
40
|
if (!isLanguageTag(baselineTag)) {
|
|
41
41
|
consoleLog.fatal('Invalid baseline language tag');
|
|
42
42
|
}
|
|
@@ -46,10 +46,7 @@ const syncKeys = {
|
|
|
46
46
|
if (baselineTag === targetTag) {
|
|
47
47
|
consoleLog.fatal('Baseline and target cannot be the same');
|
|
48
48
|
}
|
|
49
|
-
|
|
50
|
-
consoleLog.fatal('Invalid package name, expected `phrases` or `phrases-experience`');
|
|
51
|
-
}
|
|
52
|
-
const instancePath = await inquireInstancePath(inputPath);
|
|
49
|
+
const instancePath = await inquireInstancePath(inputPath, skipCoreCheck);
|
|
53
50
|
const phrasesPath = path.join(instancePath, 'packages', packageName);
|
|
54
51
|
const localesPath = path.join(phrasesPath, 'src/locales');
|
|
55
52
|
const entrypoint = path.join(localesPath, baselineTag.toLowerCase(), 'index.ts');
|
|
@@ -1,15 +1,21 @@
|
|
|
1
1
|
import { languages } from '@logto/language-kit';
|
|
2
2
|
import { isBuiltInLanguageTag as isPhrasesBuiltInLanguageTag } from '@logto/phrases';
|
|
3
|
-
import { isBuiltInLanguageTag as isPhrasesUiBuiltInLanguageTag } from '@logto/phrases-experience';
|
|
4
3
|
import PQueue from 'p-queue';
|
|
5
4
|
import { inquireInstancePath, lintLocaleFiles } from '../../utils.js';
|
|
6
5
|
import { baseLanguage, syncTranslation } from './utils.js';
|
|
7
6
|
const sync = {
|
|
8
7
|
command: ['sync'],
|
|
9
8
|
describe: 'Translate all untranslated phrases using ChatGPT. Note the environment variable `OPENAI_API_KEY` is required to work.',
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
9
|
+
builder: (yargs) => yargs.option('package', {
|
|
10
|
+
alias: 'pkg',
|
|
11
|
+
type: 'string',
|
|
12
|
+
describe: 'The package name of the phrases, e.g. `phrases` or `phrases-experience`',
|
|
13
|
+
default: 'phrases',
|
|
14
|
+
}),
|
|
15
|
+
handler: async ({ path: inputPath, skipCoreCheck, package: packageName }) => {
|
|
16
|
+
const queue = new PQueue({ concurrency: 10 });
|
|
17
|
+
const instancePath = await inquireInstancePath(inputPath, skipCoreCheck);
|
|
18
|
+
const packages = packageName ? [packageName] : ['phrases', 'phrases-experience'];
|
|
13
19
|
for (const languageTag of Object.keys(languages)) {
|
|
14
20
|
if (languageTag === baseLanguage) {
|
|
15
21
|
continue;
|
|
@@ -19,25 +25,20 @@ const sync = {
|
|
|
19
25
|
verbose: false,
|
|
20
26
|
queue,
|
|
21
27
|
};
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
28
|
+
for (const packageName of packages) {
|
|
29
|
+
/* eslint-disable no-await-in-loop */
|
|
30
|
+
if (isPhrasesBuiltInLanguageTag(languageTag)) {
|
|
31
|
+
await syncTranslation({
|
|
32
|
+
...baseOptions,
|
|
33
|
+
packageName,
|
|
34
|
+
languageTag,
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
/* eslint-enable no-await-in-loop */
|
|
29
38
|
}
|
|
30
|
-
if (isPhrasesUiBuiltInLanguageTag(languageTag)) {
|
|
31
|
-
await syncTranslation({
|
|
32
|
-
...baseOptions,
|
|
33
|
-
packageName: 'phrases-experience',
|
|
34
|
-
languageTag,
|
|
35
|
-
});
|
|
36
|
-
}
|
|
37
|
-
/* eslint-enable no-await-in-loop */
|
|
38
39
|
}
|
|
39
40
|
await queue.onIdle();
|
|
40
|
-
void lintLocaleFiles(instancePath);
|
|
41
|
+
void lintLocaleFiles(instancePath, packageName);
|
|
41
42
|
},
|
|
42
43
|
};
|
|
43
44
|
export default sync;
|
|
@@ -5,7 +5,7 @@ export declare const readLocaleFiles: (directory: string) => Promise<string[]>;
|
|
|
5
5
|
export declare const readBaseLocaleFiles: (directory: string) => Promise<string[]>;
|
|
6
6
|
export type TranslationOptions = {
|
|
7
7
|
instancePath: string;
|
|
8
|
-
packageName:
|
|
8
|
+
packageName: string;
|
|
9
9
|
languageTag: LanguageTag;
|
|
10
10
|
verbose?: boolean;
|
|
11
11
|
queue?: PQueue;
|
|
@@ -25,7 +25,7 @@ export const readBaseLocaleFiles = async (directory) => {
|
|
|
25
25
|
}
|
|
26
26
|
return readLocaleFiles(enDirectory);
|
|
27
27
|
};
|
|
28
|
-
export const createFullTranslation = async ({ instancePath, packageName, languageTag, verbose = true, queue = new PQueue({ concurrency:
|
|
28
|
+
export const createFullTranslation = async ({ instancePath, packageName, languageTag, verbose = true, queue = new PQueue({ concurrency: 10 }), }) => {
|
|
29
29
|
const localeFiles = await getBaseAndTargetLocaleFiles(instancePath, packageName, languageTag);
|
|
30
30
|
if (verbose) {
|
|
31
31
|
consoleLog.info('Found ' +
|
|
@@ -54,7 +54,7 @@ export const createFullTranslation = async ({ instancePath, packageName, languag
|
|
|
54
54
|
}
|
|
55
55
|
return queue.onIdle();
|
|
56
56
|
};
|
|
57
|
-
export const syncTranslation = async ({ instancePath, packageName, languageTag, verbose = true, queue = new PQueue({ concurrency:
|
|
57
|
+
export const syncTranslation = async ({ instancePath, packageName, languageTag, verbose = true, queue = new PQueue({ concurrency: 10 }), }) => {
|
|
58
58
|
const localeFiles = await getBaseAndTargetLocaleFiles(instancePath, packageName, languageTag);
|
|
59
59
|
if (verbose) {
|
|
60
60
|
consoleLog.info('Found ' +
|
package/lib/database.d.ts
CHANGED
|
@@ -9,4 +9,8 @@ export declare const createPoolFromConfig: () => Promise<import("slonik").Databa
|
|
|
9
9
|
* @returns A new database pool with the database URL in config.
|
|
10
10
|
*/
|
|
11
11
|
export declare const createPoolAndDatabaseIfNeeded: () => Promise<import("slonik").DatabasePool>;
|
|
12
|
-
|
|
12
|
+
/**
|
|
13
|
+
* Build an `insert into` query from the given payload. If the payload is an array, it will insert
|
|
14
|
+
* multiple rows.
|
|
15
|
+
*/
|
|
16
|
+
export declare const insertInto: <T extends SchemaLike<string>>(payload: T | T[], table: string) => import("slonik").TaggedTemplateLiteralInvocation<import("slonik").QueryResultRow>;
|
package/lib/database.js
CHANGED
|
@@ -54,11 +54,20 @@ export const createPoolAndDatabaseIfNeeded = async () => {
|
|
|
54
54
|
return createPoolFromConfig();
|
|
55
55
|
}
|
|
56
56
|
};
|
|
57
|
-
|
|
58
|
-
|
|
57
|
+
/**
|
|
58
|
+
* Build an `insert into` query from the given payload. If the payload is an array, it will insert
|
|
59
|
+
* multiple rows.
|
|
60
|
+
*/
|
|
61
|
+
export const insertInto = (payload, table) => {
|
|
62
|
+
const first = Array.isArray(payload) ? payload[0] : payload;
|
|
63
|
+
if (!first) {
|
|
64
|
+
throw new Error('Payload cannot be empty');
|
|
65
|
+
}
|
|
66
|
+
const keys = Object.keys(first);
|
|
67
|
+
const values = Array.isArray(payload) ? payload : [payload];
|
|
59
68
|
return sql `
|
|
60
69
|
insert into ${sql.identifier([table])}
|
|
61
70
|
(${sql.join(keys.map((key) => sql.identifier([decamelize(key)])), sql `, `)})
|
|
62
|
-
values (${sql.join(keys.map((key) => convertToPrimitiveOrSql(key, object[key] ?? null)), sql `, `)})
|
|
71
|
+
values ${sql.join(values.map((object) => sql `(${sql.join(keys.map((key) => convertToPrimitiveOrSql(key, object[key] ?? null)), sql `, `)})`), sql `, `)}
|
|
63
72
|
`;
|
|
64
73
|
};
|
package/lib/package-json.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export const packageJson = {
|
|
2
2
|
"name": "@logto/cli",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.13.0",
|
|
4
4
|
"description": "Logto CLI.",
|
|
5
5
|
"author": "Silverhand Inc. <contact@silverhand.io>",
|
|
6
6
|
"homepage": "https://github.com/logto-io/logto#readme",
|
|
@@ -37,30 +37,30 @@ export const packageJson = {
|
|
|
37
37
|
"prepack": "pnpm build"
|
|
38
38
|
},
|
|
39
39
|
"engines": {
|
|
40
|
-
"node": "^
|
|
40
|
+
"node": "^20.9.0"
|
|
41
41
|
},
|
|
42
42
|
"bugs": {
|
|
43
43
|
"url": "https://github.com/logto-io/logto/issues"
|
|
44
44
|
},
|
|
45
45
|
"dependencies": {
|
|
46
|
-
"@logto/connector-kit": "workspace:^2.
|
|
47
|
-
"@logto/core-kit": "workspace:^2.
|
|
48
|
-
"@logto/language-kit": "workspace:^1.
|
|
49
|
-
"@logto/phrases": "workspace:^1.
|
|
50
|
-
"@logto/phrases-experience": "workspace:^1.
|
|
51
|
-
"@logto/schemas": "workspace:1.
|
|
52
|
-
"@logto/shared": "workspace:^3.
|
|
53
|
-
"@silverhand/essentials": "^2.
|
|
46
|
+
"@logto/connector-kit": "workspace:^2.1.0",
|
|
47
|
+
"@logto/core-kit": "workspace:^2.3.0",
|
|
48
|
+
"@logto/language-kit": "workspace:^1.1.0",
|
|
49
|
+
"@logto/phrases": "workspace:^1.9.0",
|
|
50
|
+
"@logto/phrases-experience": "workspace:^1.6.0",
|
|
51
|
+
"@logto/schemas": "workspace:1.13.0",
|
|
52
|
+
"@logto/shared": "workspace:^3.1.0",
|
|
53
|
+
"@silverhand/essentials": "^2.9.0",
|
|
54
54
|
"chalk": "^5.0.0",
|
|
55
55
|
"decamelize": "^6.0.0",
|
|
56
56
|
"dotenv": "^16.0.0",
|
|
57
|
-
"got": "^
|
|
57
|
+
"got": "^14.0.0",
|
|
58
58
|
"hpagent": "^1.2.0",
|
|
59
59
|
"inquirer": "^9.0.0",
|
|
60
60
|
"nanoid": "^5.0.1",
|
|
61
|
-
"ora": "^
|
|
62
|
-
"p-limit": "^
|
|
63
|
-
"p-queue": "^
|
|
61
|
+
"ora": "^8.0.1",
|
|
62
|
+
"p-limit": "^5.0.0",
|
|
63
|
+
"p-queue": "^8.0.0",
|
|
64
64
|
"p-retry": "^6.0.0",
|
|
65
65
|
"pg-protocol": "^1.6.0",
|
|
66
66
|
"roarr": "^7.11.0",
|
|
@@ -69,23 +69,23 @@ export const packageJson = {
|
|
|
69
69
|
"slonik-interceptor-preset": "^1.2.10",
|
|
70
70
|
"slonik-sql-tag-raw": "^1.1.4",
|
|
71
71
|
"tar": "^6.1.11",
|
|
72
|
-
"typescript": "^5.
|
|
72
|
+
"typescript": "^5.3.3",
|
|
73
73
|
"yargs": "^17.6.0",
|
|
74
74
|
"zod": "^3.22.4"
|
|
75
75
|
},
|
|
76
76
|
"devDependencies": {
|
|
77
|
-
"@silverhand/eslint-config": "
|
|
78
|
-
"@silverhand/ts-config": "
|
|
77
|
+
"@silverhand/eslint-config": "5.0.0",
|
|
78
|
+
"@silverhand/ts-config": "5.0.0",
|
|
79
79
|
"@withtyped/server": "^0.12.9",
|
|
80
80
|
"@types/inquirer": "^9.0.0",
|
|
81
81
|
"@types/jest": "^29.4.0",
|
|
82
|
-
"@types/node": "^
|
|
82
|
+
"@types/node": "^20.9.5",
|
|
83
83
|
"@types/semver": "^7.3.12",
|
|
84
|
-
"@types/sinon": "^
|
|
84
|
+
"@types/sinon": "^17.0.0",
|
|
85
85
|
"@types/tar": "^6.1.2",
|
|
86
86
|
"@types/yargs": "^17.0.13",
|
|
87
87
|
"eslint": "^8.44.0",
|
|
88
|
-
"jest": "^29.
|
|
88
|
+
"jest": "^29.7.0",
|
|
89
89
|
"lint-staged": "^15.0.0",
|
|
90
90
|
"prettier": "^3.0.0",
|
|
91
91
|
"sinon": "^17.0.0"
|
package/lib/utils.d.ts
CHANGED
|
@@ -28,10 +28,10 @@ export declare const getCliConfigWithPrompt: ({ key, readableKey, comments, defa
|
|
|
28
28
|
* findLastIndex immediately returns that element index. Otherwise, findLastIndex returns -1.
|
|
29
29
|
*/
|
|
30
30
|
export declare function findLastIndex<T>(array: readonly T[], predicate: (value: T, index: number, object: readonly T[]) => boolean): number;
|
|
31
|
-
export declare const inquireInstancePath: (initialPath?: string) => Promise<string>;
|
|
31
|
+
export declare const inquireInstancePath: (initialPath?: string, skipCoreCheck?: boolean) => Promise<string>;
|
|
32
32
|
export type ConnectorPackage = {
|
|
33
33
|
name: string;
|
|
34
34
|
path: string;
|
|
35
35
|
};
|
|
36
36
|
export declare const getConnectorPackagesFromDirectory: (directory: string) => Promise<ConnectorPackage[]>;
|
|
37
|
-
export declare const lintLocaleFiles: (instancePath: string, packageName?:
|
|
37
|
+
export declare const lintLocaleFiles: (instancePath: string, packageName?: string) => Promise<void>;
|
package/lib/utils.js
CHANGED
|
@@ -5,7 +5,7 @@ import { createRequire } from 'node:module';
|
|
|
5
5
|
import path from 'node:path';
|
|
6
6
|
import { promisify } from 'node:util';
|
|
7
7
|
import { ConsoleLog } from '@logto/shared';
|
|
8
|
-
import { assert, conditionalString } from '@silverhand/essentials';
|
|
8
|
+
import { assert, conditional, conditionalString } from '@silverhand/essentials';
|
|
9
9
|
import chalk from 'chalk';
|
|
10
10
|
import { got } from 'got';
|
|
11
11
|
import { HttpsProxyAgent } from 'hpagent';
|
|
@@ -139,9 +139,9 @@ const validatePath = async (value) => {
|
|
|
139
139
|
}
|
|
140
140
|
return true;
|
|
141
141
|
};
|
|
142
|
-
export const inquireInstancePath = async (initialPath) => {
|
|
142
|
+
export const inquireInstancePath = async (initialPath, skipCoreCheck) => {
|
|
143
143
|
const inquire = async () => {
|
|
144
|
-
if (!initialPath && (await validatePath('.')) === true) {
|
|
144
|
+
if (!initialPath && (skipCoreCheck ?? (await validatePath('.')) === true)) {
|
|
145
145
|
return path.resolve('.');
|
|
146
146
|
}
|
|
147
147
|
if (!isTty()) {
|
|
@@ -154,14 +154,16 @@ export const inquireInstancePath = async (initialPath) => {
|
|
|
154
154
|
type: 'input',
|
|
155
155
|
default: defaultPath,
|
|
156
156
|
filter: (value) => value.trim(),
|
|
157
|
-
validate: validatePath,
|
|
157
|
+
validate: conditional(!skipCoreCheck && validatePath),
|
|
158
158
|
}, { instancePath: initialPath });
|
|
159
159
|
return instancePath;
|
|
160
160
|
};
|
|
161
161
|
const instancePath = await inquire();
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
162
|
+
if (!skipCoreCheck) {
|
|
163
|
+
const validated = await validatePath(instancePath);
|
|
164
|
+
if (validated !== true) {
|
|
165
|
+
consoleLog.fatal(validated);
|
|
166
|
+
}
|
|
165
167
|
}
|
|
166
168
|
return instancePath;
|
|
167
169
|
};
|
|
@@ -188,7 +190,7 @@ const execPromise = promisify(execFile);
|
|
|
188
190
|
export const lintLocaleFiles = async (
|
|
189
191
|
/** Logto instance path */
|
|
190
192
|
instancePath,
|
|
191
|
-
/** Target package name, ignore to lint both packages */
|
|
193
|
+
/** Target package name, ignore to lint both `phrases` and `phrases-experience` packages */
|
|
192
194
|
packageName) => {
|
|
193
195
|
const spinner = ora({
|
|
194
196
|
text: 'Running `eslint --fix` for locales',
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@logto/cli",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.13.0",
|
|
4
4
|
"description": "Logto CLI.",
|
|
5
5
|
"author": "Silverhand Inc. <contact@silverhand.io>",
|
|
6
6
|
"homepage": "https://github.com/logto-io/logto#readme",
|
|
@@ -22,30 +22,30 @@
|
|
|
22
22
|
"url": "git+https://github.com/logto-io/logto.git"
|
|
23
23
|
},
|
|
24
24
|
"engines": {
|
|
25
|
-
"node": "^
|
|
25
|
+
"node": "^20.9.0"
|
|
26
26
|
},
|
|
27
27
|
"bugs": {
|
|
28
28
|
"url": "https://github.com/logto-io/logto/issues"
|
|
29
29
|
},
|
|
30
30
|
"dependencies": {
|
|
31
|
-
"@logto/connector-kit": "^2.
|
|
32
|
-
"@logto/core-kit": "^2.
|
|
33
|
-
"@logto/language-kit": "^1.
|
|
34
|
-
"@logto/phrases": "^1.
|
|
35
|
-
"@logto/phrases-experience": "^1.
|
|
36
|
-
"@logto/schemas": "1.
|
|
37
|
-
"@logto/shared": "^3.
|
|
38
|
-
"@silverhand/essentials": "^2.
|
|
31
|
+
"@logto/connector-kit": "^2.1.0",
|
|
32
|
+
"@logto/core-kit": "^2.3.0",
|
|
33
|
+
"@logto/language-kit": "^1.1.0",
|
|
34
|
+
"@logto/phrases": "^1.9.0",
|
|
35
|
+
"@logto/phrases-experience": "^1.6.0",
|
|
36
|
+
"@logto/schemas": "1.13.0",
|
|
37
|
+
"@logto/shared": "^3.1.0",
|
|
38
|
+
"@silverhand/essentials": "^2.9.0",
|
|
39
39
|
"chalk": "^5.0.0",
|
|
40
40
|
"decamelize": "^6.0.0",
|
|
41
41
|
"dotenv": "^16.0.0",
|
|
42
|
-
"got": "^
|
|
42
|
+
"got": "^14.0.0",
|
|
43
43
|
"hpagent": "^1.2.0",
|
|
44
44
|
"inquirer": "^9.0.0",
|
|
45
45
|
"nanoid": "^5.0.1",
|
|
46
|
-
"ora": "^
|
|
47
|
-
"p-limit": "^
|
|
48
|
-
"p-queue": "^
|
|
46
|
+
"ora": "^8.0.1",
|
|
47
|
+
"p-limit": "^5.0.0",
|
|
48
|
+
"p-queue": "^8.0.0",
|
|
49
49
|
"p-retry": "^6.0.0",
|
|
50
50
|
"pg-protocol": "^1.6.0",
|
|
51
51
|
"roarr": "^7.11.0",
|
|
@@ -54,23 +54,23 @@
|
|
|
54
54
|
"slonik-interceptor-preset": "^1.2.10",
|
|
55
55
|
"slonik-sql-tag-raw": "^1.1.4",
|
|
56
56
|
"tar": "^6.1.11",
|
|
57
|
-
"typescript": "^5.
|
|
57
|
+
"typescript": "^5.3.3",
|
|
58
58
|
"yargs": "^17.6.0",
|
|
59
59
|
"zod": "^3.22.4"
|
|
60
60
|
},
|
|
61
61
|
"devDependencies": {
|
|
62
|
-
"@silverhand/eslint-config": "
|
|
63
|
-
"@silverhand/ts-config": "
|
|
62
|
+
"@silverhand/eslint-config": "5.0.0",
|
|
63
|
+
"@silverhand/ts-config": "5.0.0",
|
|
64
64
|
"@withtyped/server": "^0.12.9",
|
|
65
65
|
"@types/inquirer": "^9.0.0",
|
|
66
66
|
"@types/jest": "^29.4.0",
|
|
67
|
-
"@types/node": "^
|
|
67
|
+
"@types/node": "^20.9.5",
|
|
68
68
|
"@types/semver": "^7.3.12",
|
|
69
|
-
"@types/sinon": "^
|
|
69
|
+
"@types/sinon": "^17.0.0",
|
|
70
70
|
"@types/tar": "^6.1.2",
|
|
71
71
|
"@types/yargs": "^17.0.13",
|
|
72
72
|
"eslint": "^8.44.0",
|
|
73
|
-
"jest": "^29.
|
|
73
|
+
"jest": "^29.7.0",
|
|
74
74
|
"lint-staged": "^15.0.0",
|
|
75
75
|
"prettier": "^3.0.0",
|
|
76
76
|
"sinon": "^17.0.0"
|