mirror of
https://github.com/logto-io/logto.git
synced 2024-12-30 20:33:54 -05:00
refactor: @logto/essentials
-> @silverhand/essentials
(#118)
* refactor: `@logto/essentials` -> `@silverhand/essentials` * chore: remove useless comment
This commit is contained in:
parent
5ccf903b4a
commit
7c69896126
26 changed files with 65 additions and 143 deletions
|
@ -16,9 +16,9 @@
|
|||
"test": "jest"
|
||||
},
|
||||
"dependencies": {
|
||||
"@logto/essentials": "^1.1.0-rc.2",
|
||||
"@logto/phrases": "^0.1.0",
|
||||
"@logto/schemas": "^0.1.0",
|
||||
"@silverhand/essentials": "^1.1.0",
|
||||
"dayjs": "^1.10.5",
|
||||
"decamelize": "^5.0.0",
|
||||
"dotenv": "^10.0.0",
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { SchemaLike, GeneratedSchema } from '@logto/schemas';
|
||||
import { DatabasePoolType, IdentifierSqlTokenType, sql } from 'slonik';
|
||||
|
||||
import assert from '@/utils/assert';
|
||||
import assertThat from '@/utils/assert-that';
|
||||
|
||||
import {
|
||||
conditionalSql,
|
||||
|
@ -77,7 +77,7 @@ export const buildInsertInto: BuildInsertInto = <Schema extends SchemaLike>(
|
|||
)}
|
||||
`);
|
||||
|
||||
assert(!returning || entry, 'entity.create_failed', { name: rest.tableSingular });
|
||||
assertThat(!returning || entry, 'entity.create_failed', { name: rest.tableSingular });
|
||||
return entry;
|
||||
};
|
||||
};
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
import { getEnv } from '@silverhand/essentials';
|
||||
import { createPool } from 'slonik';
|
||||
import { createInterceptors } from 'slonik-interceptor-preset';
|
||||
|
||||
import { getEnv } from '@/utils/env';
|
||||
|
||||
const interceptors = [...createInterceptors()];
|
||||
|
||||
const pool = createPool(getEnv('DB_URL'), { interceptors });
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import { notFalsy, Truthy } from '@logto/essentials';
|
||||
import { SchemaLike, GeneratedSchema } from '@logto/schemas';
|
||||
import { notFalsy, Truthy } from '@silverhand/essentials';
|
||||
import { DatabasePoolType, sql } from 'slonik';
|
||||
|
||||
import RequestError from '@/errors/RequestError';
|
||||
import assert from '@/utils/assert';
|
||||
import assertThat from '@/utils/assert-that';
|
||||
import { isKeyOf } from '@/utils/schema';
|
||||
|
||||
import { conditionalSql, convertToIdentifiers, convertToPrimitiveOrSql } from './utils';
|
||||
|
@ -51,7 +51,7 @@ export const buildUpdateWhere: BuildUpdateWhere = <Schema extends SchemaLike>(
|
|||
${conditionalSql(returning, () => sql`returning *`)}
|
||||
`);
|
||||
|
||||
assert(
|
||||
assertThat(
|
||||
!returning || entry,
|
||||
new RequestError({
|
||||
code: where.id ? 'entity.not_exists_with_id' : 'entity.not_exists',
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { Falsy, notFalsy } from '@logto/essentials';
|
||||
import { SchemaValuePrimitive, SchemaValue } from '@logto/schemas';
|
||||
import { Falsy, notFalsy } from '@silverhand/essentials';
|
||||
import dayjs from 'dayjs';
|
||||
import { sql, SqlSqlTokenType, SqlTokenType } from 'slonik';
|
||||
|
||||
|
|
2
packages/core/src/env/consts.ts
vendored
2
packages/core/src/env/consts.ts
vendored
|
@ -1,4 +1,4 @@
|
|||
import { assertEnv, getEnv } from '@/utils/env';
|
||||
import { assertEnv, getEnv } from '@silverhand/essentials';
|
||||
|
||||
export const signIn = assertEnv('UI_SIGN_IN_ROUTE');
|
||||
export const isProduction = getEnv('NODE_ENV') === 'production';
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { IncomingHttpHeaders } from 'http';
|
||||
|
||||
import { Optional } from '@logto/essentials';
|
||||
import { Optional, normalizeValueToStringArray } from '@silverhand/essentials';
|
||||
import { ParameterizedContext } from 'koa';
|
||||
import { IRouterParamContext } from 'koa-router';
|
||||
|
||||
|
@ -27,14 +27,6 @@ const resolveLanguage = (languageString: string): Optional<[string, number]> =>
|
|||
return [language, 1];
|
||||
};
|
||||
|
||||
const normalizeValueToStringArray = (value?: string | string[]): string[] => {
|
||||
if (value) {
|
||||
return Array.isArray(value) ? value : [value];
|
||||
}
|
||||
|
||||
return [];
|
||||
};
|
||||
|
||||
const detectLanguageFromHeaders = (headers: IncomingHttpHeaders): string[] =>
|
||||
headers['accept-language']
|
||||
?.split(',')
|
||||
|
|
|
@ -10,7 +10,7 @@ import { developmentUserId, isProduction } from '@/env/consts';
|
|||
import RequestError from '@/errors/RequestError';
|
||||
import { publicKey, issuer, adminResource } from '@/oidc/consts';
|
||||
import { findUserById } from '@/queries/user';
|
||||
import assert from '@/utils/assert';
|
||||
import assertThat from '@/utils/assert-that';
|
||||
|
||||
export type WithAuthContext<ContextT extends IRouterParamContext = IRouterParamContext> =
|
||||
ContextT & {
|
||||
|
@ -20,11 +20,11 @@ export type WithAuthContext<ContextT extends IRouterParamContext = IRouterParamC
|
|||
const bearerTokenIdentifier = 'Bearer';
|
||||
|
||||
const extractBearerTokenFromHeaders = ({ authorization }: IncomingHttpHeaders) => {
|
||||
assert(
|
||||
assertThat(
|
||||
authorization,
|
||||
new RequestError({ code: 'auth.authorization_header_missing', status: 401 })
|
||||
);
|
||||
assert(
|
||||
assertThat(
|
||||
authorization.startsWith(bearerTokenIdentifier),
|
||||
new RequestError(
|
||||
{ code: 'auth.authorization_type_not_supported', status: 401 },
|
||||
|
@ -45,7 +45,7 @@ const getUserIdFromRequest = async (request: Request) => {
|
|||
issuer,
|
||||
audience: adminResource,
|
||||
});
|
||||
assert(sub, new RequestError({ code: 'auth.jwt_sub_missing', status: 401 }));
|
||||
assertThat(sub, new RequestError({ code: 'auth.jwt_sub_missing', status: 401 }));
|
||||
return sub;
|
||||
};
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { has } from '@logto/essentials';
|
||||
import { has } from '@silverhand/essentials';
|
||||
import { MiddlewareType } from 'koa';
|
||||
import koaBody from 'koa-body';
|
||||
import { IMiddleware, IRouterParamContext } from 'koa-router';
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
import crypto from 'crypto';
|
||||
|
||||
import { getEnv } from '@silverhand/essentials';
|
||||
|
||||
import { port } from '@/env/consts';
|
||||
import { getEnv } from '@/utils/env';
|
||||
|
||||
export const privateKey = crypto.createPrivateKey(
|
||||
Buffer.from(getEnv('OIDC_PROVIDER_PRIVATE_KEY_BASE64'), 'base64')
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import { conditional } from '@logto/essentials';
|
||||
import {
|
||||
OidcModelInstanceDBEntry,
|
||||
OidcModelInstancePayload,
|
||||
OidcModelInstances,
|
||||
} from '@logto/schemas';
|
||||
import { conditional } from '@silverhand/essentials';
|
||||
import { sql, ValueExpressionType } from 'slonik';
|
||||
|
||||
import { buildInsertInto } from '@/database/insert-into';
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
import { conditional } from '@logto/essentials';
|
||||
import { LogtoErrorCode } from '@logto/phrases';
|
||||
import { conditional } from '@silverhand/essentials';
|
||||
import { Provider } from 'oidc-provider';
|
||||
import { object, string } from 'zod';
|
||||
|
||||
import RequestError from '@/errors/RequestError';
|
||||
import koaGuard from '@/middleware/koa-guard';
|
||||
import { findUserByUsername } from '@/queries/user';
|
||||
import assert from '@/utils/assert';
|
||||
import assertThat from '@/utils/assert-that';
|
||||
import { encryptPassword } from '@/utils/password';
|
||||
|
||||
import { AnonymousRouter } from './types';
|
||||
|
@ -24,17 +24,17 @@ export default function sessionRoutes<T extends AnonymousRouter>(router: T, prov
|
|||
if (name === 'login') {
|
||||
const { username, password } = ctx.guard.body;
|
||||
|
||||
assert(username && password, 'session.insufficient_info');
|
||||
assertThat(username && password, 'session.insufficient_info');
|
||||
|
||||
try {
|
||||
const { id, passwordEncrypted, passwordEncryptionMethod, passwordEncryptionSalt } =
|
||||
await findUserByUsername(username);
|
||||
|
||||
assert(
|
||||
assertThat(
|
||||
passwordEncrypted && passwordEncryptionMethod && passwordEncryptionSalt,
|
||||
'session.invalid_sign_in_method'
|
||||
);
|
||||
assert(
|
||||
assertThat(
|
||||
encryptPassword(id, password, passwordEncryptionSalt, passwordEncryptionMethod) ===
|
||||
passwordEncrypted,
|
||||
'session.invalid_credentials'
|
||||
|
@ -69,7 +69,7 @@ export default function sessionRoutes<T extends AnonymousRouter>(router: T, prov
|
|||
router.post('/session/consent', async (ctx, next) => {
|
||||
const interaction = await provider.interactionDetails(ctx.req, ctx.res);
|
||||
const { session, grantId, params, prompt } = interaction;
|
||||
assert(session, 'session.not_found');
|
||||
assertThat(session, 'session.not_found');
|
||||
|
||||
const { scope } = object({
|
||||
scope: string().optional(),
|
||||
|
@ -78,7 +78,7 @@ export default function sessionRoutes<T extends AnonymousRouter>(router: T, prov
|
|||
// LOG-49: Connect and check scope with resource indicators
|
||||
const scopes = scope?.split(' ') ?? [];
|
||||
const invalidScopes = scopes.filter((scope) => !['openid', 'offline_access'].includes(scope));
|
||||
assert(invalidScopes.length === 0, 'oidc.invalid_scope', {
|
||||
assertThat(invalidScopes.length === 0, 'oidc.invalid_scope', {
|
||||
count: invalidScopes.length,
|
||||
scopes: invalidScopes.join(', '),
|
||||
});
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import { toTitle } from '@silverhand/essentials';
|
||||
import { IMiddleware } from 'koa-router';
|
||||
import { OpenAPIV3 } from 'openapi-types';
|
||||
|
||||
import { isGuardMiddleware, WithGuardConfig } from '@/middleware/koa-guard';
|
||||
import { toTitle } from '@/utils/string';
|
||||
import { zodTypeToSwagger } from '@/utils/zod';
|
||||
|
||||
import { AnonymousRouter } from './types';
|
||||
|
|
19
packages/core/src/utils/assert-that.ts
Normal file
19
packages/core/src/utils/assert-that.ts
Normal file
|
@ -0,0 +1,19 @@
|
|||
import { LogtoErrorCode } from '@logto/phrases';
|
||||
import { assert } from '@silverhand/essentials';
|
||||
|
||||
import RequestError from '@/errors/RequestError';
|
||||
|
||||
export type AssertThatFunction = <E extends Error>(
|
||||
value: unknown,
|
||||
error: E | LogtoErrorCode,
|
||||
interpolation?: Record<string, unknown>
|
||||
) => asserts value;
|
||||
|
||||
const assertThat: AssertThatFunction = (value, error, interpolation): asserts value => {
|
||||
assert(
|
||||
value,
|
||||
error instanceof Error ? error : new RequestError({ code: error, ...interpolation })
|
||||
);
|
||||
};
|
||||
|
||||
export default assertThat;
|
|
@ -1,25 +0,0 @@
|
|||
// https://github.com/facebook/jest/issues/7547
|
||||
|
||||
import { LogtoErrorCode } from '@logto/phrases';
|
||||
|
||||
import RequestError from '@/errors/RequestError';
|
||||
|
||||
export type AssertFunction = <E extends Error>(
|
||||
value: unknown,
|
||||
error: E | LogtoErrorCode,
|
||||
interpolation?: Record<string, unknown>
|
||||
) => asserts value;
|
||||
|
||||
const assert: AssertFunction = (value, error, interpolation): asserts value => {
|
||||
if (!value) {
|
||||
if (error instanceof Error) {
|
||||
// https://github.com/typescript-eslint/typescript-eslint/issues/3814
|
||||
// eslint-disable-next-line @typescript-eslint/no-throw-literal
|
||||
throw error;
|
||||
}
|
||||
|
||||
throw new RequestError({ code: error, ...interpolation });
|
||||
}
|
||||
};
|
||||
|
||||
export default assert;
|
|
@ -1,29 +0,0 @@
|
|||
import { assertEnv, getEnv } from './env';
|
||||
|
||||
describe('getEnv()', () => {
|
||||
beforeAll(() => {
|
||||
process.env = { FOO: 'bar' };
|
||||
});
|
||||
|
||||
it('returns correct env value', () => {
|
||||
expect(getEnv('FOO')).toEqual('bar');
|
||||
});
|
||||
|
||||
it("returns fallback if env doesn't exist", () => {
|
||||
expect(getEnv('BAR', '123')).toEqual('123');
|
||||
});
|
||||
});
|
||||
|
||||
describe('assertEnv()', () => {
|
||||
beforeAll(() => {
|
||||
process.env = { FOO: 'bar' };
|
||||
});
|
||||
|
||||
it('returns correct env value', () => {
|
||||
expect(assertEnv('FOO')).toEqual('bar');
|
||||
});
|
||||
|
||||
it("throws an error if env doesn't exist", () => {
|
||||
expect(() => assertEnv('BAR')).toThrow('env variable BAR not found');
|
||||
});
|
||||
});
|
|
@ -1,8 +0,0 @@
|
|||
import assert from '@/utils/assert';
|
||||
|
||||
export const getEnv = (key: string, fallback = ''): string => process.env[key] ?? fallback;
|
||||
export const assertEnv = (key: string): string => {
|
||||
const value = process.env[key];
|
||||
assert(value, new Error(`env variable ${key} not found`));
|
||||
return value;
|
||||
};
|
|
@ -1,12 +1,10 @@
|
|||
import { createHash } from 'crypto';
|
||||
|
||||
import { PasswordEncryptionMethod } from '@logto/schemas';
|
||||
import { assertEnv, repeat } from '@silverhand/essentials';
|
||||
import { number, string } from 'zod';
|
||||
|
||||
import assert from '@/utils/assert';
|
||||
|
||||
import { assertEnv } from './env';
|
||||
import repeat from './repeat';
|
||||
import assertThat from '@/utils/assert-that';
|
||||
|
||||
const peppers = string()
|
||||
.array()
|
||||
|
@ -21,7 +19,7 @@ export const encryptPassword = (
|
|||
salt: string,
|
||||
method: PasswordEncryptionMethod
|
||||
): string => {
|
||||
assert(
|
||||
assertThat(
|
||||
method === PasswordEncryptionMethod.SaltAndPepper,
|
||||
'password.unsupported_encryption_method',
|
||||
{ method }
|
||||
|
@ -30,7 +28,7 @@ export const encryptPassword = (
|
|||
const sum = [...id].reduce((accumulator, current) => accumulator + current.charCodeAt(0), 0);
|
||||
const pepper = peppers[sum % peppers.length];
|
||||
|
||||
assert(pepper, 'password.pepper_not_found');
|
||||
assertThat(pepper, 'password.pepper_not_found');
|
||||
|
||||
const result = repeat(iterationCount, password, (password) =>
|
||||
createHash('sha256')
|
||||
|
|
|
@ -1,15 +0,0 @@
|
|||
// LOG-79: Add this into `essentials` package
|
||||
// Disable FP rules here to use the performant approach while keep the function itself "FP"
|
||||
/* eslint-disable @silverhand/fp/no-let, @silverhand/fp/no-mutation */
|
||||
const repeat = <T>(times: number, initial: T, iterate: (accumulator: T) => T) => {
|
||||
let result = initial;
|
||||
|
||||
while (times--) {
|
||||
result = iterate(result);
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
/* eslint-enable @silverhand/fp/no-let, @silverhand/fp/no-mutation */
|
||||
|
||||
export default repeat;
|
|
@ -1,10 +0,0 @@
|
|||
export function toTitle(string: string) {
|
||||
if (typeof string !== 'string') {
|
||||
throw new TypeError('Expected a string');
|
||||
}
|
||||
|
||||
return string
|
||||
.toLowerCase()
|
||||
.replace(/(?:^|\s|-)\S/g, (x) => x.toUpperCase())
|
||||
.replace(/-/g, ' ');
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
import { conditional } from '@logto/essentials';
|
||||
import { conditional } from '@silverhand/essentials';
|
||||
import { OpenAPIV3 } from 'openapi-types';
|
||||
import { ZodArray, ZodBoolean, ZodNumber, ZodObject, ZodOptional, ZodString } from 'zod';
|
||||
|
||||
|
|
|
@ -21,8 +21,8 @@
|
|||
"node": ">=14.15.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@logto/essentials": "^1.1.0-rc.2",
|
||||
"@silverhand/eslint-config": "^0.2.2",
|
||||
"@silverhand/essentials": "^1.1.0",
|
||||
"@silverhand/ts-config": "^0.2.2",
|
||||
"@types/lodash.uniq": "^4.5.6",
|
||||
"@types/node": "14",
|
||||
|
|
|
@ -4,7 +4,7 @@ import assert from 'assert';
|
|||
import fs from 'fs/promises';
|
||||
import path from 'path';
|
||||
|
||||
import { conditional, conditionalString } from '@logto/essentials';
|
||||
import { conditional, conditionalString } from '@silverhand/essentials';
|
||||
import camelcase from 'camelcase';
|
||||
import uniq from 'lodash.uniq';
|
||||
import pluralize from 'pluralize';
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { conditionalString } from '@logto/essentials';
|
||||
import { conditionalString } from '@silverhand/essentials';
|
||||
import camelcase from 'camelcase';
|
||||
import pluralize from 'pluralize';
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { Optional } from '@logto/essentials';
|
||||
import { Optional } from '@silverhand/essentials';
|
||||
|
||||
export const normalizeWhitespaces = (string: string): string => string.replace(/\s+/g, ' ').trim();
|
||||
|
||||
|
|
|
@ -20,10 +20,10 @@ importers:
|
|||
|
||||
packages/core:
|
||||
specifiers:
|
||||
'@logto/essentials': ^1.1.0-rc.2
|
||||
'@logto/phrases': ^0.1.0
|
||||
'@logto/schemas': ^0.1.0
|
||||
'@silverhand/eslint-config': ^0.2.2
|
||||
'@silverhand/essentials': ^1.1.0
|
||||
'@silverhand/ts-config': ^0.2.2
|
||||
'@types/jest': ^27.0.1
|
||||
'@types/koa': ^2.13.3
|
||||
|
@ -66,9 +66,9 @@ importers:
|
|||
typescript: ^4.3.5
|
||||
zod: ^3.8.1
|
||||
dependencies:
|
||||
'@logto/essentials': 1.1.0-rc.2
|
||||
'@logto/phrases': link:../phrases
|
||||
'@logto/schemas': link:../schemas
|
||||
'@silverhand/essentials': 1.1.0
|
||||
dayjs: 1.10.6
|
||||
decamelize: 5.0.0
|
||||
dotenv: 10.0.0
|
||||
|
@ -131,9 +131,9 @@ importers:
|
|||
|
||||
packages/schemas:
|
||||
specifiers:
|
||||
'@logto/essentials': ^1.1.0-rc.2
|
||||
'@logto/phrases': ^0.1.0
|
||||
'@silverhand/eslint-config': ^0.2.2
|
||||
'@silverhand/essentials': ^1.1.0
|
||||
'@silverhand/ts-config': ^0.2.2
|
||||
'@types/lodash.uniq': ^4.5.6
|
||||
'@types/node': '14'
|
||||
|
@ -151,8 +151,8 @@ importers:
|
|||
'@logto/phrases': link:../phrases
|
||||
zod: 3.8.1
|
||||
devDependencies:
|
||||
'@logto/essentials': 1.1.0-rc.2
|
||||
'@silverhand/eslint-config': 0.2.2_aff669e8eb0d21fc4e2068e6112ef4d0
|
||||
'@silverhand/essentials': 1.1.0
|
||||
'@silverhand/ts-config': 0.2.2_typescript@4.3.5
|
||||
'@types/lodash.uniq': 4.5.6
|
||||
'@types/node': 14.17.6
|
||||
|
@ -2986,13 +2986,6 @@ packages:
|
|||
write-file-atomic: 3.0.3
|
||||
dev: true
|
||||
|
||||
/@logto/essentials/1.1.0-rc.2:
|
||||
resolution: {integrity: sha512-MqmA566CFSg0HsvJwoR+mggHVoJ6WlpNLCYBLI93KImzAgk4ecnt41wTPw2/mqAkCLSJlecyXwTOzSaUj3ykZA==}
|
||||
engines: {node: '>=14.15.0', pnpm: '>=6'}
|
||||
dependencies:
|
||||
lodash.orderby: 4.6.0
|
||||
lodash.pick: 4.4.0
|
||||
|
||||
/@nodelib/fs.scandir/2.1.5:
|
||||
resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==}
|
||||
engines: {node: '>= 8'}
|
||||
|
@ -3270,6 +3263,13 @@ packages:
|
|||
lodash: 4.17.21
|
||||
dev: true
|
||||
|
||||
/@silverhand/essentials/1.1.0:
|
||||
resolution: {integrity: sha512-O2ROFk1TgBLf4QwGBc5iF16TvhsIJAShbg/zHzHTF1PrSNAL/Dq2F29Fdr5BG22rJpSmypGQsk9SWVIM1tPRqA==}
|
||||
engines: {node: '>=14.15.0', pnpm: '>=6'}
|
||||
dependencies:
|
||||
lodash.orderby: 4.6.0
|
||||
lodash.pick: 4.4.0
|
||||
|
||||
/@silverhand/ts-config-react/0.2.2_typescript@4.3.5:
|
||||
resolution: {integrity: sha512-zYddjCjxmdo/W+K+olqotrFwawjv0m3YGGVHomp+7oLqfR/6SwWxPg4lR8aWcfxs4XH1DiYTBNakrkFVN8vwug==}
|
||||
engines: {node: '>=14.15.0'}
|
||||
|
|
Loading…
Reference in a new issue