0
Fork 0
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:
Gao Sun 2021-09-16 23:48:06 +08:00 committed by GitHub
parent 5ccf903b4a
commit 7c69896126
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
26 changed files with 65 additions and 143 deletions

View file

@ -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",

View file

@ -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;
};
};

View file

@ -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 });

View file

@ -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',

View file

@ -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';

View file

@ -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';

View file

@ -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(',')

View file

@ -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;
};

View file

@ -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';

View file

@ -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')

View file

@ -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';

View file

@ -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(', '),
});

View file

@ -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';

View 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;

View file

@ -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;

View file

@ -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');
});
});

View file

@ -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;
};

View file

@ -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')

View file

@ -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;

View file

@ -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, ' ');
}

View file

@ -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';

View file

@ -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",

View file

@ -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';

View file

@ -1,4 +1,4 @@
import { conditionalString } from '@logto/essentials';
import { conditionalString } from '@silverhand/essentials';
import camelcase from 'camelcase';
import pluralize from 'pluralize';

View file

@ -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();

View file

@ -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'}