refactor with extensive, experimental use of valibot for typecheck, vailidation and conversation

This commit is contained in:
einhornimmond 2025-07-03 15:35:42 +02:00
parent df28a5b4d1
commit 78351257ee
89 changed files with 2275 additions and 8729 deletions

2
.gitignore vendored
View File

@ -12,7 +12,7 @@ messages.pot
nbproject
.metadata
/out/*
/.env
.env
package-lock.json
/deployment/bare_metal/.env
/deployment/bare_metal/nginx/sites-available/gradido.conf

View File

@ -1,5 +1,4 @@
import { DltTransaction } from '@entity/DltTransaction'
import { TransactionLink } from '@entity/TransactionLink'
import { DltTransaction, TransactionLink } from 'database'
import { DltTransactionType } from '@dltConnector/enum/DltTransactionType'
import { TransactionType } from '@dltConnector/enum/TransactionType'

View File

@ -1,21 +0,0 @@
CONFIG_VERSION=$DLT_CONNECTOR_CONFIG_VERSION
JWT_SECRET=$JWT_SECRET
#IOTA
IOTA_API_URL=$IOTA_API_URL
IOTA_COMMUNITY_ALIAS=$IOTA_COMMUNITY_ALIAS
IOTA_HOME_COMMUNITY_SEED=$IOTA_HOME_COMMUNITY_SEED
# DLT-Connector
DLT_CONNECTOR_PORT=$DLT_CONNECTOR_PORT
# Gradido Node Server URL
NODE_SERVER_URL=$NODE_SERVER_URL
# Gradido Blockchain
GRADIDO_BLOCKCHAIN_CRYPTO_APP_SECRET=$GRADIDO_BLOCKCHAIN_CRYPTO_APP_SECRET
GRADIDO_BLOCKCHAIN_SERVER_CRYPTO_KEY=$GRADIDO_BLOCKCHAIN_SERVER_CRYPTO_KEY
# Route to Backend
BACKEND_SERVER_URL=http://localhost:4000

View File

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

View File

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

View File

@ -1,8 +0,0 @@
/node_modules/
/.env
/.env.bak
/build/
package-json.lock
coverage
# emacs
*~

View File

@ -1 +0,0 @@
v19.5.0

View File

@ -1,9 +0,0 @@
module.exports = {
semi: false,
printWidth: 100,
singleQuote: true,
trailingComma: "all",
tabWidth: 2,
bracketSpacing: true,
endOfLine: "auto",
};

View File

@ -1 +0,0 @@
declare module 'bip32-ed25519'

View File

@ -1,119 +0,0 @@
##################################################################################
# BASE ###########################################################################
##################################################################################
FROM node:19.5.0-alpine3.17 as base
#FROM ubuntu:latest as base
# ENVs (available in production aswell, can be overwritten by commandline or env file)
## DOCKER_WORKDIR would be a classical ARG, but that is not multi layer persistent - shame
ENV DOCKER_WORKDIR="/app"
## We Cannot do `$(date -u +'%Y-%m-%dT%H:%M:%SZ')` here so we use unix timestamp=0
ENV BUILD_DATE="1970-01-01T00:00:00.00Z"
## We cannot do $(npm run version).${BUILD_NUMBER} here so we default to 0.0.0.0
ENV BUILD_VERSION="0.0.0.0"
## We cannot do `$(git rev-parse --short HEAD)` here so we default to 0000000
ENV BUILD_COMMIT="0000000"
## SET NODE_ENV
ENV NODE_ENV="production"
## App relevant Envs
ENV PORT="6010"
# Labels
LABEL org.label-schema.build-date="${BUILD_DATE}"
LABEL org.label-schema.name="gradido:dlt-connector"
LABEL org.label-schema.description="Gradido dlt-connector"
LABEL org.label-schema.usage="https://github.com/gradido/gradido/blob/master/README.md"
LABEL org.label-schema.url="https://gradido.net"
LABEL org.label-schema.vcs-url="https://github.com/gradido/gradido/tree/master/dlt-connector"
LABEL org.label-schema.vcs-ref="${BUILD_COMMIT}"
LABEL org.label-schema.vendor="Gradido Community"
LABEL org.label-schema.version="${BUILD_VERSION}"
LABEL org.label-schema.schema-version="1.0"
LABEL maintainer="support@gradido.net"
# Install Additional Software
## install: @iota/client requirements
# Install Build Tool for Rust for @iota/client
RUN apk add --no-cache rust cargo python3 make g++
# Settings
## Expose Container Port
EXPOSE ${PORT}
## Workdir
RUN mkdir -p ${DOCKER_WORKDIR}
WORKDIR ${DOCKER_WORKDIR}
RUN mkdir -p /dlt-database
##################################################################################
# DEVELOPMENT (Connected to the local environment, to reload on demand) ##########
##################################################################################
FROM base as development
# We don't need to copy or build anything since we gonna bind to the
# local filesystem which will need a rebuild anyway
# Run command
# (for development we need to execute yarn install since the
# node_modules are on another volume and need updating)
CMD /bin/sh -c "cd /dlt-database && yarn install && yarn build && cd /app && yarn install && yarn run dev"
##################################################################################
# BUILD (Does contain all files and is therefore bloated) ########################
##################################################################################
FROM base as build
# Copy everything from dlt-connector
COPY ./dlt-connector/ ./
# Copy everything from dlt-database
COPY ./dlt-database/ ../dlt-database/
# yarn install dlt-connector
RUN yarn install --production=false --frozen-lockfile --non-interactive
# yarn install dlt-database
RUN cd ../dlt-database && yarn install --production=false --frozen-lockfile --non-interactive
# yarn build
RUN yarn run build
# yarn build dlt-database
RUN cd ../dlt-database && yarn run build
##################################################################################
# TEST ###########################################################################
##################################################################################
FROM build as test
# Run command
CMD /bin/sh -c "yarn run start"
##################################################################################
# PRODUCTION (Does contain only "binary"- and static-files to reduce image size) #
##################################################################################
FROM base as production
# remove iota build tools to have production docker image smaller
RUN apk del rust cargo python3 make g++
# Copy "binary"-files from build image
COPY --from=build ${DOCKER_WORKDIR}/build ./build
COPY --from=build ${DOCKER_WORKDIR}/../dlt-database/build ../dlt-database/build
# We also copy the node_modules express and serve-static for the run script
COPY --from=build ${DOCKER_WORKDIR}/node_modules ./node_modules
COPY --from=build ${DOCKER_WORKDIR}/../dlt-database/node_modules ../dlt-database/node_modules
# Copy static files
# COPY --from=build ${DOCKER_WORKDIR}/public ./public
# Copy package.json for script definitions (lock file should not be needed)
COPY --from=build ${DOCKER_WORKDIR}/package.json ./package.json
# Copy tsconfig.json to provide alias path definitions
COPY --from=build ${DOCKER_WORKDIR}/tsconfig.json ./tsconfig.json
# Copy log4js-config.json to provide log configuration
COPY --from=build ${DOCKER_WORKDIR}/log4js-config.json ./log4js-config.json
# Copy run scripts run/
# COPY --from=build ${DOCKER_WORKDIR}/run ./run
# Run command
CMD /bin/sh -c "yarn run start"

1
dlt-connector/README.md Normal file
View File

@ -0,0 +1 @@
# Elysia with Bun runtime

577
dlt-connector/bun.lock Normal file
View File

@ -0,0 +1,577 @@
{
"lockfileVersion": 1,
"workspaces": {
"": {
"name": "dlt-connector",
"dependencies": {
"@iota/client": "^2.2.4",
"gradido-blockchain-js": "git+https://github.com/gradido/gradido-blockchain-js#217d03b",
},
"devDependencies": {
"@biomejs/biome": "2.0.0",
"@elysiajs/trpc": "^1.1.0",
"@elysiajs/websocket": "^0.2.8",
"@trpc/server": "^11.4.3",
"@types/bun": "^1.2.17",
"dotenv": "^10.0.0",
"elysia": "^1.3.5",
"graphql-request": "^7.2.0",
"jose": "^5.2.2",
"jsonrpc-ts-client": "^0.2.3",
"log4js": "^6.9.1",
"typescript": "^5.8.3",
"uuid": "^8.3.2",
"valibot": "^1.1.0",
"zod": "^3.25.61",
},
},
},
"trustedDependencies": [
"@iota/client",
],
"packages": {
"@biomejs/biome": ["@biomejs/biome@2.0.0", "", { "optionalDependencies": { "@biomejs/cli-darwin-arm64": "2.0.0", "@biomejs/cli-darwin-x64": "2.0.0", "@biomejs/cli-linux-arm64": "2.0.0", "@biomejs/cli-linux-arm64-musl": "2.0.0", "@biomejs/cli-linux-x64": "2.0.0", "@biomejs/cli-linux-x64-musl": "2.0.0", "@biomejs/cli-win32-arm64": "2.0.0", "@biomejs/cli-win32-x64": "2.0.0" }, "bin": { "biome": "bin/biome" } }, "sha512-BlUoXEOI/UQTDEj/pVfnkMo8SrZw3oOWBDrXYFT43V7HTkIUDkBRY53IC5Jx1QkZbaB+0ai1wJIfYwp9+qaJTQ=="],
"@biomejs/cli-darwin-arm64": ["@biomejs/cli-darwin-arm64@2.0.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-QvqWYtFFhhxdf8jMAdJzXW+Frc7X8XsnHQLY+TBM1fnT1TfeV/v9vsFI5L2J7GH6qN1+QEEJ19jHibCY2Ypplw=="],
"@biomejs/cli-darwin-x64": ["@biomejs/cli-darwin-x64@2.0.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-5JFhls1EfmuIH4QGFPlNpxJQFC6ic3X1ltcoLN+eSRRIPr6H/lUS1ttuD0Fj7rPgPhZqopK/jfH8UVj/1hIsQw=="],
"@biomejs/cli-linux-arm64": ["@biomejs/cli-linux-arm64@2.0.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-BAH4QVi06TzAbVchXdJPsL0Z/P87jOfes15rI+p3EX9/EGTfIjaQ9lBVlHunxcmoptaA5y1Hdb9UYojIhmnjIw=="],
"@biomejs/cli-linux-arm64-musl": ["@biomejs/cli-linux-arm64-musl@2.0.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-Bxsz8ki8+b3PytMnS5SgrGV+mbAWwIxI3ydChb/d1rURlJTMdxTTq5LTebUnlsUWAX6OvJuFeiVq9Gjn1YbCyA=="],
"@biomejs/cli-linux-x64": ["@biomejs/cli-linux-x64@2.0.0", "", { "os": "linux", "cpu": "x64" }, "sha512-09PcOGYTtkopWRm6mZ/B6Mr6UHdkniUgIG/jLBv+2J8Z61ezRE+xQmpi3yNgUrFIAU4lPA9atg7mhvE/5Bo7Wg=="],
"@biomejs/cli-linux-x64-musl": ["@biomejs/cli-linux-x64-musl@2.0.0", "", { "os": "linux", "cpu": "x64" }, "sha512-tiQ0ABxMJb9I6GlfNp0ulrTiQSFacJRJO8245FFwE3ty3bfsfxlU/miblzDIi+qNrgGsLq5wIZcVYGp4c+HXZA=="],
"@biomejs/cli-win32-arm64": ["@biomejs/cli-win32-arm64@2.0.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-vrTtuGu91xNTEQ5ZcMJBZuDlqr32DWU1r14UfePIGndF//s2WUAmer4FmgoPgruo76rprk37e8S2A2c0psXdxw=="],
"@biomejs/cli-win32-x64": ["@biomejs/cli-win32-x64@2.0.0", "", { "os": "win32", "cpu": "x64" }, "sha512-2USVQ0hklNsph/KIR72ZdeptyXNnQ3JdzPn3NbjI4Sna34CnxeiYAaZcZzXPDl5PYNFBivV4xmvT3Z3rTmyDBg=="],
"@elysiajs/trpc": ["@elysiajs/trpc@1.1.0", "", { "peerDependencies": { "elysia": ">= 1.1.0" } }, "sha512-M8QWC+Wa5Z5MWY/+uMQuwZ+JoQkp4jOc1ra4SncFy1zSjFGin59LO1AT0pE+DRJaFV17gha9y7cB6Q7GnaJEAw=="],
"@elysiajs/websocket": ["@elysiajs/websocket@0.2.8", "", { "dependencies": { "nanoid": "^4.0.0", "raikiri": "^0.0.0-beta.3" }, "peerDependencies": { "elysia": ">= 0.2.2" } }, "sha512-K9KLmYL1SYuAV353GvmK0V9DG5w7XTOGsa1H1dGB5BUTzvBaMvnwNeqnJQ3cjf9V1c0EjQds0Ty4LfUFvV45jw=="],
"@graphql-typed-document-node/core": ["@graphql-typed-document-node/core@3.2.0", "", { "peerDependencies": { "graphql": "^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" } }, "sha512-mB9oAsNCm9aM3/SOv4YtBMqZbYj10R7dkq8byBqxGY/ncFwhf2oQzMV+LCRlWoDSEBJ3COiR1yeDvMtsoOsuFQ=="],
"@iota/client": ["@iota/client@2.2.4", "", { "dependencies": { "neon-cli": "^0.8", "prebuild-install": "^6.1.2" } }, "sha512-6zjtqJgkSgrMUFLbxr9k+zXGnEVw6gjTFn5emN2nKpR78+mwW4jUXuNcy/M194bK4sLHjj0T0L4pRWJ6XTGaew=="],
"@sinclair/typebox": ["@sinclair/typebox@0.34.37", "", {}, "sha512-2TRuQVgQYfy+EzHRTIvkhv2ADEouJ2xNS/Vq+W5EuuewBdOrvATvljZTxHWZSTYr2sTjTHpGvucaGAt67S2akw=="],
"@tokenizer/inflate": ["@tokenizer/inflate@0.2.7", "", { "dependencies": { "debug": "^4.4.0", "fflate": "^0.8.2", "token-types": "^6.0.0" } }, "sha512-MADQgmZT1eKjp06jpI2yozxaU9uVs4GzzgSL+uEq7bVcJ9V1ZXQkeGNql1fsSI0gMy1vhvNTNbUqrx+pZfJVmg=="],
"@tokenizer/token": ["@tokenizer/token@0.3.0", "", {}, "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A=="],
"@trpc/server": ["@trpc/server@11.4.3", "", { "peerDependencies": { "typescript": ">=5.7.2" } }, "sha512-wnWq3wiLlMOlYkaIZz+qbuYA5udPTLS4GVVRyFkr6aT83xpdCHyVtURT+u4hSoIrOXQM9OPCNXSXsAujWZDdaw=="],
"@types/bun": ["@types/bun@1.2.17", "", { "dependencies": { "bun-types": "1.2.17" } }, "sha512-l/BYs/JYt+cXA/0+wUhulYJB6a6p//GTPiJ7nV+QHa8iiId4HZmnu/3J/SowP5g0rTiERY2kfGKXEK5Ehltx4Q=="],
"@types/node": ["@types/node@24.0.7", "", { "dependencies": { "undici-types": "~7.8.0" } }, "sha512-YIEUUr4yf8q8oQoXPpSlnvKNVKDQlPMWrmOcgzoduo7kvA2UF0/BwJ/eMKFTiTtkNL17I0M6Xe2tvwFU7be6iw=="],
"ansi-escapes": ["ansi-escapes@4.3.2", "", { "dependencies": { "type-fest": "^0.21.3" } }, "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ=="],
"ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
"ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="],
"aproba": ["aproba@1.2.0", "", {}, "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw=="],
"are-we-there-yet": ["are-we-there-yet@1.1.7", "", { "dependencies": { "delegates": "^1.0.0", "readable-stream": "^2.0.6" } }, "sha512-nxwy40TuMiUGqMyRHgCSWZ9FM4VAoRP4xUYSTv5ImRog+h9yISPbVH7H8fASCIzYn9wlEv4zvFL7uKDMCFQm3g=="],
"array-back": ["array-back@3.1.0", "", {}, "sha512-TkuxA4UCOvxuDK6NZYXCalszEzj+TLszyASooky+i742l9TqsOdYCMJJupxRic61hwquNtppB3hgcuq9SVSH1Q=="],
"asynckit": ["asynckit@0.4.0", "", {}, "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="],
"axios": ["axios@0.24.0", "", { "dependencies": { "follow-redirects": "^1.14.4" } }, "sha512-Q6cWsys88HoPgAaFAVUb0WpPk0O8iTeisR9IMqy9G8AbO4NlpVknrnQS03zzF9PGAWgO3cgletO3VjV/P7VztA=="],
"balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="],
"base64-js": ["base64-js@1.5.1", "", {}, "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="],
"bindings": ["bindings@1.5.0", "", { "dependencies": { "file-uri-to-path": "1.0.0" } }, "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ=="],
"bl": ["bl@4.1.0", "", { "dependencies": { "buffer": "^5.5.0", "inherits": "^2.0.4", "readable-stream": "^3.4.0" } }, "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w=="],
"brace-expansion": ["brace-expansion@1.1.12", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg=="],
"buffer": ["buffer@5.7.1", "", { "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.1.13" } }, "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ=="],
"builtins": ["builtins@1.0.3", "", {}, "sha512-uYBjakWipfaO/bXI7E8rq6kpwHRZK5cNYrUv2OzZSI/FvmdMyXJ2tG9dKcjEC5YHmHpUAwsargWIZNWdxb/bnQ=="],
"bun-types": ["bun-types@1.2.17", "", { "dependencies": { "@types/node": "*" } }, "sha512-ElC7ItwT3SCQwYZDYoAH+q6KT4Fxjl8DtZ6qDulUFBmXA8YB4xo+l54J9ZJN+k2pphfn9vk7kfubeSd5QfTVJQ=="],
"call-bind-apply-helpers": ["call-bind-apply-helpers@1.0.2", "", { "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" } }, "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ=="],
"chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="],
"chardet": ["chardet@0.7.0", "", {}, "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA=="],
"chownr": ["chownr@1.1.4", "", {}, "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg=="],
"cli-cursor": ["cli-cursor@3.1.0", "", { "dependencies": { "restore-cursor": "^3.1.0" } }, "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw=="],
"cli-width": ["cli-width@3.0.0", "", {}, "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw=="],
"cliui": ["cliui@8.0.1", "", { "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.1", "wrap-ansi": "^7.0.0" } }, "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ=="],
"cmake-js": ["cmake-js@7.3.1", "", { "dependencies": { "axios": "^1.6.5", "debug": "^4", "fs-extra": "^11.2.0", "memory-stream": "^1.0.0", "node-api-headers": "^1.1.0", "npmlog": "^6.0.2", "rc": "^1.2.7", "semver": "^7.5.4", "tar": "^6.2.0", "url-join": "^4.0.1", "which": "^2.0.2", "yargs": "^17.7.2" }, "bin": { "cmake-js": "bin/cmake-js" } }, "sha512-aJtHDrTFl8qovjSSqXT9aC2jdGfmP8JQsPtjdLAXFfH1BF4/ImZ27Jx0R61TFg8Apc3pl6e2yBKMveAeRXx2Rw=="],
"code-point-at": ["code-point-at@1.1.0", "", {}, "sha512-RpAVKQA5T63xEj6/giIbUEtZwJ4UFIc3ZtvEkiaUERylqe8xb5IvqcgOurZLahv93CLKfxcw5YI+DZcUBRyLXA=="],
"color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="],
"color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="],
"color-support": ["color-support@1.1.3", "", { "bin": { "color-support": "bin.js" } }, "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg=="],
"combined-stream": ["combined-stream@1.0.8", "", { "dependencies": { "delayed-stream": "~1.0.0" } }, "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg=="],
"command-line-args": ["command-line-args@5.2.1", "", { "dependencies": { "array-back": "^3.1.0", "find-replace": "^3.0.0", "lodash.camelcase": "^4.3.0", "typical": "^4.0.0" } }, "sha512-H4UfQhZyakIjC74I9d34fGYDwk3XpSr17QhEd0Q3I9Xq1CETHo4Hcuo87WyWHpAF1aSLjLRf5lD9ZGX2qStUvg=="],
"command-line-commands": ["command-line-commands@3.0.2", "", { "dependencies": { "array-back": "^4.0.1" } }, "sha512-ac6PdCtdR6q7S3HN+JiVLIWGHY30PRYIEl2qPo+FuEuzwAUk0UYyimrngrg7FvF/mCr4Jgoqv5ZnHZgads50rw=="],
"command-line-usage": ["command-line-usage@6.1.3", "", { "dependencies": { "array-back": "^4.0.2", "chalk": "^2.4.2", "table-layout": "^1.0.2", "typical": "^5.2.0" } }, "sha512-sH5ZSPr+7UStsloltmDh7Ce5fb8XPlHyoPzTpyyMuYCtervL65+ubVZ6Q61cFtFl62UyJlc8/JwERRbAFPUqgw=="],
"commander": ["commander@2.20.3", "", {}, "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="],
"concat-map": ["concat-map@0.0.1", "", {}, "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="],
"console-control-strings": ["console-control-strings@1.1.0", "", {}, "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ=="],
"cookie": ["cookie@1.0.2", "", {}, "sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA=="],
"core-util-is": ["core-util-is@1.0.3", "", {}, "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ=="],
"date-format": ["date-format@4.0.14", "", {}, "sha512-39BOQLs9ZjKh0/patS9nrT8wc3ioX3/eA/zgbKNopnF2wCqJEoxywwwElATYvRsXdnOxA/OQeQoFZ3rFjVajhg=="],
"debug": ["debug@4.4.1", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ=="],
"decompress-response": ["decompress-response@4.2.1", "", { "dependencies": { "mimic-response": "^2.0.0" } }, "sha512-jOSne2qbyE+/r8G1VU+G/82LBs2Fs4LAsTiLSHOCOMZQl2OKZ6i8i4IyHemTe+/yIXOtTcRQMzPcgyhoFlqPkw=="],
"deep-extend": ["deep-extend@0.6.0", "", {}, "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA=="],
"delayed-stream": ["delayed-stream@1.0.0", "", {}, "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ=="],
"delegates": ["delegates@1.0.0", "", {}, "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ=="],
"detect-libc": ["detect-libc@1.0.3", "", { "bin": { "detect-libc": "./bin/detect-libc.js" } }, "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg=="],
"dotenv": ["dotenv@10.0.0", "", {}, "sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q=="],
"dunder-proto": ["dunder-proto@1.0.1", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" } }, "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A=="],
"elysia": ["elysia@1.3.5", "", { "dependencies": { "cookie": "^1.0.2", "exact-mirror": "0.1.2", "fast-decode-uri-component": "^1.0.1" }, "optionalDependencies": { "@sinclair/typebox": "^0.34.33", "openapi-types": "^12.1.3" }, "peerDependencies": { "file-type": ">= 20.0.0", "typescript": ">= 5.0.0" } }, "sha512-XVIKXlKFwUT7Sta8GY+wO5reD9I0rqAEtaz1Z71UgJb61csYt8Q3W9al8rtL5RgumuRR8e3DNdzlUN9GkC4KDw=="],
"emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="],
"end-of-stream": ["end-of-stream@1.4.5", "", { "dependencies": { "once": "^1.4.0" } }, "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg=="],
"es-define-property": ["es-define-property@1.0.1", "", {}, "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g=="],
"es-errors": ["es-errors@1.3.0", "", {}, "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw=="],
"es-object-atoms": ["es-object-atoms@1.1.1", "", { "dependencies": { "es-errors": "^1.3.0" } }, "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA=="],
"es-set-tostringtag": ["es-set-tostringtag@2.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "get-intrinsic": "^1.2.6", "has-tostringtag": "^1.0.2", "hasown": "^2.0.2" } }, "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA=="],
"escalade": ["escalade@3.2.0", "", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="],
"escape-string-regexp": ["escape-string-regexp@1.0.5", "", {}, "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg=="],
"exact-mirror": ["exact-mirror@0.1.2", "", { "peerDependencies": { "@sinclair/typebox": "^0.34.15" }, "optionalPeers": ["@sinclair/typebox"] }, "sha512-wFCPCDLmHbKGUb8TOi/IS7jLsgR8WVDGtDK3CzcB4Guf/weq7G+I+DkXiRSZfbemBFOxOINKpraM6ml78vo8Zw=="],
"execspawn": ["execspawn@1.0.1", "", { "dependencies": { "util-extend": "^1.0.1" } }, "sha512-s2k06Jy9i8CUkYe0+DxRlvtkZoOkwwfhB+Xxo5HGUtrISVW2m98jO2tr67DGRFxZwkjQqloA3v/tNtjhBRBieg=="],
"expand-template": ["expand-template@2.0.3", "", {}, "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg=="],
"external-editor": ["external-editor@3.1.0", "", { "dependencies": { "chardet": "^0.7.0", "iconv-lite": "^0.4.24", "tmp": "^0.0.33" } }, "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew=="],
"fast-decode-uri-component": ["fast-decode-uri-component@1.0.1", "", {}, "sha512-WKgKWg5eUxvRZGwW8FvfbaH7AXSh2cL+3j5fMGzUMCxWBJ3dV3a7Wz8y2f/uQ0e3B6WmodD3oS54jTQ9HVTIIg=="],
"fflate": ["fflate@0.8.2", "", {}, "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A=="],
"figures": ["figures@3.2.0", "", { "dependencies": { "escape-string-regexp": "^1.0.5" } }, "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg=="],
"file-type": ["file-type@21.0.0", "", { "dependencies": { "@tokenizer/inflate": "^0.2.7", "strtok3": "^10.2.2", "token-types": "^6.0.0", "uint8array-extras": "^1.4.0" } }, "sha512-ek5xNX2YBYlXhiUXui3D/BXa3LdqPmoLJ7rqEx2bKJ7EAUEfmXgW0Das7Dc6Nr9MvqaOnIqiPV0mZk/r/UpNAg=="],
"file-uri-to-path": ["file-uri-to-path@1.0.0", "", {}, "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw=="],
"find-replace": ["find-replace@3.0.0", "", { "dependencies": { "array-back": "^3.0.1" } }, "sha512-6Tb2myMioCAgv5kfvP5/PkZZ/ntTpVK39fHY7WkWBgvbeE+VHd/tZuZ4mrC+bxh4cfOZeYKVPaJIZtZXV7GNCQ=="],
"flatted": ["flatted@3.3.3", "", {}, "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg=="],
"follow-redirects": ["follow-redirects@1.15.9", "", {}, "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ=="],
"form-data": ["form-data@4.0.3", "", { "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "es-set-tostringtag": "^2.1.0", "hasown": "^2.0.2", "mime-types": "^2.1.12" } }, "sha512-qsITQPfmvMOSAdeyZ+12I1c+CKSstAFAwu+97zrnWAbIr5u8wfsExUzCesVLC8NgHuRUqNN4Zy6UPWUTRGslcA=="],
"fs-constants": ["fs-constants@1.0.0", "", {}, "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow=="],
"fs-extra": ["fs-extra@8.1.0", "", { "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^4.0.0", "universalify": "^0.1.0" } }, "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g=="],
"fs-minipass": ["fs-minipass@2.1.0", "", { "dependencies": { "minipass": "^3.0.0" } }, "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg=="],
"fs.realpath": ["fs.realpath@1.0.0", "", {}, "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw=="],
"function-bind": ["function-bind@1.1.2", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="],
"gauge": ["gauge@2.7.4", "", { "dependencies": { "aproba": "^1.0.3", "console-control-strings": "^1.0.0", "has-unicode": "^2.0.0", "object-assign": "^4.1.0", "signal-exit": "^3.0.0", "string-width": "^1.0.1", "strip-ansi": "^3.0.1", "wide-align": "^1.1.0" } }, "sha512-14x4kjc6lkD3ltw589k0NrPD6cCNTD6CWoVUNpB85+DrtONoZn+Rug6xZU5RvSC4+TZPxA5AnBibQYAvZn41Hg=="],
"get-caller-file": ["get-caller-file@2.0.5", "", {}, "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg=="],
"get-intrinsic": ["get-intrinsic@1.3.0", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", "math-intrinsics": "^1.1.0" } }, "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ=="],
"get-proto": ["get-proto@1.0.1", "", { "dependencies": { "dunder-proto": "^1.0.1", "es-object-atoms": "^1.0.0" } }, "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g=="],
"git-config": ["git-config@0.0.7", "", { "dependencies": { "iniparser": "~1.0.5" } }, "sha512-LidZlYZXWzVjS+M3TEwhtYBaYwLeOZrXci1tBgqp/vDdZTBMl02atvwb6G35L64ibscYoPnxfbwwUS+VZAISLA=="],
"github-from-package": ["github-from-package@0.0.0", "", {}, "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw=="],
"glob": ["glob@7.2.3", "", { "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" } }, "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q=="],
"gopd": ["gopd@1.2.0", "", {}, "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg=="],
"graceful-fs": ["graceful-fs@4.2.11", "", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="],
"gradido-blockchain-js": ["gradido-blockchain-js@github:gradido/gradido-blockchain-js#217d03b", { "dependencies": { "bindings": "^1.5.0", "nan": "^2.20.0", "node-addon-api": "^7.1.1", "node-gyp-build": "^4.8.1", "prebuildify": "git+https://github.com/einhornimmond/prebuildify#cmake_js" } }, "gradido-gradido-blockchain-js-217d03b"],
"graphql": ["graphql@16.11.0", "", {}, "sha512-mS1lbMsxgQj6hge1XZ6p7GPhbrtFwUFYi3wRzXAC/FmYnyXMTvvI3td3rjmQ2u8ewXueaSvRPWaEcgVVOT9Jnw=="],
"graphql-request": ["graphql-request@7.2.0", "", { "dependencies": { "@graphql-typed-document-node/core": "^3.2.0" }, "peerDependencies": { "graphql": "14 - 16" } }, "sha512-0GR7eQHBFYz372u9lxS16cOtEekFlZYB2qOyq8wDvzRmdRSJ0mgUVX1tzNcIzk3G+4NY+mGtSz411wZdeDF/+A=="],
"handlebars": ["handlebars@4.7.8", "", { "dependencies": { "minimist": "^1.2.5", "neo-async": "^2.6.2", "source-map": "^0.6.1", "wordwrap": "^1.0.0" }, "optionalDependencies": { "uglify-js": "^3.1.4" }, "bin": { "handlebars": "bin/handlebars" } }, "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ=="],
"has-flag": ["has-flag@4.0.0", "", {}, "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="],
"has-symbols": ["has-symbols@1.1.0", "", {}, "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ=="],
"has-tostringtag": ["has-tostringtag@1.0.2", "", { "dependencies": { "has-symbols": "^1.0.3" } }, "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw=="],
"has-unicode": ["has-unicode@2.0.1", "", {}, "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ=="],
"hasown": ["hasown@2.0.2", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ=="],
"iconv-lite": ["iconv-lite@0.4.24", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3" } }, "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA=="],
"ieee754": ["ieee754@1.2.1", "", {}, "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="],
"inflight": ["inflight@1.0.6", "", { "dependencies": { "once": "^1.3.0", "wrappy": "1" } }, "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA=="],
"inherits": ["inherits@2.0.4", "", {}, "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="],
"ini": ["ini@1.3.8", "", {}, "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew=="],
"iniparser": ["iniparser@1.0.5", "", {}, "sha512-i40MWqgTU6h/70NtMsDVVDLjDYWwcIR1yIEVDPfxZIJno9z9L4s83p/V7vAu2i48Vj0gpByrkGFub7ko9XvPrw=="],
"inquirer": ["inquirer@7.3.3", "", { "dependencies": { "ansi-escapes": "^4.2.1", "chalk": "^4.1.0", "cli-cursor": "^3.1.0", "cli-width": "^3.0.0", "external-editor": "^3.0.3", "figures": "^3.0.0", "lodash": "^4.17.19", "mute-stream": "0.0.8", "run-async": "^2.4.0", "rxjs": "^6.6.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0", "through": "^2.3.6" } }, "sha512-JG3eIAj5V9CwcGvuOmoo6LB9kbAYT8HXffUl6memuszlwDC/qvFAJw49XJ5NROSFNPxp3iQg1GqkFhaY/CR0IA=="],
"is-fullwidth-code-point": ["is-fullwidth-code-point@3.0.0", "", {}, "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="],
"isarray": ["isarray@1.0.0", "", {}, "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ=="],
"isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="],
"jose": ["jose@5.10.0", "", {}, "sha512-s+3Al/p9g32Iq+oqXxkW//7jk2Vig6FF1CFqzVXoTUXt2qz89YWbL+OwS17NFYEvxC35n0FKeGO2LGYSxeM2Gg=="],
"jsonfile": ["jsonfile@4.0.0", "", { "optionalDependencies": { "graceful-fs": "^4.1.6" } }, "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg=="],
"jsonrpc-ts-client": ["jsonrpc-ts-client@0.2.3", "", { "dependencies": { "axios": "^0.24.0", "debug": "^4.3.3" } }, "sha512-9uYpKrZKN3/3+9MYA/0vdhl9/esn59u6I9Qj6ohczxKwJ+e7DD4prf3i2nSdAl0Wlw5gBHZOL3wajSa1uiE16g=="],
"lodash": ["lodash@4.17.21", "", {}, "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="],
"lodash.camelcase": ["lodash.camelcase@4.3.0", "", {}, "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA=="],
"log4js": ["log4js@6.9.1", "", { "dependencies": { "date-format": "^4.0.14", "debug": "^4.3.4", "flatted": "^3.2.7", "rfdc": "^1.3.0", "streamroller": "^3.1.5" } }, "sha512-1somDdy9sChrr9/f4UlzhdaGfDR2c/SaD2a4T7qEkG4jTS57/B3qmnjLYePwQ8cqWnUHZI0iAKxMBpCZICiZ2g=="],
"make-promises-safe": ["make-promises-safe@5.1.0", "", {}, "sha512-AfdZ49rtyhQR/6cqVKGoH7y4ql7XkS5HJI1lZm0/5N6CQosy1eYbBJ/qbhkKHzo17UH7M918Bysf6XB9f3kS1g=="],
"math-intrinsics": ["math-intrinsics@1.1.0", "", {}, "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="],
"memory-stream": ["memory-stream@1.0.0", "", { "dependencies": { "readable-stream": "^3.4.0" } }, "sha512-Wm13VcsPIMdG96dzILfij09PvuS3APtcKNh7M28FsCA/w6+1mjR7hhPmfFNoilX9xU7wTdhsH5lJAm6XNzdtww=="],
"mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="],
"mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="],
"mimic-fn": ["mimic-fn@2.1.0", "", {}, "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg=="],
"mimic-response": ["mimic-response@2.1.0", "", {}, "sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA=="],
"minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="],
"minimist": ["minimist@1.2.8", "", {}, "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA=="],
"minipass": ["minipass@5.0.0", "", {}, "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ=="],
"minizlib": ["minizlib@2.1.2", "", { "dependencies": { "minipass": "^3.0.0", "yallist": "^4.0.0" } }, "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg=="],
"mkdirp": ["mkdirp@1.0.4", "", { "bin": { "mkdirp": "bin/cmd.js" } }, "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw=="],
"mkdirp-classic": ["mkdirp-classic@0.5.3", "", {}, "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A=="],
"ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="],
"mute-stream": ["mute-stream@0.0.8", "", {}, "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA=="],
"nan": ["nan@2.22.2", "", {}, "sha512-DANghxFkS1plDdRsX0X9pm0Z6SJNN6gBdtXfanwoZ8hooC5gosGFSBGRYHUVPz1asKA/kMRqDRdHrluZ61SpBQ=="],
"nanoid": ["nanoid@4.0.2", "", { "bin": { "nanoid": "bin/nanoid.js" } }, "sha512-7ZtY5KTCNheRGfEFxnedV5zFiORN1+Y1N6zvPTnHQd8ENUvfaDBeuJDZb2bN/oXwXxu3qkTXDzy57W5vAmDTBw=="],
"napi-build-utils": ["napi-build-utils@1.0.2", "", {}, "sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg=="],
"neo-async": ["neo-async@2.6.2", "", {}, "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw=="],
"neon-cli": ["neon-cli@0.8.3", "", { "dependencies": { "chalk": "^4.1.0", "command-line-args": "^5.1.1", "command-line-commands": "^3.0.1", "command-line-usage": "^6.1.0", "git-config": "0.0.7", "handlebars": "^4.7.6", "inquirer": "^7.3.3", "make-promises-safe": "^5.1.0", "rimraf": "^3.0.2", "semver": "^7.3.2", "toml": "^3.0.0", "ts-typed-json": "^0.3.2", "validate-npm-package-license": "^3.0.4", "validate-npm-package-name": "^3.0.0" }, "bin": { "neon": "bin/cli.js" } }, "sha512-I44MB8PD0AEyFr/b5icR4sX1tsjdkb2T2uWEStG4Uf5C/jzalZPn7eazbQrW6KDyXNd8bc+LVuOr1v6CGTa1KQ=="],
"node-abi": ["node-abi@2.30.1", "", { "dependencies": { "semver": "^5.4.1" } }, "sha512-/2D0wOQPgaUWzVSVgRMx+trKJRC2UG4SUc4oCJoXx9Uxjtp0Vy3/kt7zcbxHF8+Z/pK3UloLWzBISg72brfy1w=="],
"node-addon-api": ["node-addon-api@7.1.1", "", {}, "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ=="],
"node-api-headers": ["node-api-headers@1.5.0", "", {}, "sha512-Yi/FgnN8IU/Cd6KeLxyHkylBUvDTsSScT0Tna2zTrz8klmc8qF2ppj6Q1LHsmOueJWhigQwR4cO2p0XBGW5IaQ=="],
"node-gyp-build": ["node-gyp-build@4.8.4", "", { "bin": { "node-gyp-build": "bin.js", "node-gyp-build-optional": "optional.js", "node-gyp-build-test": "build-test.js" } }, "sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ=="],
"npm-path": ["npm-path@2.0.4", "", { "dependencies": { "which": "^1.2.10" }, "bin": { "npm-path": "bin/npm-path" } }, "sha512-IFsj0R9C7ZdR5cP+ET342q77uSRdtWOlWpih5eC+lu29tIDbNEgDbzgVJ5UFvYHWhxDZ5TFkJafFioO0pPQjCw=="],
"npm-run-path": ["npm-run-path@3.1.0", "", { "dependencies": { "path-key": "^3.0.0" } }, "sha512-Dbl4A/VfiVGLgQv29URL9xshU8XDY1GeLy+fsaZ1AA8JDSfjvr5P5+pzRbWqRSBxk6/DW7MIh8lTM/PaGnP2kg=="],
"npm-which": ["npm-which@3.0.1", "", { "dependencies": { "commander": "^2.9.0", "npm-path": "^2.0.2", "which": "^1.2.10" }, "bin": { "npm-which": "bin/npm-which.js" } }, "sha512-CM8vMpeFQ7MAPin0U3wzDhSGV0hMHNwHU0wjo402IVizPDrs45jSfSuoC+wThevY88LQti8VvaAnqYAeVy3I1A=="],
"npmlog": ["npmlog@4.1.2", "", { "dependencies": { "are-we-there-yet": "~1.1.2", "console-control-strings": "~1.1.0", "gauge": "~2.7.3", "set-blocking": "~2.0.0" } }, "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg=="],
"number-is-nan": ["number-is-nan@1.0.1", "", {}, "sha512-4jbtZXNAsfZbAHiiqjLPBiCl16dES1zI4Hpzzxw61Tk+loF+sBDBKx1ICKKKwIqQ7M0mFn1TmkN7euSncWgHiQ=="],
"object-assign": ["object-assign@4.1.1", "", {}, "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="],
"once": ["once@1.4.0", "", { "dependencies": { "wrappy": "1" } }, "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w=="],
"onetime": ["onetime@5.1.2", "", { "dependencies": { "mimic-fn": "^2.1.0" } }, "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg=="],
"openapi-types": ["openapi-types@12.1.3", "", {}, "sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw=="],
"os-tmpdir": ["os-tmpdir@1.0.2", "", {}, "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g=="],
"path-is-absolute": ["path-is-absolute@1.0.1", "", {}, "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg=="],
"path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="],
"prebuild-install": ["prebuild-install@6.1.4", "", { "dependencies": { "detect-libc": "^1.0.3", "expand-template": "^2.0.3", "github-from-package": "0.0.0", "minimist": "^1.2.3", "mkdirp-classic": "^0.5.3", "napi-build-utils": "^1.0.1", "node-abi": "^2.21.0", "npmlog": "^4.0.1", "pump": "^3.0.0", "rc": "^1.2.7", "simple-get": "^3.0.3", "tar-fs": "^2.0.0", "tunnel-agent": "^0.6.0" }, "bin": { "prebuild-install": "bin.js" } }, "sha512-Z4vpywnK1lBg+zdPCVCsKq0xO66eEV9rWo2zrROGGiRS4JtueBOdlB1FnY8lcy7JsUud/Q3ijUxyWN26Ika0vQ=="],
"prebuildify": ["prebuildify@github:einhornimmond/prebuildify#91f4e76", { "dependencies": { "cmake-js": "^7.2.1", "execspawn": "^1.0.1", "minimist": "^1.2.5", "mkdirp-classic": "^0.5.3", "node-abi": "^3.3.0", "npm-run-path": "^3.1.0", "npm-which": "^3.0.1", "pump": "^3.0.0", "tar-fs": "^2.1.0" }, "bin": { "prebuildify": "./bin.js" } }, "einhornimmond-prebuildify-91f4e76"],
"process-nextick-args": ["process-nextick-args@2.0.1", "", {}, "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="],
"proxy-from-env": ["proxy-from-env@1.1.0", "", {}, "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="],
"pump": ["pump@3.0.3", "", { "dependencies": { "end-of-stream": "^1.1.0", "once": "^1.3.1" } }, "sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA=="],
"raikiri": ["raikiri@0.0.0-beta.8", "", {}, "sha512-cH/yfvkiGkN8IBB2MkRHikpPurTnd2sMkQ/xtGpXrp3O76P4ppcWPb+86mJaBDzKaclLnSX+9NnT79D7ifH4/w=="],
"rc": ["rc@1.2.8", "", { "dependencies": { "deep-extend": "^0.6.0", "ini": "~1.3.0", "minimist": "^1.2.0", "strip-json-comments": "~2.0.1" }, "bin": { "rc": "./cli.js" } }, "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw=="],
"readable-stream": ["readable-stream@2.3.8", "", { "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", "isarray": "~1.0.0", "process-nextick-args": "~2.0.0", "safe-buffer": "~5.1.1", "string_decoder": "~1.1.1", "util-deprecate": "~1.0.1" } }, "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA=="],
"reduce-flatten": ["reduce-flatten@2.0.0", "", {}, "sha512-EJ4UNY/U1t2P/2k6oqotuX2Cc3T6nxJwsM0N0asT7dhrtH1ltUxDn4NalSYmPE2rCkVpcf/X6R0wDwcFpzhd4w=="],
"require-directory": ["require-directory@2.1.1", "", {}, "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q=="],
"restore-cursor": ["restore-cursor@3.1.0", "", { "dependencies": { "onetime": "^5.1.0", "signal-exit": "^3.0.2" } }, "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA=="],
"rfdc": ["rfdc@1.4.1", "", {}, "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA=="],
"rimraf": ["rimraf@3.0.2", "", { "dependencies": { "glob": "^7.1.3" }, "bin": { "rimraf": "bin.js" } }, "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA=="],
"run-async": ["run-async@2.4.1", "", {}, "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ=="],
"rxjs": ["rxjs@6.6.7", "", { "dependencies": { "tslib": "^1.9.0" } }, "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ=="],
"safe-buffer": ["safe-buffer@5.2.1", "", {}, "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="],
"safer-buffer": ["safer-buffer@2.1.2", "", {}, "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="],
"semver": ["semver@7.7.2", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA=="],
"set-blocking": ["set-blocking@2.0.0", "", {}, "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw=="],
"signal-exit": ["signal-exit@3.0.7", "", {}, "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="],
"simple-concat": ["simple-concat@1.0.1", "", {}, "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q=="],
"simple-get": ["simple-get@3.1.1", "", { "dependencies": { "decompress-response": "^4.2.0", "once": "^1.3.1", "simple-concat": "^1.0.0" } }, "sha512-CQ5LTKGfCpvE1K0n2us+kuMPbk/q0EKl82s4aheV9oXjFEz6W/Y7oQFVJuU6QG77hRT4Ghb5RURteF5vnWjupA=="],
"source-map": ["source-map@0.6.1", "", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="],
"spdx-correct": ["spdx-correct@3.2.0", "", { "dependencies": { "spdx-expression-parse": "^3.0.0", "spdx-license-ids": "^3.0.0" } }, "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA=="],
"spdx-exceptions": ["spdx-exceptions@2.5.0", "", {}, "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w=="],
"spdx-expression-parse": ["spdx-expression-parse@3.0.1", "", { "dependencies": { "spdx-exceptions": "^2.1.0", "spdx-license-ids": "^3.0.0" } }, "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q=="],
"spdx-license-ids": ["spdx-license-ids@3.0.21", "", {}, "sha512-Bvg/8F5XephndSK3JffaRqdT+gyhfqIPwDHpX80tJrF8QQRYMo8sNMeaZ2Dp5+jhwKnUmIOyFFQfHRkjJm5nXg=="],
"streamroller": ["streamroller@3.1.5", "", { "dependencies": { "date-format": "^4.0.14", "debug": "^4.3.4", "fs-extra": "^8.1.0" } }, "sha512-KFxaM7XT+irxvdqSP1LGLgNWbYN7ay5owZ3r/8t77p+EtSUAfUgtl7be3xtqtOmGUl9K9YPO2ca8133RlTjvKw=="],
"string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="],
"string_decoder": ["string_decoder@1.1.1", "", { "dependencies": { "safe-buffer": "~5.1.0" } }, "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg=="],
"strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
"strip-json-comments": ["strip-json-comments@2.0.1", "", {}, "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ=="],
"strtok3": ["strtok3@10.3.1", "", { "dependencies": { "@tokenizer/token": "^0.3.0" } }, "sha512-3JWEZM6mfix/GCJBBUrkA8p2Id2pBkyTkVCJKto55w080QBKZ+8R171fGrbiSp+yMO/u6F8/yUh7K4V9K+YCnw=="],
"supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="],
"table-layout": ["table-layout@1.0.2", "", { "dependencies": { "array-back": "^4.0.1", "deep-extend": "~0.6.0", "typical": "^5.2.0", "wordwrapjs": "^4.0.0" } }, "sha512-qd/R7n5rQTRFi+Zf2sk5XVVd9UQl6ZkduPFC3S7WEGJAmetDTjY3qPN50eSKzwuzEyQKy5TN2TiZdkIjos2L6A=="],
"tar": ["tar@6.2.1", "", { "dependencies": { "chownr": "^2.0.0", "fs-minipass": "^2.0.0", "minipass": "^5.0.0", "minizlib": "^2.1.1", "mkdirp": "^1.0.3", "yallist": "^4.0.0" } }, "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A=="],
"tar-fs": ["tar-fs@2.1.3", "", { "dependencies": { "chownr": "^1.1.1", "mkdirp-classic": "^0.5.2", "pump": "^3.0.0", "tar-stream": "^2.1.4" } }, "sha512-090nwYJDmlhwFwEW3QQl+vaNnxsO2yVsd45eTKRBzSzu+hlb1w2K9inVq5b0ngXuLVqQ4ApvsUHHnu/zQNkWAg=="],
"tar-stream": ["tar-stream@2.2.0", "", { "dependencies": { "bl": "^4.0.3", "end-of-stream": "^1.4.1", "fs-constants": "^1.0.0", "inherits": "^2.0.3", "readable-stream": "^3.1.1" } }, "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ=="],
"through": ["through@2.3.8", "", {}, "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg=="],
"tmp": ["tmp@0.0.33", "", { "dependencies": { "os-tmpdir": "~1.0.2" } }, "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw=="],
"token-types": ["token-types@6.0.3", "", { "dependencies": { "@tokenizer/token": "^0.3.0", "ieee754": "^1.2.1" } }, "sha512-IKJ6EzuPPWtKtEIEPpIdXv9j5j2LGJEYk0CKY2efgKoYKLBiZdh6iQkLVBow/CB3phyWAWCyk+bZeaimJn6uRQ=="],
"toml": ["toml@3.0.0", "", {}, "sha512-y/mWCZinnvxjTKYhJ+pYxwD0mRLVvOtdS2Awbgxln6iEnt4rk0yBxeSBHkGJcPucRiG0e55mwWp+g/05rsrd6w=="],
"ts-typed-json": ["ts-typed-json@0.3.2", "", {}, "sha512-Tdu3BWzaer7R5RvBIJcg9r8HrTZgpJmsX+1meXMJzYypbkj8NK2oJN0yvm4Dp/Iv6tzFa/L5jKRmEVTga6K3nA=="],
"tslib": ["tslib@1.14.1", "", {}, "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="],
"tunnel-agent": ["tunnel-agent@0.6.0", "", { "dependencies": { "safe-buffer": "^5.0.1" } }, "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w=="],
"type-fest": ["type-fest@0.21.3", "", {}, "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w=="],
"typescript": ["typescript@5.8.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ=="],
"typical": ["typical@4.0.0", "", {}, "sha512-VAH4IvQ7BDFYglMd7BPRDfLgxZZX4O4TFcRDA6EN5X7erNJJq+McIEp8np9aVtxrCJ6qx4GTYVfOWNjcqwZgRw=="],
"uglify-js": ["uglify-js@3.19.3", "", { "bin": { "uglifyjs": "bin/uglifyjs" } }, "sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ=="],
"uint8array-extras": ["uint8array-extras@1.4.0", "", {}, "sha512-ZPtzy0hu4cZjv3z5NW9gfKnNLjoz4y6uv4HlelAjDK7sY/xOkKZv9xK/WQpcsBB3jEybChz9DPC2U/+cusjJVQ=="],
"undici-types": ["undici-types@7.8.0", "", {}, "sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw=="],
"universalify": ["universalify@0.1.2", "", {}, "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg=="],
"url-join": ["url-join@4.0.1", "", {}, "sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA=="],
"util-deprecate": ["util-deprecate@1.0.2", "", {}, "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="],
"util-extend": ["util-extend@1.0.3", "", {}, "sha512-mLs5zAK+ctllYBj+iAQvlDCwoxU/WDOUaJkcFudeiAX6OajC6BKXJUa9a+tbtkC11dz2Ufb7h0lyvIOVn4LADA=="],
"uuid": ["uuid@8.3.2", "", { "bin": { "uuid": "dist/bin/uuid" } }, "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg=="],
"valibot": ["valibot@1.1.0", "", { "peerDependencies": { "typescript": ">=5" }, "optionalPeers": ["typescript"] }, "sha512-Nk8lX30Qhu+9txPYTwM0cFlWLdPFsFr6LblzqIySfbZph9+BFsAHsNvHOymEviUepeIW6KFHzpX8TKhbptBXXw=="],
"validate-npm-package-license": ["validate-npm-package-license@3.0.4", "", { "dependencies": { "spdx-correct": "^3.0.0", "spdx-expression-parse": "^3.0.0" } }, "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew=="],
"validate-npm-package-name": ["validate-npm-package-name@3.0.0", "", { "dependencies": { "builtins": "^1.0.3" } }, "sha512-M6w37eVCMMouJ9V/sdPGnC5H4uDr73/+xdq0FBLO3TFFX1+7wiUY6Es328NN+y43tmY+doUdN9g9J21vqB7iLw=="],
"which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="],
"wide-align": ["wide-align@1.1.5", "", { "dependencies": { "string-width": "^1.0.2 || 2 || 3 || 4" } }, "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg=="],
"wordwrap": ["wordwrap@1.0.0", "", {}, "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q=="],
"wordwrapjs": ["wordwrapjs@4.0.1", "", { "dependencies": { "reduce-flatten": "^2.0.0", "typical": "^5.2.0" } }, "sha512-kKlNACbvHrkpIw6oPeYDSmdCTu2hdMHoyXLTcUKala++lx5Y+wjJ/e474Jqv5abnVmwxw08DiTuHmw69lJGksA=="],
"wrap-ansi": ["wrap-ansi@7.0.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q=="],
"wrappy": ["wrappy@1.0.2", "", {}, "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="],
"y18n": ["y18n@5.0.8", "", {}, "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA=="],
"yallist": ["yallist@4.0.0", "", {}, "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="],
"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@21.1.1", "", {}, "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw=="],
"zod": ["zod@3.25.67", "", {}, "sha512-idA2YXwpCdqUSKRCACDE6ItZD9TZzy3OZMtpfLoh6oPR47lipysRrJfjzMqFxQ3uJuUPyUeWe1r9vLH33xO/Qw=="],
"bl/readable-stream": ["readable-stream@3.6.2", "", { "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", "util-deprecate": "^1.0.1" } }, "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA=="],
"cmake-js/axios": ["axios@1.10.0", "", { "dependencies": { "follow-redirects": "^1.15.6", "form-data": "^4.0.0", "proxy-from-env": "^1.1.0" } }, "sha512-/1xYAC4MP/HEG+3duIhFr4ZQXR4sQXOIe+o6sdqzeykGLx6Upp/1p8MHqhINOvGeP7xyNHe7tsiJByc4SSVUxw=="],
"cmake-js/fs-extra": ["fs-extra@11.3.0", "", { "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", "universalify": "^2.0.0" } }, "sha512-Z4XaCL6dUDHfP/jT25jJKMmtxvuwbkrD1vNSMFlo9lNLY2c5FHYSQgHPRZUjAB26TpDEoW9HCOgplrdbaPV/ew=="],
"cmake-js/npmlog": ["npmlog@6.0.2", "", { "dependencies": { "are-we-there-yet": "^3.0.0", "console-control-strings": "^1.1.0", "gauge": "^4.0.3", "set-blocking": "^2.0.0" } }, "sha512-/vBvz5Jfr9dT/aFWd0FIRf+T/Q2WBsLENygUaFUqstqsycmZAP/t5BvFJTK0viFmSUxiUKTUplWy5vt+rvKIxg=="],
"command-line-commands/array-back": ["array-back@4.0.2", "", {}, "sha512-NbdMezxqf94cnNfWLL7V/im0Ub+Anbb0IoZhvzie8+4HJ4nMQuzHuy49FkGYCJK2yAloZ3meiB6AVMClbrI1vg=="],
"command-line-usage/array-back": ["array-back@4.0.2", "", {}, "sha512-NbdMezxqf94cnNfWLL7V/im0Ub+Anbb0IoZhvzie8+4HJ4nMQuzHuy49FkGYCJK2yAloZ3meiB6AVMClbrI1vg=="],
"command-line-usage/chalk": ["chalk@2.4.2", "", { "dependencies": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", "supports-color": "^5.3.0" } }, "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ=="],
"command-line-usage/typical": ["typical@5.2.0", "", {}, "sha512-dvdQgNDNJo+8B2uBQoqdb11eUCE1JQXhvjC/CZtgvZseVd5TYMXnq0+vuUemXbd/Se29cTaUuPX3YIc2xgbvIg=="],
"fs-minipass/minipass": ["minipass@3.3.6", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw=="],
"gauge/string-width": ["string-width@1.0.2", "", { "dependencies": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", "strip-ansi": "^3.0.0" } }, "sha512-0XsVpQLnVCXHJfyEs8tC0zpTVIr5PKKsQtkT29IwupnPTjtPmQ3xT/4yCREF9hYkV/3M3kzcUTSAZT6a6h81tw=="],
"gauge/strip-ansi": ["strip-ansi@3.0.1", "", { "dependencies": { "ansi-regex": "^2.0.0" } }, "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg=="],
"memory-stream/readable-stream": ["readable-stream@3.6.2", "", { "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", "util-deprecate": "^1.0.1" } }, "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA=="],
"minizlib/minipass": ["minipass@3.3.6", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw=="],
"node-abi/semver": ["semver@5.7.2", "", { "bin": { "semver": "bin/semver" } }, "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g=="],
"npm-path/which": ["which@1.3.1", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "which": "./bin/which" } }, "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ=="],
"npm-which/which": ["which@1.3.1", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "which": "./bin/which" } }, "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ=="],
"prebuildify/node-abi": ["node-abi@3.75.0", "", { "dependencies": { "semver": "^7.3.5" } }, "sha512-OhYaY5sDsIka7H7AtijtI9jwGYLyl29eQn/W623DiN/MIv5sUqc4g7BIDThX+gb7di9f6xK02nkp8sdfFWZLTg=="],
"readable-stream/safe-buffer": ["safe-buffer@5.1.2", "", {}, "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="],
"string_decoder/safe-buffer": ["safe-buffer@5.1.2", "", {}, "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="],
"table-layout/array-back": ["array-back@4.0.2", "", {}, "sha512-NbdMezxqf94cnNfWLL7V/im0Ub+Anbb0IoZhvzie8+4HJ4nMQuzHuy49FkGYCJK2yAloZ3meiB6AVMClbrI1vg=="],
"table-layout/typical": ["typical@5.2.0", "", {}, "sha512-dvdQgNDNJo+8B2uBQoqdb11eUCE1JQXhvjC/CZtgvZseVd5TYMXnq0+vuUemXbd/Se29cTaUuPX3YIc2xgbvIg=="],
"tar/chownr": ["chownr@2.0.0", "", {}, "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ=="],
"tar-stream/readable-stream": ["readable-stream@3.6.2", "", { "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", "util-deprecate": "^1.0.1" } }, "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA=="],
"wordwrapjs/typical": ["typical@5.2.0", "", {}, "sha512-dvdQgNDNJo+8B2uBQoqdb11eUCE1JQXhvjC/CZtgvZseVd5TYMXnq0+vuUemXbd/Se29cTaUuPX3YIc2xgbvIg=="],
"bl/readable-stream/string_decoder": ["string_decoder@1.3.0", "", { "dependencies": { "safe-buffer": "~5.2.0" } }, "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA=="],
"cmake-js/fs-extra/jsonfile": ["jsonfile@6.1.0", "", { "dependencies": { "universalify": "^2.0.0" }, "optionalDependencies": { "graceful-fs": "^4.1.6" } }, "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ=="],
"cmake-js/fs-extra/universalify": ["universalify@2.0.1", "", {}, "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw=="],
"cmake-js/npmlog/are-we-there-yet": ["are-we-there-yet@3.0.1", "", { "dependencies": { "delegates": "^1.0.0", "readable-stream": "^3.6.0" } }, "sha512-QZW4EDmGwlYur0Yyf/b2uGucHQMa8aFUP7eu9ddR73vvhFyt4V0Vl3QHPcTNJ8l6qYOBdxgXdnBXQrHilfRQBg=="],
"cmake-js/npmlog/gauge": ["gauge@4.0.4", "", { "dependencies": { "aproba": "^1.0.3 || ^2.0.0", "color-support": "^1.1.3", "console-control-strings": "^1.1.0", "has-unicode": "^2.0.1", "signal-exit": "^3.0.7", "string-width": "^4.2.3", "strip-ansi": "^6.0.1", "wide-align": "^1.1.5" } }, "sha512-f9m+BEN5jkg6a0fZjleidjN51VE1X+mPFQ2DJ0uv1V39oCLCbsGe6yjbBnp7eK7z/+GAon99a3nHuqbuuthyPg=="],
"command-line-usage/chalk/ansi-styles": ["ansi-styles@3.2.1", "", { "dependencies": { "color-convert": "^1.9.0" } }, "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA=="],
"command-line-usage/chalk/supports-color": ["supports-color@5.5.0", "", { "dependencies": { "has-flag": "^3.0.0" } }, "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow=="],
"gauge/string-width/is-fullwidth-code-point": ["is-fullwidth-code-point@1.0.0", "", { "dependencies": { "number-is-nan": "^1.0.0" } }, "sha512-1pqUqRjkhPJ9miNq9SwMfdvi6lBJcd6eFxvfaivQhaH3SgisfiuudvFntdKOmxuee/77l+FPjKrQjWvmPjWrRw=="],
"gauge/strip-ansi/ansi-regex": ["ansi-regex@2.1.1", "", {}, "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA=="],
"memory-stream/readable-stream/string_decoder": ["string_decoder@1.3.0", "", { "dependencies": { "safe-buffer": "~5.2.0" } }, "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA=="],
"tar-stream/readable-stream/string_decoder": ["string_decoder@1.3.0", "", { "dependencies": { "safe-buffer": "~5.2.0" } }, "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA=="],
"cmake-js/npmlog/are-we-there-yet/readable-stream": ["readable-stream@3.6.2", "", { "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", "util-deprecate": "^1.0.1" } }, "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA=="],
"command-line-usage/chalk/ansi-styles/color-convert": ["color-convert@1.9.3", "", { "dependencies": { "color-name": "1.1.3" } }, "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg=="],
"command-line-usage/chalk/supports-color/has-flag": ["has-flag@3.0.0", "", {}, "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw=="],
"cmake-js/npmlog/are-we-there-yet/readable-stream/string_decoder": ["string_decoder@1.3.0", "", { "dependencies": { "safe-buffer": "~5.2.0" } }, "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA=="],
"command-line-usage/chalk/ansi-styles/color-convert/color-name": ["color-name@1.1.3", "", {}, "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw=="],
}
}

View File

@ -1,37 +0,0 @@
/** @type {import('ts-jest/dist/types').InitialOptionsTsJest} */
module.exports = {
verbose: true,
preset: 'ts-jest',
collectCoverage: true,
collectCoverageFrom: ['src/**/*.ts', '!**/node_modules/**', '!src/seeds/**', '!build/**'],
coverageThreshold: {
global: {
lines: 72,
},
},
setupFiles: ['<rootDir>/test/testSetup.ts'],
setupFilesAfterEnv: [],
modulePathIgnorePatterns: ['<rootDir>/build/'],
moduleNameMapper: {
'@/(.*)': '<rootDir>/src/$1',
'@arg/(.*)': '<rootDir>/src/graphql/arg/$1',
'@controller/(.*)': '<rootDir>/src/controller/$1',
'@enum/(.*)': '<rootDir>/src/graphql/enum/$1',
'@model/(.*)': '<rootDir>/src/graphql/model/$1',
'@resolver/(.*)': '<rootDir>/src/graphql/resolver/$1',
'@input/(.*)': '<rootDir>/src/graphql/input/$1',
'@proto/(.*)': '<rootDir>/src/proto/$1',
'@test/(.*)': '<rootDir>/test/$1',
'@client/(.*)': '<rootDir>/src/client/$1',
'@validator/(.*)': '<rootDir>/src/graphql/validator/$1',
},
}
/*
@arg/*": ["src/graphql/arg/*"],
"@enum/*": ["src/graphql/enum/*"],
"@input/*": ["src/graphql/input/*"],
"@resolver/*": ["src/graphql/resolver/*"],
"@scalar/*": ["src/graphql/scalar/*"],
"@test/*": ["test/*"],
"@proto/*" : ["src/proto/*"],
*/

View File

@ -8,7 +8,7 @@
"pattern": "yyyy-MM-dd",
"layout":
{
"type": "pattern", "pattern": "%d{ISO8601} %p %c [%X{user}] [%f : %l] - %m"
"type": "pattern", "pattern": "%d{ISO8601} %p %c [%X{url}] [%f : %l] - %m"
},
"compress": true,
"keepFileExt" : true,
@ -22,7 +22,7 @@
"pattern": "yyyy-MM-dd",
"layout":
{
"type": "pattern", "pattern": "%d{ISO8601} %p %c [%X{user}] [%f : %l] - %m"
"type": "pattern", "pattern": "%d{ISO8601} %p %c [%X{url}] [%f : %l] - %m"
},
"compress": true,
"keepFileExt" : true,
@ -40,7 +40,7 @@
"type": "stdout",
"layout":
{
"type": "pattern", "pattern": "%d{ISO8601} %p %c [%X{user}] [%f : %l] - %m"
"type": "pattern", "pattern": "%d{ISO8601} %p %c [%X{url}] [%f : %l] - %m"
}
}
},

View File

@ -1,73 +1,42 @@
{
"name": "gradido-dlt-connector",
"version": "2.6.0",
"description": "Gradido DLT-Connector",
"main": "src/index.ts",
"repository": "https://github.com/gradido/gradido/",
"author": "Dario Rekowski",
"license": "Apache-2.0",
"private": false,
"name": "dlt-connector",
"version": "1.0.50",
"scripts": {
"build": "tsc --build",
"clean": "tsc --build --clean",
"start": "cross-env TZ=UTC TS_NODE_BASEURL=./build node -r tsconfig-paths/register build/src/index.js",
"dev": "cross-env TZ=UTC nodemon -w src --ext ts --exec ts-node -r dotenv/config -r tsconfig-paths/register src/index.ts",
"lint": "eslint --max-warnings=0 --ext .js,.ts .",
"test": "cross-env TZ=UTC NODE_ENV=development jest --forceExit --detectOpenHandles"
"start": "bun run src/index.ts",
"build": "bun build src/index.ts --outdir=build --target=bun --external=gradido-blockchain-js --external=@iota/client",
"dev": "bun run --watch src/index.ts",
"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"
},
"dependencies": {
"@apollo/server": "^4.7.5",
"@apollo/utils.fetcher": "^3.0.0",
"@iota/client": "^2.2.4",
"body-parser": "^1.20.2",
"class-validator": "^0.14.0",
"cors": "^2.8.5",
"cross-env": "^7.0.3",
"dotenv": "10.0.0",
"express": "4.17.1",
"express-slow-down": "^2.0.1",
"gradido-blockchain-js": "git+https://github.com/gradido/gradido-blockchain-js#1c75576",
"graphql": "^16.7.1",
"graphql-request": "^6.1.0",
"graphql-scalars": "^1.22.2",
"helmet": "^7.1.0",
"jose": "^5.2.2",
"jsonrpc-ts-client": "^0.2.3",
"log4js": "^6.7.1",
"nodemon": "^2.0.20",
"reflect-metadata": "^0.1.13",
"tsconfig-paths": "^4.1.2",
"type-graphql": "^2.0.0-beta.2",
"uuid": "^9.0.1"
"gradido-blockchain-js": "git+https://github.com/gradido/gradido-blockchain-js#217d03b",
"@iota/client": "^2.2.4"
},
"devDependencies": {
"@eslint-community/eslint-plugin-eslint-comments": "^3.2.1",
"@graphql-tools/mock": "^9.0.0",
"@types/cors": "^2.8.13",
"@types/jest": "^27.0.2",
"@types/node": "^18.11.18",
"@types/sodium-native": "^2.3.5",
"@types/uuid": "^8.3.4",
"@typescript-eslint/eslint-plugin": "^5.57.1",
"@typescript-eslint/parser": "^5.57.1",
"eslint": "^8.37.0",
"eslint-config-prettier": "^8.8.0",
"eslint-config-standard": "^17.0.0",
"eslint-import-resolver-typescript": "^3.5.4",
"eslint-plugin-dci-lint": "^0.3.0",
"eslint-plugin-import": "^2.27.5",
"eslint-plugin-jest": "^27.2.1",
"eslint-plugin-n": "^15.7.0",
"eslint-plugin-prettier": "^4.2.1",
"eslint-plugin-promise": "^6.1.1",
"eslint-plugin-security": "^1.7.1",
"jest": "^27.2.4",
"prettier": "^2.8.7",
"ts-jest": "^27.0.5",
"ts-node": "^10.9.1",
"typescript": "^4.9.4"
"@biomejs/biome": "2.0.0",
"@elysiajs/trpc": "^1.1.0",
"@elysiajs/websocket": "^0.2.8",
"@trpc/server": "^11.4.3",
"@types/bun": "^1.2.17",
"dotenv": "^10.0.0",
"elysia": "^1.3.5",
"graphql-request": "^7.2.0",
"jose": "^5.2.2",
"jsonrpc-ts-client": "^0.2.3",
"log4js": "^6.9.1",
"typescript": "^5.8.3",
"uuid": "^8.3.2",
"valibot": "^1.1.0",
"zod": "^3.25.61"
},
"engines": {
"node": ">=14"
}
"node": ">=18"
},
"module": "src/index.js",
"trustedDependencies": [
"@iota/client"
]
}

View File

@ -1,98 +0,0 @@
# -----------------------------------------------
# !!! THIS FILE WAS GENERATED BY TYPE-GRAPHQL !!!
# !!! DO NOT MODIFY THIS FILE BY YOURSELF !!!
# -----------------------------------------------
type Community {
confirmedAt: String!
createdAt: String!
foreign: Boolean!
id: Int!
iotaTopic: String!
rootPublicKeyHex: String!
}
input CommunityDraft {
createdAt: String!
foreign: Boolean!
uuid: String!
}
"""The `Decimal` scalar type to represent currency values"""
scalar Decimal
"""Type of the transaction"""
enum InputTransactionType {
CREATION
RECEIVE
SEND
}
type Mutation {
addCommunity(data: CommunityDraft!): TransactionResult!
sendTransaction(data: TransactionDraft!): TransactionResult!
}
type Query {
communities(confirmed: Boolean, foreign: Boolean, uuid: String): [Community!]!
community(confirmed: Boolean, foreign: Boolean, uuid: String): Community!
isCommunityExist(confirmed: Boolean, foreign: Boolean, uuid: String): Boolean!
}
input TransactionDraft {
amount: Decimal!
backendTransactionId: Int!
createdAt: String!
recipientUser: UserIdentifier!
senderUser: UserIdentifier!
targetDate: String
type: InputTransactionType!
}
type TransactionError {
message: String!
name: String!
type: TransactionErrorType!
}
"""Transaction Error Type"""
enum TransactionErrorType {
ALREADY_EXIST
DB_ERROR
INVALID_SIGNATURE
LOGIC_ERROR
MISSING_PARAMETER
NOT_FOUND
NOT_IMPLEMENTED_YET
PROTO_DECODE_ERROR
PROTO_ENCODE_ERROR
}
type TransactionRecipe {
createdAt: String!
id: Int!
topic: String!
type: TransactionType!
}
type TransactionResult {
error: TransactionError
recipe: TransactionRecipe
succeed: Boolean!
}
"""Type of the transaction"""
enum TransactionType {
COMMUNITY_ROOT
GRADIDO_CREATION
GRADIDO_DEFERRED_TRANSFER
GRADIDO_TRANSFER
GROUP_FRIENDS_UPDATE
REGISTER_ADDRESS
}
input UserIdentifier {
accountNr: Int = 1
communityUuid: String
uuid: String!
}

View File

@ -1,7 +1,8 @@
import { KeyPairEd25519 } from 'gradido-blockchain-js'
import { KeyPairIdentifier } from '@/data/KeyPairIdentifier'
import { logger } from '@/logging/logger'
import { KeyPairIdentifier } from './data/KeyPairIdentifier.logic'
import { getLogger, Logger } from 'log4js'
import { LOG4JS_BASE_CATEGORY } from './config/const'
// Source: https://refactoring.guru/design-patterns/singleton/typescript/example
// and ../federation/client/FederationClientFactory.ts
@ -9,19 +10,19 @@ import { logger } from '@/logging/logger'
* A Singleton class defines the `getInstance` method that lets clients access
* the unique singleton instance.
*/
// eslint-disable-next-line @typescript-eslint/no-extraneous-class
export class KeyPairCacheManager {
// eslint-disable-next-line no-use-before-define
private static instance: KeyPairCacheManager
private cache: Map<string, KeyPairEd25519> = new Map<string, KeyPairEd25519>()
private homeCommunityUUID: string
private homeCommunityUUID: string | undefined
private logger: Logger
/**
* The Singleton's constructor should always be private to prevent direct
* construction calls with the `new` operator.
*/
// eslint-disable-next-line no-useless-constructor, @typescript-eslint/no-empty-function
private constructor() {}
private constructor() {
this.logger = getLogger(`${LOG4JS_BASE_CATEGORY}.client.KeyPairCacheManager`)
}
/**
* The static method that controls the access to the singleton instance.
@ -41,22 +42,34 @@ export class KeyPairCacheManager {
}
public getHomeCommunityUUID(): string {
if (!this.homeCommunityUUID) {
throw new Error('home community uuid is not set')
}
return this.homeCommunityUUID
}
public findKeyPair(input: KeyPairIdentifier): KeyPairEd25519 | undefined {
return this.cache.get(input.getKey())
public findKeyPair(input: string): KeyPairEd25519 | undefined {
return this.cache.get(input)
}
public addKeyPair(input: KeyPairIdentifier, keyPair: KeyPairEd25519): void {
const key = input.getKey()
if (this.cache.has(key)) {
logger.warn('key already exist, cannot add', {
key,
public addKeyPair(input: string, keyPair: KeyPairEd25519): void {
if (this.cache.has(input)) {
this.logger.warn('key already exist, cannot add', {
key: input,
publicKey: keyPair.getPublicKey()?.convertToHex(),
})
return
}
this.cache.set(key, keyPair)
this.cache.set(input, keyPair)
}
public async getKeyPair(input: string, createKeyPair: () => Promise<KeyPairEd25519>): Promise<KeyPairEd25519> {
const keyPair = this.cache.get(input)
if (!keyPair) {
const keyPair = await createKeyPair()
this.cache.set(input, keyPair)
return keyPair
}
return keyPair
}
}

View File

@ -1,12 +1,11 @@
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
import { gql, GraphQLClient } from 'graphql-request'
import { SignJWT } from 'jose'
import { CONFIG } from '@/config'
import { CommunityDraft } from '@/graphql/input/CommunityDraft'
import { logger } from '@/logging/logger'
import { LogError } from '@/server/LogError'
import { CONFIG } from '../config'
import { communitySchema, type Community } from '../schemas/rpcParameter.schema'
import { getLogger, Logger } from 'log4js'
import { LOG4JS_BASE_CATEGORY } from '../config/const'
import * as v from 'valibot'
const homeCommunity = gql`
query {
@ -17,30 +16,36 @@ const homeCommunity = gql`
}
}
`
interface Community {
homeCommunity: {
uuid: string
foreign: boolean
creationDate: string
}
}
// Source: https://refactoring.guru/design-patterns/singleton/typescript/example
// and ../federation/client/FederationClientFactory.ts
/**
* A Singleton class defines the `getInstance` method that lets clients access
* the unique singleton instance.
*/
// eslint-disable-next-line @typescript-eslint/no-extraneous-class
export class BackendClient {
// eslint-disable-next-line no-use-before-define
private static instance: BackendClient
client: GraphQLClient
logger: Logger
/**
* The Singleton's constructor should always be private to prevent direct
* construction calls with the `new` operator.
*/
// eslint-disable-next-line no-useless-constructor, @typescript-eslint/no-empty-function
private constructor() {}
private constructor() {
this.logger = getLogger(`${LOG4JS_BASE_CATEGORY}.client.BackendClient`)
this.logger.addContext('url', CONFIG.BACKEND_SERVER_URL)
this.client = new GraphQLClient(CONFIG.BACKEND_SERVER_URL, {
headers: {
'content-type': 'application/json',
},
method: 'GET',
jsonSerializer: {
parse: JSON.parse,
stringify: JSON.stringify,
},
})
}
/**
* The static method that controls the access to the singleton instance.
@ -51,44 +56,29 @@ export class BackendClient {
public static getInstance(): BackendClient | undefined {
if (!BackendClient.instance) {
BackendClient.instance = new BackendClient()
}
if (!BackendClient.instance.client) {
try {
BackendClient.instance.client = new GraphQLClient(CONFIG.BACKEND_SERVER_URL, {
headers: {
'content-type': 'application/json',
},
method: 'GET',
jsonSerializer: {
parse: JSON.parse,
stringify: JSON.stringify,
},
})
} catch (e) {
logger.error("couldn't connect to backend: ", e)
return
}
}
}
return BackendClient.instance
}
public async getHomeCommunityDraft(): Promise<CommunityDraft> {
logger.info('check home community on backend')
const { data, errors } = await this.client.rawRequest<Community>(
public async getHomeCommunityDraft(): Promise<Community> {
this.logger.info('check home community on backend')
const { data, errors } = await this.client.rawRequest<{ homeCommunity: Community }>(
homeCommunity,
{},
{
authorization: 'Bearer ' + (await this.createJWTToken()),
},
{}, // empty variables
await this.getRequestHeader(),
)
if (errors) {
throw new LogError('error getting home community from backend', errors)
throw errors[0]
}
return v.parse(communitySchema, data.homeCommunity)
}
private async getRequestHeader(): Promise<{
authorization: string
}> {
return {
authorization: 'Bearer ' + (await this.createJWTToken()),
}
const communityDraft = new CommunityDraft()
communityDraft.uuid = data.homeCommunity.uuid
communityDraft.foreign = data.homeCommunity.foreign
communityDraft.createdAt = data.homeCommunity.creationDate
return communityDraft
}
private async createJWTToken(): Promise<string> {

View File

@ -1,156 +0,0 @@
/* eslint-disable camelcase */
import { AddressType, ConfirmedTransaction, MemoryBlock, stringToAddressType } from 'gradido-blockchain-js'
import JsonRpcClient from 'jsonrpc-ts-client'
import { JsonRpcEitherResponse } from 'jsonrpc-ts-client/dist/types/utils/jsonrpc'
import { CONFIG } from '@/config'
import { logger } from '@/logging/logger'
import { LogError } from '@/server/LogError'
import { confirmedTransactionFromBase64 } from '@/utils/typeConverter'
const client = new JsonRpcClient({
url: CONFIG.NODE_SERVER_URL,
})
/*
enum JsonRPCErrorCodes {
NONE = 0,
GRADIDO_NODE_ERROR = -10000,
UNKNOWN_GROUP = -10001,
NOT_IMPLEMENTED = -10002,
TRANSACTION_NOT_FOUND = -10003,
// default errors from json rpc standard: https://www.jsonrpc.org/specification
// -32700 Parse error Invalid JSON was received by the server.
PARSE_ERROR = -32700,
// -32600 Invalid Request The JSON sent is not a valid Request object.
INVALID_REQUEST = -32600,
// -32601 Method not found The method does not exist / is not available.
METHODE_NOT_FOUND = -32601,
// -32602 Invalid params Invalid method parameter(s).
INVALID_PARAMS = -32602,
// -32603 Internal error Internal JSON - RPC error.
INTERNAL_ERROR = -32603,
// -32000 to -32099 Server error Reserved for implementation-defined server-errors.
}
*/
interface ConfirmedTransactionList {
transactions: string[]
timeUsed: string
}
interface ConfirmedTransactionResponse {
transaction: string
timeUsed: string
}
interface AddressTypeResult {
addressType: string
}
function resolveResponse<T, R>(response: JsonRpcEitherResponse<T>, onSuccess: (result: T) => R): R {
if (response.isSuccess()) {
return onSuccess(response.result)
} else if (response.isError()) {
throw new LogError('error by json rpc request to gradido node server', response.error)
}
throw new LogError('no success and no error', response)
}
async function getTransactions(
fromTransactionId: number,
maxResultCount: number,
iotaTopic: string,
): Promise<ConfirmedTransaction[]> {
const parameter = {
format: 'base64',
fromTransactionId,
maxResultCount,
communityId: iotaTopic,
}
logger.info('call getTransactions on Node Server via jsonrpc 2.0 with ', parameter)
const response = await client.exec<ConfirmedTransactionList>('getTransactions', parameter) // sends payload {jsonrpc: '2.0', params: ...}
return resolveResponse(response, (result: ConfirmedTransactionList) => {
logger.debug('GradidoNode used time', result.timeUsed)
return result.transactions.map((transactionBase64) =>
confirmedTransactionFromBase64(transactionBase64),
)
})
}
async function getTransaction(
transactionId: number | Buffer,
iotaTopic: string,
): Promise<ConfirmedTransaction | undefined> {
logger.info('call gettransaction on Node Server via jsonrpc 2.0')
const response = await client.exec<ConfirmedTransactionResponse>('gettransaction', {
format: 'base64',
communityId: iotaTopic,
transactionId: typeof transactionId === 'number' ? transactionId : undefined,
iotaMessageId: transactionId instanceof Buffer ? transactionId.toString('hex') : undefined,
})
return resolveResponse(response, (result: ConfirmedTransactionResponse) => {
logger.debug('GradidoNode used time', result.timeUsed)
return result.transaction && result.transaction !== ''
? confirmedTransactionFromBase64(result.transaction)
: undefined
})
}
async function getLastTransaction(iotaTopic: string): Promise<ConfirmedTransaction | undefined> {
logger.info('call getlasttransaction on Node Server via jsonrpc 2.0')
const response = await client.exec<ConfirmedTransactionResponse>('getlasttransaction', {
format: 'base64',
communityId: iotaTopic,
})
return resolveResponse(response, (result: ConfirmedTransactionResponse) => {
logger.debug('GradidoNode used time', result.timeUsed)
return result.transaction && result.transaction !== ''
? confirmedTransactionFromBase64(result.transaction)
: undefined
})
}
async function getAddressType(pubkey: Buffer, iotaTopic: string): Promise<AddressType | undefined> {
logger.info('call getaddresstype on Node Server via jsonrpc 2.0')
const response = await client.exec<AddressTypeResult>('getaddresstype', {
pubkey: pubkey.toString('hex'),
communityId: iotaTopic,
})
return resolveResponse(response, (result: AddressTypeResult) =>
stringToAddressType(result.addressType),
)
}
async function getTransactionsForAccount(
pubkey: MemoryBlock,
iotaTopic: string,
maxResultCount = 0,
firstTransactionNr = 1,
): Promise<ConfirmedTransaction[] | undefined> {
const parameter = {
pubkey: pubkey.convertToHex(),
format: 'base64',
firstTransactionNr,
maxResultCount,
communityId: iotaTopic,
}
logger.info('call listtransactionsforaddress on Node Server via jsonrpc 2.0', parameter)
const response = await client.exec<ConfirmedTransactionList>(
'listtransactionsforaddress',
parameter,
)
return resolveResponse(response, (result: ConfirmedTransactionList) => {
logger.debug('GradidoNode used time', result.timeUsed)
return result.transactions.map((transactionBase64) =>
confirmedTransactionFromBase64(transactionBase64),
)
})
}
export {
getTransaction,
getLastTransaction,
getTransactions,
getAddressType,
getTransactionsForAccount,
}

View File

@ -0,0 +1,33 @@
import * as v from 'valibot'
import { uuid4ToTopicSchema } from '../../schemas/typeConverter.schema'
export enum TransactionFormatType {
BASE64 = 'base64',
JSON = 'json',
}
export const transactionFormatTypeSchema = v.nullish(
v.enum(TransactionFormatType),
TransactionFormatType.BASE64
)
export type TransactionFormatTypeInput = v.InferInput<typeof transactionFormatTypeSchema>
export const getTransactionsInputSchema = v.object({
format: transactionFormatTypeSchema,
// default value is 1, from first transactions
fromTransactionId: v.undefinedable(v.pipe(v.number(), v.minValue(1, 'expect number >= 1')), 1),
// default value is 100, max 100 transactions
maxResultCount: v.undefinedable(v.pipe(v.number(), v.minValue(1, 'expect number >= 1')), 100),
communityId: uuid4ToTopicSchema,
})
export type GetTransactionsInputType = v.InferInput<typeof getTransactionsInputSchema>
export const getTransactionInputSchema = v.object({
transactionIdentifier: v.object({
iotaTopic: uuid4ToTopicSchema,
transactionNr: v.number(),
iotaMessageId: v.string(),
}),
})

View File

@ -0,0 +1,174 @@
/* eslint-disable camelcase */
import { AddressType, ConfirmedTransaction, MemoryBlock, stringToAddressType } from 'gradido-blockchain-js'
import JsonRpcClient from 'jsonrpc-ts-client'
import { JsonRpcEitherResponse } from 'jsonrpc-ts-client/dist/types/utils/jsonrpc'
import { CONFIG } from '../../config'
import { getLogger } from 'log4js'
import { LOG4JS_BASE_CATEGORY } from '../../config/const'
import * as v from 'valibot'
import { confirmedTransactionFromBase64Schema } from '../../schemas/typeConverter.schema'
import { isPortOpenRetry } from '../../utils/network'
import { TransactionIdentifierInput, transactionIdentifierSchema } from '../../schemas/transaction.schema'
import { GradidoNodeErrorCodes } from '../../enum/GradidoNodeErrorCodes'
import { GetTransactionsInputType, TransactionFormatTypeInput, getTransactionsInputSchema, transactionFormatTypeSchema } from './input.schema'
const logger = getLogger(`${LOG4JS_BASE_CATEGORY}.client.GradidoNode`)
const client = new JsonRpcClient({
url: CONFIG.NODE_SERVER_URL,
})
interface ConfirmedTransactionList {
transactions: string[]
timeUsed: string
}
interface ConfirmedTransactionResponse {
transaction: string
timeUsed: string
}
interface AddressTypeResult {
addressType: string
}
interface FindUserResponse {
pubkey: string
timeUsed: string
}
export class GradidoNodeRequestError<T> extends Error {
private response?: JsonRpcEitherResponse<T>
constructor(message: string, response?: JsonRpcEitherResponse<T>) {
super(message)
this.name = 'GradidoNodeRequestError'
this.response = response
}
getResponse(): JsonRpcEitherResponse<T> | undefined {
return this.response
}
}
function resolveResponse<T, R>(response: JsonRpcEitherResponse<T>, onSuccess: (result: T) => R): R {
if (response.isSuccess()) {
return onSuccess(response.result)
} else if (response.isError()) {
throw new GradidoNodeRequestError(response.error.message, response)
}
throw new GradidoNodeRequestError('no success and no error')
}
async function getTransactions(input: GetTransactionsInputType): Promise<ConfirmedTransaction[]> {
const parameter = v.parse(getTransactionsInputSchema, input)
logger.debug('call getTransactions with ', parameter)
await isPortOpenRetry(CONFIG.NODE_SERVER_URL)
const response = await client.exec<ConfirmedTransactionList>('getTransactions', parameter)
return resolveResponse(response, (result: ConfirmedTransactionList) => {
logger.info(`call getTransactions, used ${result.timeUsed}`)
return result.transactions.map((transactionBase64) =>
v.parse(confirmedTransactionFromBase64Schema, transactionBase64),
)
})
}
async function getTransaction(
transactionIdentifier: TransactionIdentifierInput,
format: TransactionFormatTypeInput
)
: Promise<ConfirmedTransaction | undefined> {
const parameter = {
...v.parse(transactionIdentifierSchema, transactionIdentifier),
format: v.parse(transactionFormatTypeSchema, format),
}
logger.debug('call gettransaction with ', parameter)
await isPortOpenRetry(CONFIG.NODE_SERVER_URL)
const response = await client.exec<ConfirmedTransactionResponse>('gettransaction', parameter)
return resolveResponse(response, (result: ConfirmedTransactionResponse) => {
logger.info(`call gettransaction, used ${result.timeUsed}`)
return result.transaction && result.transaction !== ''
? v.parse(confirmedTransactionFromBase64Schema, result.transaction)
: undefined
})
}
async function getLastTransaction(iotaTopic: string): Promise<ConfirmedTransaction | undefined> {
const parameter = {
format: 'base64',
communityId: iotaTopic,
}
logger.debug('call getlasttransaction with ', parameter)
await isPortOpenRetry(CONFIG.NODE_SERVER_URL)
const response = await client.exec<ConfirmedTransactionResponse>('getlasttransaction', parameter)
return resolveResponse(response, (result: ConfirmedTransactionResponse) => {
logger.info(`call getlasttransaction, used ${result.timeUsed}`)
return result.transaction && result.transaction !== ''
? v.parse(confirmedTransactionFromBase64Schema, result.transaction)
: undefined
})
}
async function getAddressType(pubkey: Buffer, iotaTopic: string): Promise<AddressType | undefined> {
const parameter = {
pubkey: pubkey.toString('hex'),
communityId: iotaTopic,
}
logger.debug('call getaddresstype with ', parameter)
await isPortOpenRetry(CONFIG.NODE_SERVER_URL)
const response = await client.exec<AddressTypeResult>('getaddresstype', parameter)
return resolveResponse(response, (result: AddressTypeResult) => {
logger.info(`call getaddresstype`)
return stringToAddressType(result.addressType)
})
}
async function findUserByNameHash(nameHash: MemoryBlock, iotaTopic: string): Promise<MemoryBlock | undefined> {
const parameter = {
nameHash: nameHash.convertToHex(),
communityId: iotaTopic,
}
logger.debug('call findUserByNameHash with ', parameter)
await isPortOpenRetry(CONFIG.NODE_SERVER_URL)
const response = await client.exec<FindUserResponse>('findUserByNameHash', parameter)
if (response.isError() && response.error.code === GradidoNodeErrorCodes.JSON_RPC_ERROR_ADDRESS_NOT_FOUND) {
return undefined
}
return resolveResponse(response, (result: FindUserResponse) => {
logger.info(`call findUserByNameHash, used ${result.timeUsed}`)
return result.pubkey && result.pubkey !== '' ? MemoryBlock.fromHex(result.pubkey) : undefined
})
}
async function getTransactionsForAccount(
pubkey: MemoryBlock,
iotaTopic: string,
maxResultCount = 0,
firstTransactionNr = 1,
): Promise<ConfirmedTransaction[] | undefined> {
const parameter = {
pubkey: pubkey.convertToHex(),
format: 'base64',
firstTransactionNr,
maxResultCount,
communityId: iotaTopic,
}
logger.debug('call listtransactionsforaddress with ', parameter)
await isPortOpenRetry(CONFIG.NODE_SERVER_URL)
const response = await client.exec<ConfirmedTransactionList>('listtransactionsforaddress', parameter)
return resolveResponse(response, (result: ConfirmedTransactionList) => {
logger.info(`call listtransactionsforaddress, used ${result.timeUsed}`)
return result.transactions.map((transactionBase64) =>
v.parse(confirmedTransactionFromBase64Schema, transactionBase64),
)
})
}
export {
getTransaction,
getLastTransaction,
getTransactions,
getAddressType,
getTransactionsForAccount,
findUserByNameHash,
}

View File

@ -1,71 +0,0 @@
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
import { sendMessage, receiveMessage } from '@/client/IotaClient'
jest.mock('@iota/client', () => {
const mockMessageSender = jest.fn().mockImplementation(() => {
return {
index: jest.fn().mockReturnThis(),
data: jest.fn().mockReturnThis(),
submit: jest
.fn()
.mockReturnValue('5498130bc3918e1a7143969ce05805502417e3e1bd596d3c44d6a0adeea22710'),
}
})
const mockMessageFinder = jest.fn().mockImplementation(() => {
return {
data: jest.fn().mockReturnValue({
message: {
networkId: '1454675179895816119',
parentMessageIds: [
'5f30efecca59fdfef7c103e85ef691b2b1dc474e9eae9056888a6d58605083e7',
'77cef2fb405daedcd7469e009bb87a6d9a4840e618cdb599cd21a30a9fec88dc',
'7d2cfb39f40585ba568a29ad7e85c1478b2584496eb736d4001ac344f6a6cacf',
'c66da602874220dfa26925f6be540d37c0084d37cd04726fcc5be9d80b36f850',
],
payload: {
type: 2,
index: '4752414449444f3a205465737448656c6c6f57656c7431',
data: '48656c6c6f20576f726c64202d20546875204a756e20303820323032332031343a35393a343520474d542b3030303020284b6f6f7264696e69657274652057656c747a65697429',
},
nonce: '13835058055282465157',
},
messageId: '5498130bc3918e1a7143969ce05805502417e3e1bd596d3c44d6a0adeea22710',
}),
}
})
const mockClient = {
message: mockMessageSender,
getMessage: mockMessageFinder,
}
const mockClientBuilder = {
node: jest.fn().mockReturnThis(),
build: jest.fn(() => mockClient),
}
return {
ClientBuilder: jest.fn(() => mockClientBuilder),
}
})
describe('Iota Tests', () => {
it('test mocked sendDataMessage', async () => {
const result = await sendMessage('Test Message', 'topic')
expect(result).toBe('5498130bc3918e1a7143969ce05805502417e3e1bd596d3c44d6a0adeea22710')
})
it('should mock getMessage', async () => {
const result = await receiveMessage(
'5498130bc3918e1a7143969ce05805502417e3e1bd596d3c44d6a0adeea22710',
)
expect(result).toMatchObject({
message: {
payload: {
data: '48656c6c6f20576f726c64202d20546875204a756e20303820323032332031343a35393a343520474d542b3030303020284b6f6f7264696e69657274652057656c747a65697429',
index: '4752414449444f3a205465737448656c6c6f57656c7431',
},
},
messageId: '5498130bc3918e1a7143969ce05805502417e3e1bd596d3c44d6a0adeea22710',
})
})
})

View File

@ -1,53 +0,0 @@
import { ClientBuilder } from '@iota/client'
import { MessageWrapper } from '@iota/client/lib/types'
import { CONFIG } from '@/config'
const client = new ClientBuilder().node(CONFIG.IOTA_API_URL).build()
/**
* send data message onto iota tangle
* @param {string | Uint8Array} message - the message as utf based string, will be converted to hex automatically from @iota/client
* @param {string | Uint8Array} topic - the iota topic to which the message will be sended
* @return {Promise<MessageWrapper>} the iota message typed
*/
function sendMessage(
message: string | Uint8Array,
topic: string | Uint8Array,
): Promise<MessageWrapper> {
return client.message().index(topic).data(message).submit()
}
/**
* receive message for known message id from iota tangle
* @param {string} messageId - as hex string
* @return {Promise<MessageWrapper>} the iota message typed
*/
function receiveMessage(messageId: string): Promise<MessageWrapper> {
return client.getMessage().data(messageId)
}
export { sendMessage, receiveMessage }
/**
* example for message:
```json
{
message: {
networkId: '1454675179895816119',
parentMessageIds: [
'5f30efecca59fdfef7c103e85ef691b2b1dc474e9eae9056888a6d58605083e7',
'77cef2fb405daedcd7469e009bb87a6d9a4840e618cdb599cd21a30a9fec88dc',
'7d2cfb39f40585ba568a29ad7e85c1478b2584496eb736d4001ac344f6a6cacf',
'c66da602874220dfa26925f6be540d37c0084d37cd04726fcc5be9d80b36f850'
],
payload: {
type: 2,
index: '4752414449444f3a205465737448656c6c6f57656c7431',
data: '48656c6c6f20576f726c64202d20546875204a756e20303820323032332031343a35393a343520474d542b3030303020284b6f6f7264696e69657274652057656c747a65697429'
},
nonce: '13835058055282465157'
},
messageId: '5498130bc3918e1a7143969ce05805502417e3e1bd596d3c44d6a0adeea22710'
}
```
*/

View File

@ -0,0 +1 @@
export const LOG4JS_BASE_CATEGORY = 'dlt'

View File

@ -2,65 +2,48 @@
import dotenv from 'dotenv'
dotenv.config()
const constants = {
const logging = {
LOG4JS_CONFIG: 'log4js-config.json',
// default log level on production should be info
LOG_LEVEL: process.env.LOG_LEVEL ?? 'info',
CONFIG_VERSION: {
DEFAULT: 'DEFAULT',
EXPECTED: 'v7.2024-09-24',
CURRENT: '',
},
}
const server = {
PRODUCTION: process.env.NODE_ENV === 'production' ?? false,
JWT_SECRET: process.env.JWT_SECRET ?? 'secret123',
}
const iota = {
IOTA_API_URL: process.env.IOTA_API_URL ?? 'https://chrysalis-nodes.iota.org',
IOTA_COMMUNITY_ALIAS: process.env.IOTA_COMMUNITY_ALIAS ?? 'GRADIDO: TestHelloWelt2',
IOTA_HOME_COMMUNITY_SEED: process.env.IOTA_HOME_COMMUNITY_SEED ?? null,
}
const dltConnector = {
PRODUCTION: process.env.NODE_ENV === 'production',
DLT_CONNECTOR_PORT: process.env.DLT_CONNECTOR_PORT ?? 6010,
}
const nodeServer = {
NODE_SERVER_URL: process.env.NODE_SERVER_URL ?? 'http://localhost:8340',
}
const gradidoBlockchain = {
const secrets = {
JWT_SECRET: process.env.JWT_SECRET ?? 'secret123',
GRADIDO_BLOCKCHAIN_CRYPTO_APP_SECRET:
process.env.GRADIDO_BLOCKCHAIN_CRYPTO_APP_SECRET ?? 'invalid',
GRADIDO_BLOCKCHAIN_SERVER_CRYPTO_KEY:
process.env.GRADIDO_BLOCKCHAIN_SERVER_CRYPTO_KEY ?? 'invalid',
}
const backendServer = {
BACKEND_SERVER_URL: process.env.BACKEND_SERVER_URL ?? 'http://backend:4000',
const iota = {
IOTA_HOME_COMMUNITY_SEED: process.env.IOTA_HOME_COMMUNITY_SEED ?? null,
}
// Check config version
constants.CONFIG_VERSION.CURRENT = process.env.CONFIG_VERSION ?? constants.CONFIG_VERSION.DEFAULT
if (
![constants.CONFIG_VERSION.EXPECTED, constants.CONFIG_VERSION.DEFAULT].includes(
constants.CONFIG_VERSION.CURRENT,
)
) {
throw new Error(
`Fatal: Config Version incorrect - expected "${constants.CONFIG_VERSION.EXPECTED}" or "${constants.CONFIG_VERSION.DEFAULT}", but found "${constants.CONFIG_VERSION.CURRENT}"`,
)
const apis = {
CONNECT_TIMEOUT_MS: process.env.CONNECT_TIMEOUT_MS
? Number.parseInt(process.env.CONNECT_TIMEOUT_MS)
: 1000,
CONNECT_RETRY_COUNT: process.env.CONNECT_RETRY_COUNT
? Number.parseInt(process.env.CONNECT_RETRY_COUNT)
: 15,
CONNECT_RETRY_DELAY_MS: process.env.CONNECT_RETRY_DELAY_MS
? Number.parseInt(process.env.CONNECT_RETRY_DELAY_MS)
: 500,
IOTA_API_URL: process.env.IOTA_API_URL ?? 'https://chrysalis-nodes.iota.org',
NODE_SERVER_URL: process.env.NODE_SERVER_URL ?? 'http://127.0.0.1:8340',
BACKEND_SERVER_URL: process.env.BACKEND_SERVER_URL ?? 'http://127.0.0.1:4000',
}
export const CONFIG = {
...constants,
...logging,
...server,
...secrets,
...iota,
...dltConnector,
...nodeServer,
...gradidoBlockchain,
...backendServer,
...apis,
}

View File

@ -0,0 +1,97 @@
import { MemoryBlock } from 'gradido-blockchain-js'
import { IdentifierAccount, IdentifierAccountInput, identifierAccountSchema } from '../schemas/account.schema'
import { ParameterError } from '../errors'
import * as v from 'valibot'
export class KeyPairIdentifierLogic {
public identifier: IdentifierAccount
public constructor(identifier: IdentifierAccountInput) {
// check if data structure is like expected and fill in defaults
this.identifier = v.parse(identifierAccountSchema, identifier)
}
isCommunityKeyPair(): boolean {
return !this.identifier.seed && !this.identifier.account
}
isSeedKeyPair(): boolean {
return this.identifier.seed !== undefined
}
isUserKeyPair(): boolean {
return (
this.identifier.seed === undefined &&
this.identifier.account != undefined &&
this.identifier.account.accountNr === 0
)
}
isAccountKeyPair(): boolean {
return (
this.identifier.seed === undefined &&
this.identifier.account != undefined &&
this.identifier.account.accountNr > 0
)
}
getSeed(): string {
if (!this.identifier.seed) {
throw new Error('get seed called on non seed key pair identifier, please check first with isSeedKeyPair()')
}
return this.identifier.seed.seed
}
getCommunityUuid(): string {
return this.identifier.communityUuid
}
getUserUuid(): string {
if (!this.identifier.account) {
throw new Error(
'get user uuid called on non user key pair identifier, please check first with isUserKeyPair() or isAccountKeyPair()'
)
}
return this.identifier.account.userUuid
}
getAccountNr(): number {
if (!this.identifier.account?.accountNr) {
throw new Error(
'get account nr called on non account key pair identifier, please check first with isAccountKeyPair()'
)
}
return this.identifier.account.accountNr
}
getSeedKey(): string { return this.getSeed() }
getCommunityKey(): string { return this.getCommunityUuid() }
getCommunityUserKey(): string {
return this.createCommunityUserHash()
}
getCommunityUserAccountKey(): string {
return this.createCommunityUserHash() + this.getAccountNr().toString()
}
getKey(): string {
if (this.isSeedKeyPair()) {
return this.getSeedKey()
} else if (this.isCommunityKeyPair()) {
return this.getCommunityKey()
} else if (this.isUserKeyPair()) {
return this.getCommunityUserKey()
} else if (this.isAccountKeyPair()) {
return this.getCommunityUserAccountKey()
}
throw new ParameterError('KeyPairIdentifier: unhandled input type')
}
private createCommunityUserHash(): string {
if (!this.identifier.account?.userUuid || !this.identifier.communityUuid) {
throw new ParameterError('userUuid and/or communityUuid is undefined')
}
const resultHexString =
this.identifier.communityUuid.replace(/-/g, '')
+ this.identifier.account.userUuid.replace(/-/g, '')
return MemoryBlock.fromHex(resultHexString).calculateHash().convertToHex()
}
}

View File

@ -1,72 +0,0 @@
import { UserIdentifier } from '@/graphql/input/UserIdentifier'
import { LogError } from '@/server/LogError'
import { uuid4sToMemoryBlock } from '@/utils/typeConverter'
export class KeyPairIdentifier {
// used for community key pair if it is only parameter or for user key pair
communityUuid?: string
// if set calculate key pair from seed, ignore all other parameter
seed?: string
// used for user key pair and account key pair, need also communityUuid
userUuid?: string
// used for account key pair, need also userUuid
accountNr?: number
public constructor(input: UserIdentifier | string | undefined = undefined) {
if (input instanceof UserIdentifier) {
if (input.seed !== undefined) {
this.seed = input.seed.seed
} else {
this.communityUuid = input.communityUuid
this.userUuid = input.communityUser?.uuid
this.accountNr = input.communityUser?.accountNr
}
} else if (typeof input === 'string') {
this.communityUuid = input
}
}
isCommunityKeyPair(): boolean {
return this.communityUuid !== undefined && this.userUuid === undefined
}
isSeedKeyPair(): boolean {
return this.seed !== undefined
}
isUserKeyPair(): boolean {
return (
this.communityUuid !== undefined &&
this.userUuid !== undefined &&
this.accountNr === undefined
)
}
isAccountKeyPair(): boolean {
return (
this.communityUuid !== undefined &&
this.userUuid !== undefined &&
this.accountNr !== undefined
)
}
getKey(): string {
if (this.seed && this.isSeedKeyPair()) {
return this.seed
} else if (this.communityUuid && this.isCommunityKeyPair()) {
return this.communityUuid
}
if (this.userUuid && this.communityUuid) {
const communityUserHash = uuid4sToMemoryBlock([this.userUuid, this.communityUuid])
.calculateHash()
.convertToHex()
if (this.isUserKeyPair()) {
return communityUserHash
}
if (this.accountNr && this.isAccountKeyPair()) {
return communityUserHash + this.accountNr.toString()
}
}
throw new LogError('KeyPairIdentifier: unhandled input type', this)
}
}

View File

@ -1,5 +1,3 @@
import { registerEnumType } from 'type-graphql'
/**
* enum for graphql
* describe input account type in UserAccountDraft
@ -14,8 +12,3 @@ export enum AccountType {
SUBACCOUNT = 'SUBACCOUNT', // no creations allowed
CRYPTO_ACCOUNT = 'CRYPTO_ACCOUNT', // user control his keys, no creations
}
registerEnumType(AccountType, {
name: 'AccountType', // this one is mandatory
description: 'Type of account', // this one is optional
})

View File

@ -0,0 +1,19 @@
import {
AddressType_COMMUNITY_AUF,
AddressType_COMMUNITY_GMW,
AddressType_COMMUNITY_HUMAN,
AddressType_COMMUNITY_PROJECT,
AddressType_CRYPTO_ACCOUNT,
AddressType_NONE,
AddressType_SUBACCOUNT,
} from 'gradido-blockchain-js'
export enum AddressType {
COMMUNITY_AUF = AddressType_COMMUNITY_AUF,
COMMUNITY_GMW = AddressType_COMMUNITY_GMW,
COMMUNITY_HUMAN = AddressType_COMMUNITY_HUMAN,
COMMUNITY_PROJECT = AddressType_COMMUNITY_PROJECT,
CRYPTO_ACCOUNT = AddressType_CRYPTO_ACCOUNT,
NONE = AddressType_NONE,
SUBACCOUNT = AddressType_SUBACCOUNT,
}

View File

@ -0,0 +1,20 @@
export enum GradidoNodeErrorCodes {
NONE = 0,
GRADIDO_NODE_ERROR = -10000,
UNKNOWN_GROUP = -10001,
NOT_IMPLEMENTED = -10002,
TRANSACTION_NOT_FOUND = -10003,
JSON_RPC_ERROR_ADDRESS_NOT_FOUND = -10004,
// default errors from json rpc standard: https://www.jsonrpc.org/specification
// -32700 Parse error Invalid JSON was received by the server.
PARSE_ERROR = -32700,
// -32600 Invalid Request The JSON sent is not a valid Request object.
INVALID_REQUEST = -32600,
// -32601 Method not found The method does not exist / is not available.
METHODE_NOT_FOUND = -32601,
// -32602 Invalid params Invalid method parameter(s).
INVALID_PARAMS = -32602,
// -32603 Internal error Internal JSON - RPC error.
INTERNAL_ERROR = -32603,
// -32000 to -32099 Server error Reserved for implementation-defined server-errors.
}

View File

@ -1,5 +1,3 @@
import { registerEnumType } from 'type-graphql'
// enum for graphql but with int because it is the same in backend
// for transaction type from backend
export enum InputTransactionType {
@ -11,8 +9,3 @@ export enum InputTransactionType {
GRADIDO_REDEEM_DEFERRED_TRANSFER = 'GRADIDO_REDEEM_DEFERRED_TRANSFER',
COMMUNITY_ROOT = 'COMMUNITY_ROOT',
}
registerEnumType(InputTransactionType, {
name: 'InputTransactionType', // this one is mandatory
description: 'Type of the transaction', // this one is optional
})

View File

@ -1,5 +1,3 @@
import { registerEnumType } from 'type-graphql'
// enum for graphql
// error groups for resolver answers
export enum TransactionErrorType {
@ -15,8 +13,3 @@ export enum TransactionErrorType {
NOT_FOUND = 'Not found',
VALIDATION_ERROR = 'Validation Error',
}
registerEnumType(TransactionErrorType, {
name: 'TransactionErrorType',
description: 'Transaction Error Type',
})

View File

@ -0,0 +1,43 @@
import { TransactionIdentifier } from './schemas/transaction.schema'
import { IdentifierAccount } from './schemas/account.schema'
export class GradidoNodeError extends Error {
constructor(message: string) {
super(message)
this.name = 'GradidoNodeError'
}
}
export class GradidoNodeMissingTransactionError extends GradidoNodeError {
public transactionIdentifier?: TransactionIdentifier
constructor(message: string, transactionIdentifier?: TransactionIdentifier) {
super(message)
this.name = 'GradidoNodeMissingTransactionError'
this.transactionIdentifier = transactionIdentifier
}
}
export class GradidoNodeMissingUserError extends GradidoNodeError {
public userIdentifier?: IdentifierAccount
constructor(message: string, userIdentifier?: IdentifierAccount) {
super(message)
this.name = 'GradidoNodeMissingUserError'
this.userIdentifier = userIdentifier
}
}
export class GradidoNodeInvalidTransactionError extends GradidoNodeError {
public transactionIdentifier?: TransactionIdentifier
constructor(message: string, transactionIdentifier?: TransactionIdentifier) {
super(message)
this.name = 'GradidoNodeInvalidTransactionError'
this.transactionIdentifier = transactionIdentifier
}
}
export class ParameterError extends Error {
constructor(message: string) {
super(message)
this.name = 'ParameterError'
}
}

View File

@ -1,19 +0,0 @@
// https://www.npmjs.com/package/@apollo/protobufjs
import { IsBoolean, IsUUID } from 'class-validator'
import { ArgsType, Field } from 'type-graphql'
@ArgsType()
export class CommunityArg {
@Field(() => String, { nullable: true })
@IsUUID('4')
uuid?: string
@Field(() => Boolean, { nullable: true })
@IsBoolean()
foreign?: boolean
@Field(() => Boolean, { nullable: true })
@IsBoolean()
confirmed?: boolean
}

View File

@ -1,2 +0,0 @@
export const MEMO_MAX_CHARS = 255
export const MEMO_MIN_CHARS = 5

View File

@ -1,20 +0,0 @@
// https://www.npmjs.com/package/@apollo/protobufjs
import { isValidDateString } from '@validator/DateString'
import { IsBoolean, IsUUID } from 'class-validator'
import { Field, InputType } from 'type-graphql'
@InputType()
export class CommunityDraft {
@Field(() => String)
@IsUUID('4')
uuid: string
@Field(() => Boolean)
@IsBoolean()
foreign: boolean
@Field(() => String)
@isValidDateString()
createdAt: string
}

View File

@ -1,15 +0,0 @@
// https://www.npmjs.com/package/@apollo/protobufjs
import { IsPositive, IsUUID } from 'class-validator'
import { Field, Int, InputType } from 'type-graphql'
@InputType()
export class CommunityUser {
@Field(() => String)
@IsUUID('4')
uuid: string
@Field(() => Int, { defaultValue: 1, nullable: true })
@IsPositive()
accountNr?: number
}

View File

@ -1,15 +0,0 @@
// https://www.npmjs.com/package/@apollo/protobufjs
import { IsString } from 'class-validator'
import { Field, InputType } from 'type-graphql'
@InputType()
export class IdentifierSeed {
@Field(() => String)
@IsString()
seed: string
constructor(seed: string) {
this.seed = seed
}
}

View File

@ -1,58 +0,0 @@
// https://www.npmjs.com/package/@apollo/protobufjs
import { InputTransactionType } from '@enum/InputTransactionType'
import { isValidDateString, isValidNumberString } from '@validator/DateString'
import { IsEnum, IsObject, IsPositive, MaxLength, MinLength, ValidateNested } from 'class-validator'
import { InputType, Field } from 'type-graphql'
import { MEMO_MAX_CHARS, MEMO_MIN_CHARS } from '@/graphql//const'
import { AccountType } from '@/graphql/enum/AccountType'
import { UserIdentifier } from './UserIdentifier'
@InputType()
export class TransactionDraft {
@Field(() => UserIdentifier)
@IsObject()
@ValidateNested()
user: UserIdentifier
// not used for simply register address
@Field(() => UserIdentifier, { nullable: true })
@IsObject()
@ValidateNested()
linkedUser?: UserIdentifier
// not used for register address
@Field(() => String, { nullable: true })
@isValidNumberString()
amount?: string
@Field(() => String, { nullable: true })
@MaxLength(MEMO_MAX_CHARS)
@MinLength(MEMO_MIN_CHARS)
memo?: string
@Field(() => InputTransactionType)
@IsEnum(InputTransactionType)
type: InputTransactionType
@Field(() => String)
@isValidDateString()
createdAt: string
// only for creation transactions
@Field(() => String, { nullable: true })
@isValidDateString()
targetDate?: string
// only for deferred transaction
// duration in seconds
@Field(() => Number, { nullable: true })
@IsPositive()
timeoutDuration?: number
// only for register address
@Field(() => AccountType, { nullable: true })
@IsEnum(AccountType)
accountType?: AccountType
}

View File

@ -1,24 +0,0 @@
// https://www.npmjs.com/package/@apollo/protobufjs
import { IsObject, IsUUID, ValidateNested } from 'class-validator'
import { Field, InputType } from 'type-graphql'
import { CommunityUser } from './CommunityUser'
import { IdentifierSeed } from './IdentifierSeed'
@InputType()
export class UserIdentifier {
@Field(() => String)
@IsUUID('4')
communityUuid: string
@Field(() => CommunityUser, { nullable: true })
@IsObject()
@ValidateNested()
communityUser?: CommunityUser
@Field(() => IdentifierSeed, { nullable: true })
@IsObject()
@ValidateNested()
seed?: IdentifierSeed
}

View File

@ -1,21 +0,0 @@
import { ObjectType, Field } from 'type-graphql'
import { TransactionErrorType } from '../enum/TransactionErrorType'
@ObjectType()
export class TransactionError implements Error {
constructor(type: TransactionErrorType, message: string) {
this.type = type
this.message = message
this.name = type.toString()
}
@Field(() => TransactionErrorType)
type: TransactionErrorType
@Field(() => String)
message: string
@Field(() => String)
name: string
}

View File

@ -1,27 +0,0 @@
import { GradidoTransaction, MemoryBlock, transactionTypeToString } from 'gradido-blockchain-js'
import { Field, ObjectType } from 'type-graphql'
import { LogError } from '@/server/LogError'
@ObjectType()
export class TransactionRecipe {
public constructor(transaction: GradidoTransaction, messageId: MemoryBlock) {
const body = transaction.getTransactionBody()
if (!body) {
throw new LogError('invalid gradido transaction, cannot geht valid TransactionBody')
}
this.createdAt = body.getCreatedAt().getDate().toString()
this.type = transactionTypeToString(body?.getTransactionType())
this.messageIdHex = messageId.convertToHex()
}
@Field(() => String)
createdAt: string
@Field(() => String)
type: string
@Field(() => String)
messageIdHex: string
}

View File

@ -1,28 +0,0 @@
import { ObjectType, Field } from 'type-graphql'
import { TransactionError } from './TransactionError'
import { TransactionRecipe } from './TransactionRecipe'
@ObjectType()
export class TransactionResult {
constructor(content?: TransactionError | TransactionRecipe) {
this.succeed = true
if (content instanceof TransactionError) {
this.error = content
this.succeed = false
} else if (content instanceof TransactionRecipe) {
this.recipe = content
}
}
// the error if one happened
@Field(() => TransactionError, { nullable: true })
error?: TransactionError
// if no error happened, the message id of the iota transaction
@Field(() => TransactionRecipe, { nullable: true })
recipe?: TransactionRecipe
@Field(() => Boolean)
succeed: boolean
}

View File

@ -1,36 +0,0 @@
/* eslint-disable camelcase */
import { AddressType_NONE } from 'gradido-blockchain-js'
import { Arg, Query, Resolver } from 'type-graphql'
import { getAddressType } from '@/client/GradidoNode'
import { KeyPairIdentifier } from '@/data/KeyPairIdentifier'
import { KeyPairCalculation } from '@/interactions/keyPairCalculation/KeyPairCalculation.context'
import { logger } from '@/logging/logger'
import { uuid4ToHash } from '@/utils/typeConverter'
import { TransactionErrorType } from '../enum/TransactionErrorType'
import { UserIdentifier } from '../input/UserIdentifier'
import { TransactionError } from '../model/TransactionError'
import { TransactionResult } from '../model/TransactionResult'
@Resolver()
export class AccountResolver {
@Query(() => Boolean)
async isAccountExist(@Arg('data') userIdentifier: UserIdentifier): Promise<boolean> {
const accountKeyPair = await KeyPairCalculation(new KeyPairIdentifier(userIdentifier))
const publicKey = accountKeyPair.getPublicKey()
if (!publicKey) {
throw new TransactionResult(
new TransactionError(TransactionErrorType.NOT_FOUND, 'cannot get user public key'),
)
}
// ask gradido node server for account type, if type !== NONE account exist
const addressType = await getAddressType(
publicKey.data(),
uuid4ToHash(userIdentifier.communityUuid).convertToHex(),
)
logger.info('isAccountExist', userIdentifier)
return addressType !== AddressType_NONE
}
}

View File

@ -1,27 +0,0 @@
import { Resolver, Arg, Mutation } from 'type-graphql'
import { SendToIotaContext } from '@/interactions/sendToIota/SendToIota.context'
import { TransactionDraft } from '../input/TransactionDraft'
import { TransactionError } from '../model/TransactionError'
import { TransactionResult } from '../model/TransactionResult'
@Resolver()
export class TransactionResolver {
@Mutation(() => TransactionResult)
async sendTransaction(
@Arg('data')
transactionDraft: TransactionDraft,
): Promise<TransactionResult> {
try {
return await SendToIotaContext(transactionDraft)
// eslint-disable-next-line @typescript-eslint/no-explicit-any
} catch (error: any) {
if (error instanceof TransactionError) {
return new TransactionResult(error)
} else {
throw error
}
}
}
}

View File

@ -1,19 +0,0 @@
import { GraphQLSchema } from 'graphql'
import { buildSchema } from 'type-graphql'
import { AccountResolver } from './resolver/AccountsResolver'
import { TransactionResolver } from './resolver/TransactionsResolver'
export const schema = async (): Promise<GraphQLSchema> => {
return buildSchema({
resolvers: [TransactionResolver, AccountResolver],
validate: {
validationError: { target: false },
skipMissingProperties: true,
skipNullProperties: true,
skipUndefinedProperties: false,
forbidUnknownValues: true,
stopAtFirstError: true,
},
})
}

View File

@ -1,41 +0,0 @@
import { registerDecorator, ValidationOptions } from 'class-validator'
export function isValidDateString(validationOptions?: ValidationOptions) {
// eslint-disable-next-line @typescript-eslint/ban-types
return function (object: Object, propertyName: string) {
registerDecorator({
name: 'isValidDateString',
target: object.constructor,
propertyName,
options: validationOptions,
validator: {
validate(value: string): boolean {
return !isNaN(Date.parse(value))
},
defaultMessage(): string {
return `${propertyName} must be a valid date string`
},
},
})
}
}
export function isValidNumberString(validationOptions?: ValidationOptions) {
// eslint-disable-next-line @typescript-eslint/ban-types
return function (object: Object, propertyName: string) {
registerDecorator({
name: 'isValidNumberString',
target: object.constructor,
propertyName,
options: validationOptions,
validator: {
validate(value: string): boolean {
return !isNaN(parseFloat(value))
},
defaultMessage(): string {
return `${propertyName} must be a valid number string`
},
},
})
}
}

View File

@ -1,45 +1,22 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import 'reflect-metadata'
import { Elysia } from 'elysia'
import { CONFIG } from './config'
import { loadCryptoKeys, MemoryBlock } from 'gradido-blockchain-js'
import { CONFIG } from '@/config'
import { getLogger, configure } from 'log4js'
import { readFileSync } from 'node:fs'
import { isPortOpenRetry } from './utils/network'
import { BackendClient } from './client/BackendClient'
import { getTransaction } from './client/GradidoNode'
import { CommunityDraft } from './graphql/input/CommunityDraft'
import { SendToIotaContext } from './interactions/sendToIota/SendToIota.context'
import { logger } from './logging/logger'
import { KeyPairCacheManager } from './manager/KeyPairCacheManager'
import createServer from './server/createServer'
import { LogError } from './server/LogError'
import { uuid4ToHash } from './utils/typeConverter'
async function waitForServer(
backend: BackendClient,
retryIntervalMs: number,
maxRetries: number,
): Promise<CommunityDraft> {
let retries = 0
while (retries < maxRetries) {
logger.info(`Attempt ${retries + 1} for connecting to backend`)
try {
// Make a HEAD request to the server
return await backend.getHomeCommunityDraft()
} catch (error) {
logger.info('Server is not reachable: ', error)
}
// Server is not reachable, wait and retry
await new Promise((resolve) => setTimeout(resolve, retryIntervalMs))
retries++
}
throw new LogError('Max retries exceeded. Server did not become reachable.')
}
import { KeyPairCacheManager } from './KeyPairCacheManager'
import { communityUuidToTopicSchema } from './schemas/rpcParameter.schema'
import { parse } from 'valibot'
import { getTransaction } from './client/GradidoNode/jsonrpc.api'
async function main() {
// configure log4js
// TODO: replace late by loader from config-schema
const options = JSON.parse(readFileSync(CONFIG.LOG4JS_CONFIG, 'utf-8'))
configure(options)
const logger = getLogger('dlt')
// TODO: replace with schema validation
if (CONFIG.IOTA_HOME_COMMUNITY_SEED) {
try {
const seed = MemoryBlock.fromHex(CONFIG.IOTA_HOME_COMMUNITY_SEED)
@ -47,62 +24,50 @@ async function main() {
throw new Error('seed need to be greater than 32 Bytes')
}
} catch (error) {
throw new LogError(
logger.error(
'IOTA_HOME_COMMUNITY_SEED must be a valid hex string, at least 64 characters long',
error,
)
process.exit(1)
}
}
// load crypto keys for gradido blockchain lib
loadCryptoKeys(
MemoryBlock.fromHex(CONFIG.GRADIDO_BLOCKCHAIN_CRYPTO_APP_SECRET),
MemoryBlock.fromHex(CONFIG.GRADIDO_BLOCKCHAIN_SERVER_CRYPTO_KEY),
)
// eslint-disable-next-line no-console
console.log(`DLT_CONNECTOR_PORT=${CONFIG.DLT_CONNECTOR_PORT}`)
const { app } = await createServer()
// ask backend for home community if we haven't one
const backend = BackendClient.getInstance()
if (!backend) {
throw new LogError('cannot create backend client')
throw new Error('cannot create backend client')
}
// wait for backend server to be ready
await waitForServer(backend, 2500, 10)
const communityDraft = await backend.getHomeCommunityDraft()
KeyPairCacheManager.getInstance().setHomeCommunityUUID(communityDraft.uuid)
logger.info('home community topic: %s', uuid4ToHash(communityDraft.uuid).convertToHex())
// wait for backend server
await isPortOpenRetry(CONFIG.BACKEND_SERVER_URL)
const homeCommunity = await backend.getHomeCommunityDraft()
KeyPairCacheManager.getInstance().setHomeCommunityUUID(homeCommunity.uuid)
logger.info('home community topic: %s', parse(communityUuidToTopicSchema, homeCommunity.uuid))
logger.info('gradido node server: %s', CONFIG.NODE_SERVER_URL)
// ask gradido node if community blockchain was created
try {
const firstTransaction = await getTransaction(
1,
uuid4ToHash(communityDraft.uuid).convertToHex(),
)
if (!firstTransaction) {
const topic = parse(communityUuidToTopicSchema, homeCommunity.uuid)
if (!await getTransaction(1, topic)) {
// if not exist, create community root transaction
await SendToIotaContext(communityDraft)
await SendToIotaContext(homeCommunity)
}
} catch (e) {
// eslint-disable-next-line no-console
// console.log('error requesting gradido node: ', e)
// if not exist, create community root transaction
await SendToIotaContext(communityDraft)
logger.error('error requesting gradido node: ', e)
}
app.listen(CONFIG.DLT_CONNECTOR_PORT, () => {
// eslint-disable-next-line no-console
console.log(`Server is running at http://localhost:${CONFIG.DLT_CONNECTOR_PORT}`)
})
process.on('exit', () => {
// Add shutdown logic here.
})
const app = new Elysia()
.get('/', () => "Hello Elysia")
.listen(CONFIG.DLT_CONNECTOR_PORT, () => {
logger.info(`Server is running at http://localhost:${CONFIG.DLT_CONNECTOR_PORT}`)
})
}
main().catch((e) => {
// eslint-disable-next-line no-console
// biome-ignore lint/suspicious/noConsole: maybe logger isn't initialized here
console.error(e)
// eslint-disable-next-line n/no-process-exit
process.exit(1)
})

View File

@ -1,5 +1,11 @@
import { KeyPairEd25519 } from 'gradido-blockchain-js'
import { communityUuidToTopicSchema } from '../../schemas/rpcParameter.schema'
import * as v from 'valibot'
export abstract class AbstractRemoteKeyPairRole {
protected topic: string
public constructor(communityUuid: string) {
this.topic = v.parse(communityUuidToTopicSchema, communityUuid)
}
public abstract retrieveKeyPair(): Promise<KeyPairEd25519>
}

View File

@ -1,30 +1,43 @@
import { KeyPairEd25519 } from 'gradido-blockchain-js'
import { getTransaction } from '@/client/GradidoNode'
import { LogError } from '@/server/LogError'
import { uuid4ToHash } from '@/utils/typeConverter'
import { getTransaction } from '../../client/GradidoNode/jsonrpc.api'
import { AbstractRemoteKeyPairRole } from './AbstractRemoteKeyPair.role'
import { GradidoNodeInvalidTransactionError, GradidoNodeMissingTransactionError } from '../../errors'
export class ForeignCommunityKeyPairRole extends AbstractRemoteKeyPairRole {
public constructor(private communityUuid: string) {
super()
public constructor(communityUuid: string) {
super(communityUuid)
}
public async retrieveKeyPair(): Promise<KeyPairEd25519> {
const firstTransaction = await getTransaction(1, uuid4ToHash(this.communityUuid).convertToHex())
const transactionIdentifier = {
transactionNr: 1,
iotaTopic: this.topic,
}
const firstTransaction = await getTransaction(transactionIdentifier)
if (!firstTransaction) {
throw new LogError(
"GradidoNode Server don't know this community with uuid " + this.communityUuid,
)
throw new GradidoNodeMissingTransactionError('Cannot find transaction', transactionIdentifier)
}
const transactionBody = firstTransaction.getGradidoTransaction()?.getTransactionBody()
if (!transactionBody || !transactionBody.isCommunityRoot()) {
throw new LogError('get invalid confirmed transaction from gradido node')
if (!transactionBody) {
throw new GradidoNodeInvalidTransactionError(
'Invalid transaction, body is missing',
transactionIdentifier
)
}
if (!transactionBody.isCommunityRoot()) {
throw new GradidoNodeInvalidTransactionError(
'Invalid transaction, community root type expected',
transactionIdentifier
)
}
const communityRoot = transactionBody.getCommunityRoot()
if (!communityRoot) {
throw new LogError('invalid confirmed transaction')
throw new GradidoNodeInvalidTransactionError(
'Invalid transaction, community root is missing',
transactionIdentifier
)
}
return new KeyPairEd25519(communityRoot.getPublicKey())
}

View File

@ -1,21 +1,21 @@
import { KeyPairEd25519, MemoryBlock } from 'gradido-blockchain-js'
import { CONFIG } from '@/config'
import { LogError } from '@/server/LogError'
import { CONFIG } from '../../config'
import { ParameterError } from '../../errors'
import { AbstractKeyPairRole } from './AbstractKeyPair.role'
export class HomeCommunityKeyPairRole extends AbstractKeyPairRole {
public generateKeyPair(): KeyPairEd25519 {
// TODO: prevent this check with valibot test on config
if (!CONFIG.IOTA_HOME_COMMUNITY_SEED) {
throw new LogError(
throw new Error(
'IOTA_HOME_COMMUNITY_SEED is missing either in config or as environment variable',
)
}
const seed = MemoryBlock.fromHex(CONFIG.IOTA_HOME_COMMUNITY_SEED)
const keyPair = KeyPairEd25519.create(seed)
if (!keyPair) {
throw new LogError("couldn't create keyPair from IOTA_HOME_COMMUNITY_SEED")
throw new ParameterError("couldn't create keyPair from IOTA_HOME_COMMUNITY_SEED")
}
return keyPair
}

View File

@ -1,10 +1,7 @@
import { KeyPairEd25519 } from 'gradido-blockchain-js'
import { KeyPairIdentifier } from '@/data/KeyPairIdentifier'
import { UserIdentifier } from '@/graphql/input/UserIdentifier'
import { KeyPairCacheManager } from '@/manager/KeyPairCacheManager'
import { LogError } from '@/server/LogError'
import { KeyPairIdentifierLogic } from '../../data/KeyPairIdentifier.logic'
import { KeyPairCacheManager } from '../../KeyPairCacheManager'
import { AccountKeyPairRole } from './AccountKeyPair.role'
import { ForeignCommunityKeyPairRole } from './ForeignCommunityKeyPair.role'
import { HomeCommunityKeyPairRole } from './HomeCommunityKeyPair.role'
@ -16,61 +13,46 @@ import { UserKeyPairRole } from './UserKeyPair.role'
* @DCI-Context
* Context for calculating key pair for signing transactions
*/
export async function KeyPairCalculation(input: KeyPairIdentifier): Promise<KeyPairEd25519> {
export async function KeyPairCalculation(input: KeyPairIdentifierLogic): Promise<KeyPairEd25519> {
const cache = KeyPairCacheManager.getInstance()
// Try cache lookup first
let keyPair = cache.findKeyPair(input)
if (keyPair) {
return keyPair
}
const retrieveKeyPair = async (input: KeyPairIdentifier): Promise<KeyPairEd25519> => {
if (input.isSeedKeyPair() && input.seed) {
return new LinkedTransactionKeyPairRole(input.seed).generateKeyPair()
}
if (!input.communityUuid) {
throw new LogError('missing community id')
return await cache.getKeyPair(input.getKey(), async () => {
if (input.isSeedKeyPair()) {
return new LinkedTransactionKeyPairRole(input.getSeed()).generateKeyPair()
}
// If input does not belong to the home community, handle as remote key pair
if (cache.getHomeCommunityUUID() !== input.communityUuid) {
if (cache.getHomeCommunityUUID() !== input.getCommunityUuid()) {
const role =
input instanceof UserIdentifier
? new RemoteAccountKeyPairRole(input)
: new ForeignCommunityKeyPairRole(input.communityUuid)
input.isAccountKeyPair()
? new RemoteAccountKeyPairRole(input.identifier)
: new ForeignCommunityKeyPairRole(input.getCommunityUuid())
return await role.retrieveKeyPair()
}
let communityKeyPair = cache.findKeyPair(input)
const communityKeyPair = await cache.getKeyPair(input.getCommunityKey(), async () => {
return new HomeCommunityKeyPairRole().generateKeyPair()
})
if (!communityKeyPair) {
communityKeyPair = new HomeCommunityKeyPairRole().generateKeyPair()
throw new Error("couldn't generate community key pair")
}
if (input.isCommunityKeyPair()) {
return communityKeyPair
}
const userKeyPairIdentifier = new KeyPairIdentifier()
userKeyPairIdentifier.communityUuid = input.communityUuid
userKeyPairIdentifier.userUuid = input.userUuid
let userKeyPair = cache.findKeyPair(userKeyPairIdentifier)
if (!userKeyPair && userKeyPairIdentifier.userUuid) {
userKeyPair = new UserKeyPairRole(
userKeyPairIdentifier.userUuid,
const userKeyPair = await cache.getKeyPair(input.getCommunityUserKey(), async () => {
return new UserKeyPairRole(
input.getUserUuid(),
communityKeyPair,
).generateKeyPair()
}
})
if (!userKeyPair) {
throw new LogError("couldn't generate user key pair")
throw new Error("couldn't generate user key pair")
}
if (input.isUserKeyPair()) {
return userKeyPair
}
const accountNr = input.accountNr ?? 1
return new AccountKeyPairRole(accountNr, userKeyPair).generateKeyPair()
}
keyPair = await retrieveKeyPair(input)
cache.addKeyPair(input, keyPair)
return keyPair
const accountNr = input.getAccountNr()
const accountKeyPair = new AccountKeyPairRole(accountNr, userKeyPair).generateKeyPair()
if (input.isAccountKeyPair()) {
return accountKeyPair
}
throw new Error("couldn't generate account key pair, unexpected type")
})
}

View File

@ -1,8 +1,7 @@
import { KeyPairEd25519, MemoryBlock } from 'gradido-blockchain-js'
import { LogError } from '@/server/LogError'
import { AbstractKeyPairRole } from './AbstractKeyPair.role'
import { ParameterError } from '../../errors'
export class LinkedTransactionKeyPairRole extends AbstractKeyPairRole {
public constructor(private seed: string) {
@ -15,7 +14,7 @@ export class LinkedTransactionKeyPairRole extends AbstractKeyPairRole {
const hash = new MemoryBlock(this.seed).calculateHash()
const keyPair = KeyPairEd25519.create(hash)
if (!keyPair) {
throw new LogError('error creating Ed25519 KeyPair from seed', this.seed)
throw new ParameterError(`error creating Ed25519 KeyPair from seed: ${this.seed.substring(0, 5)}...`)
}
return keyPair
}

View File

@ -1,39 +1,29 @@
import { KeyPairEd25519 } from 'gradido-blockchain-js'
import { getTransactions } from '@/client/GradidoNode'
import { UserIdentifier } from '@/graphql/input/UserIdentifier'
import { LogError } from '@/server/LogError'
import { uuid4ToHash } from '@/utils/typeConverter'
import { findUserByNameHash } from '../../client/GradidoNode/jsonrpc.api'
import { IdentifierAccount } from '../../schemas/account.schema'
import { GradidoNodeMissingUserError, ParameterError } from '../../errors'
import { AbstractRemoteKeyPairRole } from './AbstractRemoteKeyPair.role'
import { uuid4ToHashSchema } from '../../schemas/typeConverter.schema'
import * as v from 'valibot'
export class RemoteAccountKeyPairRole extends AbstractRemoteKeyPairRole {
public constructor(private user: UserIdentifier) {
super()
public constructor(private identifier: IdentifierAccount) {
super(identifier.communityUuid)
}
public async retrieveKeyPair(): Promise<KeyPairEd25519> {
if (!this.user.communityUser) {
throw new LogError('missing community user')
if (!this.identifier.account) {
throw new ParameterError('missing account')
}
const nameHash = uuid4ToHash(this.user.communityUser.uuid)
const confirmedTransactions = await getTransactions(
0,
30,
uuid4ToHash(this.user.communityUuid).convertToHex(),
const accountPublicKey = await findUserByNameHash(
v.parse(uuid4ToHashSchema, this.identifier.account.userUuid),
this.topic,
)
for (let i = 0; i < confirmedTransactions.length; i++) {
const transactionBody = confirmedTransactions[i].getGradidoTransaction()?.getTransactionBody()
if (transactionBody && transactionBody.isRegisterAddress()) {
const registerAddress = transactionBody.getRegisterAddress()
if (registerAddress && registerAddress.getNameHash()?.equal(nameHash)) {
return new KeyPairEd25519(registerAddress.getAccountPublicKey())
}
}
if (accountPublicKey) {
return new KeyPairEd25519(accountPublicKey)
}
throw new LogError(
'cannot find remote user in first 30 transaction from remote blockchain, please wait for better recover implementation',
)
throw new GradidoNodeMissingUserError('cannot find remote user', this.identifier)
}
}

View File

@ -1,8 +1,6 @@
import { KeyPairEd25519 } from 'gradido-blockchain-js'
import { hardenDerivationIndex } from '@/utils/derivationHelper'
import { uuid4ToBuffer } from '@/utils/typeConverter'
import { hardenDerivationIndex } from '../../utils/derivationHelper'
import { AbstractKeyPairRole } from './AbstractKeyPair.role'
export class UserKeyPairRole extends AbstractKeyPairRole {
@ -14,7 +12,7 @@ export class UserKeyPairRole extends AbstractKeyPairRole {
// example gradido id: 03857ac1-9cc2-483e-8a91-e5b10f5b8d16 =>
// wholeHex: '03857ac19cc2483e8a91e5b10f5b8d16']
const wholeHex = uuid4ToBuffer(this.userUuid)
const wholeHex = Buffer.from(this.userUuid.replace(/-/g, ''), 'hex')
const parts = []
for (let i = 0; i < 4; i++) {
parts[i] = hardenDerivationIndex(wholeHex.subarray(i * 4, (i + 1) * 4).readUInt32BE())

View File

@ -1,34 +1,37 @@
import { GradidoTransactionBuilder } from 'gradido-blockchain-js'
import { KeyPairIdentifier } from '@/data/KeyPairIdentifier'
import { CommunityDraft } from '@/graphql/input/CommunityDraft'
import { LogError } from '@/server/LogError'
import { KeyPairIdentifierLogic } from '../../data/KeyPairIdentifier.logic'
import {
AUF_ACCOUNT_DERIVATION_INDEX,
GMW_ACCOUNT_DERIVATION_INDEX,
hardenDerivationIndex,
} from '@/utils/derivationHelper'
} from '../../utils/derivationHelper'
import { KeyPairCalculation } from '../keyPairCalculation/KeyPairCalculation.context'
import { AbstractTransactionRole } from './AbstractTransaction.role'
import { Community, CommunityInput, communitySchema } from '../../schemas/rpcParameter.schema'
import * as v from 'valibot'
export class CommunityRootTransactionRole extends AbstractTransactionRole {
constructor(private self: CommunityDraft) {
private com: Community
constructor(input: CommunityInput) {
super()
this.com = v.parse(communitySchema, input)
}
getSenderCommunityUuid(): string {
return this.self.uuid
return this.com.uuid
}
getRecipientCommunityUuid(): string {
throw new LogError('cannot be used as cross group transaction')
throw new Error('cannot be used as cross group transaction')
}
public async getGradidoTransactionBuilder(): Promise<GradidoTransactionBuilder> {
const builder = new GradidoTransactionBuilder()
const communityKeyPair = await KeyPairCalculation(new KeyPairIdentifier(this.self.uuid))
const communityKeyPair = await KeyPairCalculation(
new KeyPairIdentifierLogic({ communityUuid: this.com.uuid }))
const gmwKeyPair = communityKeyPair.deriveChild(
hardenDerivationIndex(GMW_ACCOUNT_DERIVATION_INDEX),
)
@ -36,7 +39,7 @@ export class CommunityRootTransactionRole extends AbstractTransactionRole {
hardenDerivationIndex(AUF_ACCOUNT_DERIVATION_INDEX),
)
builder
.setCreatedAt(new Date(this.self.createdAt))
.setCreatedAt(this.com.createdAt)
.setCommunityRoot(
communityKeyPair.getPublicKey(),
gmwKeyPair.getPublicKey(),

View File

@ -2,72 +2,80 @@ import {
AuthenticatedEncryption,
EncryptedMemo,
GradidoTransactionBuilder,
GradidoUnit,
TransferAmount,
} from 'gradido-blockchain-js'
import { KeyPairIdentifier } from '@/data/KeyPairIdentifier'
import { TransactionErrorType } from '@/graphql/enum/TransactionErrorType'
import { TransactionDraft } from '@/graphql/input/TransactionDraft'
import { TransactionError } from '@/graphql/model/TransactionError'
import { KeyPairCacheManager } from '@/manager/KeyPairCacheManager'
import { KeyPairIdentifierLogic } from '../../data/KeyPairIdentifier.logic'
import { CreationTransactionInput, creationTransactionSchema, CreationTransaction } from '../../schemas/transaction.schema'
import { KeyPairCacheManager } from '../../KeyPairCacheManager'
import { TRPCError } from '@trpc/server'
import { KeyPairCalculation } from '../keyPairCalculation/KeyPairCalculation.context'
import * as v from 'valibot'
import { AbstractTransactionRole } from './AbstractTransaction.role'
import { Uuidv4, uuidv4Schema } from '../../schemas/typeConverter.schema'
export class CreationTransactionRole extends AbstractTransactionRole {
constructor(private self: TransactionDraft) {
private tx: CreationTransaction
private homeCommunityUuid: Uuidv4
constructor(input: CreationTransactionInput) {
super()
this.tx = v.parse(creationTransactionSchema, input)
this.homeCommunityUuid = v.parse(
uuidv4Schema,
KeyPairCacheManager.getInstance().getHomeCommunityUUID()
)
if (
this.homeCommunityUuid !== this.tx.user.communityUuid ||
this.homeCommunityUuid !== this.tx.linkedUser.communityUuid
) {
throw new TRPCError({
code: 'BAD_REQUEST',
message: 'creation: both recipient and signer must belong to home community',
})
}
}
getSenderCommunityUuid(): string {
return this.self.user.communityUuid
return this.tx.user.communityUuid
}
getRecipientCommunityUuid(): string {
throw new TransactionError(
TransactionErrorType.LOGIC_ERROR,
'creation: cannot be used as cross group transaction',
)
throw new TRPCError({
code: 'BAD_REQUEST',
message: 'creation: cannot be used as cross group transaction',
})
}
public async getGradidoTransactionBuilder(): Promise<GradidoTransactionBuilder> {
if (!this.self.targetDate) {
throw new TransactionError(
TransactionErrorType.MISSING_PARAMETER,
'creation: target date missing',
)
}
if (!this.self.linkedUser) {
throw new TransactionError(
TransactionErrorType.MISSING_PARAMETER,
'creation: linked user missing',
)
}
if (!this.self.amount) {
throw new TransactionError(TransactionErrorType.MISSING_PARAMETER, 'creation: amount missing')
}
if (!this.self.memo) {
throw new TransactionError(TransactionErrorType.MISSING_PARAMETER, 'creation: memo missing')
}
const builder = new GradidoTransactionBuilder()
const recipientKeyPair = await KeyPairCalculation(new KeyPairIdentifier(this.self.user))
const signerKeyPair = await KeyPairCalculation(new KeyPairIdentifier(this.self.linkedUser))
const homeCommunityKeyPair = await KeyPairCalculation(
new KeyPairIdentifier(KeyPairCacheManager.getInstance().getHomeCommunityUUID()),
// Recipient: user (account owner)
const recipientKeyPair = await KeyPairCalculation(
new KeyPairIdentifierLogic(this.tx.user)
)
// Signer: linkedUser (admin/moderator)
const signerKeyPair = await KeyPairCalculation(
new KeyPairIdentifierLogic(this.tx.linkedUser)
)
const homeCommunityKeyPair = await KeyPairCalculation(
new KeyPairIdentifierLogic({
communityUuid: this.homeCommunityUuid
}),
)
// Memo: encrypted, home community and recipient can decrypt it
builder
.setCreatedAt(new Date(this.self.createdAt))
.addMemo(new EncryptedMemo(this.self.memo, new AuthenticatedEncryption(homeCommunityKeyPair)))
.setCreatedAt(this.tx.createdAt)
.addMemo(new EncryptedMemo(
this.tx.memo,
new AuthenticatedEncryption(homeCommunityKeyPair),
new AuthenticatedEncryption(recipientKeyPair),
))
.setTransactionCreation(
new TransferAmount(
recipientKeyPair.getPublicKey(),
GradidoUnit.fromString(this.self.amount),
this.tx.amount,
),
new Date(this.self.targetDate),
this.tx.targetDate,
)
.sign(signerKeyPair)
return builder

View File

@ -1,72 +1,57 @@
import {
AuthenticatedEncryption,
DurationSeconds,
EncryptedMemo,
GradidoTransactionBuilder,
GradidoTransfer,
GradidoUnit,
TransferAmount,
} from 'gradido-blockchain-js'
import { KeyPairIdentifier } from '@/data/KeyPairIdentifier'
import { TransactionErrorType } from '@/graphql/enum/TransactionErrorType'
import { TransactionDraft } from '@/graphql/input/TransactionDraft'
import { TransactionError } from '@/graphql/model/TransactionError'
import { KeyPairIdentifierLogic } from '../../data/KeyPairIdentifier.logic'
import { KeyPairCalculation } from '../keyPairCalculation/KeyPairCalculation.context'
import { AbstractTransactionRole } from './AbstractTransaction.role'
import { DeferredTransferTransactionInput, deferredTransferTransactionSchema, DeferredTransferTransaction } from '../../schemas/transaction.schema'
import * as v from 'valibot'
import { TRPCError } from '@trpc/server'
import { identifierSeedSchema, IdentifierSeed } from '../../schemas/account.schema'
export class DeferredTransferTransactionRole extends AbstractTransactionRole {
constructor(protected self: TransactionDraft) {
private tx: DeferredTransferTransaction
private seed: IdentifierSeed
constructor(protected input: DeferredTransferTransactionInput) {
super()
this.tx = v.parse(deferredTransferTransactionSchema, input)
this.seed = v.parse(identifierSeedSchema, input.linkedUser.seed)
}
getSenderCommunityUuid(): string {
return this.self.user.communityUuid
return this.tx.user.communityUuid
}
getRecipientCommunityUuid(): string {
throw new TransactionError(
TransactionErrorType.LOGIC_ERROR,
'deferred transfer: cannot be used as cross group transaction',
)
throw new TRPCError({
code: 'NOT_IMPLEMENTED',
message: 'deferred transfer: cannot be used as cross group transaction yet',
})
}
public async getGradidoTransactionBuilder(): Promise<GradidoTransactionBuilder> {
if (!this.self.linkedUser || !this.self.linkedUser.seed) {
throw new TransactionError(
TransactionErrorType.MISSING_PARAMETER,
'deferred transfer: missing linked user or not a seed',
)
}
if (!this.self.amount) {
throw new TransactionError(
TransactionErrorType.MISSING_PARAMETER,
'deferred transfer: amount missing',
)
}
if (!this.self.memo) {
throw new TransactionError(
TransactionErrorType.MISSING_PARAMETER,
'deferred transfer: memo missing',
)
}
if (!this.self.timeoutDuration) {
throw new TransactionError(
TransactionErrorType.MISSING_PARAMETER,
'deferred transfer: timeout duration missing',
)
}
const builder = new GradidoTransactionBuilder()
const senderKeyPair = await KeyPairCalculation(new KeyPairIdentifier(this.self.user))
const recipientKeyPair = await KeyPairCalculation(new KeyPairIdentifier(this.self.linkedUser))
const senderKeyPair = await KeyPairCalculation(
new KeyPairIdentifierLogic(this.tx.user)
)
const recipientKeyPair = await KeyPairCalculation(
new KeyPairIdentifierLogic({
communityUuid: this.tx.linkedUser.communityUuid,
seed: this.seed,
})
)
builder
.setCreatedAt(new Date(this.self.createdAt))
.setCreatedAt(this.tx.createdAt)
.addMemo(
new EncryptedMemo(
this.self.memo,
this.tx.memo,
new AuthenticatedEncryption(senderKeyPair),
new AuthenticatedEncryption(recipientKeyPair),
),
@ -75,13 +60,13 @@ export class DeferredTransferTransactionRole extends AbstractTransactionRole {
new GradidoTransfer(
new TransferAmount(
senderKeyPair.getPublicKey(),
GradidoUnit.fromString(this.self.amount).calculateCompoundInterest(
this.self.timeoutDuration,
this.tx.amount.calculateCompoundInterest(
this.tx.timeoutDuration.getSeconds(),
),
),
recipientKeyPair.getPublicKey(),
),
new DurationSeconds(this.self.timeoutDuration),
this.tx.timeoutDuration,
)
.sign(senderKeyPair)
return builder

View File

@ -1,60 +1,69 @@
/* eslint-disable camelcase */
import { GradidoTransactionBuilder } from 'gradido-blockchain-js'
import { KeyPairIdentifier } from '@/data/KeyPairIdentifier'
import { TransactionErrorType } from '@/graphql/enum/TransactionErrorType'
import { TransactionDraft } from '@/graphql/input/TransactionDraft'
import { TransactionError } from '@/graphql/model/TransactionError'
import { LogError } from '@/server/LogError'
import { accountTypeToAddressType, uuid4ToHash } from '@/utils/typeConverter'
import { KeyPairIdentifierLogic } from '../../data/KeyPairIdentifier.logic'
import { KeyPairCalculation } from '../keyPairCalculation/KeyPairCalculation.context'
import { AbstractTransactionRole } from './AbstractTransaction.role'
import { RegisterAddressTransactionInput, registerAddressTransactionSchema, RegisterAddressTransaction } from '../../schemas/transaction.schema'
import { IdentifierAccount, IdentifierCommunityAccount, identifierCommunityAccountSchema } from '../../schemas/account.schema'
import * as v from 'valibot'
import { TRPCError } from '@trpc/server'
import { uuid4ToHashSchema } from '../../schemas/typeConverter.schema'
export class RegisterAddressTransactionRole extends AbstractTransactionRole {
constructor(private self: TransactionDraft) {
private tx: RegisterAddressTransaction
private account: IdentifierCommunityAccount
constructor(input: RegisterAddressTransactionInput) {
super()
this.tx = v.parse(registerAddressTransactionSchema, input)
this.account = v.parse(identifierCommunityAccountSchema, input.user.account)
}
getSenderCommunityUuid(): string {
return this.self.user.communityUuid
return this.tx.user.communityUuid
}
getRecipientCommunityUuid(): string {
throw new LogError('cannot yet be used as cross group transaction')
throw new TRPCError({
code: 'NOT_IMPLEMENTED',
message: 'register address: cannot be used as cross group transaction yet',
})
}
public async getGradidoTransactionBuilder(): Promise<GradidoTransactionBuilder> {
if (!this.self.accountType) {
throw new TransactionError(
TransactionErrorType.MISSING_PARAMETER,
'register address: account type missing',
)
}
if (!this.self.user.communityUser) {
throw new TransactionError(
TransactionErrorType.MISSING_PARAMETER,
"register address: user isn't a community user",
)
}
const builder = new GradidoTransactionBuilder()
const communityKeyPair = await KeyPairCalculation(
new KeyPairIdentifier(this.self.user.communityUuid),
new KeyPairIdentifierLogic({ communityUuid: this.tx.user.communityUuid }),
)
const keyPairIdentifer = new KeyPairIdentifier(this.self.user)
const accountKeyPair = await KeyPairCalculation(keyPairIdentifer)
// unsetting accountNr change identifier from account key pair to user key pair
keyPairIdentifer.accountNr = undefined
const userKeyPair = await KeyPairCalculation(keyPairIdentifer)
const userKeyPairIdentifier: IdentifierAccount = {
communityUuid: this.tx.user.communityUuid,
account: {
userUuid: this.account.userUuid,
accountNr: 0,
},
}
const accountKeyPairIdentifier: IdentifierAccount = {
communityUuid: this.tx.user.communityUuid,
account: {
userUuid: this.account.userUuid,
accountNr: this.account.accountNr,
},
}
const userKeyPair = await KeyPairCalculation(
new KeyPairIdentifierLogic(userKeyPairIdentifier)
)
const accountKeyPair = await KeyPairCalculation(
new KeyPairIdentifierLogic(accountKeyPairIdentifier)
)
builder
.setCreatedAt(new Date(this.self.createdAt))
.setCreatedAt(this.tx.createdAt)
.setRegisterAddress(
userKeyPair.getPublicKey(),
accountTypeToAddressType(this.self.accountType),
uuid4ToHash(this.self.user.communityUser.uuid),
this.tx.accountType,
v.parse(uuid4ToHashSchema, this.account.userUuid),
accountKeyPair.getPublicKey(),
)
.sign(communityKeyPair)

View File

@ -2,76 +2,62 @@ import {
AuthenticatedEncryption,
EncryptedMemo,
GradidoTransactionBuilder,
GradidoUnit,
TransferAmount,
} from 'gradido-blockchain-js'
import { KeyPairIdentifier } from '@/data/KeyPairIdentifier'
import { TransactionErrorType } from '@/graphql/enum/TransactionErrorType'
import { TransactionDraft } from '@/graphql/input/TransactionDraft'
import { UserIdentifier } from '@/graphql/input/UserIdentifier'
import { TransactionError } from '@/graphql/model/TransactionError'
import { uuid4ToHash } from '@/utils/typeConverter'
import { KeyPairIdentifierLogic } from '../../data/KeyPairIdentifier.logic'
import { KeyPairCalculation } from '../keyPairCalculation/KeyPairCalculation.context'
import { AbstractTransactionRole } from './AbstractTransaction.role'
import { TransferTransactionInput, transferTransactionSchema, TransferTransaction } from '../../schemas/transaction.schema'
import * as v from 'valibot'
import { uuid4ToTopicSchema } from '../../schemas/typeConverter.schema'
export class TransferTransactionRole extends AbstractTransactionRole {
private linkedUser: UserIdentifier
constructor(private self: TransactionDraft) {
private tx: TransferTransaction
constructor(input: TransferTransactionInput) {
super()
if (!this.self.linkedUser) {
throw new TransactionError(
TransactionErrorType.MISSING_PARAMETER,
'transfer: linked user missing',
)
}
this.linkedUser = this.self.linkedUser
this.tx = v.parse(transferTransactionSchema, input)
}
getSenderCommunityUuid(): string {
return this.self.user.communityUuid
return this.tx.user.communityUuid
}
getRecipientCommunityUuid(): string {
return this.linkedUser.communityUuid
return this.tx.linkedUser.communityUuid
}
public async getGradidoTransactionBuilder(): Promise<GradidoTransactionBuilder> {
if (!this.self.amount) {
throw new TransactionError(TransactionErrorType.MISSING_PARAMETER, 'transfer: amount missing')
}
if (!this.self.memo) {
throw new TransactionError(
TransactionErrorType.MISSING_PARAMETER,
'deferred transfer: memo missing',
)
}
const builder = new GradidoTransactionBuilder()
const senderKeyPair = await KeyPairCalculation(new KeyPairIdentifier(this.self.user))
const recipientKeyPair = await KeyPairCalculation(new KeyPairIdentifier(this.linkedUser))
// sender + signer
const senderKeyPair = await KeyPairCalculation(
new KeyPairIdentifierLogic(this.tx.user)
)
// recipient
const recipientKeyPair = await KeyPairCalculation(
new KeyPairIdentifierLogic(this.tx.linkedUser)
)
builder
.setCreatedAt(new Date(this.self.createdAt))
.setCreatedAt(new Date(this.tx.createdAt))
.addMemo(
new EncryptedMemo(
this.self.memo,
this.tx.memo,
new AuthenticatedEncryption(senderKeyPair),
new AuthenticatedEncryption(recipientKeyPair),
),
)
.setTransactionTransfer(
new TransferAmount(senderKeyPair.getPublicKey(), GradidoUnit.fromString(this.self.amount)),
new TransferAmount(senderKeyPair.getPublicKey(), this.tx.amount),
recipientKeyPair.getPublicKey(),
)
const senderCommunity = this.self.user.communityUuid
const recipientCommunity = this.linkedUser.communityUuid
const senderCommunity = this.tx.user.communityUuid
const recipientCommunity = this.tx.linkedUser.communityUuid
if (senderCommunity !== recipientCommunity) {
// we have a cross group transaction
builder
.setSenderCommunity(uuid4ToHash(senderCommunity).convertToHex())
.setRecipientCommunity(uuid4ToHash(recipientCommunity).convertToHex())
.setSenderCommunity(v.parse(uuid4ToTopicSchema, senderCommunity))
.setRecipientCommunity(v.parse(uuid4ToTopicSchema, recipientCommunity))
}
builder.sign(senderKeyPair)
return builder

View File

@ -1,38 +0,0 @@
import util from 'util'
import { Timestamp, TimestampSeconds } from 'gradido-blockchain-js'
export abstract class AbstractLoggingView {
protected bufferStringFormat: BufferEncoding = 'hex'
// This function gets called automatically when JSON.stringify() is called on this class instance
// eslint-disable-next-line @typescript-eslint/no-explicit-any
public abstract toJSON(): any
public toString(): string {
return JSON.stringify(this.toJSON(), null, 2)
}
// called form console.log or log4js logging functions
[util.inspect.custom](): string {
return this.toString()
}
protected dateToString(date: Date | undefined | null): string | undefined {
if (date) {
return date.toISOString()
}
return undefined
}
protected timestampSecondsToDateString(timestamp: TimestampSeconds): string | undefined {
if (timestamp && timestamp.getSeconds()) {
return timestamp.getDate().toISOString()
}
}
protected timestampToDateString(timestamp: Timestamp): string | undefined {
if (timestamp && (timestamp.getSeconds() || timestamp.getNanos())) {
return timestamp.getDate().toISOString()
}
}
}

View File

@ -1,17 +0,0 @@
import { CommunityUser } from '@/graphql/input/CommunityUser'
import { AbstractLoggingView } from './AbstractLogging.view'
export class CommunityUserLoggingView extends AbstractLoggingView {
public constructor(private self: CommunityUser) {
super()
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
public toJSON(): any {
return {
uuid: this.self.uuid,
accountNr: this.self.accountNr,
}
}
}

View File

@ -1,16 +0,0 @@
import { IdentifierSeed } from '@/graphql/input/IdentifierSeed'
import { AbstractLoggingView } from './AbstractLogging.view'
export class IdentifierSeedLoggingView extends AbstractLoggingView {
public constructor(private self: IdentifierSeed) {
super()
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
public toJSON(): any {
return {
seed: this.self.seed,
}
}
}

View File

@ -1,28 +0,0 @@
import { InputTransactionType } from '@/graphql/enum/InputTransactionType'
import { TransactionDraft } from '@/graphql/input/TransactionDraft'
import { UserIdentifier } from '@/graphql/input/UserIdentifier'
import { getEnumValue } from '@/utils/typeConverter'
import { AbstractLoggingView } from './AbstractLogging.view'
import { UserIdentifierLoggingView } from './UserIdentifierLogging.view'
export class TransactionDraftLoggingView extends AbstractLoggingView {
public constructor(private self: TransactionDraft) {
super()
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
public toJSON(): any {
return {
user: new UserIdentifierLoggingView(this.self.user).toJSON(),
linkedUser:
this.self.linkedUser instanceof UserIdentifier
? new UserIdentifierLoggingView(this.self.linkedUser).toJSON()
: 'seed',
amount: Number(this.self.amount),
type: getEnumValue(InputTransactionType, this.self.type),
createdAt: this.self.createdAt,
targetDate: this.self.targetDate,
}
}
}

View File

@ -1,22 +0,0 @@
import { UserIdentifier } from '@/graphql/input/UserIdentifier'
import { AbstractLoggingView } from './AbstractLogging.view'
import { CommunityUserLoggingView } from './CommunityUserLogging.view'
import { IdentifierSeedLoggingView } from './IdentifierSeedLogging.view'
export class UserIdentifierLoggingView extends AbstractLoggingView {
public constructor(private self: UserIdentifier) {
super()
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
public toJSON(): any {
return {
communityUuid: this.self.communityUuid,
communityUser: this.self.communityUser
? new CommunityUserLoggingView(this.self.communityUser).toJSON()
: undefined,
seed: this.self.seed ? new IdentifierSeedLoggingView(this.self.seed).toJSON() : undefined,
}
}
}

View File

@ -1,13 +0,0 @@
import { readFileSync } from 'fs'
import log4js from 'log4js'
import { CONFIG } from '@/config'
const options = JSON.parse(readFileSync(CONFIG.LOG4JS_CONFIG, 'utf-8'))
log4js.configure(options)
const logger = log4js.getLogger('dlt')
export { logger }

View File

@ -0,0 +1,40 @@
import * as v from 'valibot'
import { uuidv4Schema } from './typeConverter.schema'
// use code from transaction links
export const identifierSeedSchema = v.object({
seed: v.pipe(
v.string('expect string type'),
v.length(24, 'expect seed length 24')
)
})
export type IdentifierSeed = v.InferOutput<typeof identifierSeedSchema>
// identifier for gradido community accounts, inside a community
export const identifierCommunityAccountSchema = v.object({
userUuid: uuidv4Schema,
accountNr: v.nullish(v.number('expect number type'), 0),
})
export type IdentifierCommunityAccount = v.InferOutput<typeof identifierCommunityAccountSchema>
// identifier for gradido account, including the community uuid
export const identifierAccountSchema = v.pipe(
v.object({
communityUuid: uuidv4Schema,
account: v.nullish(identifierCommunityAccountSchema, undefined),
seed: v.nullish(identifierSeedSchema, undefined),
}),
v.custom((value: any) => {
const setFieldsCount = Number(value.seed !== undefined) + Number(value.account !== undefined)
if (setFieldsCount !== 1) {
return false
}
return true
}, 'expect seed or account')
)
export type IdentifierAccountInput = v.InferInput<typeof identifierAccountSchema>
export type IdentifierAccount = v.InferOutput<typeof identifierAccountSchema>

View File

@ -0,0 +1,21 @@
import { communitySchema } from './rpcParameter.schema'
import { uuidv4Schema } from './typeGuard.schema'
import * as v from 'valibot'
// only for IDE, bun don't need this to work
import { describe, expect, it } from 'bun:test'
describe('rpcParameter.schema', () => {
it('community', () => {
expect(v.parse(communitySchema, {
uuid: '4f28e081-5c39-4dde-b6a4-3bde71de8d65',
foreign: false,
createdAt: '2021-01-01',
})).toEqual(
{
uuid: v.parse(uuidv4Schema, '4f28e081-5c39-4dde-b6a4-3bde71de8d65'),
foreign: false,
createdAt: new Date('2021-01-01'),
},
)
})
})

View File

@ -0,0 +1,19 @@
import * as v from 'valibot'
import { uuidv4Schema } from './typeGuard.schema'
import { dateSchema } from './typeConverter.schema'
/**
* Schema Definitions for rpc call parameter, when dlt-connector is called from backend
*/
/**
* Schema for community, for creating new CommunityRoot Transaction on gradido blockchain
*/
export const communitySchema = v.object({
uuid: uuidv4Schema,
foreign: v.boolean('expect boolean type'),
createdAt: dateSchema,
})
export type CommunityInput = v.InferInput<typeof communitySchema>
export type Community = v.InferOutput<typeof communitySchema>

View File

@ -0,0 +1,207 @@
import { describe, it, expect } from 'bun:test'
import { transactionIdentifierSchema, transactionSchema, TransactionInput, memoSchema } from './transaction.schema'
import { InputTransactionType } from '../enum/InputTransactionType'
import { v4 as uuidv4 } from 'uuid'
import * as v from 'valibot'
import { GradidoUnit, DurationSeconds } from 'gradido-blockchain-js'
import { randomBytes } from 'crypto'
const transactionLinkCode = (date: Date): string => {
const time = date.getTime().toString(16)
return (
randomBytes(12)
.toString('hex')
.substring(0, 24 - time.length) + time
)
}
describe('transaction schemas', () => {
describe('transactionIdentifierSchema ', () => {
it('valid, transaction identified by transactionNr and topic', () => {
expect(v.parse(transactionIdentifierSchema, {
transactionNr: 1,
iotaTopic: 'c00b210fc0a189df054eb9dafb584c527e9aeb537a62a35d44667f54529c73f5'
})).toEqual({
transactionNr: 1,
iotaMessageId: undefined,
iotaTopic: 'c00b210fc0a189df054eb9dafb584c527e9aeb537a62a35d44667f54529c73f5'
})
})
it('valid, transaction identified by iotaMessageId and topic', () => {
expect(v.parse(transactionIdentifierSchema, {
iotaMessageId: '1b33a3cf7eb5dde04ed7ae571db1763006811ff6b7bb35b3d1c780de153af9dd',
iotaTopic: 'c00b210fc0a189df054eb9dafb584c527e9aeb537a62a35d44667f54529c73f5'
})).toEqual({
transactionNr: 0,
iotaMessageId: '1b33a3cf7eb5dde04ed7ae571db1763006811ff6b7bb35b3d1c780de153af9dd',
iotaTopic: 'c00b210fc0a189df054eb9dafb584c527e9aeb537a62a35d44667f54529c73f5'
})
})
it('invalid, missing topic', () => {
expect(() => v.parse(transactionIdentifierSchema, {
transactionNr: 1,
iotaMessageId: '1b33a3cf7eb5dde04ed7ae571db1763006811ff6b7bb35b3d1c780de153af9dd',
})).toThrowError(new Error('Invalid key: Expected "iotaTopic" but received undefined'))
})
it('invalid, transactionNr and iotaMessageId set', () => {
expect(() => v.parse(transactionIdentifierSchema, {
transactionNr: 1,
iotaMessageId: '1b33a3cf7eb5dde04ed7ae571db1763006811ff6b7bb35b3d1c780de153af9dd',
iotaTopic: 'c00b210fc0a189df054eb9dafb584c527e9aeb537a62a35d44667f54529c73f5'
})).toThrowError(new Error('expect transactionNr or iotaMessageId not both'))
})
})
describe('transactionSchema', () => {
it('valid, register new user address', () => {
const registerAddress: TransactionInput = {
user: {
communityUuid: uuidv4(),
account: {
userUuid: uuidv4(),
}
},
type: InputTransactionType.REGISTER_ADDRESS,
createdAt: '2022-01-01T00:00:00.000Z',
}
expect(v.parse(transactionSchema, registerAddress)).toEqual({
user: {
communityUuid: registerAddress.user.communityUuid,
account: {
userUuid: registerAddress.user.account!.userUuid,
accountNr: 1,
}
},
type: registerAddress.type,
createdAt: new Date(registerAddress.createdAt),
})
})
it('valid, gradido transfer', () => {
const communityUuid = uuidv4()
const gradidoTransfer: TransactionInput = {
user: {
communityUuid,
account: {
userUuid: uuidv4(),
}
},
linkedUser: {
communityUuid,
account: {
userUuid: uuidv4(),
}
},
amount: '100',
memo: 'TestMemo',
type: InputTransactionType.GRADIDO_TRANSFER,
createdAt: '2022-01-01T00:00:00.000Z',
}
expect(v.parse(transactionSchema, gradidoTransfer)).toEqual({
user: {
communityUuid,
account: {
userUuid: gradidoTransfer.user.account!.userUuid,
accountNr: 1,
}
},
linkedUser: {
communityUuid,
account: {
userUuid: gradidoTransfer.linkedUser!.account!.userUuid,
accountNr: 1,
}
},
amount: GradidoUnit.fromString(gradidoTransfer.amount!),
memo: gradidoTransfer.memo,
type: gradidoTransfer.type,
createdAt: new Date(gradidoTransfer.createdAt),
})
})
it('valid, gradido creation', () => {
const communityUuid = uuidv4()
const gradidoCreation: TransactionInput = {
user: {
communityUuid,
account: {
userUuid: uuidv4(),
}
},
linkedUser: {
communityUuid,
account: {
userUuid: uuidv4(),
}
},
amount: '1000',
memo: 'For your help',
type: InputTransactionType.GRADIDO_CREATION,
createdAt: '2022-01-01T00:00:00.000Z',
targetDate: '2021-11-01T10:00'
}
expect(v.parse(transactionSchema, gradidoCreation)).toEqual({
user: {
communityUuid,
account: {
userUuid: gradidoCreation.user.account!.userUuid,
accountNr: 1,
}
},
linkedUser: {
communityUuid,
account: {
userUuid: gradidoCreation.linkedUser!.account!.userUuid,
accountNr: 1,
}
},
amount: GradidoUnit.fromString(gradidoCreation.amount!),
memo: gradidoCreation.memo,
type: gradidoCreation.type,
createdAt: new Date(gradidoCreation.createdAt),
targetDate: new Date(gradidoCreation.targetDate!),
})
})
it('valid, gradido transaction link / deferred transfer', () => {
const gradidoTransactionLink: TransactionInput = {
user: {
communityUuid: uuidv4(),
account: {
userUuid: uuidv4(),
}
},
linkedUser: {
communityUuid: uuidv4(),
seed: {
seed: transactionLinkCode(new Date()),
}
},
amount: '100',
memo: 'use link wisely',
type: InputTransactionType.GRADIDO_DEFERRED_TRANSFER,
createdAt: '2022-01-01T00:00:00.000Z',
timeoutDuration: 60*60*24*30,
}
expect(v.parse(transactionSchema, gradidoTransactionLink)).toEqual({
user: {
communityUuid: gradidoTransactionLink.user.communityUuid,
account: {
userUuid: gradidoTransactionLink.user.account!.userUuid,
accountNr: 1,
}
},
linkedUser: {
communityUuid: gradidoTransactionLink.linkedUser!.communityUuid,
seed: {
seed: gradidoTransactionLink.linkedUser!.seed!.seed,
}
},
amount: GradidoUnit.fromString(gradidoTransactionLink.amount!),
memo: gradidoTransactionLink.memo,
type: gradidoTransactionLink.type,
createdAt: new Date(gradidoTransactionLink.createdAt),
timeoutDuration: new DurationSeconds(gradidoTransactionLink.timeoutDuration!),
})
})
})
})

View File

@ -0,0 +1,87 @@
import * as v from 'valibot'
import { dateFromStringSchema } from './typeConverter.schema'
import { identifierAccountSchema } from './account.schema'
import { InputTransactionType } from '../enum/InputTransactionType'
import { accountTypeToAddressTypeSchema } from './typeConverter.schema'
// allow TransactionIdentifier to only contain either transactionNr or iotaMessageId
export const transactionIdentifierSchema = v.pipe(
v.object({
transactionNr: v.nullish(
v.pipe(v.number('expect number type'), v.minValue(0, 'expect number >= 0')),
0
),
iotaMessageId: v.nullish(iotaMessageIdSchema, undefined),
communityId: uuid4ToTopicSchema,
}),
v.custom((value: any) => {
const setFieldsCount = Number(value.transactionNr !== 0) + Number(value.iotaMessageId !== undefined)
if (setFieldsCount !== 1) {
return false
}
return true
}, 'expect transactionNr or iotaMessageId not both')
)
export type TransactionIdentifierInput = v.InferInput<typeof transactionIdentifierSchema>
export type TransactionIdentifier = v.InferOutput<typeof transactionIdentifierSchema>
export const transactionSchema = v.object({
user: identifierAccountSchema,
linkedUser: v.nullish(identifierAccountSchema, undefined),
amount: v.nullish(amountToGradidoUnitSchema, undefined),
memo: v.nullish(memoSchema, undefined),
type: v.enum(InputTransactionType),
createdAt: dateFromStringSchema,
targetDate: v.nullish(dateFromStringSchema, undefined),
timeoutDuration: v.nullish(timeoutDurationSchema, undefined),
accountType: v.nullish(accountTypeToAddressTypeSchema, undefined),
})
export type TransactionInput = v.InferInput<typeof transactionSchema>
export type Transaction = v.InferOutput<typeof transactionSchema>
export const creationTransactionSchema = v.object({
user: identifierAccountSchema,
linkedUser: identifierAccountSchema,
amount: amountToGradidoUnitSchema,
memo: memoSchema,
createdAt: dateFromStringSchema,
targetDate: dateFromStringSchema,
})
export type CreationTransactionInput = v.InferInput<typeof creationTransactionSchema>
export type CreationTransaction = v.InferOutput<typeof creationTransactionSchema>
export const transferTransactionSchema = v.object({
user: identifierAccountSchema,
linkedUser: identifierAccountSchema,
amount: amountToGradidoUnitSchema,
memo: memoSchema,
createdAt: dateFromStringSchema,
})
export type TransferTransactionInput = v.InferInput<typeof transferTransactionSchema>
export type TransferTransaction = v.InferOutput<typeof transferTransactionSchema>
// linked user is later needed for move account transaction
export const registerAddressTransactionSchema = v.object({
user: identifierAccountSchema,
createdAt: dateFromStringSchema,
accountType: accountTypeToAddressTypeSchema,
})
export type RegisterAddressTransactionInput = v.InferInput<typeof registerAddressTransactionSchema>
export type RegisterAddressTransaction = v.InferOutput<typeof registerAddressTransactionSchema>
export const deferredTransferTransactionSchema = v.object({
user: identifierAccountSchema,
linkedUser: identifierAccountSchema,
amount: amountToGradidoUnitSchema,
memo: memoSchema,
createdAt: dateFromStringSchema,
timeoutDuration: timeoutDurationSchema,
})
export type DeferredTransferTransactionInput = v.InferInput<typeof deferredTransferTransactionSchema>
export type DeferredTransferTransaction = v.InferOutput<typeof deferredTransferTransactionSchema>

View File

@ -0,0 +1,51 @@
import { accountTypeSchema, addressTypeSchema, confirmedTransactionFromBase64Schema } from './typeConverter.schema'
import * as v from 'valibot'
// only for IDE, bun don't need this to work
import { describe, expect, it } from 'bun:test'
import { dateSchema } from './typeConverter.schema'
import { AddressType_COMMUNITY_AUF } from 'gradido-blockchain-js'
import { AccountType } from '../enum/AccountType'
describe('basic.schema', () => {
describe('date', () => {
it('from string', () => {
const date = v.parse(dateSchema, '2021-01-01:10:10')
expect(date.toISOString()).toBe('2021-01-01T10:10:00.000Z')
})
it('from Date', () => {
const date = v.parse(dateSchema, new Date('2021-01-01'))
expect(date.toISOString()).toBe('2021-01-01T00:00:00.000Z')
})
it('invalid date', () => {
expect(() => v.parse(dateSchema, 'invalid date')).toThrow(new Error('invalid date'))
})
})
describe('AddressType and AccountType', () => {
it('AddressType from AddressType', () => {
const addressType = v.parse(addressTypeSchema, AddressType_COMMUNITY_AUF)
expect(addressType).toBe(AddressType_COMMUNITY_AUF)
})
it('AddressType from AccountType', () => {
const accountType = v.parse(addressTypeSchema, AccountType.COMMUNITY_AUF)
expect(accountType).toBe(AddressType_COMMUNITY_AUF)
})
it('AccountType from AccountType', () => {
const accountType = v.parse(accountTypeSchema, AccountType.COMMUNITY_AUF)
expect(accountType).toBe(AccountType.COMMUNITY_AUF)
})
it('AccountType from AddressType', () => {
const accountType = v.parse(accountTypeSchema, AddressType_COMMUNITY_AUF)
expect(accountType).toBe(AccountType.COMMUNITY_AUF)
})
})
it('confirmedTransactionFromBase64Schema', () => {
const confirmedTransaction = v.parse(confirmedTransactionFromBase64Schema, 'CAcSAgoAGgYIwvK5/wUiAzMuNCogAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAyIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA')
expect(confirmedTransaction.getId()).toBe(7)
expect(confirmedTransaction.getConfirmedAt().getSeconds()).toBe(1609464130)
expect(confirmedTransaction.getVersionNumber()).toBe('3.4')
})
})

View File

@ -0,0 +1,125 @@
import {
AddressType as AddressType,
AddressType_COMMUNITY_AUF,
AddressType_COMMUNITY_GMW,
AddressType_COMMUNITY_HUMAN,
AddressType_COMMUNITY_PROJECT,
AddressType_CRYPTO_ACCOUNT,
AddressType_NONE,
AddressType_SUBACCOUNT,
ConfirmedTransaction,
DeserializeType_CONFIRMED_TRANSACTION,
InteractionDeserialize,
MemoryBlock,
} from 'gradido-blockchain-js'
import { AccountType } from '../enum/AccountType'
// import { AddressType as AddressTypeWrapper } from '../enum/AddressType'
import * as v from 'valibot'
/**
* dateSchema for creating a date from string or Date object
*/
export const dateSchema = v.pipe(
v.union([
v.string('expect valid date string'),
v.instance(Date, 'expect Date object')
]),
v.transform<string | Date, Date>((input) => {
let date: Date
if (input instanceof Date) {
date = input
} else {
date = new Date(input)
}
if (isNaN(date.getTime())) {
throw new Error('invalid date')
}
return date
})
)
/**
* AddressType is defined in gradido-blockchain C++ Code
* AccountType is the enum defined in TypeScript but with the same options
* addressTypeSchema and accountTypeSchema are for easy handling and conversion between both
*/
const accountToAddressMap: Record<AccountType, AddressType> = {
[AccountType.COMMUNITY_AUF]: AddressType_COMMUNITY_AUF,
[AccountType.COMMUNITY_GMW]: AddressType_COMMUNITY_GMW,
[AccountType.COMMUNITY_HUMAN]: AddressType_COMMUNITY_HUMAN,
[AccountType.COMMUNITY_PROJECT]: AddressType_COMMUNITY_PROJECT,
[AccountType.CRYPTO_ACCOUNT]: AddressType_CRYPTO_ACCOUNT,
[AccountType.SUBACCOUNT]: AddressType_SUBACCOUNT,
[AccountType.NONE]: AddressType_NONE,
}
const addressToAccountMap: Record<AddressType, AccountType> = Object.entries(accountToAddressMap).reduce((acc, [accKey, addrVal]) => {
acc[addrVal] = String(accKey) as AccountType
return acc;
}, {} as Record<AddressType, AccountType>)
function isAddressType(val: unknown): val is AddressType {
return typeof val === 'number' && Object.keys(addressToAccountMap).includes(val.toString())
}
function isAccountType(val: unknown): val is AccountType {
return Object.values(AccountType).includes(val as AccountType);
}
/**
* Schema for address type, can also convert from account type (if used with v.parse)
*/
export const addressTypeSchema = v.pipe(
v.union([
v.enum(AccountType, 'expect account type'),
v.custom<AddressType>((val): val is AddressType => isAddressType(val), 'expect AddressType'),
]),
v.transform<AccountType | AddressType, AddressType>((value) => {
if (isAddressType(value)) {
return value;
}
return accountToAddressMap[value as AccountType] ?? AddressType_NONE
}),
)
/**
* Schema for account type, can also convert from address type (if used with v.parse)
*/
export const accountTypeSchema = v.pipe(
v.union([
v.custom<AddressType>(isAddressType, 'expect AddressType'),
v.enum(AccountType, 'expect AccountType'),
]),
v.transform<AddressType | AccountType, AccountType>((value) => {
if (isAccountType(value)) {
return value;
}
return addressToAccountMap[value as AddressType] ?? AccountType.NONE;
}),
)
const confirmedTransactionFromBase64 = (base64: string): ConfirmedTransaction => {
const deserializer = new InteractionDeserialize(
MemoryBlock.fromBase64(base64),
DeserializeType_CONFIRMED_TRANSACTION,
)
deserializer.run()
const confirmedTransaction = deserializer.getConfirmedTransaction()
if (!confirmedTransaction) {
throw new Error("invalid data, couldn't deserialize")
}
return confirmedTransaction
}
export const confirmedTransactionFromBase64Schema = v.pipe(
v.pipe(
v.string('expect confirmed Transaction base64 as string type'),
v.base64('expect to be valid base64')
),
v.transform<string, ConfirmedTransaction>(
(base64: string) => confirmedTransactionFromBase64(base64),
),
)

View File

@ -0,0 +1,67 @@
import { describe, it, expect } from 'bun:test'
import { uuidv4Schema, topicIndexSchema, uuid4HashSchema, memoSchema } from './typeGuard.schema'
import * as v from 'valibot'
import { v4 as uuidv4 } from 'uuid'
import { MemoryBlock } from 'gradido-blockchain-js'
describe('typeGuard.schema', () => {
describe('Uuidv4', () => {
const uuidv4String = uuidv4()
const uuidv4Hash = MemoryBlock.fromHex(uuidv4String.replace(/-/g, '')).calculateHash()
it('from string to uuidv4', () => {
const uuidv4Value = v.parse(uuidv4Schema, uuidv4String)
expect(uuidv4Value.toString()).toBe(uuidv4String)
})
it('from uuidv4 to hash', () => {
const uuidv4Value = v.parse(uuidv4Schema, uuidv4String)
const uuidv4HashParsed = v.parse(uuid4HashSchema, uuidv4Value)
expect(uuidv4HashParsed.copyAsString()).toBe(uuidv4Hash.copyAsString())
})
it('from uuidv4 string to hash', () => {
const uuidv4HashParsed = v.parse(uuid4HashSchema, uuidv4String)
expect(uuidv4HashParsed.copyAsString()).toBe(uuidv4Hash.copyAsString())
})
it('from uuidv4 hash to topicIndex (hash in hex format', () => {
const uuidv4HashParsed = v.parse(uuid4HashSchema, uuidv4String)
const topicIndex = v.parse(topicIndexSchema, uuidv4HashParsed)
expect(topicIndex.toString()).toBe(uuidv4Hash.convertToHex())
})
it('from uuidv4 to topicIndex (hash in hex format)', () => {
const uuidv4Value = v.parse(uuidv4Schema, uuidv4String)
const topicIndex = v.parse(topicIndexSchema, uuidv4Value)
expect(topicIndex.toString()).toBe(uuidv4Hash.convertToHex())
})
it('from uuidv4 string to topicIndex (hash in hex format)', () => {
const topicIndex = v.parse(topicIndexSchema, uuidv4String)
expect(topicIndex.toString()).toBe(uuidv4Hash.convertToHex())
})
})
describe('Basic Type Schemas for transactions', () => {
describe('Memo', () => {
it('min length', () => {
const memoValue = 'memo1'
const memoValueParsed = v.parse(memoSchema, memoValue)
expect(memoValueParsed.toString()).toBe(memoValue)
})
it('max length', () => {
const memoValue = 's'.repeat(255)
const memoValueParsed = v.parse(memoSchema, memoValue)
expect(memoValueParsed.toString()).toBe(memoValue)
})
it('to short', () => {
const memoValue = 'memo'
expect(() => v.parse(memoSchema, memoValue)).toThrow(new Error('expect string length >= 5'))
})
it('to long', () => {
const memoValue = 's'.repeat(256)
expect(() => v.parse(memoSchema, memoValue)).toThrow(new Error('expect string length <= 255'))
})
})
})
})

View File

@ -0,0 +1,146 @@
/**
* # TypeGuards
* Expand TypeScript Default Types with custom type which a based on a default type (or class)
* Use valibot, so we can describe the type and validate it easy at runtime
* After transpiling TypeScript unique symbol are gone
* Infos at opaque type in typescript: https://evertpot.com/opaque-ts-types/
*
* declare const validAmount: unique symbol
* export type Amount = number & { [validAmount]: true };
* Can be compared with using `typedef int Amount;` in C/C++
* Example:
* To create a instance of Amount:
* `const amount: Amount = v.parse(amountSchema, 1.21)`
* must be called and ensure the value is valid
* If it isn't valid, v.parse will throw an error
*/
import { validate, version } from 'uuid'
import * as v from 'valibot'
import { MemoryBlock, DurationSeconds, GradidoUnit } from 'gradido-blockchain-js'
/**
* type guard for uuid v4
* create with `v.parse(uuidv4Schema, 'uuid')`
* uuidv4 is used for communityUuid and userUuid
*/
declare const validUuidv4: unique symbol
export type Uuidv4 = string & { [validUuidv4]: true };
export const uuidv4Schema = v.custom<Uuidv4>((value) =>
(typeof value === 'string' && validate(value) && version(value) === 4),
'uuid v4 expected'
)
/**
* type guard for uuid v4 hash
* const uuidv4Value: Uuidv4 = v.parse(uuidv4Schema, 'uuid')
* create with `v.parse(uuidv4HashSchema, uuidv4Value)`
* uuidv4Hash is uuidv4 value hashed with BLAKE2b as Binary Type MemoryBlock from gradido-blockchain similar to Node.js Buffer Type,
* used for iota topic
*/
declare const validUuidv4Hash: unique symbol
export type Uuidv4Hash = MemoryBlock & { [validUuidv4Hash]: true };
export const uuid4HashSchema = v.pipe(
uuidv4Schema,
v.transform<Uuidv4, Uuidv4Hash>(
(input: Uuidv4) => MemoryBlock.fromHex(input.replace(/-/g, '')).calculateHash() as Uuidv4Hash,
)
)
/**
* type guard for topic index
* const uuidv4Value: Uuidv4 = v.parse(uuidv4Schema, 'uuid')
* const uuidv4Hash: Uuidv4Hash = v.parse(uuid4HashSchema, uuidv4Value)
* create with `v.parse(topicIndexSchema, uuidv4Hash)`
* topicIndex is uuidv4Hash value converted to hex string used for iota topic
* The beauty of valibot allow also parse a uuidv4 string directly to topicIndex
* const topic: TopicIndex = v.parse(topicIndexSchema, 'uuid')
*/
declare const validTopicIndex: unique symbol
export type TopicIndex = string & { [validTopicIndex]: true };
export const topicIndexSchema = v.pipe(
v.union([uuidv4Schema, v.custom((val): val is Uuidv4Hash => val instanceof MemoryBlock)]),
v.transform<any, TopicIndex>((input) => {
const hash = typeof input === 'string'
? MemoryBlock.fromHex(input.replace(/-/g, '')).calculateHash()
: input;
return hash.convertToHex() as TopicIndex;
})
)
/**
* type guard for memo
* create with `v.parse(memoSchema, 'memo')`
* memo string inside bounds [5, 255]
*/
export const MEMO_MIN_CHARS = 5
export const MEMO_MAX_CHARS = 255
declare const validMemo: unique symbol
export type Memo = string & { [validMemo]: true };
export const memoSchema = v.pipe(
v.string('expect string type'),
v.maxLength(MEMO_MAX_CHARS, `expect string length <= ${MEMO_MAX_CHARS}`),
v.minLength(MEMO_MIN_CHARS, `expect string length >= ${MEMO_MIN_CHARS}`),
v.transform<string, Memo>(
(input: string) => input as Memo,
),
)
/**
* type guard for timeout duration
* create with `v.parse(timeoutDurationSchema, 123)`
* timeout duration is a number in seconds inside bounds
* [1 hour, 3 months]
* for Transaction Links / Deferred Transactions
* seconds starting from createdAt Date in which the transaction link can be redeemed
*/
const LINKED_TRANSACTION_TIMEOUT_DURATION_MIN = 60*60
const LINKED_TRANSACTION_TIMEOUT_DURATION_MAX = 60*60*24*31*3
declare const validTimeoutDuration: unique symbol
export type TimeoutDuration = DurationSeconds & { [validTimeoutDuration]: true };
export const timeoutDurationSchema = v.pipe(
v.number('expect number type'),
v.minValue(LINKED_TRANSACTION_TIMEOUT_DURATION_MIN, 'expect number >= 1 hour'),
v.maxValue(LINKED_TRANSACTION_TIMEOUT_DURATION_MAX, 'expect number <= 3 months'),
v.transform<number, TimeoutDuration>(
(input: number) => new DurationSeconds(input) as TimeoutDuration,
),
)
/**
* type guard for amount
* create with `v.parse(amountSchema, '123')`
* amount is a string representing a positive decimal number, compatible with decimal.js
*/
declare const validAmount: unique symbol
export type Amount = string & { [validAmount]: true };
export const amountSchema = v.pipe(
v.string('expect string type'),
v.regex(/^[0-9]+(\.[0-9]+)?$/, 'expect positive number'),
v.transform<string, Amount>(
(input: string) => input as Amount,
),
)
/**
* type guard for gradido amount
* create with `v.parse(gradidoAmountSchema, '123')`
* gradido amount is a string representing a positive decimal number, compatible with decimal.js
*/
declare const validGradidoAmount: unique symbol
export type GradidoAmount = GradidoUnit & { [validGradidoAmount]: true };
export const gradidoAmountSchema = v.pipe(
amountSchema,
v.transform<Amount, GradidoAmount>(
(input: Amount) => GradidoUnit.fromString(input) as GradidoAmount,
),
)

View File

@ -1,27 +0,0 @@
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
import { logger } from '@test/testSetup'
import { LogError } from './LogError'
describe('LogError', () => {
it('logs an Error when created', () => {
/* eslint-disable-next-line no-new */
new LogError('new LogError')
expect(logger.error).toBeCalledWith('new LogError')
})
it('logs an Error including additional data when created', () => {
/* eslint-disable-next-line no-new */
new LogError('new LogError', { some: 'data' })
expect(logger.error).toBeCalledWith('new LogError', { some: 'data' })
})
it('does not contain additional data in Error object when thrown', () => {
try {
throw new LogError('new LogError', { someWeirdValue123: 'arbitraryData456' })
/* eslint-disable-next-line @typescript-eslint/no-explicit-any */
} catch (e: any) {
expect(e.stack).not.toMatch(/(someWeirdValue123|arbitraryData456)/i)
}
})
})

View File

@ -1,10 +0,0 @@
/* eslint-disable @typescript-eslint/no-unsafe-argument */
import { logger } from '@/logging/logger'
export class LogError extends Error {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
constructor(msg: string, ...details: any[]) {
super(msg)
logger.error(msg, ...details)
}
}

View File

@ -1,8 +0,0 @@
import corsLib from 'cors'
const corsOptions = {
origin: '*',
exposedHeaders: ['token'],
}
export const cors = corsLib(corsOptions)

View File

@ -1,79 +0,0 @@
import 'reflect-metadata'
import { ApolloServer } from '@apollo/server'
import { expressMiddleware } from '@apollo/server/express4'
import bodyParser from 'body-parser'
import cors from 'cors'
import express, { Express } from 'express'
// graphql
import { slowDown } from 'express-slow-down'
import helmet from 'helmet'
import { Logger } from 'log4js'
import { schema } from '@/graphql/schema'
import { logger as dltLogger } from '@/logging/logger'
type ServerDef = { apollo: ApolloServer; app: Express }
interface MyContext {
token?: string
}
const createServer = async (
// eslint-disable-next-line @typescript-eslint/no-explicit-any
// context: any = serverContext,
logger: Logger = dltLogger,
// localization: i18n.I18n = i18n,
): Promise<ServerDef> => {
logger.addContext('user', 'unknown')
logger.debug('createServer...')
// Express Server
const app = express()
// Apollo Server
const apollo = new ApolloServer<MyContext>({
schema: await schema(),
introspection: true,
// context,
// plugins
logger,
})
// Helmet helps secure Express apps by setting HTTP response headers.
app.use(helmet())
// rate limiter/ slow down to many requests
const limiter = slowDown({
windowMs: 1000, // 1 second
delayAfter: 10, // Allow 10 requests per 1 second.
delayMs: (hits) => hits * 50, // Add 100 ms of delay to every request after the 10th one.
/**
* So:
*
* - requests 1-10 are not delayed.
* - request 11 is delayed by 550ms
* - request 12 is delayed by 600ms
* - request 13 is delayed by 650ms
*
* and so on. After 1 seconds, the delay is reset to 0.
*/
})
app.use(limiter)
// because of nginx proxy, needed for limiter
app.set('trust proxy', 1)
await apollo.start()
app.use(
'/',
cors<cors.CorsRequest>(),
bodyParser.json(),
expressMiddleware(apollo, {
context: async ({ req }) => ({ token: req.headers.token }),
}),
)
logger.debug('createServer...successful')
return { apollo, app }
}
export default createServer

View File

@ -1,6 +1,6 @@
import 'reflect-metadata'
import { hardenDerivationIndex, HARDENED_KEY_BITMASK } from './derivationHelper'
// only for IDE, bun don't need this to work
import { describe, expect, it } from 'bun:test'
describe('utils', () => {
it('test bitmask for hardened keys', () => {

View File

@ -0,0 +1,51 @@
import net from 'node:net'
import { getLogger } from 'log4js'
import { LOG4JS_BASE_CATEGORY } from '../config/const'
import { CONFIG } from '../config'
export async function isPortOpen(
url: string,
timeoutMs: number = CONFIG.CONNECT_TIMEOUT_MS,
): Promise<boolean> {
return new Promise((resolve) => {
const socket = new net.Socket()
const { hostname, port } = new URL(url)
// auto-destroy socket after timeout
const timer = setTimeout(() => {
socket.destroy()
resolve(false)
}, timeoutMs)
socket.connect(Number(port), hostname, () => {
// connection successful
clearTimeout(timer)
socket.end()
resolve(true)
})
socket.on('error', (err: any) => {
clearTimeout(timer)
socket.destroy()
const logger = getLogger(`${LOG4JS_BASE_CATEGORY}.network.isPortOpen`)
logger.addContext('url', url)
logger.error(`${err.message}: ${err.code}`)
resolve(false)
})
})
}
const wait = (ms: number) => new Promise(resolve => setTimeout(resolve, ms))
export async function isPortOpenRetry(
url: string,
timeoutMs: number = CONFIG.CONNECT_TIMEOUT_MS,
delayMs: number = CONFIG.CONNECT_RETRY_DELAY_MS,
retries: number = CONFIG.CONNECT_RETRY_COUNT,
): Promise<boolean> {
for (let i = 0; i < retries; i++) {
if (await isPortOpen(url, timeoutMs)) return true
await wait(delayMs)
}
throw new Error(`${url} port is not open after ${retries} retries`)
}

View File

@ -1,23 +0,0 @@
import 'reflect-metadata'
import { base64ToBuffer, uuid4ToHash, uuid4ToBuffer } from './typeConverter'
describe('utils/typeConverter', () => {
it('uuid4ToBuffer', () => {
expect(uuid4ToBuffer('4f28e081-5c39-4dde-b6a4-3bde71de8d65')).toStrictEqual(
Buffer.from('4f28e0815c394ddeb6a43bde71de8d65', 'hex'),
)
})
it('iotaTopicFromCommunityUUID', () => {
expect(uuid4ToHash('4f28e081-5c39-4dde-b6a4-3bde71de8d65')).toBe(
'3138b3590311fdf0a823e173caa9487b7d275c23fab07106b4b1364cb038affd',
)
})
it('base64ToBuffer', () => {
expect(base64ToBuffer('MTizWQMR/fCoI+FzyqlIe30nXCP6sHEGtLE2TLA4r/0=')).toStrictEqual(
Buffer.from('3138b3590311fdf0a823e173caa9487b7d275c23fab07106b4b1364cb038affd', 'hex'),
)
})
})

View File

@ -1,120 +0,0 @@
/* eslint-disable camelcase */
import {
AddressType,
AddressType_COMMUNITY_AUF,
AddressType_COMMUNITY_GMW,
AddressType_COMMUNITY_HUMAN,
AddressType_COMMUNITY_PROJECT,
AddressType_CRYPTO_ACCOUNT,
AddressType_NONE,
AddressType_SUBACCOUNT,
ConfirmedTransaction,
DeserializeType_CONFIRMED_TRANSACTION,
InteractionDeserialize,
MemoryBlock,
} from 'gradido-blockchain-js'
import { AccountType } from '@/graphql/enum/AccountType'
import { LogError } from '@/server/LogError'
export const uuid4ToBuffer = (uuid: string): Buffer => {
// Remove dashes from the UUIDv4 string
const cleanedUUID = uuid.replace(/-/g, '')
// Create a Buffer object from the hexadecimal values
const buffer = Buffer.from(cleanedUUID, 'hex')
return buffer
}
export const uuid4ToMemoryBlock = (uuid: string): MemoryBlock => {
// Remove dashes from the UUIDv4 string
return MemoryBlock.fromHex(uuid.replace(/-/g, ''))
}
export const uuid4sToMemoryBlock = (uuid: string[]): MemoryBlock => {
let resultHexString = ''
for (let i = 0; i < uuid.length; i++) {
resultHexString += uuid[i].replace(/-/g, '')
}
return MemoryBlock.fromHex(resultHexString)
}
export const uuid4ToHash = (communityUUID: string): MemoryBlock => {
return uuid4ToMemoryBlock(communityUUID).calculateHash()
}
export const base64ToBuffer = (base64: string): Buffer => {
return Buffer.from(base64, 'base64')
}
export const communityUuidToTopic = (communityUUID: string): string => {
return uuid4ToHash(communityUUID).convertToHex()
}
export function getEnumValue<T extends Record<string, unknown>>(
enumType: T,
value: number | string,
): T[keyof T] | undefined {
if (typeof value === 'number' && typeof enumType === 'object') {
return enumType[value as keyof T] as T[keyof T]
} else if (typeof value === 'string') {
for (const key in enumType) {
if (enumType[key as keyof T] === value) {
return enumType[key as keyof T] as T[keyof T]
}
}
}
return undefined
}
export const accountTypeToAddressType = (type: AccountType): AddressType => {
switch (type) {
case AccountType.COMMUNITY_AUF:
return AddressType_COMMUNITY_AUF
case AccountType.COMMUNITY_GMW:
return AddressType_COMMUNITY_GMW
case AccountType.COMMUNITY_HUMAN:
return AddressType_COMMUNITY_HUMAN
case AccountType.COMMUNITY_PROJECT:
return AddressType_COMMUNITY_PROJECT
case AccountType.CRYPTO_ACCOUNT:
return AddressType_CRYPTO_ACCOUNT
case AccountType.SUBACCOUNT:
return AddressType_SUBACCOUNT
default:
return AddressType_NONE
}
}
export const addressTypeToAccountType = (type: AddressType): AccountType => {
switch (type) {
case AddressType_COMMUNITY_AUF:
return AccountType.COMMUNITY_AUF
case AddressType_COMMUNITY_GMW:
return AccountType.COMMUNITY_GMW
case AddressType_COMMUNITY_HUMAN:
return AccountType.COMMUNITY_HUMAN
case AddressType_COMMUNITY_PROJECT:
return AccountType.COMMUNITY_PROJECT
case AddressType_CRYPTO_ACCOUNT:
return AccountType.CRYPTO_ACCOUNT
case AddressType_SUBACCOUNT:
return AccountType.SUBACCOUNT
default:
return AccountType.NONE
}
}
export const confirmedTransactionFromBase64 = (base64: string): ConfirmedTransaction => {
const deserializer = new InteractionDeserialize(
MemoryBlock.fromBase64(base64),
DeserializeType_CONFIRMED_TRANSACTION,
)
deserializer.run()
const confirmedTransaction = deserializer.getConfirmedTransaction()
if (!confirmedTransaction) {
throw new LogError("invalid data, couldn't deserialize")
}
return confirmedTransaction
}

View File

@ -1,20 +0,0 @@
import { ApolloServer } from '@apollo/server'
import { addMocksToSchema } from '@graphql-tools/mock'
import { schema } from '@/graphql/schema'
let apolloTestServer: ApolloServer
export async function createApolloTestServer() {
if (apolloTestServer === undefined) {
apolloTestServer = new ApolloServer({
// addMocksToSchema accepts a schema instance and provides
// mocked data for each field in the schema
schema: addMocksToSchema({
schema: await schema(),
preserveResolvers: true,
}),
})
}
return apolloTestServer
}

View File

@ -1,22 +0,0 @@
import { logger } from '@/logging/logger'
jest.setTimeout(1000000)
jest.mock('@/logging/logger', () => {
const originalModule = jest.requireActual('@/logging/logger')
return {
__esModule: true,
...originalModule,
logger: {
addContext: jest.fn(),
trace: jest.fn(),
debug: jest.fn(),
warn: jest.fn(),
info: jest.fn(),
error: jest.fn(),
fatal: jest.fn(),
},
}
})
export { logger }

View File

@ -1,88 +1,104 @@
{
"include": ["src/**/*", "types/**/*"],
"compilerOptions": {
/* Visit https://aka.ms/tsconfig.json to read more about this file */
/* Visit https://aka.ms/tsconfig 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": "./", /* 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'). */
/* Projects */
// "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */
// "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */
// "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */
// "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */
// "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */
// "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */
/* 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. */
/* Language and Environment */
"target": "ES2021", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
// "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */
// "jsx": "preserve", /* Specify what JSX code is generated. */
// "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */
// "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */
// "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */
// "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */
// "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */
// "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */
// "noLib": true, /* Disable including any library files, including the default lib.d.ts. */
// "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */
// "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */
/* 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'. */
"@/*": ["src/*"],
"@arg/*": ["src/graphql/arg/*"],
"@enum/*": ["src/graphql/enum/*"],
"@input/*": ["src/graphql/input/*"],
"@model/*": ["src/graphql/model/*"],
"@resolver/*": ["src/graphql/resolver/*"],
"@test/*": ["test/*"],
"@proto/*" : ["src/proto/*"],
"@validator/*" : ["src/graphql/validator/*"],
},
// "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */
"typeRoots": ["node_modules/@types", "@types"], /* List of folders to include type definitions from. */
// "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. */
/* Modules */
"module": "ES2022", /* Specify what module code is generated. */
// "rootDir": "./", /* Specify the root folder within your source files. */
"moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */
// "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
// "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */
// "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */
// "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */
"types": ["bun-types"], /* Specify type package names to be included without being referenced in a source file. */
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
// "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */
// "resolveJsonModule": true, /* Enable importing .json files. */
// "noResolve": true, /* Disallow 'import's, 'require's or '<reference>'s from expanding the number of files TypeScript should add to a project. */
/* 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. */
/* JavaScript Support */
// "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */
// "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */
// "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */
/* Experimental Options */
"experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */
"emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */
/* Emit */
// "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */
// "declarationMap": true, /* Create sourcemaps for d.ts files. */
// "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */
// "sourceMap": true, /* Create source map files for emitted JavaScript files. */
// "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */
// "outDir": "./", /* Specify an output folder for all emitted files. */
// "removeComments": true, /* Disable emitting comments. */
// "noEmit": true, /* Disable emitting files from a compilation. */
// "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */
// "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */
// "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */
// "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */
// "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
// "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */
// "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */
// "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */
// "newLine": "crlf", /* Set the newline character for emitting files. */
// "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */
// "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */
// "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */
// "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */
// "declarationDir": "./", /* Specify the output directory for generated declaration files. */
// "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */
/* Advanced Options */
"skipLibCheck": true, /* Skip type checking of declaration files. */
"forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */
},
"references": [
{
// add 'prepend' if you want to include the referenced project in your output file
// "prepend": true
}
]
/* Interop Constraints */
// "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */
// "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */
"esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */
// "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */
"forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */
/* Type Checking */
"strict": true, /* Enable all strict type-checking options. */
// "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */
// "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */
// "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */
// "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */
// "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */
// "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */
// "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */
// "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */
// "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */
// "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */
// "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */
// "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */
// "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */
// "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */
// "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */
// "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */
// "allowUnusedLabels": true, /* Disable error reporting for unused labels. */
// "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */
/* Completeness */
// "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */
"skipLibCheck": true /* Skip type checking all .d.ts files. */
}
}

2
dlt-connector/types/global.d.ts vendored Normal file
View File

@ -0,0 +1,2 @@
// types/global.d.ts
/// <reference types="bun-types" /

File diff suppressed because it is too large Load Diff