Merge pull request #3507 from gradido/refactor_shared_module

refactor(other): add shared module
This commit is contained in:
einhornimmond 2025-06-27 14:32:35 +02:00 committed by GitHub
commit 52942fa908
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
111 changed files with 2524 additions and 2002 deletions

View File

@ -36,6 +36,12 @@ backend: &backend
config: &config
- 'config-schema/**/*'
shared: &shared
- 'shared/**/*'
core: &core
- 'core/**/*'
database: &database
- 'database/**/*'

View File

@ -7,6 +7,8 @@ jobs:
runs-on: ubuntu-latest
outputs:
config-schema: ${{ steps.config-schema.outputs.success }}
shared: ${{ steps.shared.outputs.success }}
core: ${{ steps.core.outputs.success }}
backend: ${{ steps.backend.outputs.success }}
database: ${{ steps.database.outputs.success }}
dht-node: ${{ steps.dht-node.outputs.success }}
@ -25,6 +27,18 @@ jobs:
biome ci .
echo $?
echo "success=$([ $? -eq 0 ] && echo true || echo false)" >> $GITHUB_OUTPUT
- name: Lint - Shared
id: shared
run: |
cd ./shared
biome ci .
echo "success=$([ $? -eq 0 ] && echo true || echo false)" >> $GITHUB_OUTPUT
- name: Lint - Core
id: core
run: |
cd ./core
biome ci .
echo "success=$([ $? -eq 0 ] && echo true || echo false)" >> $GITHUB_OUTPUT
- name: Lint - Backend
id: backend
run: |
@ -58,6 +72,22 @@ jobs:
- name: Check result from previous step
run: if [ "${{ needs.lint.outputs.config-schema }}" != "true" ]; then exit 1; fi
lint_shared:
name: Lint - Shared
needs: lint
runs-on: ubuntu-latest
steps:
- name: Check result from previous step
run: if [ "${{ needs.lint.outputs.shared }}" != "true" ]; then exit 1; fi
lint_core:
name: Lint - Core
needs: lint
runs-on: ubuntu-latest
steps:
- name: Check result from previous step
run: if [ "${{ needs.lint.outputs.core }}" != "true" ]; then exit 1; fi
lint_backend:
name: Lint - Backend
needs: lint

View File

@ -35,6 +35,7 @@ jobs:
workflow
docker
other
shared
# Configure that a scope must always be provided.
requireScope: true
# Configure which scopes (newline delimited) are disallowed in PR

View File

@ -9,6 +9,8 @@ jobs:
outputs:
backend: ${{ steps.changes.outputs.backend }}
config: ${{ steps.changes.outputs.config }}
core: ${{ steps.changes.outputs.core }}
shared: ${{ steps.changes.outputs.shared }}
database: ${{ steps.changes.outputs.database }}
docker-compose: ${{ steps.changes.outputs.docker-compose }}
mariadb: ${{ steps.changes.outputs.mariadb }}
@ -24,7 +26,7 @@ jobs:
list-files: shell
build_test:
if: needs.files-changed.outputs.backend == 'true' || needs.files-changed.outputs.config == 'true' || needs.files-changed.outputs.database == 'true'
if: needs.files-changed.outputs.backend == 'true' || needs.files-changed.outputs.config == 'true' || needs.files-changed.outputs.core == 'true' || needs.files-changed.outputs.database == 'true' || needs.files-changed.outputs.shared == 'true' || needs.files-changed.outputs.docker-compose == 'true'
name: Docker Build Test - Backend
needs: files-changed
runs-on: ubuntu-latest
@ -36,7 +38,7 @@ jobs:
run: docker build -f ./backend/Dockerfile --target production -t "gradido/backend:production" .
unit_test:
if: needs.files-changed.outputs.backend == 'true' || needs.files-changed.outputs.database == 'true' || needs.files-changed.outputs.docker-compose == 'true' || needs.files-changed.outputs.mariadb == 'true' || needs.files-changed.outputs.config == 'true'
if: needs.files-changed.outputs.backend == 'true' || needs.files-changed.outputs.database == 'true' || needs.files-changed.outputs.docker-compose == 'true' || needs.files-changed.outputs.mariadb == 'true' || needs.files-changed.outputs.config == 'true' || needs.files-changed.outputs.core == 'true' || needs.files-changed.outputs.shared == 'true'
name: Unit tests - Backend
needs: files-changed
runs-on: ubuntu-latest
@ -57,14 +59,14 @@ jobs:
- name: install dependencies
run: |
bun install --filter backend --frozen-lockfile
bun install --filter backend --filter core --frozen-lockfile
bun install --global --no-save turbo@^2
- name: Backend | Unit tests
run: turbo backend#test
typecheck:
if: needs.files-changed.outputs.backend == 'true'
if: needs.files-changed.outputs.backend == 'true' || needs.files-changed.outputs.core == 'true' || needs.files-changed.outputs.shared == 'true' || needs.files-changed.outputs.database == 'true' || needs.files-changed.outputs.config == 'true'
name: Typecheck - Backend
needs: files-changed
runs-on: ubuntu-latest

43
.github/workflows/test_core.yml vendored Normal file
View File

@ -0,0 +1,43 @@
name: Gradido Core Test CI
on: push
jobs:
files-changed:
name: Detect File Changes - Core
runs-on: ubuntu-latest
outputs:
core: ${{ steps.changes.outputs.core }}
database: ${{ steps.changes.outputs.database }}
shared: ${{ steps.changes.outputs.shared }}
steps:
- uses: actions/checkout@v3.3.0
- name: Check for core file changes
uses: dorny/paths-filter@v2.11.1
id: changes
with:
token: ${{ github.token }}
filters: .github/file-filters.yml
list-files: shell
build:
name: Unit Tests, typecheck - Core
if: needs.files-changed.outputs.core == 'true' || needs.files-changed.outputs.database == 'true' || needs.files-changed.outputs.shared == 'true'
needs: files-changed
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: install bun
uses: oven-sh/setup-bun@v2
- name: install dependencies
run: |
bun install --filter core --frozen-lockfile
bun install --global turbo@^2
- name: typecheck && unit test
run: turbo core#test core#typecheck

View File

@ -8,6 +8,7 @@ jobs:
runs-on: ubuntu-latest
outputs:
database: ${{ steps.changes.outputs.database }}
shared: ${{ steps.changes.outputs.shared }}
docker-compose: ${{ steps.changes.outputs.docker-compose }}
mariadb: ${{ steps.changes.outputs.mariadb }}
steps:
@ -22,7 +23,7 @@ jobs:
list-files: shell
build:
if: needs.files-changed.outputs.database == 'true'
if: needs.files-changed.outputs.database == 'true' || needs.files-changed.outputs.docker-compose == 'true' || needs.files-changed.outputs.shared == 'true'
name: Docker Build Test - Database up
needs: files-changed
runs-on: ubuntu-latest
@ -34,7 +35,7 @@ jobs:
run: docker build --target build -t "gradido/database:build" -f database/Dockerfile .
database_migration_test:
if: needs.files-changed.outputs.database == 'true' || needs.files-changed.outputs.docker-compose == 'true' || needs.files-changed.outputs.mariadb == 'true'
if: needs.files-changed.outputs.database == 'true' || needs.files-changed.outputs.docker-compose == 'true' || needs.files-changed.outputs.mariadb == 'true' || needs.files-changed.outputs.shared == 'true'
name: Database Migration Test - Up + Reset
needs: files-changed
runs-on: ubuntu-latest
@ -58,11 +59,11 @@ jobs:
bun install --filter database --frozen-lockfile
bun install --global --no-save turbo@^2
- name: Database | up
run: turbo up
- name: Database | up + test
run: turbo database#test
- name: Database | reset
run: turbo reset
run: turbo database#reset
lint:
if: needs.files-changed.outputs.database == 'true'

42
.github/workflows/test_shared.yml vendored Normal file
View File

@ -0,0 +1,42 @@
name: Gradido Shared Test CI
on: push
jobs:
files-changed:
name: Detect File Changes - Shared
runs-on: ubuntu-latest
outputs:
shared: ${{ steps.changes.outputs.shared }}
steps:
- uses: actions/checkout@v3.3.0
- name: Check for shared file changes
uses: dorny/paths-filter@v2.11.1
id: changes
with:
token: ${{ github.token }}
filters: .github/file-filters.yml
list-files: shell
build:
name: Unit Tests, typecheck - Shared
if: needs.files-changed.outputs.shared == 'true'
needs: files-changed
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: install bun
uses: oven-sh/setup-bun@v2
- name: install dependencies
run: bun install --filter shared --frozen-lockfile
- name: typecheck
run: cd shared && yarn typecheck
- name: unit tests
run: cd shared && yarn test

2
.gitignore vendored
View File

@ -6,6 +6,8 @@
vite.config.mjs.timestamp-*
log4js-config*.json
/node_modules/*
node_modules
build
messages.pot
nbproject
.metadata

33
.vscode/launch.json vendored
View File

@ -4,6 +4,23 @@
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"type": "node",
"request": "launch",
"name": "Database Debug Tests",
"stopOnEntry": true,
"runtimeExecutable": "yarn",
"runtimeArgs": [
"run",
"test"
],
"skipFiles": [
"<node_internals>/**"
],
"console": "integratedTerminal",
"internalConsoleOptions": "neverOpen",
"cwd": "${workspaceFolder}/database"
},
{
"type": "node",
"request": "launch",
@ -86,6 +103,22 @@
"console": "integratedTerminal",
"internalConsoleOptions": "openOnSessionStart",
"cwd": "${workspaceFolder}/backend"
},
{
"type": "node",
"request": "launch",
"name": "Shared Debug Test",
"runtimeExecutable": "bun",
"runtimeArgs": [
"run",
"test:debug"
],
"skipFiles": [
"<node_internals>/**"
],
"console": "integratedTerminal",
"internalConsoleOptions": "openOnSessionStart",
"cwd": "${workspaceFolder}/shared"
}
]
}

View File

@ -57,6 +57,7 @@
"axios": "^0.21.1",
"class-validator": "^0.13.1",
"config-schema": "*",
"core": "*",
"cors": "^2.8.5",
"database": "*",
"decimal.js-light": "^2.5.1",
@ -65,7 +66,7 @@
"express": "^4.17.21",
"express-slow-down": "^2.0.1",
"faker": "^5.5.3",
"graphql": "^15.10.1",
"graphql": "15.10.1",
"graphql-parse-resolve-info": "^4.13.1",
"graphql-request": "5.0.0",
"graphql-tag": "^2.12.6",
@ -88,13 +89,14 @@
"random-bigint": "^0.0.1",
"reflect-metadata": "^0.1.13",
"regenerator-runtime": "^0.14.1",
"shared": "*",
"source-map-support": "^0.5.21",
"ts-jest": "27.0.5",
"ts-jest": "29.4.0",
"ts-node": "^10.9.2",
"tsconfig-paths": "^4.1.1",
"type-graphql": "^1.1.1",
"typed-rest-client": "^1.8.11",
"typeorm": "^0.3.22",
"typeorm": "^0.3.25",
"typescript": "^4.9.5",
"uuid": "^8.3.2",
"workerpool": "^9.2.0",

View File

@ -5,11 +5,10 @@ import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const'
// import { createGmsUser } from '@/apis/gms/GmsClient'
// import { GmsUser } from '@/apis/gms/model/GmsUser'
import { CONFIG } from '@/config'
import { getHomeCommunity } from '@/graphql/resolver/util/communities'
import { sendUserToGms } from '@/graphql/resolver/util/sendUserToGms'
import { LogError } from '@/server/LogError'
import { initLogging } from '@/server/logger'
import { AppDatabase } from 'database'
import { AppDatabase, getHomeCommunity } from 'database'
import { getLogger } from 'log4js'
const logger = getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.apis.gms.ExportUsers`)
@ -25,6 +24,9 @@ async function main() {
await con.init()
const homeCom = await getHomeCommunity()
if (!homeCom) {
throw new LogError('HomeCommunity not found')
}
if (homeCom.gmsApiKey === null) {
throw new LogError('HomeCommunity needs GMS-ApiKey to publish user data to GMS.')
}

View File

@ -2,7 +2,7 @@ import { ApolloServerTestClient } from 'apollo-server-testing'
import { FederatedCommunity as DbFederatedCommunity } from 'database'
import { GraphQLClient } from 'graphql-request'
import { Response } from 'graphql-request/dist/types'
import { DataSource } from 'typeorm'
import { DataSource, Not } from 'typeorm'
import { cleanDB, testEnvironment } from '@test/helpers'
import { getLogger } from 'config-schema/test/testSetup'
@ -203,7 +203,7 @@ describe('validate Communities', () => {
overwrite: ['end_point', 'last_announced_at'],
})
.execute()
await DbFederatedCommunity.update({}, { verifiedAt: null })
await DbFederatedCommunity.update({ id: Not(0) }, { verifiedAt: null })
// jest.clearAllMocks()
await validateCommunities()
})
@ -270,7 +270,7 @@ describe('validate Communities', () => {
})
.execute()
await DbFederatedCommunity.update({}, { verifiedAt: null })
await DbFederatedCommunity.update({ id: Not(0) }, { verifiedAt: null })
// jest.clearAllMocks()
await validateCommunities()
})
@ -322,7 +322,7 @@ describe('validate Communities', () => {
dbCom = await DbFederatedCommunity.findOneOrFail({
where: { publicKey: variables3.publicKey, apiVersion: variables3.apiVersion },
})
await DbFederatedCommunity.update({}, { verifiedAt: null })
await DbFederatedCommunity.update({ id: Not(0) }, { verifiedAt: null })
// jest.clearAllMocks()
await validateCommunities()
})

View File

@ -1,14 +1,6 @@
import { Decimal } from 'decimal.js-light'
import { Field, Int, ObjectType } from 'type-graphql'
interface DecayInterface {
balance: Decimal
decay: Decimal
roundedDecay: Decimal
start: Date | null
end: Date | null
duration: number | null
}
import { Decay as DecayInterface } from 'shared'
@ObjectType()
export class Decay {

View File

@ -9,7 +9,7 @@ import { RIGHTS } from '@/auth/RIGHTS'
import { BalanceLoggingView } from '@/logging/BalanceLogging.view'
import { DecayLoggingView } from '@/logging/DecayLogging.view'
import { Context, getUser } from '@/server/context'
import { calculateDecay } from '@/util/decay'
import { calculateDecay } from 'shared'
import { getLogger } from 'log4js'
import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const'

View File

@ -1,4 +1,4 @@
import { Community as DbCommunity, FederatedCommunity as DbFederatedCommunity } from 'database'
import { Community as DbCommunity, FederatedCommunity as DbFederatedCommunity, getHomeCommunity } from 'database'
import { Arg, Args, Authorized, Mutation, Query, Resolver } from 'type-graphql'
import { IsNull, Not } from 'typeorm'
@ -16,7 +16,6 @@ import {
getAllCommunities,
getCommunityByIdentifier,
getCommunityByUuid,
getHomeCommunity,
} from './util/communities'
@Resolver()

View File

@ -19,7 +19,6 @@ import { ContributionType } from '@enum/ContributionType'
import { TransactionTypeId } from '@enum/TransactionTypeId'
import { AdminUpdateContribution } from '@model/AdminUpdateContribution'
import { Contribution, ContributionListResult } from '@model/Contribution'
import { Decay } from '@model/Decay'
import { OpenCreation } from '@model/OpenCreation'
import { UnconfirmedContribution } from '@model/UnconfirmedContribution'
@ -44,7 +43,7 @@ import { UpdateUnconfirmedContributionContext } from '@/interactions/updateUncon
import { LogError } from '@/server/LogError'
import { Context, getClientTimezoneOffset, getUser } from '@/server/context'
import { TRANSACTIONS_LOCK } from '@/util/TRANSACTIONS_LOCK'
import { calculateDecay } from '@/util/decay'
import { calculateDecay, Decay } from 'shared'
import { fullName } from '@/util/utilities'
import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const'

View File

@ -5,7 +5,7 @@ import { Authorized, FieldResolver, Query, Resolver } from 'type-graphql'
import { CommunityStatistics, DynamicStatisticsFields } from '@model/CommunityStatistics'
import { RIGHTS } from '@/auth/RIGHTS'
import { calculateDecay } from '@/util/decay'
import { calculateDecay } from 'shared'
const db = AppDatabase.getInstance()

View File

@ -21,6 +21,7 @@ import {
Transaction as DbTransaction,
TransactionLink as DbTransactionLink,
User as DbUser,
getHomeCommunity,
} from 'database'
import { Decimal } from 'decimal.js-light'
import { Arg, Args, Authorized, Ctx, Int, Mutation, Query, Resolver } from 'type-graphql'
@ -38,7 +39,7 @@ import { LogError } from '@/server/LogError'
import { Context, getClientTimezoneOffset, getUser } from '@/server/context'
import { TRANSACTIONS_LOCK } from '@/util/TRANSACTIONS_LOCK'
import { TRANSACTION_LINK_LOCK } from '@/util/TRANSACTION_LINK_LOCK'
import { calculateDecay } from '@/util/decay'
import { calculateDecay } from 'shared'
import { fullName } from '@/util/utilities'
import { calculateBalance } from '@/util/validate'
@ -49,7 +50,6 @@ import { executeTransaction } from './TransactionResolver'
import {
getAuthenticatedCommunities,
getCommunityByUuid,
getHomeCommunity,
} from './util/communities'
import { getUserCreation, validateContribution } from './util/creations'
import { getLastTransaction } from './util/getLastTransaction'
@ -442,6 +442,9 @@ export class TransactionLinkResolver {
)
// TODO:encode/sign the jwt normally with the private key of the sender/home community, but interims with uuid
const homeCom = await getHomeCommunity()
if (!homeCom) {
throw new LogError('Home community not found')
}
if (!homeCom.communityUuid) {
throw new LogError('Home community UUID is not set')
}
@ -630,6 +633,9 @@ export class TransactionLinkResolver {
)
}
const homeCommunity = await getHomeCommunity()
if (!homeCommunity) {
throw new LogError('Home community not found')
}
const recipientCommunity = new Community(homeCommunity)
const senderCommunity = new Community(senderCom)
const senderUser = new User(null)

View File

@ -137,19 +137,11 @@ describe('send coins', () => {
}),
).toEqual(
expect.objectContaining({
errors: [new GraphQLError('No user with this credentials')],
errors: [new GraphQLError('The recipient user was not found')],
}),
)
})
it('logs the error thrown', () => {
expect(logger.error).toBeCalledWith(
'No user with this credentials',
'wrong@email.com',
homeCom.communityUuid,
)
})
describe('deleted recipient', () => {
it('throws an error', async () => {
jest.clearAllMocks()
@ -170,18 +162,10 @@ describe('send coins', () => {
}),
).toEqual(
expect.objectContaining({
errors: [new GraphQLError('No user with this credentials')],
errors: [new GraphQLError('The recipient user was not found')],
}),
)
})
it('logs the error thrown', () => {
expect(logger.error).toBeCalledWith(
'No user with this credentials',
'stephen@hawking.uk',
homeCom.communityUuid,
)
})
})
describe('recipient account not activated', () => {
@ -204,18 +188,10 @@ describe('send coins', () => {
}),
).toEqual(
expect.objectContaining({
errors: [new GraphQLError('No user with this credentials')],
errors: [new GraphQLError('The recipient user was not found')],
}),
)
})
it('logs the error thrown', () => {
expect(logger.error).toBeCalledWith(
'No user with this credentials',
'garrick@ollivander.com',
homeCom.communityUuid,
)
})
})
})

View File

@ -5,6 +5,7 @@ import {
Transaction as dbTransaction,
TransactionLink as dbTransactionLink,
User as dbUser,
findUserByIdentifier
} from 'database'
import { Decimal } from 'decimal.js-light'
import { Args, Authorized, Ctx, Mutation, Query, Resolver } from 'type-graphql'
@ -40,7 +41,6 @@ import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const'
import { BalanceResolver } from './BalanceResolver'
import { GdtResolver } from './GdtResolver'
import { getCommunityByIdentifier, getCommunityName, isHomeCommunity } from './util/communities'
import { findUserByIdentifier } from './util/findUserByIdentifier'
import { getLastTransaction } from './util/getLastTransaction'
import { getTransactionList } from './util/getTransactionList'
import {

View File

@ -2693,166 +2693,6 @@ describe('UserResolver', () => {
expect(logErrorLogger.error).toBeCalledWith('401 Unauthorized')
})
})
describe('authenticated', () => {
const uuid = uuidv4()
beforeAll(async () => {
user = await userFactory(testEnv, bibiBloxberg)
await mutate({
mutation: login,
variables: { email: 'bibi@bloxberg.de', password: 'Aa12345_' },
})
// first set alias to null, because updating alias isn't currently allowed
await User.update({ alias: 'BBB' }, { alias: () => 'NULL' })
await mutate({
mutation: updateUserInfos,
variables: {
alias: 'bibi',
},
})
})
describe('identifier is no gradido ID, no email and no alias', () => {
it('throws and logs "Unknown identifier type" error', async () => {
await expect(
query({
query: userQuery,
variables: {
identifier: 'identifier_is_no_valid_alias!',
communityIdentifier: homeCom1.communityUuid,
},
}),
).resolves.toEqual(
expect.objectContaining({
errors: [new GraphQLError('Unknown identifier type')],
}),
)
expect(logErrorLogger.error).toBeCalledWith(
'Unknown identifier type',
'identifier_is_no_valid_alias!',
)
})
})
describe('identifier is not found', () => {
it('throws and logs "No user found to given identifier" error', async () => {
await expect(
query({
query: userQuery,
variables: {
identifier: uuid,
communityIdentifier: homeCom1.communityUuid,
},
}),
).resolves.toEqual(
expect.objectContaining({
errors: [new GraphQLError('No user found to given identifier(s)')],
}),
)
expect(logErrorLogger.error).toBeCalledWith(
'No user found to given identifier(s)',
uuid,
homeCom1.communityUuid,
)
})
})
describe('identifier is found via email, but not matching community', () => {
it('returns user', async () => {
await expect(
query({
query: userQuery,
variables: {
identifier: 'bibi@bloxberg.de',
communityIdentifier: foreignCom1.communityUuid,
},
}),
).resolves.toEqual(
expect.objectContaining({
errors: [new GraphQLError('No user with this credentials')],
}),
)
expect(logErrorLogger.error).toBeCalledWith(
'No user with this credentials',
'bibi@bloxberg.de',
foreignCom1.communityUuid,
)
})
})
describe('identifier is found via email', () => {
it('returns user', async () => {
await expect(
query({
query: userQuery,
variables: {
identifier: 'bibi@bloxberg.de',
communityIdentifier: homeCom1.communityUuid,
},
}),
).resolves.toEqual(
expect.objectContaining({
data: {
user: expect.objectContaining({
firstName: 'Bibi',
lastName: 'Bloxberg',
}),
},
errors: undefined,
}),
)
})
})
describe('identifier is found via gradidoID', () => {
it('returns user', async () => {
await expect(
query({
query: userQuery,
variables: {
identifier: user.gradidoID,
communityIdentifier: homeCom1.communityUuid,
},
}),
).resolves.toEqual(
expect.objectContaining({
data: {
user: expect.objectContaining({
firstName: 'Bibi',
lastName: 'Bloxberg',
}),
},
errors: undefined,
}),
)
})
})
describe('identifier is found via alias', () => {
it('returns user', async () => {
await expect(
query({
query: userQuery,
variables: {
identifier: 'bibi',
communityIdentifier: homeCom1.communityUuid,
},
}),
).resolves.toEqual(
expect.objectContaining({
data: {
user: expect.objectContaining({
firstName: 'Bibi',
lastName: 'Bloxberg',
}),
},
errors: undefined,
}),
)
})
})
})
})
describe('check username', () => {

View File

@ -6,6 +6,8 @@ import {
UserContact as DbUserContact,
ProjectBranding,
UserLoggingView,
getHomeCommunity,
findUserByIdentifier
} from 'database'
import { GraphQLResolveInfo } from 'graphql'
import i18n from 'i18n'
@ -93,17 +95,15 @@ import { Logger, getLogger } from 'log4js'
import { FULL_CREATION_AVAILABLE } from './const/const'
import { Location2Point, Point2Location } from './util/Location2Point'
import { authenticateGmsUserPlayground } from './util/authenticateGmsUserPlayground'
import { getHomeCommunity } from './util/communities'
import { compareGmsRelevantUserSettings } from './util/compareGmsRelevantUserSettings'
import { getUserCreations } from './util/creations'
import { extractGraphQLFieldsForSelect } from './util/extractGraphQLFields'
import { findUserByIdentifier } from './util/findUserByIdentifier'
import { findUsers } from './util/findUsers'
import { getKlicktippState } from './util/getKlicktippState'
import { deleteUserRole, setUserRole } from './util/modifyUserRole'
import { sendUserToGms } from './util/sendUserToGms'
import { syncHumhub } from './util/syncHumhub'
import { validateAlias } from './util/validateAlias'
import { validateAlias } from 'core'
const LANGUAGES = ['de', 'en', 'es', 'fr', 'nl']
const DEFAULT_LANGUAGE = 'de'
@ -380,6 +380,10 @@ export class UserResolver {
)
let dbUser = new DbUser()
const homeCom = await getHomeCommunity()
if (!homeCom) {
logger.error('no home community found, please start the dht-node first')
throw new Error(`Error creating user, please write the support team: ${CONFIG.COMMUNITY_SUPPORT_MAIL}`)
}
if (homeCom.communityUuid) {
dbUser.communityUuid = homeCom.communityUuid
}
@ -820,6 +824,12 @@ export class UserResolver {
if (CONFIG.GMS_ACTIVE && updateUserInGMS) {
logger.debug(`changed user-settings relevant for gms-user update...`)
const homeCom = await getHomeCommunity()
if (!homeCom) {
logger.error('no home community found, please start the dht-node first')
throw new Error(
`Error updating user, please write the support team: ${CONFIG.COMMUNITY_SUPPORT_MAIL}`
)
}
if (homeCom.gmsApiKey !== null) {
logger.debug(`send User to Gms...`)
await sendUserToGms(user, homeCom)
@ -862,6 +872,12 @@ export class UserResolver {
let result = new GmsUserAuthenticationResult()
if (context.token) {
const homeCom = await getHomeCommunity()
if (!homeCom) {
logger.error("couldn't authenticate for gms, no home community found, please start the dht-node first")
throw new Error(
`Error authenticating for gms, please write the support team: ${CONFIG.COMMUNITY_SUPPORT_MAIL}`
)
}
if (!homeCom.gmsApiKey) {
throw new LogError('authenticateGmsUserSearch missing HomeCommunity GmsApiKey')
}
@ -885,6 +901,12 @@ export class UserResolver {
const result = new UserLocationResult()
if (context.token) {
const homeCom = await getHomeCommunity()
if (!homeCom) {
logger.error("couldn't load home community location, no home community found, please start the dht-node first")
throw new Error(
`Error loading user location, please write the support team: ${CONFIG.COMMUNITY_SUPPORT_MAIL}`
)
}
result.communityLocation = Point2Location(homeCom.location as Point)
result.userLocation = Point2Location(dbUser.location as Point)
logger.info('userLocation=', result)
@ -1130,8 +1152,11 @@ export class UserResolver {
{ identifier, communityIdentifier }: UserArgs,
): Promise<User> {
const foundDbUser = await findUserByIdentifier(identifier, communityIdentifier)
const modelUser = new User(foundDbUser)
return modelUser
if (!foundDbUser) {
createLogger().debug('User not found', identifier, communityIdentifier)
throw new Error('User not found')
}
return new User(foundDbUser)
}
// FIELD RESOLVERS

View File

@ -36,16 +36,6 @@ export async function isHomeCommunity(communityIdentifier: string): Promise<bool
}))
}
/**
* Retrieves the home community, i.e., a community that is not foreign.
* @returns A promise that resolves to the home community, or throw if no home community was found
*/
export async function getHomeCommunity(): Promise<DbCommunity> {
return await DbCommunity.findOneOrFail({
where: [{ foreign: false }],
})
}
/**
* TODO: Check if it is needed, because currently it isn't used at all
* Retrieves the URL of the community with the given identifier.

View File

@ -1,65 +0,0 @@
import { isURL } from 'class-validator'
import { Community, User as DbUser, UserContact as DbUserContact } from 'database'
import { FindOptionsWhere } from 'typeorm'
import { validate, version } from 'uuid'
import { LogError } from '@/server/LogError'
import { isEMail, isUUID4 } from '@/util/validate'
import { VALID_ALIAS_REGEX } from './validateAlias'
/**
*
* @param identifier could be gradidoID, alias or email of user
* @param communityIdentifier could be uuid or name of community
* @returns
*/
export const findUserByIdentifier = async (
identifier: string,
communityIdentifier: string,
): Promise<DbUser> => {
let user: DbUser | null
const communityWhere: FindOptionsWhere<Community> = isURL(communityIdentifier)
? { url: communityIdentifier }
: isUUID4(communityIdentifier)
? { communityUuid: communityIdentifier }
: { name: communityIdentifier }
if (validate(identifier) && version(identifier) === 4) {
user = await DbUser.findOne({
where: { gradidoID: identifier, community: communityWhere },
relations: ['emailContact', 'community'],
})
if (!user) {
throw new LogError('No user found to given identifier(s)', identifier, communityIdentifier)
}
} else if (isEMail(identifier)) {
const userContact = await DbUserContact.findOne({
where: {
email: identifier,
emailChecked: true,
user: {
community: communityWhere,
},
},
relations: { user: { community: true } },
})
if (!userContact) {
throw new LogError('No user with this credentials', identifier, communityIdentifier)
}
user = userContact.user
user.emailContact = userContact
} else if (VALID_ALIAS_REGEX.exec(identifier)) {
user = await DbUser.findOne({
where: { alias: identifier, community: communityWhere },
relations: ['emailContact', 'community'],
})
if (!user) {
throw new LogError('No user found to given identifier(s)', identifier, communityIdentifier)
}
} else {
throw new LogError('Unknown identifier type', identifier)
}
return user
}

View File

@ -1,92 +0,0 @@
import { ApolloServerTestClient } from 'apollo-server-testing'
import { Community as DbCommunity, User as DbUser } from 'database'
import { DataSource } from 'typeorm'
import { cleanDB, testEnvironment } from '@test/helpers'
import { writeHomeCommunityEntry } from '@/seeds/community'
import { userFactory } from '@/seeds/factory/user'
import { bibiBloxberg } from '@/seeds/users/bibi-bloxberg'
import { bobBaumeister } from '@/seeds/users/bob-baumeister'
import { peterLustig } from '@/seeds/users/peter-lustig'
import { findUserByIdentifier } from './findUserByIdentifier'
jest.mock('@/password/EncryptorUtils')
let con: DataSource
let testEnv: {
mutate: ApolloServerTestClient['mutate']
query: ApolloServerTestClient['query']
con: DataSource
}
beforeAll(async () => {
testEnv = await testEnvironment()
con = testEnv.con
await cleanDB()
})
afterAll(async () => {
await cleanDB()
await con.destroy()
})
describe('graphql/resolver/util/findUserByIdentifier', () => {
let homeCom: DbCommunity
let communityUuid: string
let communityName: string
let userBibi: DbUser
beforeAll(async () => {
homeCom = await writeHomeCommunityEntry()
communityUuid = homeCom.communityUuid!
communityName = homeCom.communityUuid!
userBibi = await userFactory(testEnv, bibiBloxberg)
await userFactory(testEnv, peterLustig)
await userFactory(testEnv, bobBaumeister)
})
describe('communityIdentifier is community uuid', () => {
it('userIdentifier is gradido id', async () => {
const user = await findUserByIdentifier(userBibi.gradidoID, communityUuid)
user.userRoles = []
expect(user).toMatchObject(userBibi)
})
it('userIdentifier is alias', async () => {
const user = await findUserByIdentifier(userBibi.alias, communityUuid)
user.userRoles = []
expect(user).toMatchObject(userBibi)
})
it('userIdentifier is email', async () => {
const user = await findUserByIdentifier(userBibi.emailContact.email, communityUuid)
user.userRoles = []
expect(user).toMatchObject(userBibi)
})
})
describe('communityIdentifier is community name', () => {
it('userIdentifier is gradido id', async () => {
const user = await findUserByIdentifier(userBibi.gradidoID, communityName)
user.userRoles = []
expect(user).toMatchObject(userBibi)
})
it('userIdentifier is alias', async () => {
const user = await findUserByIdentifier(userBibi.alias, communityName)
user.userRoles = []
expect(user).toMatchObject(userBibi)
})
it('userIdentifier is email', async () => {
const user = await findUserByIdentifier(userBibi.emailContact.email, communityName)
user.userRoles = []
expect(user).toMatchObject(userBibi)
})
})
})

View File

@ -1,129 +0,0 @@
import { ApolloServerTestClient } from 'apollo-server-testing'
import { User } from 'database'
import { DataSource } from 'typeorm'
import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const'
import { cleanDB, testEnvironment } from '@test/helpers'
import { i18n as localization } from '@test/testSetup'
import { getLogger } from 'config-schema/test/testSetup'
import { userFactory } from '@/seeds/factory/user'
import { bibiBloxberg } from '@/seeds/users/bibi-bloxberg'
import { validateAlias } from './validateAlias'
const logger = getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.server.LogError`)
let con: DataSource
let testEnv: {
mutate: ApolloServerTestClient['mutate']
query: ApolloServerTestClient['query']
con: DataSource
}
beforeAll(async () => {
testEnv = await testEnvironment(getLogger('apollo'), localization)
con = testEnv.con
await cleanDB()
})
afterAll(async () => {
await cleanDB()
await con.destroy()
})
describe('validate alias', () => {
beforeAll(() => {
jest.clearAllMocks()
})
describe('alias too short', () => {
it('throws and logs an error', async () => {
await expect(validateAlias('Bi')).rejects.toEqual(new Error('Given alias is too short'))
expect(logger.error).toBeCalledWith('Given alias is too short', 'Bi')
})
})
describe('alias too long', () => {
it('throws and logs an error', async () => {
await expect(validateAlias('BibiBloxbergHexHexHex')).rejects.toEqual(
new Error('Given alias is too long'),
)
expect(logger.error).toBeCalledWith('Given alias is too long', 'BibiBloxbergHexHexHex')
})
})
describe('alias contains invalid characters', () => {
it('throws and logs an error', async () => {
await expect(validateAlias('Bibi.Bloxberg')).rejects.toEqual(
new Error('Invalid characters in alias'),
)
expect(logger.error).toBeCalledWith('Invalid characters in alias', 'Bibi.Bloxberg')
})
})
describe('alias is a reserved word', () => {
it('throws and logs an error', async () => {
await expect(validateAlias('admin')).rejects.toEqual(new Error('Alias is not allowed'))
expect(logger.error).toBeCalledWith('Alias is not allowed', 'admin')
})
})
describe('alias is a reserved word with uppercase characters', () => {
it('throws and logs an error', async () => {
await expect(validateAlias('Admin')).rejects.toEqual(new Error('Alias is not allowed'))
expect(logger.error).toBeCalledWith('Alias is not allowed', 'Admin')
})
})
describe('hyphens and underscore', () => {
describe('alias starts with underscore', () => {
it('throws and logs an error', async () => {
await expect(validateAlias('_bibi')).rejects.toEqual(
new Error('Invalid characters in alias'),
)
expect(logger.error).toBeCalledWith('Invalid characters in alias', '_bibi')
})
})
describe('alias contains two following hyphens', () => {
it('throws and logs an error', async () => {
await expect(validateAlias('bi--bi')).rejects.toEqual(
new Error('Invalid characters in alias'),
)
expect(logger.error).toBeCalledWith('Invalid characters in alias', 'bi--bi')
})
})
})
describe('test against existing alias in database', () => {
beforeAll(async () => {
const bibi = await userFactory(testEnv, bibiBloxberg)
const user = await User.findOne({ where: { id: bibi.id } })
if (user) {
user.alias = 'b-b'
await user.save()
}
})
describe('alias exists in database', () => {
it('throws and logs an error', async () => {
await expect(validateAlias('b-b')).rejects.toEqual(new Error('Alias already in use'))
expect(logger.error).toBeCalledWith('Alias already in use', 'b-b')
})
})
describe('alias exists in database with in lower-case', () => {
it('throws and logs an error', async () => {
await expect(validateAlias('b-B')).rejects.toEqual(new Error('Alias already in use'))
expect(logger.error).toBeCalledWith('Alias already in use', 'b-B')
})
})
describe('valid alias', () => {
it('resolves to true', async () => {
await expect(validateAlias('bibi')).resolves.toEqual(true)
})
})
})
})

View File

@ -1,46 +0,0 @@
import { User as DbUser } from 'database'
import { Raw } from 'typeorm'
import { LogError } from '@/server/LogError'
export const VALID_ALIAS_REGEX = /^(?=.{3,20}$)[a-zA-Z0-9]+(?:[_-][a-zA-Z0-9]+?)*$/
const RESERVED_ALIAS = [
'admin',
'email',
'gast',
'gdd',
'gradido',
'guest',
'home',
'root',
'support',
'temp',
'tmp',
'tmp',
'user',
'usr',
'var',
]
export const validateAlias = async (alias: string): Promise<boolean> => {
if (alias.length < 3) {
throw new LogError('Given alias is too short', alias)
}
if (alias.length > 20) {
throw new LogError('Given alias is too long', alias)
}
if (!alias.match(VALID_ALIAS_REGEX)) {
throw new LogError('Invalid characters in alias', alias)
}
if (RESERVED_ALIAS.includes(alias.toLowerCase())) {
throw new LogError('Alias is not allowed', alias)
}
const aliasInUse = await DbUser.find({
where: { alias: Raw((a) => `LOWER(${a}) = "${alias.toLowerCase()}"`) },
})
if (aliasInUse.length !== 0) {
throw new LogError('Alias already in use', alias)
}
return true
}

View File

@ -1,9 +1,10 @@
import { AbstractLoggingView } from 'database'
import { Decay } from '@/graphql/model/Decay'
import type { Decay as DecayInterface } from 'shared'
export class DecayLoggingView extends AbstractLoggingView {
public constructor(private self: Decay) {
public constructor(private self: Decay | DecayInterface) {
super()
}

View File

@ -37,7 +37,9 @@ const context = {
export const cleanDB = async () => {
// this only works as long we do not have foreign key constraints
for (const entity of entities) {
await resetEntity(entity)
if (entity.name !== 'Migration') {
await resetEntity(entity)
}
}
}

View File

@ -4,7 +4,7 @@ import { Decay } from '@model/Decay'
import { getLastTransaction } from '@/graphql/resolver/util/getLastTransaction'
import { calculateDecay } from './decay'
import { calculateDecay } from 'shared'
export async function calculateSenderBalance(
userId: number,
@ -16,7 +16,7 @@ export async function calculateSenderBalance(
return null
}
const decay = calculateDecay(lastTransaction.balance, lastTransaction.balanceDate, time)
const decay = new Decay(calculateDecay(lastTransaction.balance, lastTransaction.balanceDate, time))
const balance = decay.balance.add(amount.toString())
return { balance, lastTransactionId: lastTransaction.id, decay }

View File

@ -1,42 +0,0 @@
import { Decimal } from 'decimal.js-light'
import { calculateDecay, decayFormula } from './decay'
describe('utils/decay', () => {
describe('decayFormula', () => {
it('has base 0.99999997802044727', () => {
const amount = new Decimal(1.0)
const seconds = 1
// TODO: toString() was required, we could not compare two decimals
expect(decayFormula(amount, seconds).toString()).toBe('0.999999978035040489732012')
})
it('has correct backward calculation', () => {
const amount = new Decimal(1.0)
const seconds = -1
expect(decayFormula(amount, seconds).toString()).toBe('1.000000021964959992727444')
})
// we get pretty close, but not exact here, skipping
it.skip('has correct forward calculation', () => {
const amount = new Decimal(1.0).div(
new Decimal('0.99999997803504048973201202316767079413460520837376'),
)
const seconds = 1
expect(decayFormula(amount, seconds).toString()).toBe('1.0')
})
})
it('has base 0.99999997802044727', () => {
const now = new Date()
now.setSeconds(1)
const oneSecondAgo = new Date(now.getTime())
oneSecondAgo.setSeconds(0)
expect(calculateDecay(new Decimal(1.0), oneSecondAgo, now).balance.toString()).toBe(
'0.999999978035040489732012',
)
})
it('returns input amount when from and to is the same', () => {
const now = new Date()
expect(calculateDecay(new Decimal(100.0), now, now).balance.toString()).toBe('100')
})
})

View File

@ -1,71 +0,0 @@
import { Decimal } from 'decimal.js-light'
import { Decay } from '@model/Decay'
import { LogError } from '@/server/LogError'
import { DECAY_START_TIME } from 'config-schema'
Decimal.set({
precision: 25,
rounding: Decimal.ROUND_HALF_UP,
})
// TODO: externalize all those definitions and functions into an external decay library
function decayFormula(value: Decimal, seconds: number): Decimal {
// TODO why do we need to convert this here to a stting to work properly?
return value.mul(
new Decimal('0.99999997803504048973201202316767079413460520837376').pow(seconds).toString(),
)
}
function calculateDecay(
amount: Decimal,
from: Date,
to: Date,
startBlock: Date = DECAY_START_TIME,
): Decay {
const fromMs = from.getTime()
const toMs = to.getTime()
const startBlockMs = startBlock.getTime()
if (toMs < fromMs) {
throw new LogError('calculateDecay: to < from, reverse decay calculation is invalid')
}
// Initialize with no decay
const decay: Decay = {
balance: amount,
decay: new Decimal(0),
roundedDecay: new Decimal(0),
start: null,
end: null,
duration: null,
}
// decay started after end date; no decay
if (startBlockMs > toMs) {
return decay
}
// decay started before start date; decay for full duration
if (startBlockMs < fromMs) {
decay.start = from
decay.duration = (toMs - fromMs) / 1000
}
// decay started between start and end date; decay from decay start till end date
else {
decay.start = startBlock
decay.duration = (toMs - startBlockMs) / 1000
}
decay.end = to
decay.balance = decayFormula(amount, decay.duration)
decay.decay = decay.balance.minus(amount)
decay.roundedDecay = amount
.toDecimalPlaces(2, Decimal.ROUND_DOWN)
.minus(decay.balance.toDecimalPlaces(2, Decimal.ROUND_DOWN).toString())
.mul(-1)
return decay
}
export { decayFormula, calculateDecay }

View File

@ -7,7 +7,7 @@ import { Decay } from '@model/Decay'
import { getLastTransaction } from '@/graphql/resolver/util/getLastTransaction'
import { transactionLinkSummary } from '@/graphql/resolver/util/transactionLinkSummary'
import { calculateDecay } from './decay'
import { calculateDecay } from 'shared'
function isStringBoolean(value: string): boolean {
const lowerValue = value.toLowerCase()
@ -36,7 +36,7 @@ async function calculateBalance(
return null
}
const decay = calculateDecay(lastTransaction.balance, lastTransaction.balanceDate, time)
const decay = new Decay(calculateDecay(lastTransaction.balance, lastTransaction.balanceDate, time))
const balance = decay.balance.add(amount.toString())
const { sumHoldAvailableAmount } = await transactionLinkSummary(userId, time)

View File

@ -6,7 +6,7 @@ import { TransactionTypeId } from '@enum/TransactionTypeId'
import { Transaction } from '@model/Transaction'
import { User } from '@model/User'
import { calculateDecay } from './decay'
import { calculateDecay } from 'shared'
const defaultModelFunctions = {
hasId: function (): boolean {

View File

@ -23,7 +23,9 @@ const context = {
export const cleanDB = async () => {
// this only works as long we do not have foreign key constraints
for (const entity of entities) {
await resetEntity(entity)
if (entity.name !== 'Migration') {
await resetEntity(entity)
}
}
}

View File

@ -2,25 +2,22 @@
"extends": ["//"],
"tasks": {
"seed": {
"dependsOn": ["database#up", "config-schema#build", "database#build"],
"dependsOn": ["database#up", "^build"],
"cache": false
},
"locales": {},
"locales:fix": {},
"lint": {
"dependsOn": ["locales", "database#build"]
"dependsOn": ["locales"]
},
"lint:fix": {
"dependsOn": ["locales:fix", "database#build"]
},
"typecheck": {
"dependsOn": ["database#build", "config-schema#build"]
"dependsOn": ["locales:fix"]
},
"test": {
"dependsOn": ["database#up:backend_test", "config-schema#build", "database#build"]
"dependsOn": ["database#up:backend_test", "^build"]
},
"dev": {
"dependsOn": ["database#up"]
"dependsOn": ["database#up", "^build"]
},
"start": {
"dependsOn": ["database#up", "build"]

254
bun.lock
View File

@ -112,16 +112,17 @@
"await-semaphore": "^0.1.3",
"axios": "^0.21.1",
"class-validator": "^0.13.1",
"config-schema": "*",
"config-schema": "workspace:*",
"core": "workspace:*",
"cors": "^2.8.5",
"database": "*",
"database": "workspace:*",
"decimal.js-light": "^2.5.1",
"dotenv": "^10.0.0",
"esbuild": "^0.25.2",
"express": "^4.17.21",
"express-slow-down": "^2.0.1",
"faker": "^5.5.3",
"graphql": "^15.10.1",
"graphql": "15.10.1",
"graphql-parse-resolve-info": "^4.13.1",
"graphql-request": "5.0.0",
"graphql-tag": "^2.12.6",
@ -144,13 +145,14 @@
"random-bigint": "^0.0.1",
"reflect-metadata": "^0.1.13",
"regenerator-runtime": "^0.14.1",
"shared": "workspace:*",
"source-map-support": "^0.5.21",
"ts-jest": "27.0.5",
"ts-jest": "29.4.0",
"ts-node": "^10.9.2",
"tsconfig-paths": "^4.1.1",
"type-graphql": "^1.1.1",
"typed-rest-client": "^1.8.11",
"typeorm": "^0.3.22",
"typeorm": "^0.3.25",
"typescript": "^4.9.5",
"uuid": "^8.3.2",
"workerpool": "^9.2.0",
@ -159,7 +161,7 @@
},
"config-schema": {
"name": "config-schema",
"version": "1.0.0",
"version": "2.6.0",
"dependencies": {
"esbuild": "^0.25.2",
"joi": "^17.13.3",
@ -175,6 +177,21 @@
"typescript": "^4.9.5",
},
},
"core": {
"name": "core",
"version": "2.6.0",
"dependencies": {
"database": "*",
"esbuild": "^0.25.2",
"log4js": "^6.9.1",
"zod": "^3.25.61",
},
"devDependencies": {
"@biomejs/biome": "2.0.0",
"@types/node": "^17.0.21",
"typescript": "^4.9.5",
},
},
"database": {
"name": "database",
"version": "2.6.0",
@ -189,19 +206,30 @@
"log4js": "^6.9.1",
"mysql2": "^2.3.0",
"reflect-metadata": "^0.1.13",
"shared": "*",
"source-map-support": "^0.5.21",
"ts-mysql-migrate": "^1.0.2",
"tsx": "^4.19.4",
"typeorm": "^0.3.22",
"tsx": "^4.20.3",
"typeorm": "^0.3.25",
"uuid": "^8.3.2",
"wkx": "^0.5.0",
},
"devDependencies": {
"@biomejs/biome": "2.0.0",
"@swc-node/register": "^1.10.10",
"@swc/cli": "^0.7.3",
"@swc/core": "^1.11.24",
"@swc/helpers": "^0.5.17",
"@types/faker": "^5.5.9",
"@types/geojson": "^7946.0.13",
"@types/node": "^17.0.21",
"@types/jest": "27.0.2",
"@types/node": "^18.7.14",
"crypto-random-bigint": "^2.1.1",
"jest": "27.2.4",
"ts-jest": "27.0.5",
"ts-node": "^10.9.2",
"typescript": "^4.9.5",
"vitest": "^3.2.4",
},
},
"dht-node": {
@ -235,8 +263,9 @@
"prettier": "^2.8.8",
"source-map-support": "^0.5.21",
"ts-jest": "27.1.4",
"tsconfig-paths": "^4.1.1",
"tsx": "^4.19.4",
"typeorm": "^0.3.22",
"typeorm": "^0.3.25",
"typescript": "^4.9.5",
"uuid": "^8.3.2",
},
@ -287,7 +316,7 @@
"ts-jest": "27.0.5",
"tsconfig-paths": "^4.1.1",
"type-graphql": "^1.1.1",
"typeorm": "^0.3.22",
"typeorm": "^0.3.25",
"typescript": "^4.9.5",
"uuid": "8.3.2",
},
@ -387,6 +416,23 @@
"webpack": "^5",
},
},
"shared": {
"name": "shared",
"version": "2.6.0",
"dependencies": {
"decimal.js-light": "^2.5.1",
"esbuild": "^0.25.2",
"log4js": "^6.9.1",
"zod": "^3.25.61",
},
"devDependencies": {
"@biomejs/biome": "2.0.0",
"@types/node": "^17.0.21",
"@types/uuid": "^10.0.0",
"typescript": "^4.9.5",
"uuid": "^8.3.2",
},
},
},
"packages": {
"@ampproject/remapping": ["@ampproject/remapping@2.3.0", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw=="],
@ -515,6 +561,12 @@
"@dual-bundle/import-meta-resolve": ["@dual-bundle/import-meta-resolve@4.1.0", "", {}, "sha512-+nxncfwHM5SgAtrVzgpzJOI1ol0PkumhVo469KCf9lUi21IGcY90G98VuHm9VRrUypmAzawAHO9bs6hqeADaVg=="],
"@emnapi/core": ["@emnapi/core@1.4.3", "", { "dependencies": { "@emnapi/wasi-threads": "1.0.2", "tslib": "^2.4.0" } }, "sha512-4m62DuCE07lw01soJwPiBGC0nAww0Q+RY70VZ+n49yDIO13yyinhbWCeNnaob0lakDtWQzSdtNWzJeOJt2ma+g=="],
"@emnapi/runtime": ["@emnapi/runtime@1.4.3", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-pBPWdu6MLKROBX05wSNKcNb++m5Er+KQ9QkB+WVM+pW2Kx9hoSrVTnu3BdkI5eBLZoKu/J6mW/B6i6bJB2ytXQ=="],
"@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.0.2", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-5n3nTJblwRi8LlXkJ9eBzu+kZR8Yxcc7ubakyQTFzPMtIhFpUBRbsnc2Dv88IZDIbCDlBiWrknhB4Lsz7mg6BA=="],
"@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.4", "", { "os": "aix", "cpu": "ppc64" }, "sha512-1VCICWypeQKhVbE9oW/sJaAmjLxhVqacdkvPLEjwlttjfwENRSClS8EjBz0KzRyFSCPDIkuXW34Je/vk7zdB7Q=="],
"@esbuild/android-arm": ["@esbuild/android-arm@0.25.4", "", { "os": "android", "cpu": "arm" }, "sha512-QNdQEps7DfFwE3hXiU4BZeOV68HHzYwGd0Nthhd3uCkkEKK7/R6MTgM0P7H7FAs5pU/DIWsviMmEGxEoxIZ+ZQ=="],
@ -731,6 +783,8 @@
"@napi-rs/nice-win32-x64-msvc": ["@napi-rs/nice-win32-x64-msvc@1.0.1", "", { "os": "win32", "cpu": "x64" }, "sha512-JlF+uDcatt3St2ntBG8H02F1mM45i5SF9W+bIKiReVE6wiy3o16oBP/yxt+RZ+N6LbCImJXJ6bXNO2kn9AXicg=="],
"@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@0.2.11", "", { "dependencies": { "@emnapi/core": "^1.4.3", "@emnapi/runtime": "^1.4.3", "@tybys/wasm-util": "^0.9.0" } }, "sha512-9DPkXtvHydrcOsopiYpUgPHpmj0HWZKMUnL2dZqpvC42lsratuBG06V5ipyno0fUek5VlFsNQ+AcFATSrJXgMA=="],
"@nodelib/fs.scandir": ["@nodelib/fs.scandir@2.1.5", "", { "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" } }, "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g=="],
"@nodelib/fs.stat": ["@nodelib/fs.stat@2.0.5", "", {}, "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A=="],
@ -741,6 +795,32 @@
"@one-ini/wasm": ["@one-ini/wasm@0.1.1", "", {}, "sha512-XuySG1E38YScSJoMlqovLru4KTUNSjgVTIjyh7qMX6aNN5HY5Ct5LhRJdxO79JtTzKfzV/bnWpz+zquYrISsvw=="],
"@oxc-resolver/binding-darwin-arm64": ["@oxc-resolver/binding-darwin-arm64@5.3.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-hXem5ZAguS7IlSiHg/LK0tEfLj4eUo+9U6DaFwwBEGd0L0VIF9LmuiHydRyOrdnnmi9iAAFMAn/wl2cUoiuruA=="],
"@oxc-resolver/binding-darwin-x64": ["@oxc-resolver/binding-darwin-x64@5.3.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-wgSwfsZkRbuYCIBLxeg1bYrtKnirAy+IJF0lwfz4z08clgdNBDbfGECJe/cd0csIZPpRcvPFe8317yf31sWhtA=="],
"@oxc-resolver/binding-freebsd-x64": ["@oxc-resolver/binding-freebsd-x64@5.3.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-kzeE2WHgcRMmWjB071RdwEV5Pwke4o0WWslCKoh8if1puvxIxfzu3o7g6P2+v77BP5qop4cri+uvLABSO0WZjg=="],
"@oxc-resolver/binding-linux-arm-gnueabihf": ["@oxc-resolver/binding-linux-arm-gnueabihf@5.3.0", "", { "os": "linux", "cpu": "arm" }, "sha512-I8np34yZP/XfIkZNDbw3rweqVgfjmHYpNX3xnJZWg+f4mgO9/UNWBwetSaqXeDZqvIch/aHak+q4HVrQhQKCqg=="],
"@oxc-resolver/binding-linux-arm64-gnu": ["@oxc-resolver/binding-linux-arm64-gnu@5.3.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-u2ndfeEUrW898eXM+qPxIN8TvTPjI90NDQBRgaxxkOfNw3xaotloeiZGz5+Yzlfxgvxr9DY9FdYkqhUhSnGhOw=="],
"@oxc-resolver/binding-linux-arm64-musl": ["@oxc-resolver/binding-linux-arm64-musl@5.3.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-TzbjmFkcnESGuVItQ2diKacX8vu5G0bH3BHmIlmY4OSRLyoAlrJFwGKAHmh6C9+Amfcjo2rx8vdm7swzmsGC6Q=="],
"@oxc-resolver/binding-linux-riscv64-gnu": ["@oxc-resolver/binding-linux-riscv64-gnu@5.3.0", "", { "os": "linux", "cpu": "none" }, "sha512-NH3pjAqh8nuN29iRuRfTY42Vn03ctoR9VE8llfoUKUfhHUjFHYOXK5VSkhjj1usG8AeuesvqrQnLptCRQVTi/Q=="],
"@oxc-resolver/binding-linux-s390x-gnu": ["@oxc-resolver/binding-linux-s390x-gnu@5.3.0", "", { "os": "linux", "cpu": "s390x" }, "sha512-tuZtkK9sJYh2MC2uhol1M/8IMTB6ZQ5jmqP2+k5XNXnOb/im94Y5uV/u2lXwVyIuKHZZHtr+0d1HrOiNahoKpw=="],
"@oxc-resolver/binding-linux-x64-gnu": ["@oxc-resolver/binding-linux-x64-gnu@5.3.0", "", { "os": "linux", "cpu": "x64" }, "sha512-VzhPYmZCtoES/ThcPdGSmMop7JlwgqtSvlgtKCW15ByV2JKyl8kHAHnPSBfpIooXb0ehFnRdxFtL9qtAEWy01g=="],
"@oxc-resolver/binding-linux-x64-musl": ["@oxc-resolver/binding-linux-x64-musl@5.3.0", "", { "os": "linux", "cpu": "x64" }, "sha512-Hi39cWzul24rGljN4Vf1lxjXzQdCrdxO5oCT7KJP4ndSlqIUODJnfnMAP1YhcnIRvNvk+5E6sZtnEmFUd/4d8Q=="],
"@oxc-resolver/binding-wasm32-wasi": ["@oxc-resolver/binding-wasm32-wasi@5.3.0", "", { "dependencies": { "@napi-rs/wasm-runtime": "^0.2.9" }, "cpu": "none" }, "sha512-ddujvHhP3chmHnSXRlkPVUeYj4/B7eLZwL4yUid+df3WCbVh6DgoT9RmllZn21AhxgKtMdekDdyVJYKFd8tl4A=="],
"@oxc-resolver/binding-win32-arm64-msvc": ["@oxc-resolver/binding-win32-arm64-msvc@5.3.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-j1YYPLvUkMVNKmIFQZZJ7q6Do4cI3htUnyxNLwDSBVhSohvPIK2VG+IdtOAlWZGa7v+phEZsHfNbXVwB0oPYFQ=="],
"@oxc-resolver/binding-win32-x64-msvc": ["@oxc-resolver/binding-win32-x64-msvc@5.3.0", "", { "os": "win32", "cpu": "x64" }, "sha512-LT9eOPPUqfZscQRd5mc08RBeDWOQf+dnOrKnanMallTGPe6g7+rcAlFTA8SWoJbcD45PV8yArFtCmSQSpzHZmg=="],
"@parcel/watcher": ["@parcel/watcher@2.5.1", "", { "dependencies": { "detect-libc": "^1.0.3", "is-glob": "^4.0.3", "micromatch": "^4.0.5", "node-addon-api": "^7.0.0" }, "optionalDependencies": { "@parcel/watcher-android-arm64": "2.5.1", "@parcel/watcher-darwin-arm64": "2.5.1", "@parcel/watcher-darwin-x64": "2.5.1", "@parcel/watcher-freebsd-x64": "2.5.1", "@parcel/watcher-linux-arm-glibc": "2.5.1", "@parcel/watcher-linux-arm-musl": "2.5.1", "@parcel/watcher-linux-arm64-glibc": "2.5.1", "@parcel/watcher-linux-arm64-musl": "2.5.1", "@parcel/watcher-linux-x64-glibc": "2.5.1", "@parcel/watcher-linux-x64-musl": "2.5.1", "@parcel/watcher-win32-arm64": "2.5.1", "@parcel/watcher-win32-ia32": "2.5.1", "@parcel/watcher-win32-x64": "2.5.1" } }, "sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg=="],
"@parcel/watcher-android-arm64": ["@parcel/watcher-android-arm64@2.5.1", "", { "os": "android", "cpu": "arm64" }, "sha512-KF8+j9nNbUN8vzOFDpRMsaKBHZ/mcjEjMToVMJOhTozkDonQFFrRcfdLWn6yWKCmJKmdVxSgHiYvTCef4/qcBA=="],
@ -859,29 +939,35 @@
"@sqltools/formatter": ["@sqltools/formatter@1.2.5", "", {}, "sha512-Uy0+khmZqUrUGm5dmMqVlnvufZRSK0FbYzVgp0UMstm+F5+W2/jnEEQyc9vo1ZR/E5ZI/B1WjjoTqBqwJL6Krw=="],
"@swc/cli": ["@swc/cli@0.7.3", "", { "dependencies": { "@swc/counter": "^0.1.3", "@xhmikosr/bin-wrapper": "^13.0.5", "commander": "^8.3.0", "fast-glob": "^3.2.5", "minimatch": "^9.0.3", "piscina": "^4.3.1", "semver": "^7.3.8", "slash": "3.0.0", "source-map": "^0.7.3" }, "peerDependencies": { "@swc/core": "^1.2.66", "chokidar": "^4.0.1" }, "optionalPeers": ["chokidar"], "bin": { "swc": "bin/swc.js", "swcx": "bin/swcx.js", "spack": "bin/spack.js" } }, "sha512-rnVXNnlURjdOuPaBIwZ3TmBA44BF/eP0j154LanlgPEYfau74ige7cpKlKkZr1IBqMOG99lAnYNxQipDWA3hdg=="],
"@swc-node/core": ["@swc-node/core@1.13.3", "", { "peerDependencies": { "@swc/core": ">= 1.4.13", "@swc/types": ">= 0.1" } }, "sha512-OGsvXIid2Go21kiNqeTIn79jcaX4l0G93X2rAnas4LFoDyA9wAwVK7xZdm+QsKoMn5Mus2yFLCc4OtX2dD/PWA=="],
"@swc/core": ["@swc/core@1.11.24", "", { "dependencies": { "@swc/counter": "^0.1.3", "@swc/types": "^0.1.21" }, "optionalDependencies": { "@swc/core-darwin-arm64": "1.11.24", "@swc/core-darwin-x64": "1.11.24", "@swc/core-linux-arm-gnueabihf": "1.11.24", "@swc/core-linux-arm64-gnu": "1.11.24", "@swc/core-linux-arm64-musl": "1.11.24", "@swc/core-linux-x64-gnu": "1.11.24", "@swc/core-linux-x64-musl": "1.11.24", "@swc/core-win32-arm64-msvc": "1.11.24", "@swc/core-win32-ia32-msvc": "1.11.24", "@swc/core-win32-x64-msvc": "1.11.24" }, "peerDependencies": { "@swc/helpers": ">=0.5.17" }, "optionalPeers": ["@swc/helpers"] }, "sha512-MaQEIpfcEMzx3VWWopbofKJvaraqmL6HbLlw2bFZ7qYqYw3rkhM0cQVEgyzbHtTWwCwPMFZSC2DUbhlZgrMfLg=="],
"@swc-node/register": ["@swc-node/register@1.10.10", "", { "dependencies": { "@swc-node/core": "^1.13.3", "@swc-node/sourcemap-support": "^0.5.1", "colorette": "^2.0.20", "debug": "^4.3.5", "oxc-resolver": "^5.0.0", "pirates": "^4.0.6", "tslib": "^2.6.3" }, "peerDependencies": { "@swc/core": ">= 1.4.13", "typescript": ">= 4.3" } }, "sha512-jYWaI2WNEKz8KZL3sExd2KVL1JMma1/J7z+9iTpv0+fRN7LGMF8VTGGuHI2bug/ztpdZU1G44FG/Kk6ElXL9CQ=="],
"@swc/core-darwin-arm64": ["@swc/core-darwin-arm64@1.11.24", "", { "os": "darwin", "cpu": "arm64" }, "sha512-dhtVj0PC1APOF4fl5qT2neGjRLgHAAYfiVP8poJelhzhB/318bO+QCFWAiimcDoyMgpCXOhTp757gnoJJrheWA=="],
"@swc-node/sourcemap-support": ["@swc-node/sourcemap-support@0.5.1", "", { "dependencies": { "source-map-support": "^0.5.21", "tslib": "^2.6.3" } }, "sha512-JxIvIo/Hrpv0JCHSyRpetAdQ6lB27oFYhv0PKCNf1g2gUXOjpeR1exrXccRxLMuAV5WAmGFBwRnNOJqN38+qtg=="],
"@swc/core-darwin-x64": ["@swc/core-darwin-x64@1.11.24", "", { "os": "darwin", "cpu": "x64" }, "sha512-H/3cPs8uxcj2Fe3SoLlofN5JG6Ny5bl8DuZ6Yc2wr7gQFBmyBkbZEz+sPVgsID7IXuz7vTP95kMm1VL74SO5AQ=="],
"@swc/cli": ["@swc/cli@0.7.7", "", { "dependencies": { "@swc/counter": "^0.1.3", "@xhmikosr/bin-wrapper": "^13.0.5", "commander": "^8.3.0", "fast-glob": "^3.2.5", "minimatch": "^9.0.3", "piscina": "^4.3.1", "semver": "^7.3.8", "slash": "3.0.0", "source-map": "^0.7.3" }, "peerDependencies": { "@swc/core": "^1.2.66", "chokidar": "^4.0.1" }, "optionalPeers": ["chokidar"], "bin": { "swc": "bin/swc.js", "swcx": "bin/swcx.js", "spack": "bin/spack.js" } }, "sha512-j4yYm9bx3pxWofaJKX1BFwj/3ngUDynN4UIQ2Xd2h0h/7Gt7zkReBTpDN7g5S13mgAYxacaTHTOUsz18097E8w=="],
"@swc/core-linux-arm-gnueabihf": ["@swc/core-linux-arm-gnueabihf@1.11.24", "", { "os": "linux", "cpu": "arm" }, "sha512-PHJgWEpCsLo/NGj+A2lXZ2mgGjsr96ULNW3+T3Bj2KTc8XtMUkE8tmY2Da20ItZOvPNC/69KroU7edyo1Flfbw=="],
"@swc/core": ["@swc/core@1.12.4", "", { "dependencies": { "@swc/counter": "^0.1.3", "@swc/types": "^0.1.23" }, "optionalDependencies": { "@swc/core-darwin-arm64": "1.12.4", "@swc/core-darwin-x64": "1.12.4", "@swc/core-linux-arm-gnueabihf": "1.12.4", "@swc/core-linux-arm64-gnu": "1.12.4", "@swc/core-linux-arm64-musl": "1.12.4", "@swc/core-linux-x64-gnu": "1.12.4", "@swc/core-linux-x64-musl": "1.12.4", "@swc/core-win32-arm64-msvc": "1.12.4", "@swc/core-win32-ia32-msvc": "1.12.4", "@swc/core-win32-x64-msvc": "1.12.4" }, "peerDependencies": { "@swc/helpers": ">=0.5.17" }, "optionalPeers": ["@swc/helpers"] }, "sha512-hn30ebV4njAn0NAUM+3a0qCF+MJgqTNSrfA/hUAbC6TVjOQy2OYGQwkUvCu/V7S2+rZxrUsTpKOnZ7qqECZV9Q=="],
"@swc/core-linux-arm64-gnu": ["@swc/core-linux-arm64-gnu@1.11.24", "", { "os": "linux", "cpu": "arm64" }, "sha512-C2FJb08+n5SD4CYWCTZx1uR88BN41ZieoHvI8A55hfVf2woT8+6ZiBzt74qW2g+ntZ535Jts5VwXAKdu41HpBg=="],
"@swc/core-darwin-arm64": ["@swc/core-darwin-arm64@1.12.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-HihKfeitjZU2ab94Zf893sxzFryLKX0TweGsNXXOLNtkSMLw50auuYfpRM0BOL9/uXXtuCWgRIF6P030SAX5xQ=="],
"@swc/core-linux-arm64-musl": ["@swc/core-linux-arm64-musl@1.11.24", "", { "os": "linux", "cpu": "arm64" }, "sha512-ypXLIdszRo0re7PNNaXN0+2lD454G8l9LPK/rbfRXnhLWDBPURxzKlLlU/YGd2zP98wPcVooMmegRSNOKfvErw=="],
"@swc/core-darwin-x64": ["@swc/core-darwin-x64@1.12.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-meYCXHyYb6RDdu2N5PNAf0EelyxPBFhRcVo4kBFLuvuNb0m6EUg///VWy8MUMXq9/s9uzGS9kJVXXdRdr/d6FA=="],
"@swc/core-linux-x64-gnu": ["@swc/core-linux-x64-gnu@1.11.24", "", { "os": "linux", "cpu": "x64" }, "sha512-IM7d+STVZD48zxcgo69L0yYptfhaaE9cMZ+9OoMxirNafhKKXwoZuufol1+alEFKc+Wbwp+aUPe/DeWC/Lh3dg=="],
"@swc/core-linux-arm-gnueabihf": ["@swc/core-linux-arm-gnueabihf@1.12.4", "", { "os": "linux", "cpu": "arm" }, "sha512-szfDbf7mE8V64of0q/LSqbk+em+T+TD3uqnH40Z7Qu/aL8vi5CHgyLjWG2SLkLLpyjgkAUF6AKrupgnBYcC2NA=="],
"@swc/core-linux-x64-musl": ["@swc/core-linux-x64-musl@1.11.24", "", { "os": "linux", "cpu": "x64" }, "sha512-DZByJaMVzSfjQKKQn3cqSeqwy6lpMaQDQQ4HPlch9FWtDx/dLcpdIhxssqZXcR2rhaQVIaRQsCqwV6orSDGAGw=="],
"@swc/core-linux-arm64-gnu": ["@swc/core-linux-arm64-gnu@1.12.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-n0IY76w+Scx8m3HIVRvLkoResuwsQgjDfAk9bxn99dq4leQO+mE0fkPl0Yw/1BIsPh+kxGfopIJH9zsZ1Z2YrA=="],
"@swc/core-win32-arm64-msvc": ["@swc/core-win32-arm64-msvc@1.11.24", "", { "os": "win32", "cpu": "arm64" }, "sha512-Q64Ytn23y9aVDKN5iryFi8mRgyHw3/kyjTjT4qFCa8AEb5sGUuSj//AUZ6c0J7hQKMHlg9do5Etvoe61V98/JQ=="],
"@swc/core-linux-arm64-musl": ["@swc/core-linux-arm64-musl@1.12.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-wE5jmFi5cEQyLy8WmCWmNwfKETrnzy2D8YNi/xpYWpLPWqPhcelpa6tswkfYlbsMmmOh7hQNoTba1QdGu0jvHQ=="],
"@swc/core-win32-ia32-msvc": ["@swc/core-win32-ia32-msvc@1.11.24", "", { "os": "win32", "cpu": "ia32" }, "sha512-9pKLIisE/Hh2vJhGIPvSoTK4uBSPxNVyXHmOrtdDot4E1FUUI74Vi8tFdlwNbaj8/vusVnb8xPXsxF1uB0VgiQ=="],
"@swc/core-linux-x64-gnu": ["@swc/core-linux-x64-gnu@1.12.4", "", { "os": "linux", "cpu": "x64" }, "sha512-6S50Xd/7ePjEwrXyHMxpKTZ+KBrgUwMA8hQPbArUOwH4S5vHBr51heL0iXbUkppn1bkSr0J0IbOove5hzn+iqQ=="],
"@swc/core-win32-x64-msvc": ["@swc/core-win32-x64-msvc@1.11.24", "", { "os": "win32", "cpu": "x64" }, "sha512-sybnXtOsdB+XvzVFlBVGgRHLqp3yRpHK7CrmpuDKszhj/QhmsaZzY/GHSeALlMtLup13M0gqbcQvsTNlAHTg3w=="],
"@swc/core-linux-x64-musl": ["@swc/core-linux-x64-musl@1.12.4", "", { "os": "linux", "cpu": "x64" }, "sha512-hbYRyaHhC13vYKuGG5BrAG5fjjWEQFfQetuFp/4QKEoXDzdnabJoixxWTQACDL3m0JW32nJ+gUzsYIPtFYkwXg=="],
"@swc/core-win32-arm64-msvc": ["@swc/core-win32-arm64-msvc@1.12.4", "", { "os": "win32", "cpu": "arm64" }, "sha512-e6EbfjPL8GA/bb1lc9Omtxjlz+1ThTsAuBsy4Q3Kpbuh6B3jclg8KzxU/6t91v23wG593mieTyR5f3Pr7X3AWw=="],
"@swc/core-win32-ia32-msvc": ["@swc/core-win32-ia32-msvc@1.12.4", "", { "os": "win32", "cpu": "ia32" }, "sha512-RG2FzmllBTUf4EksANlIvLckcBrLZEA0t13LIa6L213UZKQfEuDNHezqESgoVhJMg2S/tWauitATOCFgZNSmjg=="],
"@swc/core-win32-x64-msvc": ["@swc/core-win32-x64-msvc@1.12.4", "", { "os": "win32", "cpu": "x64" }, "sha512-oRHKnZlR83zaMeVUCmHENa4j5uNRAWbmEpjYbzRcfC45LPFNWKGWGAGERLx0u87XMUtTGqnVYxnBTHN/rzDHOw=="],
"@swc/counter": ["@swc/counter@0.1.3", "", {}, "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ=="],
@ -889,7 +975,7 @@
"@swc/jest": ["@swc/jest@0.2.38", "", { "dependencies": { "@jest/create-cache-key-function": "^29.7.0", "@swc/counter": "^0.1.3", "jsonc-parser": "^3.2.0" }, "peerDependencies": { "@swc/core": "*" } }, "sha512-HMoZgXWMqChJwffdDjvplH53g9G2ALQes3HKXDEdliB/b85OQ0CTSbxG8VSeCwiAn7cOaDVEt4mwmZvbHcS52w=="],
"@swc/types": ["@swc/types@0.1.21", "", { "dependencies": { "@swc/counter": "^0.1.3" } }, "sha512-2YEtj5HJVbKivud9N4bpPBAyZhj4S2Ipe5LkUG94alTpr7in/GU/EARgPAd3BwU+YOmFVJC2+kjqhGRi3r0ZpQ=="],
"@swc/types": ["@swc/types@0.1.23", "", { "dependencies": { "@swc/counter": "^0.1.3" } }, "sha512-u1iIVZV9Q0jxY+yM2vw/hZGDNudsN85bBpTqzAQ9rzkxW9D+e3aEM4Han+ow518gSewkXgjmEK0BD79ZcNVgPw=="],
"@szmarczak/http-timer": ["@szmarczak/http-timer@5.0.1", "", { "dependencies": { "defer-to-connect": "^2.0.1" } }, "sha512-+PmQX0PiAYPMeVYe237LJAYvOMYW1j2rH5YROyS3b4CTVJum34HfRvKvAzozHAQG0TnHNdUfY9nCeUyRAs//cw=="],
@ -905,6 +991,8 @@
"@tsconfig/node16": ["@tsconfig/node16@1.0.4", "", {}, "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA=="],
"@tybys/wasm-util": ["@tybys/wasm-util@0.9.0", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-6+7nlbMVX/PVDCwaIQ8nTOPveOcFLSt8GcXdx8hD0bt39uWxYT88uXzqTd4fTvqta7oeUJqudepapKNt2DYJFw=="],
"@types/accepts": ["@types/accepts@1.3.7", "", { "dependencies": { "@types/node": "*" } }, "sha512-Pay9fq2lM2wXPWbteBsRAGiWH2hig4ZE2asK+mm7kUzlxRTfL961rj89I6zV/E3PcIkDqyuBEcMxFT7rccugeQ=="],
"@types/babel__core": ["@types/babel__core@7.20.5", "", { "dependencies": { "@babel/parser": "^7.20.7", "@babel/types": "^7.20.7", "@types/babel__generator": "*", "@types/babel__template": "*", "@types/babel__traverse": "*" } }, "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA=="],
@ -917,6 +1005,8 @@
"@types/body-parser": ["@types/body-parser@1.19.5", "", { "dependencies": { "@types/connect": "*", "@types/node": "*" } }, "sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg=="],
"@types/chai": ["@types/chai@5.2.2", "", { "dependencies": { "@types/deep-eql": "*" } }, "sha512-8kB30R7Hwqf40JPiKhVzodJs2Qc1ZJ5zuT3uzw5Hq/dhNCl3G3l83jfpdI1e20BP348+fV7VIL/+FxaXkqBmWg=="],
"@types/connect": ["@types/connect@3.4.38", "", { "dependencies": { "@types/node": "*" } }, "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug=="],
"@types/content-disposition": ["@types/content-disposition@0.5.8", "", {}, "sha512-QVSSvno3dE0MgO76pJhmv4Qyi/j0Yk9pBp0Y7TJ2Tlj+KCgJWY6qX7nnxCOLkZ3VYRSIk1WTxCvwUSdx6CCLdg=="],
@ -925,6 +1015,8 @@
"@types/cors": ["@types/cors@2.8.10", "", {}, "sha512-C7srjHiVG3Ey1nR6d511dtDkCEjxuN9W1HWAEjGq8kpcwmNM6JJkpC0xvabM7BXTG2wDq8Eu33iH9aQKa7IvLQ=="],
"@types/deep-eql": ["@types/deep-eql@4.0.2", "", {}, "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw=="],
"@types/dotenv": ["@types/dotenv@8.2.3", "", { "dependencies": { "dotenv": "*" } }, "sha512-g2FXjlDX/cYuc5CiQvyU/6kkbP1JtmGzh0obW50zD7OKeILVL0NSpPWLXVfqoAGQjom2/SLLx9zHq0KXvD6mbw=="],
"@types/email-templates": ["@types/email-templates@10.0.4", "", { "dependencies": { "@types/html-to-text": "*", "@types/nodemailer": "*", "juice": "^8.0.0" } }, "sha512-8O2bdGPO6RYgH2DrnFAcuV++s+8KNA5e2Erjl6UxgKRVsBH9zXu2YLrLyOBRMn2VyEYmzgF+6QQUslpVhj0y/g=="],
@ -1499,6 +1591,8 @@
"copy-anything": ["copy-anything@3.0.5", "", { "dependencies": { "is-what": "^4.1.8" } }, "sha512-yCEafptTtb4bk7GLEQoM8KVJpxAfdBJYaXyzQEgQQQgYrZiDp8SJmGKlYza6CYjEDNstAdNdKA3UuoULlEbS6w=="],
"core": ["core@workspace:core"],
"core-js-pure": ["core-js-pure@3.42.0", "", {}, "sha512-007bM04u91fF4kMgwom2I5cQxAFIy8jVulgr9eozILl/SZE53QOqnW/+vviC+wQWLv+AunBG+8Q0TLoeSsSxRQ=="],
"core-util-is": ["core-util-is@1.0.3", "", {}, "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ=="],
@ -1515,6 +1609,8 @@
"cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="],
"crypto-random-bigint": ["crypto-random-bigint@2.1.1", "", { "dependencies": { "uint-rng": "^1.2.1" } }, "sha512-96+FDrenXybkpnLML/60S8NcG32KgJ5Y8yvNNCYPW02r+ssoXFR5XKenuIQcHLWumnGj8UPqUUHBzXNrDGkDmQ=="],
"css-functions-list": ["css-functions-list@3.2.3", "", {}, "sha512-IQOkD3hbR5KrN93MtcYuad6YPuTSUhntLHDuLEbFWE+ff2/XSZNdZG+LcbbIW5AXKg/WFIfYItIzVoHngHXZzA=="],
"css-select": ["css-select@4.3.0", "", { "dependencies": { "boolbase": "^1.0.0", "css-what": "^6.0.1", "domhandler": "^4.3.1", "domutils": "^2.8.0", "nth-check": "^2.0.1" } }, "sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ=="],
@ -1563,7 +1659,7 @@
"decompress-response": ["decompress-response@6.0.0", "", { "dependencies": { "mimic-response": "^3.1.0" } }, "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ=="],
"dedent": ["dedent@0.7.0", "", {}, "sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA=="],
"dedent": ["dedent@1.6.0", "", { "peerDependencies": { "babel-plugin-macros": "^3.1.0" }, "optionalPeers": ["babel-plugin-macros"] }, "sha512-F1Z+5UCFpmQUzJa11agbyPVMbpgT/qA3/SKyJ1jyBgm7dUcUEa8v9JwDkerSQXfakBwFljIxhOJqGkjUwZ9FSA=="],
"deep-eql": ["deep-eql@5.0.2", "", {}, "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q=="],
@ -2537,6 +2633,8 @@
"own-keys": ["own-keys@1.0.1", "", { "dependencies": { "get-intrinsic": "^1.2.6", "object-keys": "^1.1.1", "safe-push-apply": "^1.0.0" } }, "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg=="],
"oxc-resolver": ["oxc-resolver@5.3.0", "", { "optionalDependencies": { "@oxc-resolver/binding-darwin-arm64": "5.3.0", "@oxc-resolver/binding-darwin-x64": "5.3.0", "@oxc-resolver/binding-freebsd-x64": "5.3.0", "@oxc-resolver/binding-linux-arm-gnueabihf": "5.3.0", "@oxc-resolver/binding-linux-arm64-gnu": "5.3.0", "@oxc-resolver/binding-linux-arm64-musl": "5.3.0", "@oxc-resolver/binding-linux-riscv64-gnu": "5.3.0", "@oxc-resolver/binding-linux-s390x-gnu": "5.3.0", "@oxc-resolver/binding-linux-x64-gnu": "5.3.0", "@oxc-resolver/binding-linux-x64-musl": "5.3.0", "@oxc-resolver/binding-wasm32-wasi": "5.3.0", "@oxc-resolver/binding-win32-arm64-msvc": "5.3.0", "@oxc-resolver/binding-win32-x64-msvc": "5.3.0" } }, "sha512-FHqtZx0idP5QRPSNcI5g2ItmADg7fhR3XIeWg5eRMGfp44xqRpfkdvo+EX4ZceqV9bxvl0Z8vaqMqY0gYaNYNA=="],
"p-cancelable": ["p-cancelable@3.0.0", "", {}, "sha512-mlVgR3PGuzlo0MmTdk4cXqXWlwQDLnONTAg6sm62XkMJEiRxN3GL3SffkYvqwonbkJBcrI7Uvv5Zh9yjvn2iUw=="],
"p-event": ["p-event@4.2.0", "", { "dependencies": { "p-timeout": "^3.1.0" } }, "sha512-KXatOjCRXXkSePPb1Nbi0p0m+gQAwdlbhi4wQKJPI1HsMQS9g+Sqp2o+QHziPr7eYJyOZet836KoHEVM1mwOrQ=="],
@ -2603,7 +2701,7 @@
"picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="],
"picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="],
"picomatch": ["picomatch@4.0.2", "", {}, "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg=="],
"pify": ["pify@4.0.1", "", {}, "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g=="],
@ -2841,6 +2939,8 @@
"sha.js": ["sha.js@2.4.11", "", { "dependencies": { "inherits": "^2.0.1", "safe-buffer": "^5.0.1" }, "bin": { "sha.js": "./bin.js" } }, "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ=="],
"shared": ["shared@workspace:shared"],
"shebang-command": ["shebang-command@2.0.0", "", { "dependencies": { "shebang-regex": "^3.0.0" } }, "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA=="],
"shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="],
@ -3005,11 +3105,13 @@
"tiny-case": ["tiny-case@1.0.3", "", {}, "sha512-Eet/eeMhkO6TX8mnUteS9zgPbUMQa4I6Kkp5ORiBD5476/m+PIRiumP5tmh5ioJpH7k51Kehawy2UDfsnxxY8Q=="],
"tiny-webcrypto": ["tiny-webcrypto@1.0.3", "", {}, "sha512-LQQdNMAgz9BXNT2SKbYh3eCb+fLV0p7JB7MwUjzY6IOlQLGIadfnFqRpshERsS5Dl2OM/hs0+4I/XmSrF+RBbw=="],
"tinybench": ["tinybench@2.9.0", "", {}, "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg=="],
"tinyexec": ["tinyexec@0.3.2", "", {}, "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA=="],
"tinyglobby": ["tinyglobby@0.2.13", "", { "dependencies": { "fdir": "^6.4.4", "picomatch": "^4.0.2" } }, "sha512-mEwzpUgrLySlveBwEVDMKk5B57bhLPYovRfPAXD5gA/98Opn0rCDj3GtLwFvCvH5RK9uPCExUROW5NjDwvqkxw=="],
"tinyglobby": ["tinyglobby@0.2.14", "", { "dependencies": { "fdir": "^6.4.4", "picomatch": "^4.0.2" } }, "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ=="],
"tinypool": ["tinypool@1.0.2", "", {}, "sha512-al6n+QEANGFOMf/dmUMsuS5/r9B06uwlyNjZZql/zv8J7ybHCgoihBNORZCY2mzUuAnomQa2JdhyHKzZxPCrFA=="],
@ -3051,7 +3153,7 @@
"ts-invariant": ["ts-invariant@0.10.3", "", { "dependencies": { "tslib": "^2.1.0" } }, "sha512-uivwYcQaxAucv1CzRp2n/QdYPo4ILf9VXgH19zEIjFx2EJufV16P0JtJVpYHy89DItG6Kwj2oIUjrcK5au+4tQ=="],
"ts-jest": ["ts-jest@27.0.5", "", { "dependencies": { "bs-logger": "0.x", "fast-json-stable-stringify": "2.x", "jest-util": "^27.0.0", "json5": "2.x", "lodash": "4.x", "make-error": "1.x", "semver": "7.x", "yargs-parser": "20.x" }, "peerDependencies": { "@babel/core": ">=7.0.0-beta.0 <8", "@types/jest": "^27.0.0", "babel-jest": ">=27.0.0 <28", "jest": "^27.0.0", "typescript": ">=3.8 <5.0" }, "optionalPeers": ["@babel/core", "@types/jest", "babel-jest"], "bin": { "ts-jest": "cli.js" } }, "sha512-lIJApzfTaSSbtlksfFNHkWOzLJuuSm4faFAfo5kvzOiRAuoN4/eKxVJ2zEAho8aecE04qX6K1pAzfH5QHL1/8w=="],
"ts-jest": ["ts-jest@29.4.0", "", { "dependencies": { "bs-logger": "^0.2.6", "ejs": "^3.1.10", "fast-json-stable-stringify": "^2.1.0", "json5": "^2.2.3", "lodash.memoize": "^4.1.2", "make-error": "^1.3.6", "semver": "^7.7.2", "type-fest": "^4.41.0", "yargs-parser": "^21.1.1" }, "peerDependencies": { "@babel/core": ">=7.0.0-beta.0 <8", "@jest/transform": "^29.0.0 || ^30.0.0", "@jest/types": "^29.0.0 || ^30.0.0", "babel-jest": "^29.0.0 || ^30.0.0", "jest": "^29.0.0 || ^30.0.0", "jest-util": "^29.0.0 || ^30.0.0", "typescript": ">=4.3 <6" }, "optionalPeers": ["@babel/core", "@jest/transform", "@jest/types", "babel-jest", "jest-util"], "bin": { "ts-jest": "cli.js" } }, "sha512-d423TJMnJGu80/eSgfQ5w/R+0zFJvdtTxwtF9KzFFunOpSeD+79lHJQIiAhluJoyGRbvj9NZJsl9WjCUo0ND7Q=="],
"ts-mysql-migrate": ["ts-mysql-migrate@1.1.2", "", { "dependencies": { "@types/mysql": "^2.15.8", "mysql": "^2.18.1" }, "bin": { "generate-migration": "dist/generate-migration.js" } }, "sha512-jwhVaMrYBNNhAoZ5XISxzqbnXTHqwazqu/r1UQ6kUaGNPGL43ZFnBiXVj4Gm3pfe3xtCGIaNInehDfdDuigPgw=="],
@ -3061,7 +3163,7 @@
"tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
"tsx": ["tsx@4.19.4", "", { "dependencies": { "esbuild": "~0.25.0", "get-tsconfig": "^4.7.5" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "bin": { "tsx": "dist/cli.mjs" } }, "sha512-gK5GVzDkJK1SI1zwHf32Mqxf2tSJkNx+eYcNly5+nHvWqXUJYUkWBQtKauoESz3ymezAI++ZwT855x5p5eop+Q=="],
"tsx": ["tsx@4.20.3", "", { "dependencies": { "esbuild": "~0.25.0", "get-tsconfig": "^4.7.5" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "bin": { "tsx": "dist/cli.mjs" } }, "sha512-qjbnuR9Tr+FJOMBqJCW5ehvIo/buZq7vH7qD7JziU98h6l3qGy0a/yPFjwO+y0/T7GFpNgNAvEcPPVfyT8rrPQ=="],
"tua-body-scroll-lock": ["tua-body-scroll-lock@1.5.3", "", {}, "sha512-44W12iqek41kZuTdpEUt3JTUsMx0IxfTajXWfQyMLgzsPaMYUPZLcJkwa4P0x24h5DQ3lYvDuYvphBo4+L0t4w=="],
@ -3103,7 +3205,7 @@
"typedarray-to-buffer": ["typedarray-to-buffer@3.1.5", "", { "dependencies": { "is-typedarray": "^1.0.0" } }, "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q=="],
"typeorm": ["typeorm@0.3.22", "", { "dependencies": { "@sqltools/formatter": "^1.2.5", "ansis": "^3.17.0", "app-root-path": "^3.1.0", "buffer": "^6.0.3", "dayjs": "^1.11.13", "debug": "^4.4.0", "dotenv": "^16.4.7", "glob": "^10.4.5", "sha.js": "^2.4.11", "sql-highlight": "^6.0.0", "tslib": "^2.8.1", "uuid": "^11.1.0", "yargs": "^17.7.2" }, "peerDependencies": { "@google-cloud/spanner": "^5.18.0 || ^6.0.0 || ^7.0.0", "@sap/hana-client": "^2.12.25", "better-sqlite3": "^8.0.0 || ^9.0.0 || ^10.0.0 || ^11.0.0", "hdb-pool": "^0.1.6", "ioredis": "^5.0.4", "mongodb": "^5.8.0 || ^6.0.0", "mssql": "^9.1.1 || ^10.0.1 || ^11.0.1", "mysql2": "^2.2.5 || ^3.0.1", "oracledb": "^6.3.0", "pg": "^8.5.1", "pg-native": "^3.0.0", "pg-query-stream": "^4.0.0", "redis": "^3.1.1 || ^4.0.0", "reflect-metadata": "^0.1.14 || ^0.2.0", "sql.js": "^1.4.0", "sqlite3": "^5.0.3", "ts-node": "^10.7.0", "typeorm-aurora-data-api-driver": "^2.0.0 || ^3.0.0" }, "optionalPeers": ["@google-cloud/spanner", "@sap/hana-client", "better-sqlite3", "hdb-pool", "ioredis", "mongodb", "mssql", "mysql2", "oracledb", "pg", "pg-native", "pg-query-stream", "redis", "sql.js", "sqlite3", "ts-node", "typeorm-aurora-data-api-driver"], "bin": { "typeorm": "cli.js", "typeorm-ts-node-esm": "cli-ts-node-esm.js", "typeorm-ts-node-commonjs": "cli-ts-node-commonjs.js" } }, "sha512-P/Tsz3UpJ9+K0oryC0twK5PO27zejLYYwMsE8SISfZc1lVHX+ajigiOyWsKbuXpEFMjD9z7UjLzY3+ElVOMMDA=="],
"typeorm": ["typeorm@0.3.25", "", { "dependencies": { "@sqltools/formatter": "^1.2.5", "ansis": "^3.17.0", "app-root-path": "^3.1.0", "buffer": "^6.0.3", "dayjs": "^1.11.13", "debug": "^4.4.0", "dedent": "^1.6.0", "dotenv": "^16.4.7", "glob": "^10.4.5", "sha.js": "^2.4.11", "sql-highlight": "^6.0.0", "tslib": "^2.8.1", "uuid": "^11.1.0", "yargs": "^17.7.2" }, "peerDependencies": { "@google-cloud/spanner": "^5.18.0 || ^6.0.0 || ^7.0.0", "@sap/hana-client": "^2.12.25", "better-sqlite3": "^8.0.0 || ^9.0.0 || ^10.0.0 || ^11.0.0", "hdb-pool": "^0.1.6", "ioredis": "^5.0.4", "mongodb": "^5.8.0 || ^6.0.0", "mssql": "^9.1.1 || ^10.0.1 || ^11.0.1", "mysql2": "^2.2.5 || ^3.0.1", "oracledb": "^6.3.0", "pg": "^8.5.1", "pg-native": "^3.0.0", "pg-query-stream": "^4.0.0", "redis": "^3.1.1 || ^4.0.0", "reflect-metadata": "^0.1.14 || ^0.2.0", "sql.js": "^1.4.0", "sqlite3": "^5.0.3", "ts-node": "^10.7.0", "typeorm-aurora-data-api-driver": "^2.0.0 || ^3.0.0" }, "optionalPeers": ["@google-cloud/spanner", "@sap/hana-client", "better-sqlite3", "hdb-pool", "ioredis", "mongodb", "mssql", "mysql2", "oracledb", "pg", "pg-native", "pg-query-stream", "redis", "sql.js", "sqlite3", "ts-node", "typeorm-aurora-data-api-driver"], "bin": { "typeorm": "cli.js", "typeorm-ts-node-esm": "cli-ts-node-esm.js", "typeorm-ts-node-commonjs": "cli-ts-node-commonjs.js" } }, "sha512-fTKDFzWXKwAaBdEMU4k661seZewbNYET4r1J/z3Jwf+eAvlzMVpTLKAVcAzg75WwQk7GDmtsmkZ5MfkmXCiFWg=="],
"typescript": ["typescript@4.9.5", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g=="],
@ -3115,6 +3217,8 @@
"uglify-js": ["uglify-js@3.19.3", "", { "bin": { "uglifyjs": "bin/uglifyjs" } }, "sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ=="],
"uint-rng": ["uint-rng@1.2.1", "", { "dependencies": { "tiny-webcrypto": "^1.0.2" } }, "sha512-swhDg5H+3DX2sIvnYA7VMBMXV/t8mPxvh49CjCDkwFmj/3OZIDOQwJANBgM1MPSUBrUHNIlXmU7/GcL7m4907g=="],
"uint8array-extras": ["uint8array-extras@1.4.0", "", {}, "sha512-ZPtzy0hu4cZjv3z5NW9gfKnNLjoz4y6uv4HlelAjDK7sY/xOkKZv9xK/WQpcsBB3jEybChz9DPC2U/+cusjJVQ=="],
"unbox-primitive": ["unbox-primitive@1.1.0", "", { "dependencies": { "call-bound": "^1.0.3", "has-bigints": "^1.0.2", "has-symbols": "^1.1.0", "which-boxed-primitive": "^1.1.1" } }, "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw=="],
@ -3301,7 +3405,7 @@
"yargs": ["yargs@17.7.2", "", { "dependencies": { "cliui": "^8.0.1", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", "string-width": "^4.2.3", "y18n": "^5.0.5", "yargs-parser": "^21.1.1" } }, "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w=="],
"yargs-parser": ["yargs-parser@20.2.9", "", {}, "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w=="],
"yargs-parser": ["yargs-parser@21.1.1", "", {}, "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw=="],
"yauzl": ["yauzl@3.2.0", "", { "dependencies": { "buffer-crc32": "~0.2.3", "pend": "~1.2.0" } }, "sha512-Ow9nuGZE+qp1u4JIPvg+uCiUr7xGQWdff7JQSk5VGYTAZMDe2q8lxJ10ygv10qmSj031Ty/6FNJpLO4o1Sgc+w=="],
@ -3423,9 +3527,9 @@
"@nuxt/kit/pkg-types": ["pkg-types@2.1.0", "", { "dependencies": { "confbox": "^0.2.1", "exsolve": "^1.0.1", "pathe": "^2.0.3" } }, "sha512-wmJwA+8ihJixSoHKxZJRBQG1oY8Yr9pGLzRmSsNms0iNWyHHAlZCa7mmKiFR10YPZuz/2k169JiS/inOjBCZ2A=="],
"@parcel/watcher/detect-libc": ["detect-libc@1.0.3", "", { "bin": { "detect-libc": "./bin/detect-libc.js" } }, "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg=="],
"@nuxt/kit/tinyglobby": ["tinyglobby@0.2.13", "", { "dependencies": { "fdir": "^6.4.4", "picomatch": "^4.0.2" } }, "sha512-mEwzpUgrLySlveBwEVDMKk5B57bhLPYovRfPAXD5gA/98Opn0rCDj3GtLwFvCvH5RK9uPCExUROW5NjDwvqkxw=="],
"@rollup/pluginutils/picomatch": ["picomatch@4.0.2", "", {}, "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg=="],
"@parcel/watcher/detect-libc": ["detect-libc@1.0.3", "", { "bin": { "detect-libc": "./bin/detect-libc.js" } }, "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg=="],
"@selderee/plugin-htmlparser2/domhandler": ["domhandler@4.3.1", "", { "dependencies": { "domelementtype": "^2.2.0" } }, "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ=="],
@ -3493,6 +3597,8 @@
"ansi-escapes/type-fest": ["type-fest@0.21.3", "", {}, "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w=="],
"anymatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="],
"apollo-boost/ts-invariant": ["ts-invariant@0.4.4", "", { "dependencies": { "tslib": "^1.9.3" } }, "sha512-uEtWkFM/sdZvRNNDL3Ehu4WVpwaulhwQszV8mrtcdeE8nN00BV9mAmQ88RkrBhFgl9gMgvjJLAQcZbnPXI9mlA=="],
"apollo-boost/tslib": ["tslib@1.14.1", "", {}, "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="],
@ -3581,6 +3687,12 @@
"cssstyle/rrweb-cssom": ["rrweb-cssom@0.8.0", "", {}, "sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw=="],
"database/@types/node": ["@types/node@18.19.96", "", { "dependencies": { "undici-types": "~5.26.4" } }, "sha512-PzBvgsZ7YdFs/Kng1BSW8IGv68/SPcOxYYhT7luxD7QyzIhFS1xPTpfK3K9eHBa7hVwlW+z8nN0mOd515yaduQ=="],
"database/ts-jest": ["ts-jest@27.0.5", "", { "dependencies": { "bs-logger": "0.x", "fast-json-stable-stringify": "2.x", "jest-util": "^27.0.0", "json5": "2.x", "lodash": "4.x", "make-error": "1.x", "semver": "7.x", "yargs-parser": "20.x" }, "peerDependencies": { "@babel/core": ">=7.0.0-beta.0 <8", "@types/jest": "^27.0.0", "babel-jest": ">=27.0.0 <28", "jest": "^27.0.0", "typescript": ">=3.8 <5.0" }, "optionalPeers": ["@babel/core", "@types/jest", "babel-jest"], "bin": { "ts-jest": "cli.js" } }, "sha512-lIJApzfTaSSbtlksfFNHkWOzLJuuSm4faFAfo5kvzOiRAuoN4/eKxVJ2zEAho8aecE04qX6K1pAzfH5QHL1/8w=="],
"database/vitest": ["vitest@3.2.4", "", { "dependencies": { "@types/chai": "^5.2.2", "@vitest/expect": "3.2.4", "@vitest/mocker": "3.2.4", "@vitest/pretty-format": "^3.2.4", "@vitest/runner": "3.2.4", "@vitest/snapshot": "3.2.4", "@vitest/spy": "3.2.4", "@vitest/utils": "3.2.4", "chai": "^5.2.0", "debug": "^4.4.1", "expect-type": "^1.2.1", "magic-string": "^0.30.17", "pathe": "^2.0.3", "picomatch": "^4.0.2", "std-env": "^3.9.0", "tinybench": "^2.9.0", "tinyexec": "^0.3.2", "tinyglobby": "^0.2.14", "tinypool": "^1.1.1", "tinyrainbow": "^2.0.0", "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0", "vite-node": "3.2.4", "why-is-node-running": "^2.3.0" }, "peerDependencies": { "@edge-runtime/vm": "*", "@types/debug": "^4.1.12", "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "@vitest/browser": "3.2.4", "@vitest/ui": "3.2.4", "happy-dom": "*", "jsdom": "*" }, "optionalPeers": ["@edge-runtime/vm", "@types/debug", "@types/node", "@vitest/browser", "@vitest/ui", "happy-dom", "jsdom"], "bin": { "vitest": "vitest.mjs" } }, "sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A=="],
"decompress-response/mimic-response": ["mimic-response@3.1.0", "", {}, "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ=="],
"dht-node/@types/jest": ["@types/jest@27.5.1", "", { "dependencies": { "jest-matcher-utils": "^27.0.0", "pretty-format": "^27.0.0" } }, "sha512-fUy7YRpT+rHXto1YlL+J9rs0uLGyiqVt3ZOTQR+4ROc47yNl8WLdVLgUloBRhOxP1PZvguHl44T3H0wAWxahYQ=="],
@ -3633,6 +3745,8 @@
"federation/helmet": ["helmet@7.2.0", "", {}, "sha512-ZRiwvN089JfMXokizgqEPXsl2Guk094yExfoDXR0cBYWxtBbaSww/w+vT4WEJsBW2iTUi1GgZ6swmoug3Oy4Xw=="],
"federation/ts-jest": ["ts-jest@27.0.5", "", { "dependencies": { "bs-logger": "0.x", "fast-json-stable-stringify": "2.x", "jest-util": "^27.0.0", "json5": "2.x", "lodash": "4.x", "make-error": "1.x", "semver": "7.x", "yargs-parser": "20.x" }, "peerDependencies": { "@babel/core": ">=7.0.0-beta.0 <8", "@types/jest": "^27.0.0", "babel-jest": ">=27.0.0 <28", "jest": "^27.0.0", "typescript": ">=3.8 <5.0" }, "optionalPeers": ["@babel/core", "@types/jest", "babel-jest"], "bin": { "ts-jest": "cli.js" } }, "sha512-lIJApzfTaSSbtlksfFNHkWOzLJuuSm4faFAfo5kvzOiRAuoN4/eKxVJ2zEAho8aecE04qX6K1pAzfH5QHL1/8w=="],
"file-type/get-stream": ["get-stream@9.0.1", "", { "dependencies": { "@sec-ant/readable-stream": "^0.4.1", "is-stream": "^4.0.1" } }, "sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA=="],
"filelist/minimatch": ["minimatch@5.1.6", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g=="],
@ -3677,6 +3791,8 @@
"jest-circus/@types/node": ["@types/node@18.19.96", "", { "dependencies": { "undici-types": "~5.26.4" } }, "sha512-PzBvgsZ7YdFs/Kng1BSW8IGv68/SPcOxYYhT7luxD7QyzIhFS1xPTpfK3K9eHBa7hVwlW+z8nN0mOd515yaduQ=="],
"jest-circus/dedent": ["dedent@0.7.0", "", {}, "sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA=="],
"jest-cli/yargs": ["yargs@16.2.0", "", { "dependencies": { "cliui": "^7.0.2", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", "string-width": "^4.2.0", "y18n": "^5.0.5", "yargs-parser": "^20.2.2" } }, "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw=="],
"jest-environment-jsdom/@types/node": ["@types/node@18.19.96", "", { "dependencies": { "undici-types": "~5.26.4" } }, "sha512-PzBvgsZ7YdFs/Kng1BSW8IGv68/SPcOxYYhT7luxD7QyzIhFS1xPTpfK3K9eHBa7hVwlW+z8nN0mOd515yaduQ=="],
@ -3703,6 +3819,8 @@
"jest-util/@types/node": ["@types/node@18.19.96", "", { "dependencies": { "undici-types": "~5.26.4" } }, "sha512-PzBvgsZ7YdFs/Kng1BSW8IGv68/SPcOxYYhT7luxD7QyzIhFS1xPTpfK3K9eHBa7hVwlW+z8nN0mOd515yaduQ=="],
"jest-util/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="],
"jest-watcher/@types/node": ["@types/node@18.19.96", "", { "dependencies": { "undici-types": "~5.26.4" } }, "sha512-PzBvgsZ7YdFs/Kng1BSW8IGv68/SPcOxYYhT7luxD7QyzIhFS1xPTpfK3K9eHBa7hVwlW+z8nN0mOd515yaduQ=="],
"jest-worker/@types/node": ["@types/node@18.19.96", "", { "dependencies": { "undici-types": "~5.26.4" } }, "sha512-PzBvgsZ7YdFs/Kng1BSW8IGv68/SPcOxYYhT7luxD7QyzIhFS1xPTpfK3K9eHBa7hVwlW+z8nN0mOd515yaduQ=="],
@ -3725,6 +3843,8 @@
"mailparser/tlds": ["tlds@1.255.0", "", { "bin": { "tlds": "bin.js" } }, "sha512-tcwMRIioTcF/FcxLev8MJWxCp+GUALRhFEqbDoZrnowmKSGqPrl5pqS+Sut2m8BgJ6S4FExCSSpGffZ0Tks6Aw=="],
"micromatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="],
"mlly/pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="],
"multimatch/@types/minimatch": ["@types/minimatch@3.0.5", "", {}, "sha512-Klz949h02Gz2uZCMGwDUSDS1YBlTdDDgbWHi+81l29tQALUtvz4rAYi5uoVhE5Lagoq6DeqAUlbrHvW/mXDgdQ=="],
@ -3787,6 +3907,8 @@
"send/encodeurl": ["encodeurl@1.0.2", "", {}, "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w=="],
"shared/@types/uuid": ["@types/uuid@10.0.0", "", {}, "sha512-7gqG38EyHgyP1S+7+xomFtL+ZNHcKv6DwNaCZmJmo1vgMugyF3TCnXVg4t1uk89mLNwnLtnY3TpOpCOyp1/xHQ=="],
"simple-update-notifier/semver": ["semver@7.0.0", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A=="],
"sodium-secretstream/sodium-universal": ["sodium-universal@5.0.1", "", { "dependencies": { "sodium-native": "^5.0.1" }, "peerDependencies": { "sodium-javascript": "~0.8.0" }, "optionalPeers": ["sodium-javascript"] }, "sha512-rv+aH+tnKB5H0MAc2UadHShLMslpJsc4wjdnHRtiSIEYpOetCgu8MS4ExQRia+GL/MK3uuCyZPeEsi+J3h+Q+Q=="],
@ -3839,10 +3961,16 @@
"test-exclude/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="],
"tinyglobby/picomatch": ["picomatch@4.0.2", "", {}, "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg=="],
"ts-jest/jest": ["jest@27.5.1", "", { "dependencies": { "@jest/core": "^27.5.1", "import-local": "^3.0.2", "jest-cli": "^27.5.1" }, "peerDependencies": { "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" }, "optionalPeers": ["node-notifier"], "bin": { "jest": "bin/jest.js" } }, "sha512-Yn0mADZB89zTtjkPJEXwrac3LHudkQMR+Paqa8uxJHCBr9agxztUifWCyiYrjhMPBoUVBjyny0I7XH6ozDr7QQ=="],
"ts-jest/semver": ["semver@7.7.2", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA=="],
"ts-jest/type-fest": ["type-fest@4.41.0", "", {}, "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA=="],
"typed-rest-client/qs": ["qs@6.14.0", "", { "dependencies": { "side-channel": "^1.1.0" } }, "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w=="],
"typeorm/debug": ["debug@4.4.1", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ=="],
"typeorm/dotenv": ["dotenv@16.5.0", "", {}, "sha512-m/C+AwOAr9/W1UOIZUo232ejMNnJAJtYQjUbHoNTBNTJSvqzzDh7vnrei3o3r3m9blf6ZoDkvcw0VmozNRFJxg=="],
"typeorm/glob": ["glob@10.4.5", "", { "dependencies": { "foreground-child": "^3.1.0", "jackspeak": "^3.1.2", "minimatch": "^9.0.4", "minipass": "^7.1.2", "package-json-from-dist": "^1.0.0", "path-scurry": "^1.11.1" }, "bin": { "glob": "dist/esm/bin.mjs" } }, "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg=="],
@ -3863,16 +3991,14 @@
"unimport/pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="],
"unimport/picomatch": ["picomatch@4.0.2", "", {}, "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg=="],
"unimport/pkg-types": ["pkg-types@2.1.0", "", { "dependencies": { "confbox": "^0.2.1", "exsolve": "^1.0.1", "pathe": "^2.0.3" } }, "sha512-wmJwA+8ihJixSoHKxZJRBQG1oY8Yr9pGLzRmSsNms0iNWyHHAlZCa7mmKiFR10YPZuz/2k169JiS/inOjBCZ2A=="],
"unimport/tinyglobby": ["tinyglobby@0.2.13", "", { "dependencies": { "fdir": "^6.4.4", "picomatch": "^4.0.2" } }, "sha512-mEwzpUgrLySlveBwEVDMKk5B57bhLPYovRfPAXD5gA/98Opn0rCDj3GtLwFvCvH5RK9uPCExUROW5NjDwvqkxw=="],
"unimport/unplugin": ["unplugin@2.3.2", "", { "dependencies": { "acorn": "^8.14.1", "picomatch": "^4.0.2", "webpack-virtual-modules": "^0.6.2" } }, "sha512-3n7YA46rROb3zSj8fFxtxC/PqoyvYQ0llwz9wtUPUutr9ig09C8gGo5CWCwHrUzlqC1LLR43kxp5vEIyH1ac1w=="],
"unplugin-utils/pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="],
"unplugin-utils/picomatch": ["picomatch@4.0.2", "", {}, "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg=="],
"unplugin-vue-components/chokidar": ["chokidar@3.6.0", "", { "dependencies": { "anymatch": "~3.1.2", "braces": "~3.0.2", "glob-parent": "~5.1.2", "is-binary-path": "~2.1.0", "is-glob": "~4.0.1", "normalize-path": "~3.0.0", "readdirp": "~3.6.0" }, "optionalDependencies": { "fsevents": "~2.3.2" } }, "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw=="],
"unplugin-vue-components/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="],
@ -3909,8 +4035,6 @@
"xss/commander": ["commander@2.20.3", "", {}, "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="],
"yargs/yargs-parser": ["yargs-parser@21.1.1", "", {}, "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw=="],
"@apollographql/graphql-upload-8-fork/http-errors/depd": ["depd@1.1.2", "", {}, "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ=="],
"@apollographql/graphql-upload-8-fork/http-errors/statuses": ["statuses@1.5.0", "", {}, "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA=="],
@ -3995,6 +4119,34 @@
"css-select/domutils/dom-serializer": ["dom-serializer@1.4.1", "", { "dependencies": { "domelementtype": "^2.0.1", "domhandler": "^4.2.0", "entities": "^2.0.0" } }, "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag=="],
"database/ts-jest/yargs-parser": ["yargs-parser@20.2.9", "", {}, "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w=="],
"database/vitest/@vitest/expect": ["@vitest/expect@3.2.4", "", { "dependencies": { "@types/chai": "^5.2.2", "@vitest/spy": "3.2.4", "@vitest/utils": "3.2.4", "chai": "^5.2.0", "tinyrainbow": "^2.0.0" } }, "sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig=="],
"database/vitest/@vitest/mocker": ["@vitest/mocker@3.2.4", "", { "dependencies": { "@vitest/spy": "3.2.4", "estree-walker": "^3.0.3", "magic-string": "^0.30.17" }, "peerDependencies": { "msw": "^2.4.9", "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" }, "optionalPeers": ["msw", "vite"] }, "sha512-46ryTE9RZO/rfDd7pEqFl7etuyzekzEhUbTW3BvmeO/BcCMEgq59BKhek3dXDWgAj4oMK6OZi+vRr1wPW6qjEQ=="],
"database/vitest/@vitest/pretty-format": ["@vitest/pretty-format@3.2.4", "", { "dependencies": { "tinyrainbow": "^2.0.0" } }, "sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA=="],
"database/vitest/@vitest/runner": ["@vitest/runner@3.2.4", "", { "dependencies": { "@vitest/utils": "3.2.4", "pathe": "^2.0.3", "strip-literal": "^3.0.0" } }, "sha512-oukfKT9Mk41LreEW09vt45f8wx7DordoWUZMYdY/cyAk7w5TWkTRCNZYF7sX7n2wB7jyGAl74OxgwhPgKaqDMQ=="],
"database/vitest/@vitest/snapshot": ["@vitest/snapshot@3.2.4", "", { "dependencies": { "@vitest/pretty-format": "3.2.4", "magic-string": "^0.30.17", "pathe": "^2.0.3" } }, "sha512-dEYtS7qQP2CjU27QBC5oUOxLE/v5eLkGqPE0ZKEIDGMs4vKWe7IjgLOeauHsR0D5YuuycGRO5oSRXnwnmA78fQ=="],
"database/vitest/@vitest/spy": ["@vitest/spy@3.2.4", "", { "dependencies": { "tinyspy": "^4.0.3" } }, "sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw=="],
"database/vitest/@vitest/utils": ["@vitest/utils@3.2.4", "", { "dependencies": { "@vitest/pretty-format": "3.2.4", "loupe": "^3.1.4", "tinyrainbow": "^2.0.0" } }, "sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA=="],
"database/vitest/debug": ["debug@4.4.1", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ=="],
"database/vitest/pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="],
"database/vitest/tinypool": ["tinypool@1.1.1", "", {}, "sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg=="],
"database/vitest/tinyrainbow": ["tinyrainbow@2.0.0", "", {}, "sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw=="],
"database/vitest/vite-node": ["vite-node@3.2.4", "", { "dependencies": { "cac": "^6.7.14", "debug": "^4.4.1", "es-module-lexer": "^1.7.0", "pathe": "^2.0.3", "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" }, "bin": { "vite-node": "vite-node.mjs" } }, "sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg=="],
"dht-node/ts-jest/yargs-parser": ["yargs-parser@20.2.9", "", {}, "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w=="],
"dht-rpc/sodium-universal/sodium-native": ["sodium-native@5.0.1", "", { "dependencies": { "require-addon": "^1.1.0", "which-runtime": "^1.2.1" } }, "sha512-Q305aUXc0OzK7VVRvWkeEQJQIHs6slhFwWpyqLB5iJqhpyt2lYIVu96Y6PQ7TABIlWXVF3YiWDU3xS2Snkus+g=="],
"editorconfig/minimatch/brace-expansion": ["brace-expansion@2.0.1", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA=="],
@ -4005,6 +4157,8 @@
"express/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="],
"federation/ts-jest/yargs-parser": ["yargs-parser@20.2.9", "", {}, "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w=="],
"file-type/get-stream/is-stream": ["is-stream@4.0.1", "", {}, "sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A=="],
"filelist/minimatch/brace-expansion": ["brace-expansion@2.0.1", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA=="],
@ -4023,6 +4177,8 @@
"jest-cli/yargs/cliui": ["cliui@7.0.4", "", { "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.0", "wrap-ansi": "^7.0.0" } }, "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ=="],
"jest-cli/yargs/yargs-parser": ["yargs-parser@20.2.9", "", {}, "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w=="],
"jest-environment-jsdom/jsdom/cssstyle": ["cssstyle@2.3.0", "", { "dependencies": { "cssom": "~0.3.6" } }, "sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A=="],
"jest-environment-jsdom/jsdom/data-urls": ["data-urls@2.0.0", "", { "dependencies": { "abab": "^2.0.3", "whatwg-mimetype": "^2.3.0", "whatwg-url": "^8.0.0" } }, "sha512-X5eWTSXO/BJmpdIKCRuKUgSCgAN0OwliVK3yPKbwIWU1Tdw5BRajxlzMidvh+gwko9AfQ9zIj52pzF91Q3YAvQ=="],
@ -4059,6 +4215,8 @@
"jest-worker/jest-util/@jest/types": ["@jest/types@29.6.3", "", { "dependencies": { "@jest/schemas": "^29.6.3", "@types/istanbul-lib-coverage": "^2.0.0", "@types/istanbul-reports": "^3.0.0", "@types/node": "*", "@types/yargs": "^17.0.8", "chalk": "^4.0.0" } }, "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw=="],
"jest-worker/jest-util/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="],
"js-beautify/glob/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="],
"jsdom/parse5/entities": ["entities@6.0.0", "", {}, "sha512-aKstq2TDOndCn4diEyp9Uq/Flu2i1GlLkc6XIDQSDMuaFE3OPW5OphLCyQ5SpSJZTb4reN+kTcYru5yIfXoRPw=="],
@ -4115,8 +4273,6 @@
"typeorm/glob/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="],
"unctx/unplugin/picomatch": ["picomatch@4.0.2", "", {}, "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg=="],
"unimport/pkg-types/confbox": ["confbox@0.2.2", "", {}, "sha512-1NB+BKqhtNipMsov4xI/NnhCKp9XG9NamYp5PVm9klAT0fsrNPjaFICsCFhNhwZJKNh7zB/3q8qXz0E9oaMNtQ=="],
"unplugin-vue-components/chokidar/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="],
@ -4125,6 +4281,8 @@
"unplugin-vue-components/minimatch/brace-expansion": ["brace-expansion@2.0.1", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA=="],
"vite-plugin-html/@rollup/pluginutils/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="],
"vite/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.21.5", "", { "os": "aix", "cpu": "ppc64" }, "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ=="],
"vite/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.21.5", "", { "os": "android", "cpu": "arm" }, "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg=="],
@ -4201,6 +4359,8 @@
"cheerio-select/domutils/dom-serializer/entities": ["entities@2.2.0", "", {}, "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A=="],
"chokidar-cli/chokidar/readdirp/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="],
"chokidar-cli/yargs/cliui/strip-ansi": ["strip-ansi@5.2.0", "", { "dependencies": { "ansi-regex": "^4.1.0" } }, "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA=="],
"chokidar-cli/yargs/cliui/wrap-ansi": ["wrap-ansi@5.1.0", "", { "dependencies": { "ansi-styles": "^3.2.0", "string-width": "^3.0.0", "strip-ansi": "^5.0.0" } }, "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q=="],
@ -4217,6 +4377,12 @@
"css-select/domutils/dom-serializer/entities": ["entities@2.2.0", "", {}, "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A=="],
"database/vitest/@vitest/mocker/estree-walker": ["estree-walker@3.0.3", "", { "dependencies": { "@types/estree": "^1.0.0" } }, "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g=="],
"database/vitest/@vitest/spy/tinyspy": ["tinyspy@4.0.3", "", {}, "sha512-t2T/WLB2WRgZ9EpE4jgPJ9w+i66UZfDc8wHh0xrwiRNN+UwH98GIJkTeZqX9rg0i0ptwzqW+uYeIF0T4F8LR7A=="],
"database/vitest/@vitest/utils/loupe": ["loupe@3.1.4", "", {}, "sha512-wJzkKwJrheKtknCOKNEtDK4iqg/MxmZheEMtSTYvnzRdEYaZzmgH976nenp8WdJRdx5Vc1X/9MO0Oszl6ezeXg=="],
"editorconfig/minimatch/brace-expansion/balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="],
"filelist/minimatch/brace-expansion/balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="],
@ -4241,6 +4407,8 @@
"mailparser/html-to-text/selderee/parseley": ["parseley@0.12.1", "", { "dependencies": { "leac": "^0.6.0", "peberminta": "^0.9.0" } }, "sha512-e6qHKe3a9HWr0oMRVDTRhKce+bRO8VGQR3NyVwcjwrbhMmFCX9KszEV35+rn4AdilFAq9VPxP/Fe1wC9Qjd2lw=="],
"nodemon/chokidar/readdirp/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="],
"pkg-dir/find-up/locate-path/p-locate": ["p-locate@4.1.0", "", { "dependencies": { "p-limit": "^2.2.0" } }, "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A=="],
"run-applescript/execa/cross-spawn/path-key": ["path-key@2.0.1", "", {}, "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw=="],
@ -4257,6 +4425,8 @@
"typeorm/glob/minimatch/brace-expansion": ["brace-expansion@2.0.1", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA=="],
"unplugin-vue-components/chokidar/readdirp/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="],
"unplugin-vue-components/minimatch/brace-expansion/balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="],
"vue-apollo/chalk/ansi-styles/color-convert": ["color-convert@1.9.3", "", { "dependencies": { "color-name": "1.1.3" } }, "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg=="],

View File

@ -1,6 +1,6 @@
{
"name": "config-schema",
"version": "1.0.0",
"version": "2.6.0",
"description": "Gradido Config for validate config",
"main": "./build/index.js",
"types": "./src/index.ts",
@ -19,6 +19,7 @@
"build:bun": "bun build src/index.ts --outdir=build --target=bun --packages=external",
"typecheck": "tsc --noEmit",
"test": "bun test",
"test:debug": "bun test --inspect-brk",
"lint": "biome check --error-on-warnings .",
"lint:fix": "biome check --error-on-warnings . --write"
},

View File

@ -0,0 +1,102 @@
import { mock, jest } from 'bun:test'
import { inspect } from 'node:util'
/*
* This file is used to mock the log4js logger in the tests.
* It is used to collect all log entries in the logs array.
* If you want to debug your test, you can use `printLogs()` to print all log entries collected through the tests.
* To have only the relevant logs, call `clearLogs()` before your calling the methods you like to test and `printLogs()` after.
*
* This is the bun version
*/
jest.setTimeout(1000000)
type LogEntry = {
level: string;
message: string;
logger: string;
context: string;
additional: any[];
}
const loggers: { [key: string]: any } = {}
const logs: LogEntry[] = []
function addLog(level: string, message: string, logger: string, context: Map<string, string>, additional: any[]) {
logs.push({
level,
context: [...context.entries()].map(([key, value]) => `${key}=${value}`).join(' ').trimEnd(),
message,
logger,
additional
})
}
export function printLogs() {
for (const log of logs) {
const messages = []
messages.push(log.message)
// console.log('additionals: ', JSON.stringify(log.additional, null, 2))
messages.push(log.additional.map((d) => inspect(d)).filter((d) => d))
process.stdout.write(`${log.logger} [${log.level}] ${log.context} ${messages.join(' ')}\n`)
}
}
export function clearLogs(): void {
logs.length = 0
}
const getLoggerMocked = mock().mockImplementation((param: any) => {
if (loggers[param]) {
// TODO: check if it is working when tests run in parallel
loggers[param].clearContext()
return loggers[param]
}
// console.log('getLogger called with: ', param)
const fakeLogger = {
context: new Map<string, string>(),
addContext: jest.fn((key: string, value: string) => {
fakeLogger.context.set(key, value)
}),
trace: jest.fn((message: string, ...args: any[]) => {
addLog('trace', message, param, fakeLogger.context, args)
}),
debug: jest.fn((message: string, ...args: any[]) => {
addLog('debug', message, param, fakeLogger.context, args)
}),
warn: jest.fn((message: string, ...args: any[]) => {
addLog('warn', message, param, fakeLogger.context, args)
}),
info: jest.fn((message: string, ...args: any[]) => {
addLog('info', message, param, fakeLogger.context, args)
}),
error: jest.fn((message: string, ...args: any[]) => {
addLog('error', message, param, fakeLogger.context, args)
}),
fatal: jest.fn((message: string, ...args: any[]) => {
addLog('fatal', message, param, fakeLogger.context, args)
}),
removeContext: jest.fn((key: string) => {
fakeLogger.context.delete(key)
}),
clearContext: jest.fn(() => {
fakeLogger.context.clear()
}),
isDebugEnabled: jest.fn(() => {
return true
})
}
loggers[param] = fakeLogger
return fakeLogger
})
mock.module('log4js', () => ({
getLogger: getLoggerMocked
}))
export function getLogger(name: string) {
if (!loggers[name]) {
return getLoggerMocked(name)
}
return loggers[name]
}

View File

@ -0,0 +1,109 @@
import { vi } from 'vitest'
/*
* This file is used to mock the log4js logger in the tests.
* It is used to collect all log entries in the logs array.
* If you want to debug your test, you can use `printLogs()` to print all log entries collected through the tests.
* To have only the relevant logs, call `clearLogs()` before your calling the methods you like to test and `printLogs()` after.
*/
type LogEntry = {
level: string;
message: string;
logger: string;
context: string;
additional: any[];
}
const loggers: { [key: string]: any } = {}
const logs: LogEntry[] = []
function addLog(level: string, message: string, logger: string, context: Map<string, string>, additional: any[]) {
logs.push({
level,
context: [...context.entries()].map(([key, value]) => `${key}=${value}`).join(' ').trimEnd(),
message,
logger,
additional
})
}
export function printLogs() {
for (const log of logs) {
const messages = []
messages.push(log.message)
messages.push(log.additional.map((d) => {
if (typeof d === 'object' && d.toString() === '[object Object]') {
return JSON.stringify(d)
}
if (d) {
return d.toString()
}
}).filter((d) => d))
process.stdout.write(`${log.logger} [${log.level}] ${log.context} ${messages.join(' ')}\n`)
}
}
export function clearLogs(): void {
logs.length = 0
}
const getLoggerMocked = vi.fn().mockImplementation((param: any) => {
if (loggers[param]) {
// TODO: check if it is working when tests run in parallel
loggers[param].clearContext()
return loggers[param]
}
// console.log('getLogger called with: ', param)
const fakeLogger = {
context: new Map<string, string>(),
addContext: vi.fn((key: string, value: string) => {
fakeLogger.context.set(key, value)
}),
trace: vi.fn((message: string, ...args: any[]) => {
addLog('trace', message, param, fakeLogger.context, args)
}),
debug: vi.fn((message: string, ...args: any[]) => {
addLog('debug', message, param, fakeLogger.context, args)
}),
warn: vi.fn((message: string, ...args: any[]) => {
addLog('warn', message, param, fakeLogger.context, args)
}),
info: vi.fn((message: string, ...args: any[]) => {
addLog('info', message, param, fakeLogger.context, args)
}),
error: vi.fn((message: string, ...args: any[]) => {
addLog('error', message, param, fakeLogger.context, args)
}),
fatal: vi.fn((message: string, ...args: any[]) => {
addLog('fatal', message, param, fakeLogger.context, args)
}),
removeContext: vi.fn((key: string) => {
fakeLogger.context.delete(key)
}),
clearContext: vi.fn(() => {
fakeLogger.context.clear()
}),
isDebugEnabled: vi.fn(() => {
return true
})
}
loggers[param] = fakeLogger
return fakeLogger
})
vi.mock('log4js', () => {
const originalModule = vi.importActual('log4js')
return {
__esModule: true,
...originalModule,
getLogger: getLoggerMocked
}
})
export function getLogger(name: string) {
if (!loggers[name]) {
return getLoggerMocked(name)
}
return loggers[name]
}

10
core/README.md Normal file
View File

@ -0,0 +1,10 @@
# core
Gradido Core Code, High-Level Shared Code, with dependencies on other modules
## Bun-Compatibility
Full bun compatible
## Validation
All validation logic used across more than one module
Anything more complex than simple zod schemas is implemented here
Tests written for bun

2
core/bunfig.toml Normal file
View File

@ -0,0 +1,2 @@
[test]
preload = ["../config-schema/test/testSetup.bun.ts"]

40
core/package.json Normal file
View File

@ -0,0 +1,40 @@
{
"name": "core",
"version": "2.6.0",
"description": "Gradido Core Code, High-Level Shared Code, with dependencies on other modules",
"main": "./build/index.js",
"types": "./src/index.ts",
"exports": {
".": {
"import": "./build/index.js",
"require": "./build/index.js"
}
},
"repository": "https://github.com/gradido/gradido/core",
"author": "Gradido Academy - https://www.gradido.net",
"license": "Apache-2.0",
"private": true,
"scripts": {
"build": "esbuild src/index.ts --outdir=build --platform=node --target=node18.20.7 --bundle --packages=external",
"build:bun": "bun build src/index.ts --outdir=build --target=bun --packages=external",
"test": "bun test",
"test:debug": "bun test --inspect-brk",
"typecheck": "tsc --noEmit",
"lint": "biome check --error-on-warnings .",
"lint:fix": "biome check --error-on-warnings . --write"
},
"devDependencies": {
"@biomejs/biome": "2.0.0",
"@types/node": "^17.0.21",
"typescript": "^4.9.5"
},
"dependencies": {
"database": "*",
"esbuild": "^0.25.2",
"log4js": "^6.9.1",
"zod": "^3.25.61"
},
"engines": {
"node": ">=18"
}
}

1
core/src/config/const.ts Normal file
View File

@ -0,0 +1 @@
export const LOG4JS_BASE_CATEGORY_NAME = 'core'

1
core/src/index.ts Normal file
View File

@ -0,0 +1 @@
export * from './validation/user'

View File

@ -0,0 +1,68 @@
import { validateAlias } from './user'
import { getLogger } from '../../../config-schema/test/testSetup.bun'
import { describe, it, expect, beforeEach, mock, jest } from 'bun:test'
import { aliasExists } from 'database'
import { LOG4JS_BASE_CATEGORY_NAME } from '../config/const'
const logger = getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.validation.user`)
mock.module('database', () => ({
aliasExists: jest.fn(),
}))
mock.module('shared/src/schema/user.schema', () => ({
aliasSchema: {
parse: jest.fn(),
},
}))
describe('validate alias', () => {
beforeEach(() => {
jest.clearAllMocks()
})
describe('zod throw an validation error', () => {
it('throws and logs an error', () => {
expect(validateAlias('Bi')).rejects.toThrowError(new Error('Given alias is too short'))
expect(logger.warn.mock.calls[0]).toEqual([
'invalid alias',
'Bi',
expect.arrayContaining([
// error vor zod v4
/*expect.objectContaining({
code: 'too_small',
minimum: 3,
origin: 'string',
message: 'Given alias is too short',
}), */
expect.objectContaining({
code: 'too_small',
exact: false,
inclusive: true,
minimum: 3,
type: 'string',
message: 'Given alias is too short',
path: [],
}),
]),
])
})
})
describe('test against existing alias in database', () => {
describe('alias exists in database', () => {
it('throws and logs an error', () => {
(aliasExists as jest.Mock).mockResolvedValue(true)
expect(validateAlias('b-b')).rejects.toEqual(new Error('Given alias is already in use'))
expect(logger.warn.mock.calls[0]).toEqual(['alias already in use', 'b-b'])
})
})
describe('valid alias', () => {
it('resolves to true', async () => {
(aliasExists as jest.Mock).mockResolvedValue(false)
expect(validateAlias('bibi')).resolves.toEqual(true)
})
})
})
})

View File

@ -0,0 +1,27 @@
import { ZodError } from 'zod'
import { getLogger } from 'log4js'
import { LOG4JS_BASE_CATEGORY_NAME } from '../config/const'
import { aliasExists } from 'database'
import { aliasSchema } from 'shared'
const logger = getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.validation.user`)
export async function validateAlias(alias: string): Promise<true> {
try {
aliasSchema.parse(alias)
} catch (err) {
if (err instanceof ZodError || (err as Error).name === 'ZodError') {
// throw only first error, but log all errors
logger.warn('invalid alias', alias, (err as ZodError).errors)
throw new Error((err as ZodError).errors[0].message)
}
throw err
}
if (await aliasExists(alias)) {
logger.warn('alias already in use', alias)
throw new Error('Given alias is already in use')
}
return true
}

73
core/tsconfig.json Normal file
View File

@ -0,0 +1,73 @@
{
"compilerOptions": {
/* Visit https://aka.ms/tsconfig.json to read more about this file */
/* Basic Options */
// "incremental": true, /* Enable incremental compilation */
"target": "es6", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', 'ES2021', or 'ESNEXT'. */
"module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */
// "lib": [], /* Specify library files to be included in the compilation. */
// "allowJs": true, /* Allow javascript files to be compiled. */
// "checkJs": true, /* Report errors in .js files. */
// "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', 'react', 'react-jsx' or 'react-jsxdev'. */
"declaration": true, /* Generates corresponding '.d.ts' file. */
"declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */
// "sourceMap": true, /* Generates corresponding '.map' file. */
// "outFile": "./build/outfile.js", /* Concatenate and emit output to single file. */
"outDir": "./build", /* Redirect output structure to the directory. */
// "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
"composite": true, /* Enable project compilation */
// "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */
// "removeComments": true, /* Do not emit comments to output. */
// "noEmit": true, /* Do not emit outputs. */
// "importHelpers": true, /* Import emit helpers from 'tslib'. */
// "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */
// "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */
/* Strict Type-Checking Options */
"strict": true, /* Enable all strict type-checking options. */
// "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */
// "strictNullChecks": true, /* Enable strict null checks. */
// "strictFunctionTypes": true, /* Enable strict checking of function types. */
// "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */
"strictPropertyInitialization": false, /* Enable strict checking of property initialization in classes. */
// "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */
// "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */
/* Additional Checks */
// "noUnusedLocals": true, /* Report errors on unused locals. */
// "noUnusedParameters": true, /* Report errors on unused parameters. */
// "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
// "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
// "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */
// "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an 'override' modifier. */
// "noPropertyAccessFromIndexSignature": true, /* Require undeclared properties from index signatures to use element accesses. */
/* Module Resolution Options */
// "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
// "baseUrl": ".", /* Base directory to resolve non-absolute module names. */
// "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
// "rootDirs": [".", "../database"], /* List of root folders whose combined content represents the structure of the project at runtime. */
// "typeRoots": [], /* List of folders to include type definitions from. */
// "types": ["bun-types"], /* Type declaration files to be included in compilation. */
// "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
"esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
// "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
/* Source Map Options */
// "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */
// "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
// "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */
// "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */
/* Experimental Options */
"experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */
"emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */
/* Advanced Options */
"skipLibCheck": true, /* Skip type checking of declaration files. */
"forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */
},
"references": [], /* Any project that is referenced must itself have a `references` array (which may be empty). */
"exclude": ["**/*.test.ts", "**/*.spec.ts", "test/*", "**/bun.d.ts"],
}

View File

@ -55,8 +55,8 @@ ENV PATH="/root/.bun/bin:${PATH}"
##################################################################################
FROM bun-base as installer
COPY --chown=app:app ./database .
RUN bun install --production --no-cache --frozen-lockfile
COPY --chown=app:app . .
RUN bun install --filter database --production --no-cache --frozen-lockfile
##################################################################################
# Build ##########################################################################
@ -64,7 +64,8 @@ RUN bun install --production --no-cache --frozen-lockfile
FROM installer as build
RUN bun install --no-cache --frozen-lockfile \
yarn build && yarn typecheck
&& cd shared && yarn build \
&& cd ../database && yarn build && yarn typecheck
##################################################################################
# PRODUCTION IMAGE ###############################################################
@ -72,8 +73,7 @@ RUN bun install --no-cache --frozen-lockfile \
FROM base as production
COPY --chown=app:app --from=installer ${DOCKER_WORKDIR}/src ./src
COPY --chown=app:app --from=installer ${DOCKER_WORKDIR}/migrations ./migrations
COPY --chown=app:app --from=installer ${DOCKER_WORKDIR}/entity ./entity
COPY --chown=app:app --from=installer ${DOCKER_WORKDIR}/migration ./migration
COPY --chown=app:app --from=installer ${DOCKER_WORKDIR}/node_modules ./node_modules
COPY --chown=app:app --from=installer ${DOCKER_WORKDIR}/package.json ./package.json
COPY --chown=app:app --from=installer ${DOCKER_WORKDIR}/tsconfig.json ./tsconfig.json
@ -83,7 +83,6 @@ COPY --chown=app:app --from=installer ${DOCKER_WORKDIR}/tsconfig.json ./tsconfig
##################################################################################
FROM production as up
# Run command
CMD /bin/sh -c "yarn up"

View File

@ -49,3 +49,5 @@ yarn clear
```
## Tests
Currently written for vitest, but can be transformed into bun test after switching out TypeORM with DrizzleORM

View File

@ -13,26 +13,39 @@
"repository": "https://github.com/gradido/gradido/database",
"author": "Gradido Academy - https://www.gradido.net",
"license": "Apache-2.0",
"private": false,
"private": true,
"scripts": {
"build": "tsx ./esbuild.config.ts",
"typecheck": "tsc --noEmit",
"lint": "biome check --error-on-warnings .",
"lint:fix": "biome check --error-on-warnings . --write",
"clear": "cross-env TZ=UTC tsx migration/index.ts clear",
"test": "cross-env TZ=UTC NODE_ENV=development DB_DATABASE=gradido_test vitest --reporter verbose --no-file-parallelism run",
"test:debug": "cross-env TZ=UTC NODE_ENV=development DB_DATABASE=gradido_test node --inspect-brk node_modules/.bin/jest --bail --runInBand --forceExit --detectOpenHandles",
"up": "cross-env TZ=UTC tsx migration/index.ts up",
"down": "cross-env TZ=UTC tsx migration/index.ts down",
"reset": "cross-env TZ=UTC tsx migration/index.ts reset",
"up:test": "cross-env TZ=UTC DB_DATABASE=gradido_test tsx migration/index.ts up",
"up:backend_test": "cross-env TZ=UTC DB_DATABASE=gradido_test_backend tsx migration/index.ts up",
"up:federation_test": "cross-env TZ=UTC DB_DATABASE=gradido_test_federation tsx migration/index.ts up",
"up:dht_test": "cross-env TZ=UTC DB_DATABASE=gradido_test_dht tsx migration/index.ts up"
},
"devDependencies": {
"@biomejs/biome": "2.0.0",
"@swc-node/register": "^1.10.10",
"@swc/cli": "^0.7.3",
"@swc/core": "^1.11.24",
"@swc/helpers": "^0.5.17",
"@types/faker": "^5.5.9",
"@types/geojson": "^7946.0.13",
"@types/node": "^17.0.21",
"typescript": "^4.9.5"
"@types/jest": "27.0.2",
"@types/node": "^18.7.14",
"crypto-random-bigint": "^2.1.1",
"jest": "27.2.4",
"ts-jest": "27.0.5",
"ts-node": "^10.9.2",
"typescript": "^4.9.5",
"vitest": "^3.2.4"
},
"dependencies": {
"@types/uuid": "^8.3.4",
@ -45,10 +58,11 @@
"log4js": "^6.9.1",
"mysql2": "^2.3.0",
"reflect-metadata": "^0.1.13",
"shared": "*",
"source-map-support": "^0.5.21",
"ts-mysql-migrate": "^1.0.2",
"tsx": "^4.19.4",
"typeorm": "^0.3.22",
"tsx": "^4.20.3",
"typeorm": "^0.3.25",
"uuid": "^8.3.2",
"wkx": "^0.5.0"
},

View File

@ -93,6 +93,7 @@ export class AppDatabase {
public async destroy(): Promise<void> {
await this.dataSource?.destroy()
}
// ######################################
// private methods
// ######################################

View File

@ -2,6 +2,10 @@ import dotenv from 'dotenv'
dotenv.config()
const defaults = {
DEFAULT_LANGUAGE: process.env.DEFAULT_LANGUAGE ?? 'en',
}
const database = {
DB_CONNECT_RETRY_COUNT: process.env.DB_CONNECT_RETRY_COUNT
? Number.parseInt(process.env.DB_CONNECT_RETRY_COUNT)
@ -21,4 +25,4 @@ const database = {
const PRODUCTION = process.env.NODE_ENV === 'production' || false
const nodeEnv = process.env.NODE_ENV || 'development'
export const CONFIG = { ...database, NODE_ENV: nodeEnv, PRODUCTION }
export const CONFIG = { ...database, NODE_ENV: nodeEnv, PRODUCTION, ...defaults }

View File

@ -59,4 +59,5 @@ export const entities = [
export { latestDbVersion }
export * from './logging'
export * from './queries'
export { AppDatabase } from './AppDatabase'

View File

@ -0,0 +1,40 @@
import { Community as DbCommunity } from '..'
import { AppDatabase } from '../AppDatabase'
import { getHomeCommunity } from './communities'
import { describe, expect, it, beforeAll, afterAll } from 'vitest'
import { createCommunity } from '../seeds/homeCommunity'
const db = AppDatabase.getInstance()
beforeAll(async () => {
await db.init()
})
afterAll(async () => {
await db.destroy()
})
describe('community.queries', () => {
beforeAll(async () => {
await DbCommunity.clear()
})
describe('getHomeCommunity', () => {
it('should return null if no home community exists', async () => {
await createCommunity(true)
expect(await getHomeCommunity()).toBeNull()
})
it('should return the home community', async () => {
const homeCom = await createCommunity(false)
const community = await getHomeCommunity()
expect(community).toBeDefined()
expect(community?.name).toBe(homeCom.name)
expect(community?.description).toBe(homeCom.description)
expect(community?.url).toBe(homeCom.url)
expect(community?.creationDate).toStrictEqual(homeCom.creationDate)
expect(community?.communityUuid).toBe(homeCom.communityUuid)
expect(community?.authenticatedAt).toStrictEqual(homeCom.authenticatedAt)
expect(community?.foreign).toBe(homeCom.foreign)
expect(community?.publicKey).toStrictEqual(homeCom.publicKey)
expect(community?.privateKey).toStrictEqual(homeCom.privateKey)
})
})
})

View File

@ -0,0 +1,11 @@
import { Community as DbCommunity } from '../entity/Community'
/**
* Retrieves the home community, i.e., a community that is not foreign.
* @returns A promise that resolves to the home community, or null if no home community was found
*/
export async function getHomeCommunity(): Promise<DbCommunity | null> {
return await DbCommunity.findOne({
where: { foreign: false },
})
}

View File

@ -0,0 +1,6 @@
import { LOG4JS_BASE_CATEGORY_NAME } from '../config/const'
export * from './user'
export * from './communities'
export const LOG4JS_QUERIES_CATEGORY_NAME = `${LOG4JS_BASE_CATEGORY_NAME}.queries`

View File

@ -0,0 +1,141 @@
import { User as DbUser, UserContact as DbUserContact, Community as DbCommunity } from '..'
import { AppDatabase } from '../AppDatabase'
import { aliasExists, findUserByIdentifier } from './user'
import { userFactory } from '../seeds/factory/user'
import { bibiBloxberg } from '../seeds/users/bibi-bloxberg'
import { describe, expect, it, beforeAll, afterAll } from 'vitest'
import { createCommunity } from '../seeds/homeCommunity'
import { peterLustig } from '../seeds/users/peter-lustig'
import { bobBaumeister } from '../seeds/users/bob-baumeister'
import { getLogger, printLogs, clearLogs } from '../../../config-schema/test/testSetup.vitest'
import { LOG4JS_QUERIES_CATEGORY_NAME } from '.'
import { beforeEach } from 'node:test'
const db = AppDatabase.getInstance()
const userIdentifierLoggerName = `${LOG4JS_QUERIES_CATEGORY_NAME}.user.findUserByIdentifier`
beforeAll(async () => {
await db.init()
})
afterAll(async () => {
await db.destroy()
})
describe('user.queries', () => {
describe('aliasExists', () => {
beforeAll(async () => {
await DbUser.clear()
await DbUserContact.clear()
const bibi = bibiBloxberg
bibi.alias = 'b-b'
await userFactory(bibi)
})
it('should return true if alias exists', async () => {
expect(await aliasExists('b-b')).toBe(true)
})
it('should return true if alias exists even with deviating casing', async () => {
expect(await aliasExists('b-B')).toBe(true)
})
it('should return false if alias does not exist', async () => {
expect(await aliasExists('bibi')).toBe(false)
})
})
describe('findUserByIdentifier', () => {
let homeCom: DbCommunity
let communityUuid: string
let communityName: string
let userBibi: DbUser
beforeAll(async () => {
await DbUser.clear()
await DbUserContact.clear()
await DbCommunity.clear()
homeCom = await createCommunity(false)
communityUuid = homeCom.communityUuid!
communityName = homeCom.name!
userBibi = await userFactory(bibiBloxberg)
await userFactory(peterLustig)
await userFactory(bobBaumeister)
})
beforeEach(() => {
clearLogs()
})
describe('communityIdentifier is community uuid', () => {
it('userIdentifier is gradido id', async () => {
const user = await findUserByIdentifier(userBibi.gradidoID, communityUuid)
expect(user).toMatchObject(userBibi)
})
it('userIdentifier is alias', async () => {
const user = await findUserByIdentifier(userBibi.alias, communityUuid)
expect(user).toMatchObject(userBibi)
})
it('userIdentifier is email', async () => {
const user = await findUserByIdentifier(userBibi.emailContact.email, communityUuid)
expect(user).toMatchObject(userBibi)
})
it('userIdentifier is unknown', async () => {
const user = await findUserByIdentifier('unknown', communityUuid)
expect(user).toBeNull()
})
})
describe('communityIdentifier is community name', () => {
it('userIdentifier is gradido id', async () => {
const user = await findUserByIdentifier(userBibi.gradidoID, communityName)
expect(user).toMatchObject(userBibi)
})
it('userIdentifier is alias', async () => {
const user = await findUserByIdentifier(userBibi.alias, communityName)
expect(user).toMatchObject(userBibi)
})
it('userIdentifier is email', async () => {
const user = await findUserByIdentifier(userBibi.emailContact.email, communityName)
expect(user).toMatchObject(userBibi)
})
})
describe('communityIdentifier is unknown', () => {
it('userIdentifier is gradido id', async () => {
const user = await findUserByIdentifier(userBibi.gradidoID, 'unknown')
expect(user).toBeNull()
})
it('userIdentifier is unknown', async () => {
const user = await findUserByIdentifier('unknown', communityUuid)
expect(user).toBeNull()
})
})
describe('communityIdentifier is empty', () => {
it('userIdentifier is gradido id', async () => {
const user = await findUserByIdentifier(userBibi.gradidoID)
expect(user).toMatchObject(userBibi)
})
it('userIdentifier is alias', async () => {
const user = await findUserByIdentifier(userBibi.alias)
expect(user).toMatchObject(userBibi)
})
it('userIdentifier is email', async () => {
const user = await findUserByIdentifier(userBibi.emailContact.email)
expect(user).toMatchObject(userBibi)
})
it('userIdentifier is unknown type', async () => {
const user = await findUserByIdentifier('sa')
printLogs()
expect(getLogger(userIdentifierLoggerName).warn).toHaveBeenCalledWith('Unknown identifier type', 'sa')
expect(user).toBeNull()
})
})
})
})

View File

@ -0,0 +1,62 @@
import { Raw } from 'typeorm'
import { Community, User as DbUser, UserContact as DbUserContact } from '../entity'
import { FindOptionsWhere } from 'typeorm'
import { aliasSchema, emailSchema, uuidv4Schema, urlSchema } from 'shared'
import { getLogger } from 'log4js'
import { LOG4JS_QUERIES_CATEGORY_NAME } from './index'
export async function aliasExists(alias: string): Promise<boolean> {
const user = await DbUser.findOne({
where: { alias: Raw((a) => `LOWER(${a}) = LOWER(:alias)`, { alias }) },
})
return user !== null
}
/**
*
* @param identifier could be gradidoID, alias or email of user
* @param communityIdentifier could be uuid or name of community
* @returns
*/
export const findUserByIdentifier = async (
identifier: string,
communityIdentifier?: string,
): Promise<DbUser | null> => {
const communityWhere: FindOptionsWhere<Community> = urlSchema.safeParse(communityIdentifier).success
? { url: communityIdentifier }
: uuidv4Schema.safeParse(communityIdentifier).success
? { communityUuid: communityIdentifier }
: { name: communityIdentifier }
if (uuidv4Schema.safeParse(identifier).success) {
return DbUser.findOne({
where: { gradidoID: identifier, community: communityWhere },
relations: ['emailContact', 'community'],
})
} else if (emailSchema.safeParse(identifier).success) {
const userContact = await DbUserContact.findOne({
where: {
email: identifier,
emailChecked: true,
user: {
community: communityWhere,
},
},
relations: { user: { community: true } },
})
if (userContact) {
// TODO: remove circular reference
const user = userContact.user
user.emailContact = userContact
return user
}
} else if (aliasSchema.safeParse(identifier).success) {
return await DbUser.findOne({
where: { alias: identifier, community: communityWhere },
relations: ['emailContact', 'community'],
})
}
// should don't happen often, so we create only in the rare case a logger for it
getLogger(`${LOG4JS_QUERIES_CATEGORY_NAME}.user.findUserByIdentifier`).warn('Unknown identifier type', identifier)
return null
}

View File

@ -0,0 +1,43 @@
import { UserInterface } from '../users/UserInterface'
import { User, UserContact } from '../../entity'
import { v4 } from 'uuid'
import { UserContactType, OptInType, PasswordEncryptionType } from 'shared'
import { getHomeCommunity } from '../../queries/communities'
import random from 'crypto-random-bigint'
export const userFactory = async (user: UserInterface): Promise<User> => {
let dbUserContact = new UserContact()
dbUserContact.email = user.email ?? ''
dbUserContact.type = UserContactType.USER_CONTACT_EMAIL
let dbUser = new User()
dbUser.firstName = user.firstName ?? ''
dbUser.lastName = user.lastName ?? ''
dbUser.alias = user.alias ?? ''
dbUser.language = user.language ?? 'en'
dbUser.createdAt = user.createdAt ?? new Date()
dbUser.deletedAt = user.deletedAt ?? null
dbUser.publisherId = user.publisherId ?? 0
dbUser.gradidoID = v4()
if (user.emailChecked) {
dbUserContact.emailVerificationCode = random(64).toString()
dbUserContact.emailOptInTypeId = OptInType.EMAIL_OPT_IN_REGISTER
dbUserContact.emailChecked = true
dbUser.password = random(64)
dbUser.passwordEncryptionType = PasswordEncryptionType.GRADIDO_ID
}
const homeCommunity = await getHomeCommunity()
if (homeCommunity) {
dbUser.community = homeCommunity
dbUser.communityUuid = homeCommunity.communityUuid!
}
// TODO: improve with cascade
dbUser = await dbUser.save()
dbUserContact.userId = dbUser.id
dbUserContact = await dbUserContact.save()
dbUser.emailId = dbUserContact.id
dbUser.emailContact = dbUserContact
return dbUser.save()
}

View File

@ -0,0 +1,26 @@
import { Community } from '../entity'
import { randomBytes } from 'node:crypto'
import { v4 as uuidv4 } from 'uuid'
export async function createCommunity(foreign: boolean): Promise<Community> {
const homeCom = new Community()
homeCom.publicKey = randomBytes(32)
homeCom.communityUuid = uuidv4()
homeCom.authenticatedAt = new Date()
homeCom.name = 'HomeCommunity-name'
homeCom.creationDate = new Date()
if(foreign) {
homeCom.foreign = true
homeCom.name = 'ForeignCommunity-name'
homeCom.description = 'ForeignCommunity-description'
homeCom.url = 'http://foreign/api'
} else {
homeCom.foreign = false
homeCom.privateKey = randomBytes(64)
homeCom.name = 'HomeCommunity-name'
homeCom.description = 'HomeCommunity-description'
homeCom.url = 'http://localhost/api'
}
return await homeCom.save()
}

View File

@ -0,0 +1,13 @@
export interface UserInterface {
alias?: string
email?: string
firstName?: string
lastName?: string
// description?: string
createdAt?: Date
emailChecked?: boolean
language?: string
deletedAt?: Date
publisherId?: number
role?: string
}

View File

@ -0,0 +1,12 @@
import { UserInterface } from './UserInterface'
export const bibiBloxberg: UserInterface = {
email: 'bibi@bloxberg.de',
firstName: 'Bibi',
lastName: 'Bloxberg',
alias: 'BBB',
// description: 'Hex Hex',
emailChecked: true,
language: 'de',
publisherId: 1234,
}

View File

@ -0,0 +1,11 @@
import { UserInterface } from './UserInterface'
export const bobBaumeister: UserInterface = {
alias: 'MeisterBob',
email: 'bob@baumeister.de',
firstName: 'Bob',
lastName: 'der Baumeister',
// description: 'Können wir das schaffen? Ja, wir schaffen das!',
emailChecked: true,
language: 'de',
}

View File

@ -0,0 +1,12 @@
import { UserInterface } from './UserInterface'
export const garrickOllivander: UserInterface = {
email: 'garrick@ollivander.com',
firstName: 'Garrick',
lastName: 'Ollivander',
// description: `Curious ... curious ...
// Renowned wandmaker Mr Ollivander owns the wand shop Ollivanders: Makers of Fine Wands Since 382 BC in Diagon Alley. His shop is widely considered the best place to purchase a wand.`,
createdAt: new Date('2022-01-10T10:23:17'),
emailChecked: false,
language: 'en',
}

View File

@ -0,0 +1,15 @@
import { bibiBloxberg } from './bibi-bloxberg'
import { bobBaumeister } from './bob-baumeister'
import { garrickOllivander } from './garrick-ollivander'
import { peterLustig } from './peter-lustig'
import { raeuberHotzenplotz } from './raeuber-hotzenplotz'
import { stephenHawking } from './stephen-hawking'
export const users = [
peterLustig,
bibiBloxberg,
bobBaumeister,
raeuberHotzenplotz,
stephenHawking,
garrickOllivander,
]

View File

@ -0,0 +1,11 @@
import { UserInterface } from './UserInterface'
export const peterLustig: UserInterface = {
email: 'peter@lustig.de',
firstName: 'Peter',
lastName: 'Lustig',
// description: 'Latzhose und Nickelbrille',
createdAt: new Date('2020-11-25T10:48:43'),
emailChecked: true,
language: 'de'
}

View File

@ -0,0 +1,10 @@
import { UserInterface } from './UserInterface'
export const raeuberHotzenplotz: UserInterface = {
email: 'raeuber@hotzenplotz.de',
firstName: 'Räuber',
lastName: 'Hotzenplotz',
// description: 'Pfefferpistole',
emailChecked: true,
language: 'de',
}

View File

@ -0,0 +1,12 @@
import { UserInterface } from './UserInterface'
export const stephenHawking: UserInterface = {
email: 'stephen@hawking.uk',
firstName: 'Stephen',
lastName: 'Hawking',
// description: 'A Brief History of Time',
emailChecked: true,
createdAt: new Date('1942-01-08T09:17:52'),
deletedAt: new Date('2018-03-14T09:17:52'),
language: 'en',
}

View File

@ -50,7 +50,7 @@
//"@/*": ["src/*"], /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
//},
// "rootDirs": [".", "../database"], /* List of root folders whose combined content represents the structure of the project at runtime. */
// "typeRoots": [], /* List of folders to include type definitions from. */
"typeRoots": ["node_modules/@types"], /* List of folders to include type definitions from. */
// "types": ["node"], /* Type declaration files to be included in compilation. */
// "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
"esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
@ -71,5 +71,9 @@
"skipLibCheck": true, /* Skip type checking of declaration files. */
"forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */
},
"references": [] /* Any project that is referenced must itself have a `references` array (which may be empty). */
"references": [], /* Any project that is referenced must itself have a `references` array (which may be empty). */
"ts-node": {
"swc": true
},
"exclude": ["**/*.test.ts", "**/*.spec.ts", "test/*"]
}

View File

@ -13,10 +13,16 @@
"up:dht_test": {
"cache": false
},
"up:test": {
"cache": false
},
"up": {
"cache": false,
"dependsOn": ["build"]
},
"test": {
"dependsOn": ["up:test", "^build"]
},
"down": {
"cache": false,
"dependsOn": ["build"]

View File

@ -0,0 +1,7 @@
import { defineConfig } from 'vitest/config'
export default defineConfig({
test: {
setupFiles: '../config-schema/test/testSetup.vitest.ts',
},
})

File diff suppressed because it is too large Load Diff

View File

@ -3,7 +3,7 @@ module.exports = {
verbose: false,
preset: 'ts-jest',
collectCoverage: false,
collectCoverageFrom: ['src/**/*.ts', '!**/node_modules/**', '!src/seeds/**', '!build/**'],
collectCoverageFrom: ['src/**/*.ts', '!**/node_modules/**', '!build/**'],
coverageThreshold: {
global: {
lines: 82,

View File

@ -47,8 +47,9 @@
"prettier": "^2.8.8",
"source-map-support": "^0.5.21",
"ts-jest": "27.1.4",
"tsconfig-paths": "^4.1.1",
"tsx": "^4.19.4",
"typeorm": "^0.3.22",
"typeorm": "^0.3.25",
"typescript": "^4.9.5",
"uuid": "^8.3.2"
},

View File

@ -3,6 +3,7 @@ import {
CommunityLoggingView,
Community as DbCommunity,
FederatedCommunity as DbFederatedCommunity,
getHomeCommunity,
} from 'database'
import { v4 as uuidv4 } from 'uuid'
@ -233,7 +234,7 @@ async function writeFederatedHomeCommunityEntries(pubKey: string): Promise<Commu
async function writeHomeCommunityEntry(keyPair: KeyPair): Promise<void> {
try {
// check for existing homeCommunity entry
let homeCom = await DbCommunity.findOne({ where: { foreign: false } })
let homeCom = await getHomeCommunity()
if (homeCom) {
// simply update the existing entry, but it MUST keep the ID and UUID because of possible relations
homeCom.publicKey = keyPair.publicKey

View File

@ -1,4 +1,4 @@
import { contributionDateFormatter } from '@test/helpers'
import { contributionDateFormatter } from './helpers'
describe('contributionDateFormatter', () => {
it('formats the date correctly', () => {

View File

@ -16,7 +16,9 @@ const context = {
export const cleanDB = async () => {
// this only works as long we do not have foreign key constraints
for (const entity of entities) {
await resetEntity(entity)
if (entity.name !== 'Migration') {
await resetEntity(entity)
}
}
}

View File

@ -2,10 +2,10 @@
"extends": ["//"],
"tasks": {
"test": {
"dependsOn": ["config-schema#build", "database#build", "database#up:dht_test"]
"dependsOn": ["database#up:dht_test", "^build"]
},
"dev": {
"dependsOn": ["config-schema#build", "database#build", "database#up"]
"dependsOn": ["database#up", "^build"]
},
"start": {
"dependsOn": ["database#up", "build"]

View File

@ -63,7 +63,7 @@
"ts-jest": "27.0.5",
"tsconfig-paths": "^4.1.1",
"type-graphql": "^1.1.1",
"typeorm": "^0.3.22",
"typeorm": "^0.3.25",
"typescript": "^4.9.5",
"uuid": "8.3.2"
},

View File

@ -1,5 +1,4 @@
import {
DECAY_START_TIME,
GRAPHIQL,
LOG4JS_CONFIG_PLACEHOLDER,
LOG_FILES_BASE_PATH,
@ -10,7 +9,6 @@ import {
import Joi from 'joi'
export const schema = Joi.object({
DECAY_START_TIME,
GRAPHIQL,
LOG4JS_CONFIG_PLACEHOLDER,
LOG_FILES_BASE_PATH,

View File

@ -1,14 +1,6 @@
import { Decimal } from 'decimal.js-light'
import { Field, Int, ObjectType } from 'type-graphql'
interface DecayInterface {
balance: Decimal
decay: Decimal
roundedDecay: Decimal
start: Date | null
end: Date | null
duration: number | null
}
import { Decay as DecayInterface} from 'shared'
@ObjectType()
export class Decay {

View File

@ -1,10 +1,10 @@
import { findUserByIdentifier } from '@/graphql/util/findUserByIdentifier'
import { fullName } from '@/graphql/util/fullName'
import { LogError } from '@/server/LogError'
import {
Community as DbCommunity,
PendingTransaction as DbPendingTransaction,
PendingTransactionLoggingView,
findUserByIdentifier
} from 'database'
import Decimal from 'decimal.js-light'
import { getLogger } from 'log4js'
@ -43,14 +43,14 @@ export class SendCoinsResolver {
)
}
let receiverUser
try {
// second check if receiver user exists in this community
receiverUser = await findUserByIdentifier(
args.recipientUserIdentifier,
args.recipientCommunityUuid,
)
} catch (err) {
logger.error('Error in findUserByIdentifier:', err)
// second check if receiver user exists in this community
receiverUser = await findUserByIdentifier(
args.recipientUserIdentifier,
args.recipientCommunityUuid,
)
if (!receiverUser) {
logger.error('Error in findUserByIdentifier:')
throw new LogError(
`voteForSendCoins with unknown recipientUserIdentifier in the community=`,
homeCom.name,
@ -126,11 +126,11 @@ export class SendCoinsResolver {
)
}
let receiverUser
try {
// second check if receiver user exists in this community
receiverUser = await findUserByIdentifier(args.recipientUserIdentifier)
} catch (err) {
logger.error('Error in findUserByIdentifier:', err)
// second check if receiver user exists in this community
receiverUser = await findUserByIdentifier(args.recipientUserIdentifier)
if (!receiverUser) {
logger.error('Error in findUserByIdentifier')
throw new LogError(
`revertSendCoins with unknown recipientUserIdentifier in the community=`,
homeCom.name,
@ -193,12 +193,11 @@ export class SendCoinsResolver {
args.recipientCommunityUuid,
)
}
let receiverUser
try {
// second check if receiver user exists in this community
receiverUser = await findUserByIdentifier(args.recipientUserIdentifier)
} catch (err) {
logger.error('Error in findUserByIdentifier:', err)
// second check if receiver user exists in this community
const receiverUser = await findUserByIdentifier(args.recipientUserIdentifier)
if (!receiverUser) {
logger.error('Error in findUserByIdentifier')
throw new LogError(
`settleSendCoins with unknown recipientUserIdentifier in the community=`,
homeCom.name,
@ -265,13 +264,12 @@ export class SendCoinsResolver {
`revertSettledSendCoins with wrong recipientCommunityUuid`,
args.recipientCommunityUuid,
)
}
let receiverUser
try {
// second check if receiver user exists in this community
receiverUser = await findUserByIdentifier(args.recipientUserIdentifier)
} catch (err) {
logger.error('Error in findUserByIdentifier:', err)
}
// second check if receiver user exists in this community
const receiverUser = await findUserByIdentifier(args.recipientUserIdentifier)
if (!receiverUser) {
logger.error('Error in findUserByIdentifier')
throw new LogError(
`revertSettledSendCoins with unknown recipientUserIdentifier in the community=`,
homeCom.name,

View File

@ -1,4 +1,4 @@
import { calculateDecay } from '@/graphql/util/decay'
import { calculateDecay } from 'shared'
import { getLastTransaction } from '@/graphql/util/getLastTransaction'
import { Decimal } from 'decimal.js-light'
import { Decay } from '../model/Decay'
@ -13,7 +13,7 @@ export async function calculateRecipientBalance(
return null
}
const decay = calculateDecay(lastTransaction.balance, lastTransaction.balanceDate, time)
const decay = new Decay(calculateDecay(lastTransaction.balance, lastTransaction.balanceDate, time))
const balance = decay.balance.add(amount.toString())

View File

@ -1,57 +0,0 @@
import { User as DbUser, UserContact as DbUserContact } from 'database'
import { validate, version } from 'uuid'
import { LogError } from '@/server/LogError'
import { VALID_ALIAS_REGEX } from './validateAlias'
export const findUserByIdentifier = async (
identifier: string,
communityIdentifier?: string,
): Promise<DbUser> => {
let user: DbUser | null
if (validate(identifier) && version(identifier) === 4) {
user = await DbUser.findOne({
where: { gradidoID: identifier, communityUuid: communityIdentifier },
relations: ['emailContact'],
})
if (!user) {
throw new LogError('No user found to given identifier(s)', identifier, communityIdentifier)
}
} else if (/^.{2,}@.{2,}\..{2,}$/.exec(identifier)) {
const userContact = await DbUserContact.findOne({
where: {
email: identifier,
emailChecked: true,
},
relations: ['user'],
})
if (!userContact) {
throw new LogError('No user with this credentials', identifier)
}
if (!userContact.user) {
throw new LogError('No user to given contact', identifier)
}
if (userContact.user.communityUuid !== communityIdentifier) {
throw new LogError(
'Found user to given contact, but belongs to other community',
identifier,
communityIdentifier,
)
}
user = userContact.user
user.emailContact = userContact
} else if (VALID_ALIAS_REGEX.exec(identifier)) {
user = await DbUser.findOne({
where: { alias: identifier, communityUuid: communityIdentifier },
relations: ['emailContact'],
})
if (!user) {
throw new LogError('No user found to given identifier(s)', identifier, communityIdentifier)
}
} else {
throw new LogError('Unknown identifier type', identifier)
}
return user
}

View File

@ -1,46 +0,0 @@
import { User as DbUser } from 'database'
import { Raw } from 'typeorm'
import { LogError } from '@/server/LogError'
export const VALID_ALIAS_REGEX = /^(?=.{3,20}$)[a-zA-Z0-9]+(?:[_-][a-zA-Z0-9]+?)*$/
const RESERVED_ALIAS = [
'admin',
'email',
'gast',
'gdd',
'gradido',
'guest',
'home',
'root',
'support',
'temp',
'tmp',
'tmp',
'user',
'usr',
'var',
]
export const validateAlias = async (alias: string): Promise<boolean> => {
if (alias.length < 3) {
throw new LogError('Given alias is too short', alias)
}
if (alias.length > 20) {
throw new LogError('Given alias is too long', alias)
}
if (!alias.match(VALID_ALIAS_REGEX)) {
throw new LogError('Invalid characters in alias', alias)
}
if (RESERVED_ALIAS.includes(alias.toLowerCase())) {
throw new LogError('Alias is not allowed', alias)
}
const aliasInUse = await DbUser.find({
where: { alias: Raw((a) => `LOWER(${a}) = "${alias.toLowerCase()}"`) },
})
if (aliasInUse.length !== 0) {
throw new LogError('Alias already in use', alias)
}
return true
}

View File

@ -21,7 +21,9 @@ const context = {
export const cleanDB = async () => {
// this only works as long we do not have foreign key constraints
for (const entity of entities) {
await resetEntity(entity)
if (entity.name !== 'Migration') {
await resetEntity(entity)
}
}
}

View File

@ -51,7 +51,7 @@
// "@arg/*": ["src/graphql/arg/*"],
// "@enum/*": ["src/graphql/enum/*"],
// "@model/*": ["src/graphql/model/*"],
"@repository/*": ["src/typeorm/repository/*"],
// "@repository/*": ["src/typeorm/repository/*"],
"@test/*": ["test/*"],
/* common */
// "@common/*": ["../common/src/*"],

View File

@ -2,10 +2,10 @@
"extends": ["//"],
"tasks": {
"test": {
"dependsOn": ["database#up:federation_test", "config-schema#build", "database#build"]
"dependsOn": ["database#up:federation_test", "^build"]
},
"dev": {
"dependsOn": ["database#up"]
"dependsOn": ["database#up", "^build"]
},
"start": {
"dependsOn": ["database#up", "build"]

View File

@ -81,7 +81,7 @@
"@vue/test-utils": "^2.4.6",
"chokidar-cli": "^3.0.0",
"concurrently": "^9.1.2",
"config-schema": "*",
"config-schema": "2.6.0",
"cross-env": "^7.0.3",
"dotenv-webpack": "^7.0.3",
"eslint": "8.57.1",

View File

@ -12,10 +12,12 @@
"admin",
"backend",
"config-schema",
"core",
"database",
"dht-node",
"federation",
"frontend"
"frontend",
"shared"
],
"scripts": {
"release": "scripts/release.sh",

View File

@ -6,6 +6,9 @@ PROJECT_DIR="${SCRIPT_DIR}/../"
FRONTEND_DIR="${PROJECT_DIR}/frontend/"
BACKEND_DIR="${PROJECT_DIR}/backend/"
DATABASE_DIR="${PROJECT_DIR}/database/"
SHARED_DIR="${PROJECT_DIR}/shared/"
CONFIG_SCHEMA_DIR="${PROJECT_DIR}/config-schema/"
CORE_DIR="${PROJECT_DIR}/core/"
ADMIN_DIR="${PROJECT_DIR}/admin/"
DHTNODE_DIR="${PROJECT_DIR}/dht-node/"
FEDERATION_DIR="${PROJECT_DIR}/federation/"
@ -27,6 +30,12 @@ cd ${BACKEND_DIR}
yarn version --no-git-tag-version --no-commit-hooks --no-commit --new-version ${VERSION}
cd ${DATABASE_DIR}
yarn version --no-git-tag-version --no-commit-hooks --no-commit --new-version ${VERSION}
cd ${SHARED_DIR}
yarn version --no-git-tag-version --no-commit-hooks --no-commit --new-version ${VERSION}
cd ${CONFIG_SCHEMA_DIR}
yarn version --no-git-tag-version --no-commit-hooks --no-commit --new-version ${VERSION}
cd ${CORE_DIR}
yarn version --no-git-tag-version --no-commit-hooks --no-commit --new-version ${VERSION}
cd ${ADMIN_DIR}
yarn version --no-git-tag-version --no-commit-hooks --no-commit --new-version ${VERSION}
cd ${DHTNODE_DIR}

13
shared/README.md Normal file
View File

@ -0,0 +1,13 @@
# shared
Gradido Shared Code, Low-Level Shared Code, without dependencies on other modules
## Bun-Compatibility
Full bun compatible
## Enums
All enums used across more than one module
Additional with zod Schema but working only with zod v4 and this needs typescript 5
## Schemas
All schemas for data validation used across more than one module
Tests written for bun

42
shared/package.json Normal file
View File

@ -0,0 +1,42 @@
{
"name": "shared",
"version": "2.6.0",
"description": "Gradido Shared Code, Low-Level Shared Code, without dependencies on other modules",
"main": "./build/index.js",
"types": "./src/index.ts",
"exports": {
".": {
"import": "./build/index.js",
"require": "./build/index.js"
}
},
"repository": "https://github.com/gradido/gradido/shared",
"author": "Gradido Academy - https://www.gradido.net",
"license": "Apache-2.0",
"private": true,
"scripts": {
"build": "esbuild src/index.ts --outdir=build --platform=node --target=node18.20.7 --bundle --packages=external",
"build:bun": "bun build src/index.ts --outdir=build --target=bun --packages=external",
"test": "bun test",
"test:debug": "bun test --inspect-brk",
"typecheck": "tsc --noEmit",
"lint": "biome check --error-on-warnings .",
"lint:fix": "biome check --error-on-warnings . --write"
},
"devDependencies": {
"@biomejs/biome": "2.0.0",
"@types/node": "^17.0.21",
"@types/uuid": "^10.0.0",
"typescript": "^4.9.5",
"uuid": "^8.3.2"
},
"dependencies": {
"decimal.js-light": "^2.5.1",
"esbuild": "^0.25.2",
"log4js": "^6.9.1",
"zod": "^3.25.61"
},
"engines": {
"node": ">=18"
}
}

View File

@ -0,0 +1,2 @@
export const DECAY_START_TIME = new Date('2021-05-13T17:46:31Z')
export const LOG4JS_BASE_CATEGORY_NAME = 'shared'

View File

@ -0,0 +1,8 @@
// not compatible with typescript 4
// import { enum as zEnum } from 'zod/v4-mini'
export enum OptInType {
EMAIL_OPT_IN_REGISTER = 1,
EMAIL_OPT_IN_RESET_PASSWORD = 2,
}
// export const OptInTypeSchema = zEnum(OptInType)

View File

@ -0,0 +1,11 @@
// not compatible with typescript 4
// import { enum as zEnum } from 'zod/v4-mini'
export enum PasswordEncryptionType {
NO_PASSWORD = 0,
EMAIL = 1,
GRADIDO_ID = 2,
}
// export const PasswordEncryptionTypeSchema = zEnum(PasswordEncryptionType)

View File

@ -0,0 +1,13 @@
// not compatible with typescript 4
// import { enum as zEnum } from 'zod/v4-mini'
export enum RoleNames {
UNAUTHORIZED = 'UNAUTHORIZED',
USER = 'USER',
MODERATOR = 'MODERATOR',
MODERATOR_AI = 'MODERATOR_AI',
ADMIN = 'ADMIN',
DLT_CONNECTOR = 'DLT_CONNECTOR_ROLE',
}
// export const RoleNamesSchema = zEnum(RoleNames)

View File

@ -0,0 +1,9 @@
// not compatible with typescript 4
// import { enum as zEnum } from 'zod/v4-mini'
export enum UserContactType {
USER_CONTACT_EMAIL = 'EMAIL',
USER_CONTACT_PHONE = 'PHONE',
}
// export const UserContactTypeSchema = zEnum(UserContactType)

4
shared/src/enum/index.ts Normal file
View File

@ -0,0 +1,4 @@
export * from './RoleNames'
export * from './UserContactType'
export * from './PasswordEncryptionType'
export * from './OptInType'

Some files were not shown because too many files have changed in this diff Show More