@logto/cli 1.13.1 → 1.15.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/alteration/index.d.ts +1 -1
- package/lib/commands/database/alteration/index.test.js +11 -8
- package/lib/commands/database/seed/cloud.d.ts +1 -1
- package/lib/commands/database/seed/cloud.js +1 -1
- package/lib/commands/database/seed/index.d.ts +3 -2
- package/lib/commands/database/seed/index.js +11 -4
- package/lib/commands/database/seed/oidc-config.d.ts +1 -1
- package/lib/commands/database/seed/tables.d.ts +4 -2
- package/lib/commands/database/seed/tables.js +19 -9
- package/lib/commands/database/seed/tenant-organizations.d.ts +1 -1
- package/lib/commands/database/seed/tenant.d.ts +1 -1
- package/lib/commands/database/seed/tenant.js +2 -3
- package/lib/commands/translate/openai.js +3 -2
- package/lib/connector/factories.d.ts +1 -1
- package/lib/connector/factories.js +3 -3
- package/lib/connector/types.d.ts +1 -1
- package/lib/connector/utils.d.ts +1 -1
- package/lib/database.d.ts +3 -3
- package/lib/database.js +4 -5
- package/lib/package-json.d.ts +4 -7
- package/lib/package-json.js +14 -17
- package/lib/queries/database.d.ts +1 -1
- package/lib/queries/database.js +1 -1
- package/lib/queries/logto-config.d.ts +3 -3
- package/lib/queries/logto-config.js +2 -2
- package/lib/queries/system.d.ts +2 -2
- package/lib/queries/system.js +2 -2
- package/lib/queries/system.test.js +7 -7
- package/lib/sql.d.ts +27 -0
- package/lib/sql.js +52 -0
- package/lib/test-utils.d.ts +2 -2
- package/lib/test-utils.js +1 -0
- package/package.json +14 -17
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { DatabasePool } from 'slonik';
|
|
1
|
+
import type { DatabasePool } from '@silverhand/slonik';
|
|
2
2
|
import type { CommandModule } from 'yargs';
|
|
3
3
|
import type { AlterationFile } from './type.js';
|
|
4
4
|
export declare const getLatestAlterationTimestamp: () => Promise<number>;
|
|
@@ -1,22 +1,25 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { createMockPool } from '@silverhand/slonik';
|
|
2
2
|
import Sinon from 'sinon';
|
|
3
|
-
import {
|
|
3
|
+
import { vi, expect, afterAll, describe, it } from 'vitest';
|
|
4
4
|
import { chooseAlterationsByVersion } from './version.js';
|
|
5
|
-
const { jest } = import.meta;
|
|
6
|
-
const { mockEsmWithActual } = createMockUtils(jest);
|
|
7
5
|
const pool = createMockPool({
|
|
8
|
-
query:
|
|
6
|
+
query: vi.fn(),
|
|
9
7
|
});
|
|
10
8
|
const files = Object.freeze([
|
|
11
9
|
{ filename: '1.0.0-1663923770-a.js', path: '/alterations-js/1.0.0-1663923770-a.js' },
|
|
12
10
|
{ filename: '1.0.0-1663923771-b.js', path: '/alterations-js/1.0.0-1663923771-b.js' },
|
|
13
11
|
{ filename: '1.0.0-1663923772-c.js', path: '/alterations-js/1.0.0-1663923772-c.js' },
|
|
14
12
|
]);
|
|
15
|
-
|
|
13
|
+
vi.mock('./utils.js', async (importOriginal) => ({
|
|
14
|
+
// eslint-disable-next-line @typescript-eslint/ban-types
|
|
15
|
+
...(await importOriginal()),
|
|
16
16
|
getAlterationFiles: async () => files,
|
|
17
17
|
}));
|
|
18
|
-
const
|
|
19
|
-
|
|
18
|
+
const getCurrentDatabaseAlterationTimestamp = vi.fn();
|
|
19
|
+
vi.doMock('../../../queries/system.js', async (importOriginal) => ({
|
|
20
|
+
// eslint-disable-next-line @typescript-eslint/ban-types
|
|
21
|
+
...(await importOriginal()),
|
|
22
|
+
getCurrentDatabaseAlterationTimestamp,
|
|
20
23
|
}));
|
|
21
24
|
const { getAvailableAlterations } = await import('./index.js');
|
|
22
25
|
describe('getAvailableAlterations()', () => {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { CommonQueryMethods } from 'slonik';
|
|
1
|
+
import type { CommonQueryMethods } from '@silverhand/slonik';
|
|
2
2
|
/**
|
|
3
3
|
* Append Redirect URIs for the default tenant callback in cloud Admin Console.
|
|
4
4
|
* It reads the same env variables as core to construct the cloud `UrlSet`.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
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
|
-
import { sql } from 'slonik';
|
|
4
|
+
import { sql } from '@silverhand/slonik';
|
|
5
5
|
import { insertInto } from '../../../database.js';
|
|
6
6
|
import { consoleLog } from '../../../utils.js';
|
|
7
7
|
/**
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import type { DatabasePool } from 'slonik';
|
|
1
|
+
import type { DatabasePool } from '@silverhand/slonik';
|
|
2
2
|
import type { CommandModule } from 'yargs';
|
|
3
|
-
export declare const seedByPool: (pool: DatabasePool, cloud?: boolean, test?: boolean) => Promise<void>;
|
|
3
|
+
export declare const seedByPool: (pool: DatabasePool, cloud?: boolean, test?: boolean, encryptBaseRole?: boolean) => Promise<void>;
|
|
4
4
|
declare const seed: CommandModule<Record<string, unknown>, {
|
|
5
5
|
swe?: boolean;
|
|
6
6
|
cloud?: boolean;
|
|
7
7
|
test?: boolean;
|
|
8
8
|
'legacy-test-data'?: boolean;
|
|
9
|
+
'encrypt-base-role'?: boolean;
|
|
9
10
|
}>;
|
|
10
11
|
export default seed;
|
|
@@ -4,7 +4,7 @@ import { consoleLog, oraPromise } from '../../../utils.js';
|
|
|
4
4
|
import { getLatestAlterationTimestamp } from '../alteration/index.js';
|
|
5
5
|
import { getAlterationDirectory } from '../alteration/utils.js';
|
|
6
6
|
import { createTables, seedCloud, seedTables, seedTest } from './tables.js';
|
|
7
|
-
export const seedByPool = async (pool, cloud = false, test = false) => {
|
|
7
|
+
export const seedByPool = async (pool, cloud = false, test = false, encryptBaseRole = 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();
|
|
@@ -12,9 +12,12 @@ export const seedByPool = async (pool, cloud = false, test = false) => {
|
|
|
12
12
|
throw new Error(`No alteration script found when seeding the database.\n` +
|
|
13
13
|
`Please check \`${getAlterationDirectory()}\` to see if there are alteration scripts available.\n`);
|
|
14
14
|
}
|
|
15
|
-
await oraPromise(createTables(connection), {
|
|
15
|
+
const tableInfo = await oraPromise(createTables(connection, encryptBaseRole), {
|
|
16
16
|
text: 'Create tables',
|
|
17
17
|
});
|
|
18
|
+
if (tableInfo.password.length > 0) {
|
|
19
|
+
consoleLog.info('base role password:', tableInfo.password);
|
|
20
|
+
}
|
|
18
21
|
await seedTables(connection, latestTimestamp, cloud);
|
|
19
22
|
if (cloud) {
|
|
20
23
|
await seedCloud(connection);
|
|
@@ -49,8 +52,12 @@ const seed = {
|
|
|
49
52
|
.option('legacy-test-data', {
|
|
50
53
|
describe: 'Seed test data only for legacy Logto versions (<=1.12.0), this option conflicts with others',
|
|
51
54
|
type: 'boolean',
|
|
55
|
+
})
|
|
56
|
+
.option('encrypt-base-role', {
|
|
57
|
+
describe: 'Seed base role with password',
|
|
58
|
+
type: 'boolean',
|
|
52
59
|
}),
|
|
53
|
-
handler: async ({ swe, cloud, test, legacyTestData }) => {
|
|
60
|
+
handler: async ({ swe, cloud, test, legacyTestData, encryptBaseRole }) => {
|
|
54
61
|
const pool = await createPoolAndDatabaseIfNeeded();
|
|
55
62
|
if (legacyTestData) {
|
|
56
63
|
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
|
|
@@ -67,7 +74,7 @@ const seed = {
|
|
|
67
74
|
return;
|
|
68
75
|
}
|
|
69
76
|
try {
|
|
70
|
-
await seedByPool(pool, cloud, test);
|
|
77
|
+
await seedByPool(pool, cloud, test, encryptBaseRole);
|
|
71
78
|
}
|
|
72
79
|
catch (error) {
|
|
73
80
|
consoleLog.error(error);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { LogtoOidcConfigType } from '@logto/schemas';
|
|
2
2
|
import { LogtoOidcConfigKey } from '@logto/schemas';
|
|
3
|
-
import type { DatabaseTransactionConnection } from 'slonik';
|
|
3
|
+
import type { DatabaseTransactionConnection } from '@silverhand/slonik';
|
|
4
4
|
export declare const seedOidcConfigs: (pool: DatabaseTransactionConnection, tenantId: string) => Promise<void>;
|
|
5
5
|
/**
|
|
6
6
|
* Each config reader will do the following things in order:
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
import type { DatabaseTransactionConnection } from 'slonik';
|
|
2
|
-
export declare const createTables: (connection: DatabaseTransactionConnection) => Promise<
|
|
1
|
+
import type { DatabaseTransactionConnection } from '@silverhand/slonik';
|
|
2
|
+
export declare const createTables: (connection: DatabaseTransactionConnection, encryptBaseRole: boolean) => Promise<{
|
|
3
|
+
password: string;
|
|
4
|
+
}>;
|
|
3
5
|
export declare const seedTables: (connection: DatabaseTransactionConnection, latestTimestamp: number, isCloud: boolean) => Promise<void>;
|
|
4
6
|
export declare const seedCloud: (connection: DatabaseTransactionConnection) => Promise<void>;
|
|
5
7
|
/**
|
|
@@ -3,12 +3,12 @@ import path from 'node:path';
|
|
|
3
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
4
|
import { getTenantRole } from '@logto/schemas';
|
|
5
5
|
import { Tenants } from '@logto/schemas/models';
|
|
6
|
-
import {
|
|
7
|
-
import { sql } from 'slonik';
|
|
8
|
-
import { raw } from 'slonik-sql-tag-raw';
|
|
6
|
+
import { generateStandardId } from '@logto/shared';
|
|
7
|
+
import { sql } from '@silverhand/slonik';
|
|
9
8
|
import { insertInto } from '../../../database.js';
|
|
10
9
|
import { getDatabaseName } from '../../../queries/database.js';
|
|
11
10
|
import { updateDatabaseTimestamp } from '../../../queries/system.js';
|
|
11
|
+
import { convertToIdentifiers } from '../../../sql.js';
|
|
12
12
|
import { consoleLog, getPathInModule } from '../../../utils.js';
|
|
13
13
|
import { appendAdminConsoleRedirectUris, seedTenantCloudServiceApplication } from './cloud.js';
|
|
14
14
|
import { seedOidcConfigs } from './oidc-config.js';
|
|
@@ -37,7 +37,7 @@ const lifecycleNames = Object.freeze([
|
|
|
37
37
|
'after_all',
|
|
38
38
|
'after_each',
|
|
39
39
|
]);
|
|
40
|
-
export const createTables = async (connection) => {
|
|
40
|
+
export const createTables = async (connection, encryptBaseRole) => {
|
|
41
41
|
const tableDirectory = getPathInModule('@logto/schemas', 'tables');
|
|
42
42
|
const directoryFiles = await readdir(tableDirectory);
|
|
43
43
|
const tableFiles = directoryFiles.filter((file) => file.endsWith('.sql'));
|
|
@@ -48,11 +48,12 @@ export const createTables = async (connection) => {
|
|
|
48
48
|
const runLifecycleQuery = async (lifecycle, parameters = {}) => {
|
|
49
49
|
const query = queries.find(([file]) => file.slice(1, -4) === lifecycle)?.[1];
|
|
50
50
|
if (query) {
|
|
51
|
-
await connection.query(sql `${raw(
|
|
51
|
+
await connection.query(sql `${sql.raw(
|
|
52
52
|
/* eslint-disable no-template-curly-in-string */
|
|
53
53
|
query
|
|
54
54
|
.replaceAll('${name}', parameters.name ?? '')
|
|
55
55
|
.replaceAll('${database}', parameters.database ?? '')
|
|
56
|
+
.replaceAll('${password}', parameters.password ?? '')
|
|
56
57
|
/* eslint-enable no-template-curly-in-string */
|
|
57
58
|
)}`);
|
|
58
59
|
}
|
|
@@ -63,16 +64,18 @@ export const createTables = async (connection) => {
|
|
|
63
64
|
];
|
|
64
65
|
const sorted = allQueries.slice().sort(compareQuery);
|
|
65
66
|
const database = await getDatabaseName(connection, true);
|
|
66
|
-
|
|
67
|
+
const password = encryptBaseRole ? generateStandardId(32) : '';
|
|
68
|
+
await runLifecycleQuery('before_all', { database, password });
|
|
67
69
|
/* eslint-disable no-await-in-loop */
|
|
68
70
|
for (const [file, query] of sorted) {
|
|
69
|
-
await connection.query(sql `${raw(query)}`);
|
|
71
|
+
await connection.query(sql `${sql.raw(query)}`);
|
|
70
72
|
if (!query.includes('/* no_after_each */')) {
|
|
71
73
|
await runLifecycleQuery('after_each', { name: file.split('.')[0], database });
|
|
72
74
|
}
|
|
73
75
|
}
|
|
74
76
|
/* eslint-enable no-await-in-loop */
|
|
75
77
|
await runLifecycleQuery('after_all', { database });
|
|
78
|
+
return { password };
|
|
76
79
|
};
|
|
77
80
|
export const seedTables = async (connection, latestTimestamp, isCloud) => {
|
|
78
81
|
await createTenant(connection, defaultTenantId);
|
|
@@ -90,7 +93,9 @@ export const seedTables = async (connection, latestTimestamp, isCloud) => {
|
|
|
90
93
|
const applicationRole = createTenantApplicationRole();
|
|
91
94
|
await connection.query(insertInto(applicationRole, Roles.table));
|
|
92
95
|
await assignScopesToRole(connection, adminTenantId, applicationRole.id, ...cloudAdditionalScopes
|
|
93
|
-
.filter(({ name }) => name === CloudScope.SendSms ||
|
|
96
|
+
.filter(({ name }) => name === CloudScope.SendSms ||
|
|
97
|
+
name === CloudScope.SendEmail ||
|
|
98
|
+
name === CloudScope.FetchCustomJwt)
|
|
94
99
|
.map(({ id }) => id));
|
|
95
100
|
await Promise.all([
|
|
96
101
|
seedLegacyManagementApiUserRole(connection),
|
|
@@ -100,10 +105,15 @@ export const seedTables = async (connection, latestTimestamp, isCloud) => {
|
|
|
100
105
|
connection.query(insertInto(createDefaultSignInExperience(defaultTenantId, isCloud), SignInExperiences.table)),
|
|
101
106
|
connection.query(insertInto(createAdminTenantSignInExperience(), SignInExperiences.table)),
|
|
102
107
|
connection.query(insertInto(createDefaultAdminConsoleApplication(), Applications.table)),
|
|
103
|
-
|
|
108
|
+
]);
|
|
109
|
+
// The below seed data is for the Logto Cloud only. We put it here for the sack of simplicity.
|
|
110
|
+
// The data is not harmful for OSS, since they are all admin tenant data. OSS will not use them
|
|
111
|
+
// and they cannot be seen by the Console.
|
|
112
|
+
await Promise.all([
|
|
104
113
|
seedTenantOrganizations(connection),
|
|
105
114
|
seedManagementApiProxyApplications(connection),
|
|
106
115
|
]);
|
|
116
|
+
await updateDatabaseTimestamp(connection, latestTimestamp);
|
|
107
117
|
consoleLog.succeed('Seed data');
|
|
108
118
|
};
|
|
109
119
|
export const seedCloud = async (connection) => {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type AdminData, type UpdateAdminData, type CreateScope } from '@logto/schemas';
|
|
2
|
-
import type { CommonQueryMethods, DatabaseTransactionConnection } from 'slonik';
|
|
2
|
+
import type { CommonQueryMethods, DatabaseTransactionConnection } from '@silverhand/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>;
|
|
@@ -2,8 +2,7 @@ import { createTenantMetadata } from '@logto/core-kit';
|
|
|
2
2
|
import { defaultTenantId, adminTenantId, Applications, ApplicationsRoles, getMapiProxyM2mApp, getMapiProxyRole, defaultManagementApiAdminName, Roles, PredefinedScope, getManagementApiResourceIndicator, } from '@logto/schemas';
|
|
3
3
|
import { generateStandardId } from '@logto/shared';
|
|
4
4
|
import { assert } from '@silverhand/essentials';
|
|
5
|
-
import { sql } from 'slonik';
|
|
6
|
-
import { raw } from 'slonik-sql-tag-raw';
|
|
5
|
+
import { sql } from '@silverhand/slonik';
|
|
7
6
|
import { insertInto } from '../../../database.js';
|
|
8
7
|
import { getDatabaseName } from '../../../queries/database.js';
|
|
9
8
|
import { consoleLog } from '../../../utils.js';
|
|
@@ -18,7 +17,7 @@ export const createTenant = async (pool, tenantId) => {
|
|
|
18
17
|
await pool.query(insertInto(createTenant, 'tenants'));
|
|
19
18
|
await pool.query(sql `
|
|
20
19
|
create role ${sql.identifier([role])} with inherit login
|
|
21
|
-
password '${raw(password)}'
|
|
20
|
+
password '${sql.raw(password)}'
|
|
22
21
|
in role ${sql.identifier([parentRole])};
|
|
23
22
|
`);
|
|
24
23
|
};
|
|
@@ -8,7 +8,7 @@ import { getTranslationPromptMessages } from './prompts.js';
|
|
|
8
8
|
export const createOpenaiApi = () => {
|
|
9
9
|
const proxy = getProxy();
|
|
10
10
|
return got.extend({
|
|
11
|
-
prefixUrl: 'https://api.openai.com/v1',
|
|
11
|
+
prefixUrl: process.env.OPENAI_API_PROXY_ENDPOINT ?? 'https://api.openai.com/v1',
|
|
12
12
|
headers: {
|
|
13
13
|
Authorization: `Bearer ${process.env.OPENAI_API_KEY ?? ''}`,
|
|
14
14
|
},
|
|
@@ -29,7 +29,8 @@ export const translate = async ({ api, targetLanguage, sourceFilePath, extraProm
|
|
|
29
29
|
const response = await trySafe(api
|
|
30
30
|
.post('chat/completions', {
|
|
31
31
|
json: {
|
|
32
|
-
model
|
|
32
|
+
// The full list of OPENAI model can be found at https://platform.openai.com/docs/models.
|
|
33
|
+
model: process.env.OPENAI_MODEL_NAME ?? 'gpt-3.5-turbo-0125',
|
|
33
34
|
messages: getTranslationPromptMessages({
|
|
34
35
|
sourceFileContent,
|
|
35
36
|
targetLanguage,
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import type { BaseRoutes, Router } from '@withtyped/server';
|
|
2
2
|
import type { ConnectorFactory, ConnectorPackage } from './types.js';
|
|
3
|
-
export declare const loadConnectorFactories: (connectorPackages: ConnectorPackage[], ignoreVersionMismatch: boolean) => Promise<ConnectorFactory<Router<any, BaseRoutes, string>>[]>;
|
|
3
|
+
export declare const loadConnectorFactories: (connectorPackages: ConnectorPackage[], ignoreVersionMismatch: boolean) => Promise<ConnectorFactory<Router<any, any, BaseRoutes, string>>[]>;
|
|
@@ -3,7 +3,7 @@ import { consoleLog } from '../utils.js';
|
|
|
3
3
|
import { notImplemented } from './consts.js';
|
|
4
4
|
import { loadConnector } from './loader.js';
|
|
5
5
|
import { parseMetadata, validateConnectorModule } from './utils.js';
|
|
6
|
-
// eslint-disable-next-line @silverhand/fp/no-let
|
|
6
|
+
// eslint-disable-next-line @silverhand/fp/no-let
|
|
7
7
|
let cachedConnectorFactories;
|
|
8
8
|
export const loadConnectorFactories = async (connectorPackages, ignoreVersionMismatch) => {
|
|
9
9
|
if (cachedConnectorFactories) {
|
|
@@ -31,8 +31,8 @@ export const loadConnectorFactories = async (connectorPackages, ignoreVersionMis
|
|
|
31
31
|
}
|
|
32
32
|
}));
|
|
33
33
|
// eslint-disable-next-line @silverhand/fp/no-mutation
|
|
34
|
-
cachedConnectorFactories = connectorFactories.filter(
|
|
34
|
+
cachedConnectorFactories = connectorFactories.filter((connectorFactory
|
|
35
35
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
36
|
-
|
|
36
|
+
) => connectorFactory !== undefined);
|
|
37
37
|
return cachedConnectorFactories;
|
|
38
38
|
};
|
package/lib/connector/types.d.ts
CHANGED
|
@@ -3,7 +3,7 @@ import type { BaseRoutes, Router } from '@withtyped/server';
|
|
|
3
3
|
/**
|
|
4
4
|
* Dynamic loaded connector type.
|
|
5
5
|
*/
|
|
6
|
-
export type ConnectorFactory<T extends Router<any, BaseRoutes, string>, U extends AllConnector = AllConnector> = Pick<U, 'type' | 'metadata' | 'configGuard'> & {
|
|
6
|
+
export type ConnectorFactory<T extends Router<any, any, BaseRoutes, string>, U extends AllConnector = AllConnector> = Pick<U, 'type' | 'metadata' | 'configGuard'> & {
|
|
7
7
|
createConnector: CreateConnector<U, T>;
|
|
8
8
|
path: string;
|
|
9
9
|
};
|
package/lib/connector/utils.d.ts
CHANGED
|
@@ -5,7 +5,7 @@ import type { ConnectorFactory } from './types.js';
|
|
|
5
5
|
export declare function validateConnectorModule(connector: Partial<BaseConnector<ConnectorType>>): asserts connector is BaseConnector<ConnectorType>;
|
|
6
6
|
export declare const readUrl: (url: string, baseUrl: string, type: 'text' | 'image') => Promise<string>;
|
|
7
7
|
export declare const parseMetadata: (metadata: AllConnector['metadata'], packagePath: string) => Promise<AllConnector['metadata']>;
|
|
8
|
-
export declare const buildRawConnector: <T extends Router<any, BaseRoutes, string>, U extends AllConnector = AllConnector>(connectorFactory: ConnectorFactory<T, U>, getConnectorConfig?: GetConnectorConfig, getCloudServiceClient?: GetCloudServiceClient<T> | undefined) => Promise<{
|
|
8
|
+
export declare const buildRawConnector: <T extends Router<any, any, BaseRoutes, string>, U extends AllConnector = AllConnector>(connectorFactory: ConnectorFactory<T, U>, getConnectorConfig?: GetConnectorConfig, getCloudServiceClient?: GetCloudServiceClient<T> | undefined) => Promise<{
|
|
9
9
|
rawConnector: U;
|
|
10
10
|
rawMetadata: ConnectorMetadata;
|
|
11
11
|
}>;
|
package/lib/database.d.ts
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
import type { SchemaLike } from '@logto/schemas';
|
|
2
2
|
export declare const defaultDatabaseUrl = "postgresql://localhost:5432/logto";
|
|
3
3
|
export declare const getDatabaseUrlFromConfig: () => Promise<string>;
|
|
4
|
-
export declare const createPoolFromConfig: () => Promise<import("slonik").DatabasePool>;
|
|
4
|
+
export declare const createPoolFromConfig: () => Promise<import("@silverhand/slonik").DatabasePool>;
|
|
5
5
|
/**
|
|
6
6
|
* Create a database pool with the URL in CLI config; if no URL found, prompt to input.
|
|
7
7
|
* If the given database does not exists, it will try to create a new database by connecting to the maintenance database `postgres`.
|
|
8
8
|
*
|
|
9
9
|
* @returns A new database pool with the database URL in config.
|
|
10
10
|
*/
|
|
11
|
-
export declare const createPoolAndDatabaseIfNeeded: () => Promise<import("slonik").DatabasePool>;
|
|
11
|
+
export declare const createPoolAndDatabaseIfNeeded: () => Promise<import("@silverhand/slonik").DatabasePool>;
|
|
12
12
|
/**
|
|
13
13
|
* Build an `insert into` query from the given payload. If the payload is an array, it will insert
|
|
14
14
|
* multiple rows.
|
|
15
15
|
*/
|
|
16
|
-
export declare const insertInto: <T extends SchemaLike<string>>(payload: T | T[], table: string) => import("slonik").TaggedTemplateLiteralInvocation<import("slonik").QueryResultRow>;
|
|
16
|
+
export declare const insertInto: <T extends SchemaLike<string>>(payload: T | T[], table: string) => import("@silverhand/slonik").TaggedTemplateLiteralInvocation<import("@silverhand/slonik").QueryResultRow>;
|
package/lib/database.js
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
import { convertToPrimitiveOrSql } from '@logto/shared';
|
|
2
1
|
import { assert } from '@silverhand/essentials';
|
|
2
|
+
import { createPool, parseDsn, sql, stringifyDsn, createInterceptorsPreset, } from '@silverhand/slonik';
|
|
3
3
|
import decamelize from 'decamelize';
|
|
4
4
|
import { DatabaseError } from 'pg-protocol';
|
|
5
|
-
import {
|
|
6
|
-
import { createInterceptors } from 'slonik-interceptor-preset';
|
|
5
|
+
import { convertToPrimitiveOrSql } from './sql.js';
|
|
7
6
|
import { ConfigKey, consoleLog, getCliConfigWithPrompt } from './utils.js';
|
|
8
7
|
export const defaultDatabaseUrl = 'postgresql://localhost:5432/logto';
|
|
9
8
|
export const getDatabaseUrlFromConfig = async () => (await getCliConfigWithPrompt({
|
|
@@ -15,7 +14,7 @@ export const createPoolFromConfig = async () => {
|
|
|
15
14
|
const databaseUrl = await getDatabaseUrlFromConfig();
|
|
16
15
|
assert(parseDsn(databaseUrl).databaseName, new Error('Database name is required in URL'));
|
|
17
16
|
return createPool(databaseUrl, {
|
|
18
|
-
interceptors:
|
|
17
|
+
interceptors: createInterceptorsPreset(),
|
|
19
18
|
});
|
|
20
19
|
};
|
|
21
20
|
/**
|
|
@@ -41,7 +40,7 @@ export const createPoolAndDatabaseIfNeeded = async () => {
|
|
|
41
40
|
// - It will throw error when creating database using '?'
|
|
42
41
|
const databaseName = dsn.databaseName ?? '?';
|
|
43
42
|
const maintenancePool = await createPool(stringifyDsn({ ...dsn, databaseName: 'postgres' }), {
|
|
44
|
-
interceptors:
|
|
43
|
+
interceptors: createInterceptorsPreset(),
|
|
45
44
|
});
|
|
46
45
|
await maintenancePool.query(sql `
|
|
47
46
|
create database ${sql.identifier([databaseName])}
|
package/lib/package-json.d.ts
CHANGED
|
@@ -28,7 +28,6 @@ export declare const packageJson: {
|
|
|
28
28
|
"start:dev": string;
|
|
29
29
|
lint: string;
|
|
30
30
|
"lint:report": string;
|
|
31
|
-
"test:only": string;
|
|
32
31
|
test: string;
|
|
33
32
|
"test:ci": string;
|
|
34
33
|
prepack: string;
|
|
@@ -48,6 +47,7 @@ export declare const packageJson: {
|
|
|
48
47
|
"@logto/schemas": string;
|
|
49
48
|
"@logto/shared": string;
|
|
50
49
|
"@silverhand/essentials": string;
|
|
50
|
+
"@silverhand/slonik": string;
|
|
51
51
|
chalk: string;
|
|
52
52
|
decamelize: string;
|
|
53
53
|
dotenv: string;
|
|
@@ -62,9 +62,6 @@ export declare const packageJson: {
|
|
|
62
62
|
"pg-protocol": string;
|
|
63
63
|
roarr: string;
|
|
64
64
|
semver: string;
|
|
65
|
-
slonik: string;
|
|
66
|
-
"slonik-interceptor-preset": string;
|
|
67
|
-
"slonik-sql-tag-raw": string;
|
|
68
65
|
tar: string;
|
|
69
66
|
typescript: string;
|
|
70
67
|
yargs: string;
|
|
@@ -73,19 +70,19 @@ export declare const packageJson: {
|
|
|
73
70
|
devDependencies: {
|
|
74
71
|
"@silverhand/eslint-config": string;
|
|
75
72
|
"@silverhand/ts-config": string;
|
|
76
|
-
"@withtyped/server": string;
|
|
77
73
|
"@types/inquirer": string;
|
|
78
|
-
"@types/jest": string;
|
|
79
74
|
"@types/node": string;
|
|
80
75
|
"@types/semver": string;
|
|
81
76
|
"@types/sinon": string;
|
|
82
77
|
"@types/tar": string;
|
|
83
78
|
"@types/yargs": string;
|
|
79
|
+
"@vitest/coverage-v8": string;
|
|
80
|
+
"@withtyped/server": string;
|
|
84
81
|
eslint: string;
|
|
85
|
-
jest: string;
|
|
86
82
|
"lint-staged": string;
|
|
87
83
|
prettier: string;
|
|
88
84
|
sinon: string;
|
|
85
|
+
vitest: string;
|
|
89
86
|
};
|
|
90
87
|
eslintConfig: {
|
|
91
88
|
extends: string;
|
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.15.0",
|
|
4
4
|
"description": "Logto CLI.",
|
|
5
5
|
"author": "Silverhand Inc. <contact@silverhand.io>",
|
|
6
6
|
"homepage": "https://github.com/logto-io/logto#readme",
|
|
@@ -25,15 +25,14 @@ export const packageJson = {
|
|
|
25
25
|
"precommit": "lint-staged",
|
|
26
26
|
"prepare:package-json": "node -p \"'export const packageJson = ' + JSON.stringify(require('./package.json'), undefined, 2) + ';'\" > src/package-json.ts",
|
|
27
27
|
"build": "rm -rf lib && pnpm prepare:package-json && tsc -p tsconfig.build.json",
|
|
28
|
-
"build:test": "
|
|
28
|
+
"build:test": "pnpm build",
|
|
29
29
|
"dev": "tsc -p tsconfig.build.json --watch --preserveWatchOutput --incremental",
|
|
30
30
|
"start": "node .",
|
|
31
31
|
"start:dev": "pnpm build && node .",
|
|
32
32
|
"lint": "eslint --ext .ts src",
|
|
33
33
|
"lint:report": "pnpm lint --format json --output-file report.json",
|
|
34
|
-
"test
|
|
35
|
-
"test": "pnpm
|
|
36
|
-
"test:ci": "pnpm test:only",
|
|
34
|
+
"test": "vitest src",
|
|
35
|
+
"test:ci": "pnpm run test --silent --coverage",
|
|
37
36
|
"prepack": "pnpm build"
|
|
38
37
|
},
|
|
39
38
|
"engines": {
|
|
@@ -43,14 +42,15 @@ export const packageJson = {
|
|
|
43
42
|
"url": "https://github.com/logto-io/logto/issues"
|
|
44
43
|
},
|
|
45
44
|
"dependencies": {
|
|
46
|
-
"@logto/connector-kit": "workspace:^
|
|
47
|
-
"@logto/core-kit": "workspace:^2.
|
|
45
|
+
"@logto/connector-kit": "workspace:^3.0.0",
|
|
46
|
+
"@logto/core-kit": "workspace:^2.4.0",
|
|
48
47
|
"@logto/language-kit": "workspace:^1.1.0",
|
|
49
|
-
"@logto/phrases": "workspace:^1.
|
|
50
|
-
"@logto/phrases-experience": "workspace:^1.6.
|
|
51
|
-
"@logto/schemas": "workspace:1.
|
|
48
|
+
"@logto/phrases": "workspace:^1.10.0",
|
|
49
|
+
"@logto/phrases-experience": "workspace:^1.6.1",
|
|
50
|
+
"@logto/schemas": "workspace:1.15.0",
|
|
52
51
|
"@logto/shared": "workspace:^3.1.0",
|
|
53
52
|
"@silverhand/essentials": "^2.9.0",
|
|
53
|
+
"@silverhand/slonik": "31.0.0-beta.2",
|
|
54
54
|
"chalk": "^5.0.0",
|
|
55
55
|
"decamelize": "^6.0.0",
|
|
56
56
|
"dotenv": "^16.0.0",
|
|
@@ -65,9 +65,6 @@ export const packageJson = {
|
|
|
65
65
|
"pg-protocol": "^1.6.0",
|
|
66
66
|
"roarr": "^7.11.0",
|
|
67
67
|
"semver": "^7.3.8",
|
|
68
|
-
"slonik": "^30.0.0",
|
|
69
|
-
"slonik-interceptor-preset": "^1.2.10",
|
|
70
|
-
"slonik-sql-tag-raw": "^1.1.4",
|
|
71
68
|
"tar": "^6.1.11",
|
|
72
69
|
"typescript": "^5.3.3",
|
|
73
70
|
"yargs": "^17.6.0",
|
|
@@ -76,19 +73,19 @@ export const packageJson = {
|
|
|
76
73
|
"devDependencies": {
|
|
77
74
|
"@silverhand/eslint-config": "5.0.0",
|
|
78
75
|
"@silverhand/ts-config": "5.0.0",
|
|
79
|
-
"@withtyped/server": "^0.12.9",
|
|
80
76
|
"@types/inquirer": "^9.0.0",
|
|
81
|
-
"@types/jest": "^29.4.0",
|
|
82
77
|
"@types/node": "^20.9.5",
|
|
83
78
|
"@types/semver": "^7.3.12",
|
|
84
79
|
"@types/sinon": "^17.0.0",
|
|
85
80
|
"@types/tar": "^6.1.2",
|
|
86
81
|
"@types/yargs": "^17.0.13",
|
|
82
|
+
"@vitest/coverage-v8": "^1.4.0",
|
|
83
|
+
"@withtyped/server": "^0.13.3",
|
|
87
84
|
"eslint": "^8.44.0",
|
|
88
|
-
"jest": "^29.7.0",
|
|
89
85
|
"lint-staged": "^15.0.0",
|
|
90
86
|
"prettier": "^3.0.0",
|
|
91
|
-
"sinon": "^17.0.0"
|
|
87
|
+
"sinon": "^17.0.0",
|
|
88
|
+
"vitest": "^1.4.0"
|
|
92
89
|
},
|
|
93
90
|
"eslintConfig": {
|
|
94
91
|
"extends": "@silverhand",
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import type { CommonQueryMethods } from 'slonik';
|
|
1
|
+
import type { CommonQueryMethods } from '@silverhand/slonik';
|
|
2
2
|
export declare const getDatabaseName: (pool: CommonQueryMethods, normalized?: boolean) => Promise<string>;
|
package/lib/queries/database.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { LogtoConfig, LogtoConfigKey, logtoConfigGuards } from '@logto/schemas';
|
|
2
|
-
import type { CommonQueryMethods } from 'slonik';
|
|
2
|
+
import type { CommonQueryMethods } from '@silverhand/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, 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>>;
|
|
5
|
+
export declare const getRowsByKeys: (pool: CommonQueryMethods, tenantId: string, keys: LogtoConfigKey[]) => Promise<import("@silverhand/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("@silverhand/slonik").QueryResult<import("@silverhand/slonik").QueryResultRow>>;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { LogtoConfigs } from '@logto/schemas';
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { sql } from '@silverhand/slonik';
|
|
3
|
+
import { convertToIdentifiers } from '../sql.js';
|
|
4
4
|
const { table, fields } = convertToIdentifiers(LogtoConfigs);
|
|
5
5
|
export const doesConfigsTableExist = async (pool) => {
|
|
6
6
|
const { rows } = await pool.query(sql `select to_regclass(${LogtoConfigs.table}) as regclass`);
|
package/lib/queries/system.d.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import type { System, SystemKey } from '@logto/schemas';
|
|
2
2
|
import { systemGuards } from '@logto/schemas';
|
|
3
|
-
import type { CommonQueryMethods, DatabaseTransactionConnection } from 'slonik';
|
|
3
|
+
import type { CommonQueryMethods, DatabaseTransactionConnection } from '@silverhand/slonik';
|
|
4
4
|
import type { z } from 'zod';
|
|
5
5
|
export declare const doesSystemsTableExist: (pool: CommonQueryMethods) => Promise<boolean>;
|
|
6
6
|
export declare const getCurrentDatabaseAlterationTimestamp: (pool: CommonQueryMethods) => Promise<number>;
|
|
7
7
|
export declare const updateDatabaseTimestamp: (connection: DatabaseTransactionConnection, timestamp: number) => Promise<void>;
|
|
8
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>>;
|
|
9
|
+
export declare const updateValueByKey: <T extends SystemKey>(pool: CommonQueryMethods, key: T, value: z.TypeOf<import("@logto/schemas").SystemGuard[T]>) => Promise<import("@silverhand/slonik").QueryResult<import("@silverhand/slonik").QueryResultRow>>;
|
package/lib/queries/system.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { systemGuards, Systems, AlterationStateKey } from '@logto/schemas';
|
|
2
|
-
import {
|
|
2
|
+
import { sql } from '@silverhand/slonik';
|
|
3
3
|
import { DatabaseError } from 'pg-protocol';
|
|
4
|
-
import {
|
|
4
|
+
import { convertToIdentifiers } from '../sql.js';
|
|
5
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`);
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { AlterationStateKey, Systems } from '@logto/schemas';
|
|
2
|
-
import {
|
|
2
|
+
import { createMockPool, createMockQueryResult, sql } from '@silverhand/slonik';
|
|
3
3
|
import { DatabaseError } from 'pg-protocol';
|
|
4
|
-
import {
|
|
4
|
+
import { describe, it, expect, vi, afterAll, beforeAll } from 'vitest';
|
|
5
|
+
import { convertToIdentifiers } from '../sql.js';
|
|
5
6
|
import { expectSqlAssert } from '../test-utils.js';
|
|
6
7
|
import { updateDatabaseTimestamp, getCurrentDatabaseAlterationTimestamp } from './system.js';
|
|
7
|
-
const
|
|
8
|
-
const mockQuery = jest.fn();
|
|
8
|
+
const mockQuery = vi.fn();
|
|
9
9
|
const pool = createMockPool({
|
|
10
10
|
query: async (sql, values) => {
|
|
11
11
|
return mockQuery(sql, values);
|
|
@@ -71,11 +71,11 @@ describe('updateDatabaseTimestamp()', () => {
|
|
|
71
71
|
`;
|
|
72
72
|
const updatedAt = '2022-09-21T06:32:46.583Z';
|
|
73
73
|
beforeAll(() => {
|
|
74
|
-
|
|
75
|
-
|
|
74
|
+
vi.useFakeTimers();
|
|
75
|
+
vi.setSystemTime(new Date(updatedAt));
|
|
76
76
|
});
|
|
77
77
|
afterAll(() => {
|
|
78
|
-
|
|
78
|
+
vi.useRealTimers();
|
|
79
79
|
});
|
|
80
80
|
it('sends upsert sql with timestamp and updatedAt', async () => {
|
|
81
81
|
mockQuery
|
package/lib/sql.d.ts
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Copied from `@logto/core`. Originally we put them in `@logto/shared` but it
|
|
3
|
+
* requires `slonik` which makes the package too heavy.
|
|
4
|
+
*
|
|
5
|
+
* Since `@logto/cli` only use these functions in a stable manner, we copy them here for now. If
|
|
6
|
+
* the number of functions grows, we should consider moving them to a separate package. (Actually,
|
|
7
|
+
* we should remove the dependency on `slonik` at all, and this may not be an issue then.)
|
|
8
|
+
*/
|
|
9
|
+
import { type SchemaValue, type SchemaValuePrimitive, type Table } from '@logto/shared';
|
|
10
|
+
import { type IdentifierSqlToken, type SqlToken } from '@silverhand/slonik';
|
|
11
|
+
/**
|
|
12
|
+
* Note `undefined` is removed from the acceptable list,
|
|
13
|
+
* since you should NOT call this function if ignoring the field is the desired behavior.
|
|
14
|
+
* Calling this function with `null` means an explicit `null` setting in database is expected.
|
|
15
|
+
* @param key The key of value. Will treat as `timestamp` if it ends with `_at` or 'At' AND value is a number;
|
|
16
|
+
* @param value The value to convert.
|
|
17
|
+
* @returns A primitive that can be saved into database.
|
|
18
|
+
*/
|
|
19
|
+
export declare const convertToPrimitiveOrSql: (key: string, value: SchemaValue) => NonNullable<SchemaValuePrimitive> | SqlToken | null;
|
|
20
|
+
type FieldIdentifiers<Key extends string> = {
|
|
21
|
+
[key in Key]: IdentifierSqlToken;
|
|
22
|
+
};
|
|
23
|
+
export declare const convertToIdentifiers: <Key extends string>({ table, fields }: Table<Key>, withPrefix?: boolean) => {
|
|
24
|
+
table: IdentifierSqlToken;
|
|
25
|
+
fields: FieldIdentifiers<Key>;
|
|
26
|
+
};
|
|
27
|
+
export {};
|
package/lib/sql.js
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Copied from `@logto/core`. Originally we put them in `@logto/shared` but it
|
|
3
|
+
* requires `slonik` which makes the package too heavy.
|
|
4
|
+
*
|
|
5
|
+
* Since `@logto/cli` only use these functions in a stable manner, we copy them here for now. If
|
|
6
|
+
* the number of functions grows, we should consider moving them to a separate package. (Actually,
|
|
7
|
+
* we should remove the dependency on `slonik` at all, and this may not be an issue then.)
|
|
8
|
+
*/
|
|
9
|
+
import { sql } from '@silverhand/slonik';
|
|
10
|
+
/**
|
|
11
|
+
* Note `undefined` is removed from the acceptable list,
|
|
12
|
+
* since you should NOT call this function if ignoring the field is the desired behavior.
|
|
13
|
+
* Calling this function with `null` means an explicit `null` setting in database is expected.
|
|
14
|
+
* @param key The key of value. Will treat as `timestamp` if it ends with `_at` or 'At' AND value is a number;
|
|
15
|
+
* @param value The value to convert.
|
|
16
|
+
* @returns A primitive that can be saved into database.
|
|
17
|
+
*/
|
|
18
|
+
export const convertToPrimitiveOrSql = (key, value
|
|
19
|
+
// eslint-disable-next-line @typescript-eslint/ban-types
|
|
20
|
+
) => {
|
|
21
|
+
if (value === null) {
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
if (typeof value === 'object') {
|
|
25
|
+
return JSON.stringify(value);
|
|
26
|
+
}
|
|
27
|
+
if ((['_at', 'At'].some((value) => key.endsWith(value)) || key === 'date') &&
|
|
28
|
+
typeof value === 'number') {
|
|
29
|
+
return sql `to_timestamp(${value}::double precision / 1000)`;
|
|
30
|
+
}
|
|
31
|
+
if (typeof value === 'number' || typeof value === 'boolean') {
|
|
32
|
+
return value;
|
|
33
|
+
}
|
|
34
|
+
if (typeof value === 'string') {
|
|
35
|
+
if (value === '') {
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
return value;
|
|
39
|
+
}
|
|
40
|
+
throw new Error(`Cannot convert ${key} to primitive`);
|
|
41
|
+
};
|
|
42
|
+
export const convertToIdentifiers = ({ table, fields }, withPrefix = false) => {
|
|
43
|
+
const fieldsIdentifiers = Object.entries(fields).map(
|
|
44
|
+
// eslint-disable-next-line no-restricted-syntax -- Object.entries can only return string keys
|
|
45
|
+
([key, value]) => [key, sql.identifier(withPrefix ? [table, value] : [value])]);
|
|
46
|
+
return {
|
|
47
|
+
table: sql.identifier([table]),
|
|
48
|
+
// Key value inferred from the original fields directly
|
|
49
|
+
// eslint-disable-next-line no-restricted-syntax
|
|
50
|
+
fields: Object.fromEntries(fieldsIdentifiers),
|
|
51
|
+
};
|
|
52
|
+
};
|
package/lib/test-utils.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { QueryResult, QueryResultRow } from 'slonik';
|
|
2
|
-
import type { PrimitiveValueExpression } from 'slonik/dist/src/types.js';
|
|
1
|
+
import type { QueryResult, QueryResultRow } from '@silverhand/slonik';
|
|
2
|
+
import type { PrimitiveValueExpression } from '@silverhand/slonik/dist/src/types.js';
|
|
3
3
|
export type QueryType = (sql: string, values: readonly PrimitiveValueExpression[]) => Promise<QueryResult<QueryResultRow>>;
|
|
4
4
|
/**
|
|
5
5
|
* Slonik Query Mock Utils
|
package/lib/test-utils.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@logto/cli",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.15.0",
|
|
4
4
|
"description": "Logto CLI.",
|
|
5
5
|
"author": "Silverhand Inc. <contact@silverhand.io>",
|
|
6
6
|
"homepage": "https://github.com/logto-io/logto#readme",
|
|
@@ -28,14 +28,15 @@
|
|
|
28
28
|
"url": "https://github.com/logto-io/logto/issues"
|
|
29
29
|
},
|
|
30
30
|
"dependencies": {
|
|
31
|
-
"@logto/connector-kit": "^
|
|
32
|
-
"@logto/core-kit": "^2.
|
|
31
|
+
"@logto/connector-kit": "^3.0.0",
|
|
32
|
+
"@logto/core-kit": "^2.4.0",
|
|
33
33
|
"@logto/language-kit": "^1.1.0",
|
|
34
|
-
"@logto/phrases": "^1.
|
|
35
|
-
"@logto/phrases-experience": "^1.6.
|
|
36
|
-
"@logto/schemas": "1.
|
|
34
|
+
"@logto/phrases": "^1.10.0",
|
|
35
|
+
"@logto/phrases-experience": "^1.6.1",
|
|
36
|
+
"@logto/schemas": "1.15.0",
|
|
37
37
|
"@logto/shared": "^3.1.0",
|
|
38
38
|
"@silverhand/essentials": "^2.9.0",
|
|
39
|
+
"@silverhand/slonik": "31.0.0-beta.2",
|
|
39
40
|
"chalk": "^5.0.0",
|
|
40
41
|
"decamelize": "^6.0.0",
|
|
41
42
|
"dotenv": "^16.0.0",
|
|
@@ -50,9 +51,6 @@
|
|
|
50
51
|
"pg-protocol": "^1.6.0",
|
|
51
52
|
"roarr": "^7.11.0",
|
|
52
53
|
"semver": "^7.3.8",
|
|
53
|
-
"slonik": "^30.0.0",
|
|
54
|
-
"slonik-interceptor-preset": "^1.2.10",
|
|
55
|
-
"slonik-sql-tag-raw": "^1.1.4",
|
|
56
54
|
"tar": "^6.1.11",
|
|
57
55
|
"typescript": "^5.3.3",
|
|
58
56
|
"yargs": "^17.6.0",
|
|
@@ -61,19 +59,19 @@
|
|
|
61
59
|
"devDependencies": {
|
|
62
60
|
"@silverhand/eslint-config": "5.0.0",
|
|
63
61
|
"@silverhand/ts-config": "5.0.0",
|
|
64
|
-
"@withtyped/server": "^0.12.9",
|
|
65
62
|
"@types/inquirer": "^9.0.0",
|
|
66
|
-
"@types/jest": "^29.4.0",
|
|
67
63
|
"@types/node": "^20.9.5",
|
|
68
64
|
"@types/semver": "^7.3.12",
|
|
69
65
|
"@types/sinon": "^17.0.0",
|
|
70
66
|
"@types/tar": "^6.1.2",
|
|
71
67
|
"@types/yargs": "^17.0.13",
|
|
68
|
+
"@vitest/coverage-v8": "^1.4.0",
|
|
69
|
+
"@withtyped/server": "^0.13.3",
|
|
72
70
|
"eslint": "^8.44.0",
|
|
73
|
-
"jest": "^29.7.0",
|
|
74
71
|
"lint-staged": "^15.0.0",
|
|
75
72
|
"prettier": "^3.0.0",
|
|
76
|
-
"sinon": "^17.0.0"
|
|
73
|
+
"sinon": "^17.0.0",
|
|
74
|
+
"vitest": "^1.4.0"
|
|
77
75
|
},
|
|
78
76
|
"eslintConfig": {
|
|
79
77
|
"extends": "@silverhand",
|
|
@@ -89,14 +87,13 @@
|
|
|
89
87
|
"precommit": "lint-staged",
|
|
90
88
|
"prepare:package-json": "node -p \"'export const packageJson = ' + JSON.stringify(require('./package.json'), undefined, 2) + ';'\" > src/package-json.ts",
|
|
91
89
|
"build": "rm -rf lib && pnpm prepare:package-json && tsc -p tsconfig.build.json",
|
|
92
|
-
"build:test": "
|
|
90
|
+
"build:test": "pnpm build",
|
|
93
91
|
"dev": "tsc -p tsconfig.build.json --watch --preserveWatchOutput --incremental",
|
|
94
92
|
"start": "node .",
|
|
95
93
|
"start:dev": "pnpm build && node .",
|
|
96
94
|
"lint": "eslint --ext .ts src",
|
|
97
95
|
"lint:report": "pnpm lint --format json --output-file report.json",
|
|
98
|
-
"test
|
|
99
|
-
"test": "pnpm
|
|
100
|
-
"test:ci": "pnpm test:only"
|
|
96
|
+
"test": "vitest src",
|
|
97
|
+
"test:ci": "pnpm run test --silent --coverage"
|
|
101
98
|
}
|
|
102
99
|
}
|