0
Fork 0
mirror of https://github.com/logto-io/logto.git synced 2025-03-17 22:31:28 -05:00

refactor(core,cli): extract common utils to @logto/shared

This commit is contained in:
Gao Sun 2022-10-10 00:40:04 +08:00
parent 4b0970b6d8
commit ba09de9b1e
No known key found for this signature in database
GPG key ID: 13EBE123E4773688
52 changed files with 295 additions and 148 deletions

View file

@ -1 +1,7 @@
export { default } from '@silverhand/jest-config';
import { merge, Config } from '@silverhand/jest-config';
const config: Config.InitialOptions = merge({
roots: ['./src'],
});
export default config;

View file

@ -36,6 +36,7 @@
},
"dependencies": {
"@logto/schemas": "^1.0.0-beta.10",
"@logto/shared": "^1.0.0-beta.10",
"@silverhand/essentials": "^1.2.1",
"chalk": "^4.1.2",
"decamelize": "^5.0.0",

View file

@ -2,6 +2,7 @@ import { readdir, readFile } from 'fs/promises';
import path from 'path';
import { logtoConfigGuards, LogtoOidcConfigKey, seeds } from '@logto/schemas';
import { buildApplicationSecret } from '@logto/shared';
import chalk from 'chalk';
import { DatabasePool, DatabaseTransactionConnection, sql } from 'slonik';
import { raw } from 'slonik-sql-tag-raw';
@ -14,7 +15,7 @@ import {
updateDatabaseTimestamp,
updateValueByKey,
} from '../../../queries/logto-config';
import { buildApplicationSecret, getPathInModule, log, oraPromise } from '../../../utilities';
import { getPathInModule, log, oraPromise } from '../../../utilities';
import { getLatestAlterationTimestamp } from '../alteration';
import { oidcConfigReaders } from './oidc-config';

View file

@ -1,6 +1,7 @@
import { SchemaLike, SchemaValue, SchemaValuePrimitive } from '@logto/schemas';
import { SchemaLike } from '@logto/schemas';
import { convertToPrimitiveOrSql } from '@logto/shared';
import decamelize from 'decamelize';
import { createPool, IdentifierSqlToken, parseDsn, sql, SqlToken, stringifyDsn } from 'slonik';
import { createPool, parseDsn, sql, stringifyDsn } from 'slonik';
import { createInterceptors } from 'slonik-interceptor-preset';
import { z } from 'zod';
@ -62,67 +63,6 @@ export const createPoolAndDatabaseIfNeeded = async () => {
}
};
// TODO: Move database utils to `core-kit`
export type Table = { table: string; fields: Record<string, string> };
export type FieldIdentifiers<Key extends string | number | symbol> = {
[key in Key]: IdentifierSqlToken;
};
export const convertToIdentifiers = <T extends Table>({ table, fields }: T, withPrefix = false) => {
const fieldsIdentifiers = Object.entries<string>(fields).map<
[keyof T['fields'], IdentifierSqlToken]
>(([key, value]) => [key, sql.identifier(withPrefix ? [table, value] : [value])]);
return {
table: sql.identifier([table]),
// Key value inferred from the original fields directly
// eslint-disable-next-line no-restricted-syntax
fields: Object.fromEntries(fieldsIdentifiers) as FieldIdentifiers<keyof T['fields']>,
};
};
/**
* Note `undefined` is removed from the acceptable list,
* since you should NOT call this function if ignoring the field is the desired behavior.
* Calling this function with `null` means an explicit `null` setting in database is expected.
* @param key The key of value. Will treat as `timestamp` if it ends with `_at` or 'At' AND value is a number;
* @param value The value to convert.
* @returns A primitive that can be saved into database.
*/
// eslint-disable-next-line complexity
export const convertToPrimitiveOrSql = (
key: string,
// eslint-disable-next-line @typescript-eslint/ban-types
value: NonNullable<SchemaValue> | null
// eslint-disable-next-line @typescript-eslint/ban-types
): NonNullable<SchemaValuePrimitive> | SqlToken | null => {
if (value === null) {
return null;
}
if (typeof value === 'object') {
return JSON.stringify(value);
}
if (['_at', 'At'].some((value) => key.endsWith(value)) && typeof value === 'number') {
return sql`to_timestamp(${value}::double precision / 1000)`;
}
if (typeof value === 'number' || typeof value === 'boolean') {
return value;
}
if (typeof value === 'string') {
if (value === '') {
return null;
}
return value;
}
throw new Error(`Cannot convert ${key} to primitive`);
};
export const insertInto = <T extends SchemaLike>(object: T, table: string) => {
const keys = Object.keys(object);

View file

@ -1,7 +1,7 @@
import { AlterationStateKey, LogtoConfigs } from '@logto/schemas';
import { convertToIdentifiers } from '@logto/shared';
import { createMockPool, createMockQueryResult, sql } from 'slonik';
import { convertToIdentifiers } from '../database';
import { expectSqlAssert, QueryType } from '../test-utilities';
import { updateDatabaseTimestamp, getCurrentDatabaseAlterationTimestamp } from './logto-config';

View file

@ -6,11 +6,10 @@ import {
LogtoConfigs,
AlterationStateKey,
} from '@logto/schemas';
import { convertToIdentifiers } from '@logto/shared';
import { DatabasePool, DatabaseTransactionConnection, sql } from 'slonik';
import { z } from 'zod';
import { convertToIdentifiers } from '../database';
const { table, fields } = convertToIdentifiers(LogtoConfigs);
export const getRowsByKeys = async (

View file

@ -7,7 +7,6 @@ import chalk from 'chalk';
import got, { Progress } from 'got';
import { HttpsProxyAgent } from 'hpagent';
import inquirer from 'inquirer';
import { customAlphabet } from 'nanoid';
import ora from 'ora';
export const safeExecSync = (command: string) => {
@ -159,9 +158,3 @@ export const getCliConfig = async ({ key, readableKey, comments, defaultValue }:
export const noop = () => {};
export const deduplicate = <T>(array: T[]) => [...new Set(array)];
export const alphabet = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
export const buildIdGenerator = (size: number) => customAlphabet(alphabet, size);
export const buildApplicationSecret = buildIdGenerator(21);

View file

@ -30,6 +30,7 @@
"@logto/phrases": "^1.0.0-beta.10",
"@logto/phrases-ui": "^1.0.0-beta.10",
"@logto/schemas": "^1.0.0-beta.10",
"@logto/shared": "^1.0.0-beta.10",
"@silverhand/essentials": "^1.2.1",
"chalk": "^4",
"clean-deep": "^3.4.0",

View file

@ -1,7 +1,7 @@
import { SchemaLike, GeneratedSchema } from '@logto/schemas';
import { convertToIdentifiers } from '@logto/shared';
import { sql, NotFoundError } from 'slonik';
import { convertToIdentifiers } from '@/database/utils';
import envSet from '@/env-set';
import RequestError from '@/errors/RequestError';
import assertThat from '@/utils/assert-that';

View file

@ -1,4 +1,5 @@
import { CreateUser, Users } from '@logto/schemas';
import { convertToIdentifiers } from '@logto/shared';
import decamelize from 'decamelize';
import envSet from '@/env-set';
@ -6,7 +7,6 @@ import { InsertionError } from '@/errors/SlonikError';
import { createTestPool } from '@/utils/test-utils';
import { buildInsertInto } from './insert-into';
import { convertToIdentifiers } from './utils';
const poolSpy = jest.spyOn(envSet, 'pool', 'get');

View file

@ -1,4 +1,11 @@
import { GeneratedSchema, SchemaLike } from '@logto/schemas';
import {
OmitAutoSetFields,
convertToIdentifiers,
excludeAutoSetFields,
convertToPrimitiveOrSql,
conditionalSql,
} from '@logto/shared';
import { has } from '@silverhand/essentials';
import { IdentifierSqlToken, sql } from 'slonik';
@ -6,14 +13,6 @@ import envSet from '@/env-set';
import { InsertionError } from '@/errors/SlonikError';
import assertThat from '@/utils/assert-that';
import {
conditionalSql,
convertToIdentifiers,
convertToPrimitiveOrSql,
excludeAutoSetFields,
OmitAutoSetFields,
} from './utils';
const setExcluded = (...fields: IdentifierSqlToken[]) =>
sql.join(
fields.map((field) => sql`${field}=excluded.${field}`),

View file

@ -1,10 +1,10 @@
import { CreateUser, Users, Applications, User } from '@logto/schemas';
import { UpdateWhereData } from '@logto/shared';
import envSet from '@/env-set';
import { UpdateError } from '@/errors/SlonikError';
import { createTestPool } from '@/utils/test-utils';
import { UpdateWhereData } from './types';
import { buildUpdateWhere } from './update-where';
const poolSpy = jest.spyOn(envSet, 'pool', 'get');

View file

@ -1,4 +1,10 @@
import { SchemaLike, GeneratedSchema } from '@logto/schemas';
import {
UpdateWhereData,
convertToIdentifiers,
convertToPrimitiveOrSql,
conditionalSql,
} from '@logto/shared';
import { notFalsy, Truthy } from '@silverhand/essentials';
import { sql } from 'slonik';
@ -7,9 +13,6 @@ import { UpdateError } from '@/errors/SlonikError';
import assertThat from '@/utils/assert-that';
import { isKeyOf } from '@/utils/schema';
import { UpdateWhereData } from './types';
import { conditionalSql, convertToIdentifiers, convertToPrimitiveOrSql } from './utils';
type BuildUpdateWhere = {
<Schema extends SchemaLike, ReturnType extends SchemaLike>(
schema: GeneratedSchema<Schema>,

View file

@ -1,9 +1,7 @@
import { SchemaLike, GeneratedSchema } from '@logto/schemas';
import { OmitAutoSetFields, UpdateWhereData } from '@logto/shared';
import { SlonikError } from 'slonik';
import { UpdateWhereData } from '@/database/types';
import { OmitAutoSetFields } from '@/database/utils';
export class DeletionError extends SlonikError {
table?: string;
id?: string;

View file

@ -1,4 +1,5 @@
import { User, CreateUser, Users, UsersPasswordEncryptionMethod } from '@logto/schemas';
import { buildIdGenerator } from '@logto/shared';
import { argon2Verify } from 'hash-wasm';
import pRetry from 'p-retry';
@ -7,7 +8,6 @@ import envSet from '@/env-set';
import { findRolesByRoleNames, insertRoles } from '@/queries/roles';
import { findUserByUsername, hasUserWithId } from '@/queries/user';
import assertThat from '@/utils/assert-that';
import { buildIdGenerator } from '@/utils/id';
import { encryptPassword } from '@/utils/password';
const userId = buildIdGenerator(12);

View file

@ -27,7 +27,7 @@ jest.mock('@/queries/oidc-model-instance', () => ({
revokeInstanceByGrantId: jest.fn(),
}));
jest.mock('@/utils/id', () => ({
jest.mock('@logto/shared', () => ({
// eslint-disable-next-line unicorn/consistent-function-scoping
buildIdGenerator: jest.fn(() => () => 'randomId'),
}));

View file

@ -1,13 +1,9 @@
import { Applications } from '@logto/schemas';
import { convertToIdentifiers, convertToPrimitiveOrSql, excludeAutoSetFields } from '@logto/shared';
import { createMockPool, createMockQueryResult, sql } from 'slonik';
import { snakeCase } from 'snake-case';
import { mockApplication } from '@/__mocks__';
import {
convertToIdentifiers,
convertToPrimitiveOrSql,
excludeAutoSetFields,
} from '@/database/utils';
import envSet from '@/env-set';
import { DeletionError } from '@/errors/SlonikError';
import { expectSqlAssert, QueryType } from '@/utils/test-utils';

View file

@ -1,16 +1,11 @@
import { Application, CreateApplication, Applications } from '@logto/schemas';
import { convertToIdentifiers, OmitAutoSetFields, conditionalSql, manyRows } from '@logto/shared';
import { sql } from 'slonik';
import { buildFindEntityById } from '@/database/find-entity-by-id';
import { buildInsertInto } from '@/database/insert-into';
import { getTotalRowCount } from '@/database/row-count';
import { buildUpdateWhere } from '@/database/update-where';
import {
convertToIdentifiers,
OmitAutoSetFields,
conditionalSql,
manyRows,
} from '@/database/utils';
import envSet from '@/env-set';
import { DeletionError } from '@/errors/SlonikError';

View file

@ -1,8 +1,8 @@
import { Connectors } from '@logto/schemas';
import { convertToIdentifiers } from '@logto/shared';
import { createMockPool, createMockQueryResult, sql } from 'slonik';
import { mockConnector } from '@/__mocks__';
import { convertToIdentifiers } from '@/database/utils';
import envSet from '@/env-set';
import { expectSqlAssert, QueryType } from '@/utils/test-utils';

View file

@ -1,9 +1,9 @@
import { Connector, CreateConnector, Connectors } from '@logto/schemas';
import { convertToIdentifiers, manyRows } from '@logto/shared';
import { sql } from 'slonik';
import { buildInsertInto } from '@/database/insert-into';
import { buildUpdateWhere } from '@/database/update-where';
import { convertToIdentifiers, manyRows } from '@/database/utils';
import envSet from '@/env-set';
const { table, fields } = convertToIdentifiers(Connectors);

View file

@ -1,8 +1,8 @@
import { CreateCustomPhrase, CustomPhrase, CustomPhrases } from '@logto/schemas';
import { convertToIdentifiers, manyRows } from '@logto/shared';
import { sql } from 'slonik';
import { buildInsertInto } from '@/database/insert-into';
import { convertToIdentifiers, manyRows } from '@/database/utils';
import envSet from '@/env-set';
import { DeletionError } from '@/errors/SlonikError';

View file

@ -1,9 +1,9 @@
import { CreateLog, Log, Logs, LogType } from '@logto/schemas';
import { conditionalSql, convertToIdentifiers } from '@logto/shared';
import { sql } from 'slonik';
import { buildFindEntityById } from '@/database/find-entity-by-id';
import { buildInsertInto } from '@/database/insert-into';
import { conditionalSql, convertToIdentifiers } from '@/database/utils';
import envSet from '@/env-set';
const { table, fields } = convertToIdentifiers(Logs);

View file

@ -1,7 +1,7 @@
import { OidcModelInstances, CreateOidcModelInstance } from '@logto/schemas';
import { convertToIdentifiers } from '@logto/shared';
import { createMockPool, createMockQueryResult, sql } from 'slonik';
import { convertToIdentifiers } from '@/database/utils';
import envSet from '@/env-set';
import { expectSqlAssert, QueryType } from '@/utils/test-utils';
@ -24,8 +24,8 @@ jest.spyOn(envSet, 'pool', 'get').mockReturnValue(
})
);
jest.mock('@/database/utils', () => ({
...jest.requireActual('@/database/utils'),
jest.mock('@logto/shared', () => ({
...jest.requireActual('@logto/shared'),
convertToTimestamp: () => 100,
}));

View file

@ -4,12 +4,12 @@ import {
OidcModelInstancePayload,
OidcModelInstances,
} from '@logto/schemas';
import { convertToIdentifiers, convertToTimestamp } from '@logto/shared';
import { conditional, Nullable } from '@silverhand/essentials';
import dayjs from 'dayjs';
import { sql, ValueExpression } from 'slonik';
import { buildInsertInto } from '@/database/insert-into';
import { convertToIdentifiers, convertToTimestamp } from '@/database/utils';
import envSet from '@/env-set';
export type WithConsumed<T> = T & { consumed?: boolean };

View file

@ -1,13 +1,9 @@
import { Passcodes, PasscodeType } from '@logto/schemas';
import { convertToIdentifiers, convertToPrimitiveOrSql, excludeAutoSetFields } from '@logto/shared';
import { createMockPool, createMockQueryResult, sql } from 'slonik';
import { snakeCase } from 'snake-case';
import { mockPasscode } from '@/__mocks__';
import {
convertToIdentifiers,
convertToPrimitiveOrSql,
excludeAutoSetFields,
} from '@/database/utils';
import envSet from '@/env-set';
import { DeletionError } from '@/errors/SlonikError';
import { expectSqlAssert, QueryType } from '@/utils/test-utils';

View file

@ -1,8 +1,8 @@
import { PasscodeType, Passcode, Passcodes, CreatePasscode } from '@logto/schemas';
import { convertToIdentifiers } from '@logto/shared';
import { sql } from 'slonik';
import { buildInsertInto } from '@/database/insert-into';
import { convertToIdentifiers } from '@/database/utils';
import envSet from '@/env-set';
import { DeletionError } from '@/errors/SlonikError';

View file

@ -1,8 +1,8 @@
import { Resources } from '@logto/schemas';
import { convertToIdentifiers, convertToPrimitiveOrSql } from '@logto/shared';
import { createMockPool, createMockQueryResult, sql } from 'slonik';
import { mockResource } from '@/__mocks__';
import { convertToIdentifiers, convertToPrimitiveOrSql } from '@/database/utils';
import envSet from '@/env-set';
import { DeletionError } from '@/errors/SlonikError';
import { expectSqlAssert, QueryType } from '@/utils/test-utils';

View file

@ -1,16 +1,11 @@
import { Resource, CreateResource, Resources } from '@logto/schemas';
import { convertToIdentifiers, OmitAutoSetFields, conditionalSql, manyRows } from '@logto/shared';
import { sql } from 'slonik';
import { buildFindEntityById } from '@/database/find-entity-by-id';
import { buildInsertInto } from '@/database/insert-into';
import { getTotalRowCount } from '@/database/row-count';
import { buildUpdateWhere } from '@/database/update-where';
import {
convertToIdentifiers,
OmitAutoSetFields,
conditionalSql,
manyRows,
} from '@/database/utils';
import envSet from '@/env-set';
import { DeletionError } from '@/errors/SlonikError';

View file

@ -1,8 +1,8 @@
import { Roles } from '@logto/schemas';
import { convertToIdentifiers } from '@logto/shared';
import { createMockPool, createMockQueryResult, sql } from 'slonik';
import { mockRole } from '@/__mocks__';
import { convertToIdentifiers } from '@/database/utils';
import envSet from '@/env-set';
import { expectSqlAssert, QueryType } from '@/utils/test-utils';

View file

@ -1,7 +1,7 @@
import { Roles, Role } from '@logto/schemas';
import { convertToIdentifiers } from '@logto/shared';
import { sql } from 'slonik';
import { convertToIdentifiers } from '@/database/utils';
import envSet from '@/env-set';
const { table, fields } = convertToIdentifiers(Roles);

View file

@ -1,8 +1,8 @@
import { Settings } from '@logto/schemas';
import { convertToIdentifiers } from '@logto/shared';
import { createMockPool, createMockQueryResult, sql } from 'slonik';
import { mockSetting } from '@/__mocks__';
import { convertToIdentifiers } from '@/database/utils';
import envSet from '@/env-set';
import { expectSqlAssert, QueryType } from '@/utils/test-utils';

View file

@ -1,8 +1,8 @@
import { Setting, CreateSetting, Settings } from '@logto/schemas';
import { OmitAutoSetFields } from '@logto/shared';
import { buildFindEntityById } from '@/database/find-entity-by-id';
import { buildUpdateWhere } from '@/database/update-where';
import { OmitAutoSetFields } from '@/database/utils';
export const defaultSettingId = 'default';

View file

@ -1,8 +1,8 @@
import { UserRole, Users } from '@logto/schemas';
import { convertToIdentifiers } from '@logto/shared';
import { createMockPool, createMockQueryResult, sql } from 'slonik';
import { mockUser } from '@/__mocks__';
import { convertToIdentifiers } from '@/database/utils';
import envSet from '@/env-set';
import { DeletionError } from '@/errors/SlonikError';
import { expectSqlAssert, QueryType } from '@/utils/test-utils';

View file

@ -1,8 +1,8 @@
import { User, CreateUser, Users, UserRole } from '@logto/schemas';
import { conditionalSql, convertToIdentifiers, OmitAutoSetFields } from '@logto/shared';
import { sql } from 'slonik';
import { buildUpdateWhere } from '@/database/update-where';
import { conditionalSql, convertToIdentifiers, OmitAutoSetFields } from '@/database/utils';
import envSet from '@/env-set';
import { DeletionError } from '@/errors/SlonikError';

View file

@ -29,7 +29,7 @@ jest.mock('@/queries/application', () => ({
),
}));
jest.mock('@/utils/id', () => ({
jest.mock('@logto/shared', () => ({
// eslint-disable-next-line unicorn/consistent-function-scoping
buildIdGenerator: jest.fn(() => () => 'randomId'),
buildApplicationSecret: jest.fn(() => 'randomId'),

View file

@ -1,4 +1,5 @@
import { Applications } from '@logto/schemas';
import { buildApplicationSecret, buildIdGenerator } from '@logto/shared';
import { object, string } from 'zod';
import koaGuard from '@/middleware/koa-guard';
@ -12,7 +13,6 @@ import {
updateApplicationById,
findTotalNumberOfApplications,
} from '@/queries/application';
import { buildApplicationSecret, buildIdGenerator } from '@/utils/id';
import { AuthedRouter } from './types';

View file

@ -24,7 +24,7 @@ jest.mock('@/queries/resource', () => ({
deleteResourceById: jest.fn(),
}));
jest.mock('@/utils/id', () => ({
jest.mock('@logto/shared', () => ({
// eslint-disable-next-line unicorn/consistent-function-scoping
buildIdGenerator: jest.fn(() => () => 'randomId'),
}));

View file

@ -1,4 +1,5 @@
import { Resources } from '@logto/schemas';
import { buildIdGenerator } from '@logto/shared';
import { object, string } from 'zod';
import koaGuard from '@/middleware/koa-guard';
@ -11,7 +12,6 @@ import {
updateResourceById,
deleteResourceById,
} from '@/queries/resource';
import { buildIdGenerator } from '@/utils/id';
import { AuthedRouter } from './types';

View file

@ -0,0 +1,7 @@
import { merge, Config } from '@silverhand/jest-config';
const config: Config.InitialOptions = merge({
roots: ['./src'],
});
export default config;

View file

@ -0,0 +1,48 @@
{
"name": "@logto/shared",
"version": "1.0.0-beta.10",
"main": "lib/index.js",
"author": "Silverhand Inc. <contact@silverhand.io>",
"license": "MPL-2.0",
"files": [
"lib"
],
"private": true,
"scripts": {
"precommit": "lint-staged",
"build": "rm -rf lib/ && tsc -p tsconfig.build.json",
"dev": "tsc -p tsconfig.build.json --watch --preserveWatchOutput --incremental",
"lint": "eslint --ext .ts src",
"prepack": "pnpm build",
"test": "jest",
"test:ci": "jest"
},
"devDependencies": {
"@silverhand/eslint-config": "1.0.0",
"@silverhand/jest-config": "1.0.0",
"@silverhand/ts-config": "1.0.0",
"@types/jest": "^29.1.2",
"eslint": "^8.21.0",
"jest": "^28.1.3",
"lint-staged": "^13.0.0",
"prettier": "^2.7.1",
"typescript": "^4.7.4"
},
"engines": {
"node": "^16.0.0"
},
"eslintConfig": {
"extends": "@silverhand",
"rules": {
"@typescript-eslint/ban-types": "off"
}
},
"prettier": "@silverhand/eslint-config/.prettierrc",
"dependencies": {
"@logto/schemas": "^1.0.0-beta.10",
"@silverhand/essentials": "^1.2.1",
"dayjs": "^1.10.5",
"nanoid": "^3.3.4",
"slonik": "^30.0.0"
}
}

View file

@ -0,0 +1,2 @@
export * from './types';
export * from './utils';

View file

@ -29,11 +29,10 @@ export const excludeAutoSetFields = <T extends string>(fields: readonly T[]) =>
* @param value The value to convert.
* @returns A primitive that can be saved into database.
*/
// eslint-disable-next-line complexity
export const convertToPrimitiveOrSql = (
key: string,
// eslint-disable-next-line @typescript-eslint/ban-types
value: NonNullable<SchemaValue> | null
// eslint-disable-next-line @typescript-eslint/ban-types
): NonNullable<SchemaValuePrimitive> | SqlToken | null => {
if (value === null) {
return null;

View file

@ -0,0 +1,2 @@
export * from './database';
export * from './utils';

View file

@ -0,0 +1 @@
export * from './id';

View file

@ -0,0 +1,4 @@
{
"extends": "./tsconfig",
"include": ["src"]
}

View file

@ -0,0 +1,11 @@
{
"extends": "@silverhand/ts-config/tsconfig.base",
"compilerOptions": {
"outDir": "lib",
"declaration": true
},
"include": [
"src",
"jest.config.ts"
]
}

View file

@ -0,0 +1,3 @@
{
"extends": "./tsconfig"
}

167
pnpm-lock.yaml generated
View file

@ -21,6 +21,7 @@ importers:
packages/cli:
specifiers:
'@logto/schemas': ^1.0.0-beta.10
'@logto/shared': ^1.0.0-beta.10
'@silverhand/eslint-config': 1.0.0
'@silverhand/essentials': ^1.2.1
'@silverhand/jest-config': 1.0.0
@ -60,6 +61,7 @@ importers:
zod: ^3.18.0
dependencies:
'@logto/schemas': link:../schemas
'@logto/shared': link:../shared
'@silverhand/essentials': 1.2.1
chalk: 4.1.2
decamelize: 5.0.1
@ -244,6 +246,7 @@ importers:
'@logto/phrases': ^1.0.0-beta.10
'@logto/phrases-ui': ^1.0.0-beta.10
'@logto/schemas': ^1.0.0-beta.10
'@logto/shared': ^1.0.0-beta.10
'@shopify/jest-koa-mocks': ^5.0.0
'@silverhand/eslint-config': 1.0.0
'@silverhand/essentials': ^1.2.1
@ -326,6 +329,7 @@ importers:
'@logto/phrases': link:../phrases
'@logto/phrases-ui': link:../phrases-ui
'@logto/schemas': link:../schemas
'@logto/shared': link:../shared
'@silverhand/essentials': 1.2.1
chalk: 4.1.2
clean-deep: 3.4.0
@ -613,6 +617,39 @@ importers:
ts-node: 10.9.1_ccwudyfw5se7hgalwgkzhn2yp4
typescript: 4.7.4
packages/shared:
specifiers:
'@logto/schemas': ^1.0.0-beta.10
'@silverhand/eslint-config': 1.0.0
'@silverhand/essentials': ^1.2.1
'@silverhand/jest-config': 1.0.0
'@silverhand/ts-config': 1.0.0
'@types/jest': ^29.1.2
dayjs: ^1.10.5
eslint: ^8.21.0
jest: ^28.1.3
lint-staged: ^13.0.0
nanoid: ^3.3.4
prettier: ^2.7.1
slonik: ^30.0.0
typescript: ^4.7.4
dependencies:
'@logto/schemas': link:../schemas
'@silverhand/essentials': 1.2.1
dayjs: 1.10.7
nanoid: 3.3.4
slonik: 30.1.2
devDependencies:
'@silverhand/eslint-config': 1.0.0_swk2g7ygmfleszo5c33j4vooni
'@silverhand/jest-config': 1.0.0_bi2kohzqnxavgozw3csgny5hju
'@silverhand/ts-config': 1.0.0_typescript@4.7.4
'@types/jest': 29.1.2
eslint: 8.21.0
jest: 28.1.3
lint-staged: 13.0.0
prettier: 2.7.1
typescript: 4.7.4
packages/ui:
specifiers:
'@logto/core-kit': ^1.0.0-beta.13
@ -882,11 +919,11 @@ packages:
/@babel/helper-validator-identifier/7.16.7:
resolution: {integrity: sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw==}
engines: {node: '>=6.9.0'}
dev: false
/@babel/helper-validator-identifier/7.18.6:
resolution: {integrity: sha512-MmetCkz9ej86nJQV+sFCxoGGrUbU3q02kgLciwkrt9QqEB7cP39oKEY0PakknEO0Gu20SskMRi+AYZ3b1TpN9g==}
engines: {node: '>=6.9.0'}
dev: true
/@babel/helper-validator-option/7.16.7:
resolution: {integrity: sha512-TRtenOuRUVo9oIQGPC5G9DgK4743cdxvtOw0weQNpZXaS16SCBi5MNjZF8vba3ETURjZpTbVn7Vvcf2eAwFozQ==}
@ -908,7 +945,7 @@ packages:
resolution: {integrity: sha512-5FnTQLSLswEj6IkgVw5KusNUUFY9ZGqe/TRFnP/BKYHYgfh7tc+C7mwiy95/yNP7Dh9x580Vv8r7u7ZfTBFxdw==}
engines: {node: '>=6.9.0'}
dependencies:
'@babel/helper-validator-identifier': 7.16.7
'@babel/helper-validator-identifier': 7.18.6
chalk: 2.4.2
js-tokens: 4.0.0
@ -1545,6 +1582,13 @@ packages:
jest-get-type: 28.0.2
dev: true
/@jest/expect-utils/29.1.2:
resolution: {integrity: sha512-4a48bhKfGj/KAH39u0ppzNTABXQ8QPccWAFUFobWBaEMSMp+sB31Z2fK/l47c4a/Mu1po2ffmfAIPxXbVTXdtg==}
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
dependencies:
jest-get-type: 29.0.0
dev: true
/@jest/expect/28.1.3:
resolution: {integrity: sha512-lzc8CpUbSoE4dqT0U+g1qODQjBRHPpCPXissXD4mS9+sWQdmmpeJ9zSH1rS1HEkrsMN0fb7nKrJ9giAR1d3wBw==}
engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0}
@ -1635,6 +1679,13 @@ packages:
'@sinclair/typebox': 0.24.26
dev: true
/@jest/schemas/29.0.0:
resolution: {integrity: sha512-3Ab5HgYIIAnS0HjqJHQYZS+zXc4tUmTmBH3z83ajI6afXp8X3ZtdLX+nXx+I7LNkJD7uN9LAVhgnjDgZa2z0kA==}
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
dependencies:
'@sinclair/typebox': 0.24.26
dev: true
/@jest/source-map/28.1.2:
resolution: {integrity: sha512-cV8Lx3BeStJb8ipPHnqVw/IM2VCMWO3crWZzYodSIkxXnRcXJipCdx1JCK0K5MsJJouZQTH73mzf4vgxRaH9ww==}
engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0}
@ -1710,6 +1761,18 @@ packages:
chalk: 4.1.2
dev: true
/@jest/types/29.1.2:
resolution: {integrity: sha512-DcXGtoTykQB5jiwCmVr8H4vdg2OJhQex3qPkG+ISyDO7xQXbt/4R6dowcRyPemRnkH7JoHvZuxPBdlq+9JxFCg==}
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
dependencies:
'@jest/schemas': 29.0.0
'@types/istanbul-lib-coverage': 2.0.3
'@types/istanbul-reports': 3.0.1
'@types/node': 17.0.23
'@types/yargs': 17.0.13
chalk: 4.1.2
dev: true
/@jridgewell/gen-mapping/0.3.2:
resolution: {integrity: sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==}
engines: {node: '>=6.0.0'}
@ -4489,6 +4552,13 @@ packages:
pretty-format: 28.1.3
dev: true
/@types/jest/29.1.2:
resolution: {integrity: sha512-y+nlX0h87U0R+wsGn6EBuoRWYyv3KFtwRNP3QWp9+k2tJ2/bqcGS3UxD7jgT+tiwJWWq3UsyV4Y+T6rsMT4XMg==}
dependencies:
expect: 29.1.2
pretty-format: 29.1.2
dev: true
/@types/js-yaml/4.0.5:
resolution: {integrity: sha512-FhpRzf927MNQdRZP0J5DLIdTXhjLYzeUTmLAu69mnVksLH9CJY3IuSeEgbKUki7GQZm0WqDkGzyxju2EZGD2wA==}
dev: true
@ -6654,6 +6724,11 @@ packages:
engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0}
dev: true
/diff-sequences/29.0.0:
resolution: {integrity: sha512-7Qe/zd1wxSDL4D/X/FPjOMB+ZMDt71W94KYaq05I2l0oQqgXgs7s4ftYYmV38gBSrPz2vcygxfs1xn0FT+rKNA==}
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
dev: true
/diff/4.0.2:
resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==}
engines: {node: '>=0.3.1'}
@ -6940,7 +7015,7 @@ packages:
resolution: {integrity: sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=}
/escape-string-regexp/1.0.5:
resolution: {integrity: sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=}
resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==}
engines: {node: '>=0.8.0'}
/escape-string-regexp/2.0.0:
@ -7523,6 +7598,17 @@ packages:
jest-util: 28.1.3
dev: true
/expect/29.1.2:
resolution: {integrity: sha512-AuAGn1uxva5YBbBlXb+2JPxJRuemZsmlGcapPXWNSBNsQtAULfjioREGBWuI0EOvYUKjDnrCy8PW5Zlr1md5mw==}
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
dependencies:
'@jest/expect-utils': 29.1.2
jest-get-type: 29.0.0
jest-matcher-utils: 29.1.2
jest-message-util: 29.1.2
jest-util: 29.1.2
dev: true
/extend/3.0.2:
resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==}
dev: true
@ -7847,7 +7933,7 @@ packages:
dev: true
/functional-red-black-tree/1.0.1:
resolution: {integrity: sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=}
resolution: {integrity: sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==}
dev: true
/functions-have-names/1.2.3:
@ -8215,7 +8301,7 @@ packages:
dev: true
/has-flag/3.0.0:
resolution: {integrity: sha1-tdRU3CGZriJWmfNGfloH87lVuv0=}
resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==}
engines: {node: '>=4'}
/has-flag/4.0.0:
@ -8705,7 +8791,7 @@ packages:
dev: true
/imurmurhash/0.1.4:
resolution: {integrity: sha1-khi5srkoojixPcT7a21XbyMUU+o=}
resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==}
engines: {node: '>=0.8.19'}
dev: true
@ -8840,7 +8926,7 @@ packages:
dev: true
/is-arrayish/0.2.1:
resolution: {integrity: sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=}
resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==}
/is-arrayish/0.3.2:
resolution: {integrity: sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==}
@ -8933,7 +9019,7 @@ packages:
dev: true
/is-extglob/2.1.1:
resolution: {integrity: sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=}
resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==}
engines: {node: '>=0.10.0'}
dev: true
@ -9175,7 +9261,7 @@ packages:
dev: true
/isexe/2.0.0:
resolution: {integrity: sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=}
resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}
dev: true
/iso8601-duration/1.3.0:
@ -9582,6 +9668,16 @@ packages:
pretty-format: 28.1.3
dev: true
/jest-diff/29.1.2:
resolution: {integrity: sha512-4GQts0aUopVvecIT4IwD/7xsBaMhKTYoM4/njE/aVw9wpw+pIUVp8Vab/KnSzSilr84GnLBkaP3JLDnQYCKqVQ==}
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
dependencies:
chalk: 4.1.2
diff-sequences: 29.0.0
jest-get-type: 29.0.0
pretty-format: 29.1.2
dev: true
/jest-docblock/28.1.1:
resolution: {integrity: sha512-3wayBVNiOYx0cwAbl9rwm5kKFP8yHH3d/fkEaL02NPTkDojPtheGB7HZSFY4wzX+DxyrvhXz0KSCVksmCknCuA==}
engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0}
@ -9661,6 +9757,11 @@ packages:
engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0}
dev: true
/jest-get-type/29.0.0:
resolution: {integrity: sha512-83X19z/HuLKYXYHskZlBAShO7UfLFXu/vWajw9ZNJASN32li8yHMaVGAQqxFW1RCFOkB7cubaL6FaJVQqqJLSw==}
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
dev: true
/jest-haste-map/28.1.3:
resolution: {integrity: sha512-3S+RQWDXccXDKSWnkHa/dPwt+2qwA8CJzR61w3FoYCvoo3Pn8tvGcysmMF0Bj0EX5RYvAI2EIvC57OmotfdtKA==}
engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0}
@ -9702,6 +9803,16 @@ packages:
pretty-format: 28.1.3
dev: true
/jest-matcher-utils/29.1.2:
resolution: {integrity: sha512-MV5XrD3qYSW2zZSHRRceFzqJ39B2z11Qv0KPyZYxnzDHFeYZGJlgGi0SW+IXSJfOewgJp/Km/7lpcFT+cgZypw==}
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
dependencies:
chalk: 4.1.2
jest-diff: 29.1.2
jest-get-type: 29.0.0
pretty-format: 29.1.2
dev: true
/jest-message-util/27.5.1:
resolution: {integrity: sha512-rMyFe1+jnyAAf+NHwTclDz0eAaLkVDdKVHHBFWsBWHnnh5YeJMNWWsv7AbFYXfK3oTqvL7VTWkhNLu1jX24D+g==}
engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
@ -9732,6 +9843,21 @@ packages:
stack-utils: 2.0.5
dev: true
/jest-message-util/29.1.2:
resolution: {integrity: sha512-9oJ2Os+Qh6IlxLpmvshVbGUiSkZVc2FK+uGOm6tghafnB2RyjKAxMZhtxThRMxfX1J1SOMhTn9oK3/MutRWQJQ==}
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
dependencies:
'@babel/code-frame': 7.16.7
'@jest/types': 29.1.2
'@types/stack-utils': 2.0.1
chalk: 4.1.2
graceful-fs: 4.2.9
micromatch: 4.0.5
pretty-format: 29.1.2
slash: 3.0.0
stack-utils: 2.0.5
dev: true
/jest-mock/27.5.1:
resolution: {integrity: sha512-K4jKbY1d4ENhbrG2zuPWaQBvDly+iZ2yAW+T1fATN78hc0sInwn7wZB8XtlNnvHug5RMwV897Xm4LqmPM4e2Og==}
engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
@ -9931,6 +10057,18 @@ packages:
picomatch: 2.3.1
dev: true
/jest-util/29.1.2:
resolution: {integrity: sha512-vPCk9F353i0Ymx3WQq3+a4lZ07NXu9Ca8wya6o4Fe4/aO1e1awMMprZ3woPFpKwghEOW+UXgd15vVotuNN9ONQ==}
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
dependencies:
'@jest/types': 29.1.2
'@types/node': 17.0.23
chalk: 4.1.2
ci-info: 3.3.2
graceful-fs: 4.2.9
picomatch: 2.3.1
dev: true
/jest-validate/28.1.3:
resolution: {integrity: sha512-SZbOGBWEsaTxBGCOpsRWlXlvNkvTkY0XxRfh7zYmvd8uL5Qzyg0CHAXiXKROflh801quA6+/DsT4ODDthOC/OA==}
engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0}
@ -11571,7 +11709,7 @@ packages:
dev: true
/multi-fork/0.0.2:
resolution: {integrity: sha512-SHWGuze0cZNiH+JGJQFlB1k7kZLGFCvW1Xo5Fcpe86KICkC3aVTJWpjUcmyYcLCB0I6gdzKLCia/bTIw2ggl8A==}
resolution: {integrity: sha1-gFiuxGFBJMftqhWBm4juiJ0+tOA=}
/multimatch/5.0.0:
resolution: {integrity: sha512-ypMKuglUrZUD99Tk2bUQ+xNQj43lPEfAeX2o9cTteAmShXy2VHDJpuwu1o0xqoKCt9jLVAvwyFKdLTPXKAfJyA==}
@ -12974,6 +13112,15 @@ packages:
react-is: 18.2.0
dev: true
/pretty-format/29.1.2:
resolution: {integrity: sha512-CGJ6VVGXVRP2o2Dorl4mAwwvDWT25luIsYhkyVQW32E4nL+TgW939J7LlKT/npq5Cpq6j3s+sy+13yk7xYpBmg==}
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
dependencies:
'@jest/schemas': 29.0.0
ansi-styles: 5.2.0
react-is: 18.2.0
dev: true
/pretty-ms/6.0.1:
resolution: {integrity: sha512-ke4njoVmlotekHlHyCZ3wI/c5AMT8peuHs8rKJqekj/oR5G8lND2dVpicFlUz5cbZgE290vvkMuDwfj/OcW1kw==}
engines: {node: '>=10'}