add biome to config, fix lint, use biome config file from root

This commit is contained in:
einhornimmond 2025-04-26 10:22:42 +02:00
parent 90b8dbcc7b
commit 3ec68ce081
6 changed files with 116 additions and 261 deletions

View File

@ -1,4 +0,0 @@
node_modules
**/*.min.js
build
coverage

View File

@ -1,214 +0,0 @@
// eslint-disable-next-line import/no-commonjs, import/unambiguous
module.exports = {
root: true,
env: {
node: true,
},
parser: '@typescript-eslint/parser',
plugins: ['prettier', '@typescript-eslint', 'type-graphql', 'import', 'n', 'promise'],
extends: [
'standard',
'eslint:recommended',
'plugin:prettier/recommended',
'plugin:import/recommended',
'plugin:import/typescript',
'plugin:security/recommended',
'plugin:@eslint-community/eslint-comments/recommended',
],
settings: {
'import/parsers': {
'@typescript-eslint/parser': ['.ts', '.tsx'],
},
'import/resolver': {
typescript: {
project: ['./tsconfig.json', '**/tsconfig.json'],
},
node: true,
},
// the parser cannot handle the split sodium import
'import/ignore': ['sodium-native'],
},
rules: {
'no-console': 'error',
camelcase: ['error', { allow: ['crypto_*', 'randombytes_random'] }],
'no-debugger': 'error',
'prettier/prettier': [
'error',
{
htmlWhitespaceSensitivity: 'ignore',
},
],
// import
'import/export': 'error',
// 'import/no-deprecated': 'error',
'import/no-empty-named-blocks': 'error',
'import/no-extraneous-dependencies': 'error',
'import/no-mutable-exports': 'error',
'import/no-unused-modules': 'error',
'import/no-named-as-default': 'error',
'import/no-named-as-default-member': 'error',
'import/no-amd': 'error',
'import/no-commonjs': 'error',
'import/no-import-module-exports': 'error',
'import/no-nodejs-modules': 'off',
'import/unambiguous': 'error',
'import/default': 'error',
'import/named': 'error',
'import/namespace': 'error',
'import/no-absolute-path': 'error',
'import/no-cycle': 'error',
'import/no-dynamic-require': 'error',
'import/no-internal-modules': 'off',
'import/no-relative-packages': 'error',
'import/no-relative-parent-imports': [
'error',
{ ignore: ['@/*', 'random-bigint', 'sodium-native'] },
],
'import/no-self-import': 'error',
'import/no-unresolved': 'error',
'import/no-useless-path-segments': 'error',
'import/no-webpack-loader-syntax': 'error',
'import/consistent-type-specifier-style': 'error',
'import/exports-last': 'off',
'import/extensions': 'error',
'import/first': 'error',
'import/group-exports': 'off',
'import/newline-after-import': 'error',
'import/no-anonymous-default-export': 'error',
'import/no-default-export': 'error',
'import/no-duplicates': 'error',
'import/no-named-default': 'error',
'import/no-namespace': 'error',
'import/no-unassigned-import': 'error',
'import/order': [
'error',
{
groups: ['builtin', 'external', 'internal', 'parent', 'sibling', 'index', 'object', 'type'],
'newlines-between': 'always',
pathGroups: [
{
pattern: '@?*/**',
group: 'external',
position: 'after',
},
{
pattern: '@/**',
group: 'external',
position: 'after',
},
],
alphabetize: {
order: 'asc' /* sort in ascending order. Options: ['ignore', 'asc', 'desc'] */,
caseInsensitive: true /* ignore case. Options: [true, false] */,
},
distinctGroup: true,
},
],
'import/prefer-default-export': 'off',
// n
'n/handle-callback-err': 'error',
'n/no-callback-literal': 'error',
'n/no-exports-assign': 'error',
'n/no-extraneous-import': 'error',
'n/no-extraneous-require': 'error',
'n/no-hide-core-modules': 'error',
'n/no-missing-import': 'off', // not compatible with typescript
'n/no-missing-require': 'error',
'n/no-new-require': 'error',
'n/no-path-concat': 'error',
'n/no-process-exit': 'error',
'n/no-unpublished-bin': 'error',
'n/no-unpublished-import': 'off', // TODO need to exclude seeds
'n/no-unpublished-require': 'error',
'n/no-unsupported-features': ['error', { ignores: ['modules'] }],
'n/no-unsupported-features/es-builtins': 'error',
'n/no-unsupported-features/es-syntax': 'error',
'n/no-unsupported-features/node-builtins': 'error',
'n/process-exit-as-throw': 'error',
'n/shebang': 'error',
'n/callback-return': 'error',
'n/exports-style': 'error',
'n/file-extension-in-import': 'off',
'n/global-require': 'error',
'n/no-mixed-requires': 'error',
'n/no-process-env': 'error',
'n/no-restricted-import': 'error',
'n/no-restricted-require': 'error',
'n/no-sync': 'error',
'n/prefer-global/buffer': 'error',
'n/prefer-global/console': 'error',
'n/prefer-global/process': 'error',
'n/prefer-global/text-decoder': 'error',
'n/prefer-global/text-encoder': 'error',
'n/prefer-global/url': 'error',
'n/prefer-global/url-search-params': 'error',
'n/prefer-promises/dns': 'error',
'n/prefer-promises/fs': 'error',
// promise
'promise/catch-or-return': 'error',
'promise/no-return-wrap': 'error',
'promise/param-names': 'error',
'promise/always-return': 'error',
'promise/no-native': 'off',
'promise/no-nesting': 'warn',
'promise/no-promise-in-callback': 'warn',
'promise/no-callback-in-promise': 'warn',
'promise/avoid-new': 'warn',
'promise/no-new-statics': 'error',
'promise/no-return-in-finally': 'warn',
'promise/valid-params': 'warn',
'promise/prefer-await-to-callbacks': 'error',
'promise/no-multiple-resolved': 'error',
// eslint comments
'@eslint-community/eslint-comments/disable-enable-pair': ['error', { allowWholeFile: true }],
'@eslint-community/eslint-comments/no-restricted-disable': 'error',
'@eslint-community/eslint-comments/no-use': 'off',
'@eslint-community/eslint-comments/require-description': 'off',
},
overrides: [
// only for ts files
{
files: ['*.ts', '*.tsx'],
extends: [
'plugin:@typescript-eslint/recommended',
'plugin:@typescript-eslint/recommended-requiring-type-checking',
'plugin:@typescript-eslint/strict',
'plugin:type-graphql/recommended',
],
rules: {
// allow explicitly defined dangling promises
'@typescript-eslint/no-floating-promises': ['error', { ignoreVoid: true }],
'no-void': ['error', { allowAsStatement: true }],
// ignore prefer-regexp-exec rule to allow string.match(regex)
'@typescript-eslint/prefer-regexp-exec': 'off',
// this should not run on ts files: https://github.com/import-js/eslint-plugin-import/issues/2215#issuecomment-911245486
'import/unambiguous': 'off',
// this is not compatible with typeorm, due to joined tables can be null, but are not defined as nullable
'@typescript-eslint/no-unnecessary-condition': 'off',
},
parserOptions: {
tsconfigRootDir: __dirname,
project: ['./tsconfig.json', '**/tsconfig.json'],
// this is to properly reference the referenced project database without requirement of compiling it
// eslint-disable-next-line camelcase
EXPERIMENTAL_useSourceOfProjectReferenceRedirect: true,
},
},
{
files: ['*.test.ts'],
plugins: ['jest'],
env: {
jest: true,
},
rules: {
'jest/no-disabled-tests': 'error',
'jest/no-focused-tests': 'error',
'jest/no-identical-title': 'error',
'jest/prefer-to-have-length': 'error',
'jest/valid-expect': 'error',
'@typescript-eslint/unbound-method': 'off',
'jest/unbound-method': 'error',
},
},
],
}

View File

@ -11,9 +11,12 @@
"scripts": {
"build": "tsc --build",
"clean": "tsc --build --clean",
"lint": "biome check --error-on-warnings .",
"lint:fix": "biome check --error-on-warnings . --write",
"test": "node test/index"
},
"devDependencies": {
"@biomejs/biome": "1.9.4",
"@types/joi": "^17.2.3",
"@types/node": "^17.0.21",
"typescript": "^4.9.5",

View File

@ -1,22 +1,22 @@
import Joi from 'joi'
export const browserUrls = Joi.array()
.items(Joi.string().uri())
.items(Joi.string().uri())
.sparse(true)
.custom((value: string[], helpers: Joi.CustomHelpers<string[]>) => {
let protocol: string | undefined
for (const url of value) {
if (url === undefined) {
continue
if (url === undefined) {
continue
}
const urlObject = new URL(url)
if(!protocol) {
if (!protocol) {
protocol = urlObject.protocol
} else if(urlObject.protocol !== protocol) {
} else if (urlObject.protocol !== protocol) {
return helpers.error('any.invalid')
}
}
return value;
return value
})
.required()
.description('All URLs need to have same protocol to prevent mixed block errors')
@ -29,8 +29,12 @@ export const DECAY_START_TIME = Joi.date()
export const DB_VERSION = Joi.string()
.pattern(/^\d{4}-[a-z0-9-_]+$/)
.message('DB_VERSION must be in the format: YYYY-description, e.g. "0087-add_index_on_user_roles".')
.description('db version string, last migration file name without ending or last folder in entity')
.message(
'DB_VERSION must be in the format: YYYY-description, e.g. "0087-add_index_on_user_roles".',
)
.description(
'db version string, last migration file name without ending or last folder in entity',
)
.required()
export const COMMUNITY_URL = Joi.string()
@ -39,9 +43,11 @@ export const COMMUNITY_URL = Joi.string()
if (value.endsWith('/')) {
return helpers.error('any.invalid', { message: 'URL should not end with a slash (/)' })
}
return value;
return value
})
.description('The base URL of the community, should have the same protocol as frontend, admin and backend api to prevent mixed contend issues.')
.description(
'The base URL of the community, should have the same protocol as frontend, admin and backend api to prevent mixed contend issues.',
)
.default('http://0.0.0.0')
.required()
@ -62,7 +68,7 @@ export const COMMUNITY_NAME = Joi.string()
.max(40)
.description('The name of the community')
.default('Gradido Entwicklung')
.required()
.required()
export const COMMUNITY_DESCRIPTION = Joi.string()
.min(10)
@ -82,19 +88,19 @@ export const COMMUNITY_LOCATION = Joi.string()
.when('GMS_ACTIVE', {
is: true,
then: Joi.string().required(),
otherwise: Joi.string().optional()
otherwise: Joi.string().optional(),
})
.description('Geographical location of the community in "latitude, longitude" format')
.default('49.280377, 9.690151')
.default('49.280377, 9.690151')
export const GRAPHIQL = Joi.boolean()
.description('Flag for enabling GraphQL playground for debugging.')
.default(false)
.when('NODE_ENV', {
is: 'development',
then: Joi.boolean().valid(true, false).required(), // only allow true in development mode
otherwise: Joi.boolean().valid(false).required() // false in any other mode
})
.description('Flag for enabling GraphQL playground for debugging.')
.default(false)
.when('NODE_ENV', {
is: 'development',
then: Joi.boolean().valid(true, false).required(), // only allow true in development mode
otherwise: Joi.boolean().valid(false).required(), // false in any other mode
})
export const GMS_ACTIVE = Joi.boolean()
.description('Flag to indicate if the GMS (Geographic Member Search) service is used.')
@ -123,20 +129,20 @@ export const HUMHUB_API_URL = Joi.string()
.description('The API URL for HumHub integration')
export const LOG_LEVEL = Joi.string()
.valid('all', 'mark', 'trace', 'debug', 'info', 'warn', 'error', 'fatal', 'off')
.valid('all', 'mark', 'trace', 'debug', 'info', 'warn', 'error', 'fatal', 'off')
.description('set log level')
.default('info')
.required()
export const LOG4JS_CONFIG = Joi.string()
.pattern(/^[a-zA-Z0-9-_]+\.json$/)
.pattern(/^[a-zA-Z0-9-_]+\.json$/)
.message('LOG4JS_CONFIG must be a valid filename ending with .json')
.description('config file name for log4js config file')
.default('log4js-config.json')
.required()
export const LOGIN_APP_SECRET = Joi.string()
.pattern(/^[a-fA-F0-9]+$/)
.pattern(/^[a-fA-F0-9]+$/)
.message('need to be valid hex')
.default('21ffbbc616fe')
.description('App secret for salt component for libsodium crypto_pwhash')
@ -147,7 +153,9 @@ export const LOGIN_SERVER_KEY = Joi.string()
.length(32)
.message('need to be valid hex and 32 character')
.default('a51ef8ac7ef1abf162fb7a65261acd7a')
.description('Server key for password hashing as additional salt for libsodium crypto_shorthash_keygen')
.description(
'Server key for password hashing as additional salt for libsodium crypto_shorthash_keygen',
)
.required()
export const OPENAI_ACTIVE = Joi.boolean()
@ -156,7 +164,7 @@ export const OPENAI_ACTIVE = Joi.boolean()
.required()
export const TYPEORM_LOGGING_RELATIVE_PATH = Joi.string()
.pattern(new RegExp('^[a-zA-Z0-9-_\./]+\.log$'))
.pattern(/^[a-zA-Z0-9-_\.\/]+\.log$/)
.message('TYPEORM_LOGGING_RELATIVE_PATH must be a valid filename ending with .log')
.description('log file name for logging typeorm activities')
.default('typeorm.log')
@ -168,7 +176,7 @@ export const DB_HOST = Joi.string()
.description("database host like 'localhost' or 'mariadb' in docker setup")
.default('localhost')
.required()
export const DB_PORT = Joi.number()
.integer()
.min(1024)
@ -179,10 +187,10 @@ export const DB_PORT = Joi.number()
export const DB_USER = Joi.string()
.pattern(/^[A-Za-z0-9]([A-Za-z0-9-_\.]*[A-Za-z0-9])?$/) // Validates MariaDB username rules
.min(1) // Minimum length 1
.min(1) // Minimum length 1
.max(16) // Maximum length 16
.message(
'Valid database username (letters, numbers, hyphens, underscores, dots allowed; no spaces, must not start or end with hyphen, dot, or underscore)'
'Valid database username (letters, numbers, hyphens, underscores, dots allowed; no spaces, must not start or end with hyphen, dot, or underscore)',
)
.description('database username for mariadb')
.default('root')
@ -191,24 +199,26 @@ export const DB_USER = Joi.string()
export const DB_PASSWORD = Joi.string()
.when(Joi.ref('NODE_ENV'), {
is: 'development',
then: Joi.string().allow(''),
then: Joi.string().allow(''),
otherwise: Joi.string()
.min(8)
.min(8)
.max(32)
.pattern(/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[!@#$%^&*(),.?":{}|<>]).+$/)
.message(
'Password must be between 8 and 32 characters long, and contain at least one uppercase letter, one lowercase letter, one number, and one special character (e.g., !@#$%^&*).'
)
'Password must be between 8 and 32 characters long, and contain at least one uppercase letter, one lowercase letter, one number, and one special character (e.g., !@#$%^&*).',
),
})
.description(
'Password for the database user. In development mode, an empty password is allowed. In other environments, a complex password is required.'
'Password for the database user. In development mode, an empty password is allowed. In other environments, a complex password is required.',
)
.default('')
.default('')
.required()
export const DB_DATABASE = Joi.string()
.pattern(/^[a-zA-Z][a-zA-Z0-9_-]{1,63}$/)
.description('Database name like gradido_community (must start with a letter, and can only contain letters, numbers, underscores, or dashes)')
.description(
'Database name like gradido_community (must start with a letter, and can only contain letters, numbers, underscores, or dashes)',
)
.default('gradido_community')
.required()
@ -221,13 +231,13 @@ export const APP_VERSION = Joi.string()
export const BUILD_COMMIT = Joi.string()
.pattern(/^[0-9a-f]{40}$/)
.message('The commit hash must be a 40-character hexadecimal string.')
.description('The full git commit hash.')
.description('The full git commit hash.')
.optional()
export const BUILD_COMMIT_SHORT = Joi.string()
.pattern(/^[0-9a-f]{7}$/)
.message('The first 7 hexadecimal character from git commit hash.')
.description('A short version from the git commit hash.')
.description('A short version from the git commit hash.')
.required()
export const NODE_ENV = Joi.string()
@ -236,11 +246,15 @@ export const NODE_ENV = Joi.string()
.description('Specifies the environment in which the application is running.')
export const DEBUG = Joi.boolean()
.description('Indicates whether the application is in debugging mode. Set to true when NODE_ENV is not "production".')
.description(
'Indicates whether the application is in debugging mode. Set to true when NODE_ENV is not "production".',
)
.default(false)
.required()
export const PRODUCTION = Joi.boolean()
.default(false)
.description('Indicates whether the application is running in production mode. Set to true when NODE_ENV is "production".')
.description(
'Indicates whether the application is running in production mode. Set to true when NODE_ENV is "production".',
)
.required()

View File

@ -22,7 +22,9 @@ export function validate(schema: ObjectSchema, data: any) {
? schema.describe().keys[key].flags.description
: 'No description available'
if (data[key] === undefined) {
throw new Error(`Environment Variable '${key}' is missing. ${description}, details: ${details}`)
throw new Error(
`Environment Variable '${key}' is missing. ${description}, details: ${details}`,
)
} else {
throw new Error(
`Error on Environment Variable ${key} with value = ${value}: ${err.message}. ${description}`,
@ -30,4 +32,4 @@ export function validate(schema: ObjectSchema, data: any) {
}
})
}
}
}

View File

@ -2,6 +2,60 @@
# yarn lockfile v1
"@biomejs/biome@1.9.4":
version "1.9.4"
resolved "https://registry.yarnpkg.com/@biomejs/biome/-/biome-1.9.4.tgz#89766281cbc3a0aae865a7ff13d6aaffea2842bf"
integrity sha512-1rkd7G70+o9KkTn5KLmDYXihGoTaIGO9PIIN2ZB7UJxFrWw04CZHPYiMRjYsaDvVV7hP1dYNRLxSANLaBFGpog==
optionalDependencies:
"@biomejs/cli-darwin-arm64" "1.9.4"
"@biomejs/cli-darwin-x64" "1.9.4"
"@biomejs/cli-linux-arm64" "1.9.4"
"@biomejs/cli-linux-arm64-musl" "1.9.4"
"@biomejs/cli-linux-x64" "1.9.4"
"@biomejs/cli-linux-x64-musl" "1.9.4"
"@biomejs/cli-win32-arm64" "1.9.4"
"@biomejs/cli-win32-x64" "1.9.4"
"@biomejs/cli-darwin-arm64@1.9.4":
version "1.9.4"
resolved "https://registry.yarnpkg.com/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-1.9.4.tgz#dfa376d23a54a2d8f17133c92f23c1bf2e62509f"
integrity sha512-bFBsPWrNvkdKrNCYeAp+xo2HecOGPAy9WyNyB/jKnnedgzl4W4Hb9ZMzYNbf8dMCGmUdSavlYHiR01QaYR58cw==
"@biomejs/cli-darwin-x64@1.9.4":
version "1.9.4"
resolved "https://registry.yarnpkg.com/@biomejs/cli-darwin-x64/-/cli-darwin-x64-1.9.4.tgz#eafc2ce3849d385fc02238aad1ca4a73395a64d9"
integrity sha512-ngYBh/+bEedqkSevPVhLP4QfVPCpb+4BBe2p7Xs32dBgs7rh9nY2AIYUL6BgLw1JVXV8GlpKmb/hNiuIxfPfZg==
"@biomejs/cli-linux-arm64-musl@1.9.4":
version "1.9.4"
resolved "https://registry.yarnpkg.com/@biomejs/cli-linux-arm64-musl/-/cli-linux-arm64-musl-1.9.4.tgz#d780c3e01758fc90f3268357e3f19163d1f84fca"
integrity sha512-v665Ct9WCRjGa8+kTr0CzApU0+XXtRgwmzIf1SeKSGAv+2scAlW6JR5PMFo6FzqqZ64Po79cKODKf3/AAmECqA==
"@biomejs/cli-linux-arm64@1.9.4":
version "1.9.4"
resolved "https://registry.yarnpkg.com/@biomejs/cli-linux-arm64/-/cli-linux-arm64-1.9.4.tgz#8ed1dd0e89419a4b66a47f95aefb8c46ae6041c9"
integrity sha512-fJIW0+LYujdjUgJJuwesP4EjIBl/N/TcOX3IvIHJQNsAqvV2CHIogsmA94BPG6jZATS4Hi+xv4SkBBQSt1N4/g==
"@biomejs/cli-linux-x64-musl@1.9.4":
version "1.9.4"
resolved "https://registry.yarnpkg.com/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-1.9.4.tgz#f36982b966bd671a36671e1de4417963d7db15fb"
integrity sha512-gEhi/jSBhZ2m6wjV530Yy8+fNqG8PAinM3oV7CyO+6c3CEh16Eizm21uHVsyVBEB6RIM8JHIl6AGYCv6Q6Q9Tg==
"@biomejs/cli-linux-x64@1.9.4":
version "1.9.4"
resolved "https://registry.yarnpkg.com/@biomejs/cli-linux-x64/-/cli-linux-x64-1.9.4.tgz#a0a7f56680c76b8034ddc149dbf398bdd3a462e8"
integrity sha512-lRCJv/Vi3Vlwmbd6K+oQ0KhLHMAysN8lXoCI7XeHlxaajk06u7G+UsFSO01NAs5iYuWKmVZjmiOzJ0OJmGsMwg==
"@biomejs/cli-win32-arm64@1.9.4":
version "1.9.4"
resolved "https://registry.yarnpkg.com/@biomejs/cli-win32-arm64/-/cli-win32-arm64-1.9.4.tgz#e2ef4e0084e76b7e26f0fc887c5ef1265ea56200"
integrity sha512-tlbhLk+WXZmgwoIKwHIHEBZUwxml7bRJgk0X2sPyNR3S93cdRq6XulAZRQJ17FYGGzWne0fgrXBKpl7l4M87Hg==
"@biomejs/cli-win32-x64@1.9.4":
version "1.9.4"
resolved "https://registry.yarnpkg.com/@biomejs/cli-win32-x64/-/cli-win32-x64-1.9.4.tgz#4c7afa90e3970213599b4095e62f87e5972b2340"
integrity sha512-8Y5wMhVIPaWe6jw2H+KlEm4wP/f7EW3810ZLmDlrEEy5KvBsb9ECEfu/kMWD484ijfQ8+nIi0giMgu9g1UAuuA==
"@hapi/hoek@^9.0.0", "@hapi/hoek@^9.3.0":
version "9.3.0"
resolved "https://registry.yarnpkg.com/@hapi/hoek/-/hoek-9.3.0.tgz#8368869dcb735be2e7f5cb7647de78e167a251fb"